Skip to content

Commit

Permalink
Merge pull request #49 from DeFiCh/fix/remove_destroy_token
Browse files Browse the repository at this point in the history
Remove destroy token functionality
  • Loading branch information
monstrobishi authored Sep 29, 2020
2 parents dab0366 + a3764c7 commit b7459d0
Show file tree
Hide file tree
Showing 9 changed files with 6 additions and 219 deletions.
8 changes: 4 additions & 4 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,8 @@ class CMainParams : public CChainParams {
consensus.mn.anchoringFrequency = 15;
consensus.mn.anchoringLag = 15;

consensus.token.creationFee = 1 * COIN;
consensus.token.collateralAmount = 1000 * COIN;
consensus.token.creationFee = 100 * COIN;
consensus.token.collateralAmount = 1 * COIN;

consensus.spv.creationFee = 100000; // should be > bitcoin's dust
consensus.spv.anchorSubsidy = 0 * COIN;
Expand Down Expand Up @@ -307,8 +307,8 @@ class CTestNetParams : public CChainParams {
consensus.mn.anchoringFrequency = 15;
consensus.mn.anchoringLag = 15;

consensus.token.creationFee = 1 * COIN;
consensus.token.collateralAmount = 100 * COIN;
consensus.token.creationFee = 100 * COIN;
consensus.token.collateralAmount = 1 * COIN;

consensus.spv.creationFee = 100000; // should be > bitcoin's dust
consensus.spv.wallet_xpub = "tpubD9RkyYW1ixvD9vXVpYB1ka8rPZJaEQoKraYN7YnxbBxxsRYEMZgRTDRGEo1MzQd7r5KWxH8eRaQDVDaDuT4GnWgGd17xbk6An6JMdN4dwsY";
Expand Down
28 changes: 0 additions & 28 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,6 @@ Res ApplyCustomTx(CCustomCSView & base_mnview, CCoinsViewCache const & coins, CT
if(height < consensusParams.AMKHeight) { return Res::Err("Token tx before AMK height"); }
res = ApplyCreateTokenTx(mnview, coins, tx, height, metadata);
break;
case CustomTxType::DestroyToken:
if(height < consensusParams.AMKHeight) { return Res::Err("Token tx before AMK height"); }
res = ApplyDestroyTokenTx(mnview, coins, tx, height, metadata);
break;
case CustomTxType::UpdateToken:
if(height < consensusParams.AMKHeight) { return Res::Err("Token tx before AMK height"); }
res = ApplyUpdateTokenTx(mnview, coins, tx, height, metadata);
Expand Down Expand Up @@ -305,30 +301,6 @@ Res ApplyCreateTokenTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CT
return Res::Ok(base);
}

Res ApplyDestroyTokenTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, uint32_t height, std::vector<unsigned char> const & metadata)
{
const std::string base{"Token destruction"};

if (metadata.size() != sizeof(uint256)) {
return Res::Err("%s: metadata must contain 32 bytes", base);
}
uint256 tokenTx(metadata);
auto pair = mnview.GetTokenByCreationTx(tokenTx);
if (!pair) {
return Res::Err("%s: token with creationTx %s does not exist", base, tokenTx.ToString());
}
CTokenImplementation const & token = pair->second;
if (!HasCollateralAuth(tx, coins, token.creationTx)) {
return Res::Err("%s: %s", base, "tx must have at least one input from token owner");
}

auto res = mnview.DestroyToken(token.creationTx, tx.GetHash(), height);
if (!res.ok) {
return Res::Err("%s %s: %s", base, token.symbol, res.msg);
}
return Res::Ok(base);
}

Res ApplyUpdateTokenTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, uint32_t height, std::vector<unsigned char> const & metadata)
{
const std::string base{"Token update"};
Expand Down
2 changes: 0 additions & 2 deletions src/masternodes/mn_checks.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ enum class CustomTxType : unsigned char
// custom tokens:
CreateToken = 'T',
MintToken = 'M',
DestroyToken = 'D',
UpdateToken = 'N',
// dex orders - just not to overlap in future
// CreateOrder = 'O',
Expand Down Expand Up @@ -83,7 +82,6 @@ Res ApplyCreateMasternodeTx(CCustomCSView & mnview, CTransaction const & tx, uin
Res ApplyResignMasternodeTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, uint32_t height, std::vector<unsigned char> const & metadata);

Res ApplyCreateTokenTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, uint32_t height, std::vector<unsigned char> const & metadata);
Res ApplyDestroyTokenTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, uint32_t height, std::vector<unsigned char> const & metadata);
Res ApplyUpdateTokenTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, uint32_t height, std::vector<unsigned char> const & metadata);
Res ApplyMintTokenTx(CCustomCSView & mnview, CCoinsViewCache const & coins, CTransaction const & tx, std::vector<unsigned char> const & metadata);

Expand Down
101 changes: 0 additions & 101 deletions src/masternodes/mn_rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -671,106 +671,6 @@ UniValue createtoken(const JSONRPCRequest& request) {
return signsend(rawTx, request, pwallet)->GetHash().GetHex();
}

UniValue destroytoken(const JSONRPCRequest& request) {
CWallet* const pwallet = GetWallet(request);

RPCHelpMan{"destroytoken",
"\nCreates (and submits to local node and network) a transaction destroying your token. Collateral will be unlocked.\n"
"The second optional argument (may be empty array) is an array of specific UTXOs to spend. One of UTXO's must belong to the token's owner (collateral) address" +
HelpRequiringPassphrase(pwallet) + "\n",
{
{"token", RPCArg::Type::STR, RPCArg::Optional::NO, "The tokens's symbol, id or creation tx"},
{"inputs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG,
"A json array of json objects. Provide it if you want to spent specific UTXOs",
{
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
},
},
},
},
},
RPCResult{
"\"hash\" (string) The hex-encoded hash of broadcasted transaction\n"
},
RPCExamples{
HelpExampleCli("destroytoken", "\"symbol\"")
+ HelpExampleCli("destroytoken", "\"symbol\" \"[{\\\"txid\\\":\\\"id\\\",\\\"vout\\\":0}]\"")
+ HelpExampleRpc("destroytoken", "\"symbol\" \"[{\\\"txid\\\":\\\"id\\\",\\\"vout\\\":0}]\"")
},
}.Check(request);

if (pwallet->chain().isInitialBlockDownload()) {
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD,
"Cannot destroy token while still in Initial Block Download");
}
pwallet->BlockUntilSyncedToCurrentChain();

if (::ChainActive().Tip()->height < Params().GetConsensus().AMKHeight) {
throw JSONRPCError(RPC_TRANSACTION_REJECTED, "No tokenization transaction before block height " + std::to_string(Params().GetConsensus().AMKHeight));
}

RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR}, true);

std::string const tokenStr = trim_ws(request.params[0].getValStr());
UniValue txInputs = request.params[1];
if (txInputs.isNull())
{
txInputs.setArray();
}
CTxDestination ownerDest;
uint256 creationTx{};
{
LOCK(cs_main);
DCT_ID id;
auto token = pcustomcsview->GetTokenGuessId(tokenStr, id);
if (!token) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Token %s does not exist!", tokenStr));
}
if (id < CTokensView::DCT_ID_START) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Token %s is a 'stable coin'", tokenStr));
}
LOCK(pwallet->cs_wallet);
auto tokenImpl = static_cast<CTokenImplementation const& >(*token);
auto wtx = pwallet->GetWalletTx(tokenImpl.creationTx);
if (!wtx || !ExtractDestination(wtx->tx->vout[1].scriptPubKey, ownerDest)) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
strprintf("Can't extract destination for token's %s collateral", tokenStr));
}
creationTx = tokenImpl.creationTx;
}

const auto txVersion = GetTransactionVersion(::ChainActive().Height());
CMutableTransaction rawTx(txVersion);

rawTx.vin = GetAuthInputs(pwallet, ownerDest, txInputs.get_array());

CDataStream metadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION);
metadata << static_cast<unsigned char>(CustomTxType::DestroyToken)
<< creationTx;

CScript scriptMeta;
scriptMeta << OP_RETURN << ToByteVector(metadata);

rawTx.vout.push_back(CTxOut(0, scriptMeta));

rawTx = fund(rawTx, request, pwallet);

// check execution
{
LOCK(cs_main);
CCustomCSView mnview_dummy(*pcustomcsview); // don't write into actual DB
const auto res = ApplyDestroyTokenTx(mnview_dummy, ::ChainstateActive().CoinsTip(), CTransaction(rawTx), ::ChainActive().Tip()->height + 1,
ToByteVector(CDataStream{SER_NETWORK, PROTOCOL_VERSION, creationTx}));
if (!res.ok) {
throw JSONRPCError(RPC_INVALID_REQUEST, "Execution test failed:\n" + res.msg);
}
}
return signsend(rawTx, request, pwallet)->GetHash().GetHex();
}

UniValue updatetoken(const JSONRPCRequest& request) {
CWallet* const pwallet = GetWallet(request);

Expand Down Expand Up @@ -1694,7 +1594,6 @@ static const CRPCCommand commands[] =
{"masternodes", "getmasternode", &getmasternode, {"mn_id"}},
{"masternodes", "listcriminalproofs", &listcriminalproofs, {}},
{"tokens", "createtoken", &createtoken, {"metadata", "inputs"}},
{"tokens", "destroytoken", &destroytoken, {"token", "inputs"}},
{"tokens", "updatetoken", &updatetoken, {"metadata", "inputs"}},
{"tokens", "listtokens", &listtokens, {"pagination", "verbose"}},
{"tokens", "gettoken", &gettoken, {"key" }},
Expand Down
38 changes: 0 additions & 38 deletions src/masternodes/tokens.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,44 +197,6 @@ Res CTokensView::UpdateToken(const uint256 &tokenTx)
return Res::Ok();
}

Res CTokensView::DestroyToken(uint256 const & tokenTx, const uint256 & txid, int height)
{
auto pair = GetTokenByCreationTx(tokenTx);
if (!pair) {
return Res::Err("token with creationTx %s does not exist!", tokenTx.ToString());
}
/// @todo token: check for token supply / utxos

CTokenImpl & tokenImpl = pair->second;
if (tokenImpl.destructionTx != uint256{}) {
return Res::Err("token with creationTx %s was already destroyed by tx %s!", tokenTx.ToString(), tokenImpl.destructionTx.ToString());
}

tokenImpl.destructionTx = txid;
tokenImpl.destructionHeight = height;
WriteBy<ID>(WrapVarInt(pair->first.v), tokenImpl);
return Res::Ok();
}

bool CTokensView::RevertDestroyToken(uint256 const & tokenTx, const uint256 & txid)
{
auto pair = GetTokenByCreationTx(tokenTx);
if (!pair) {
LogPrintf("Token destruction revert error: token with creationTx %s does not exist!\n", tokenTx.ToString());
return false;
}
CTokenImpl & tokenImpl = pair->second;
if (tokenImpl.destructionTx != txid) {
LogPrintf("Token destruction revert error: token with creationTx %s was not destroyed by tx %s!\n", tokenTx.ToString(), txid.ToString());
return false;
}

tokenImpl.destructionTx = uint256{};
tokenImpl.destructionHeight = -1;
WriteBy<ID>(WrapVarInt(pair->first.v), tokenImpl);
return true;
}

DCT_ID CTokensView::IncrementLastDctId()
{
DCT_ID result{DCT_ID_START};
Expand Down
2 changes: 0 additions & 2 deletions src/masternodes/tokens.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,6 @@ class CTokensView : public virtual CStorageView
Res CreateToken(CTokenImpl const & token);
bool RevertCreateToken(uint256 const & txid);
Res UpdateToken(uint256 const & tokenTx);
Res DestroyToken(uint256 const & tokenTx, uint256 const & txid, int height);
bool RevertDestroyToken(uint256 const & tokenTx, uint256 const & txid);

// tags
struct ID { static const unsigned char prefix; };
Expand Down
1 change: 0 additions & 1 deletion src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createtoken", 1, "inputs"},
{ "updatetoken", 0, "metadata"},
{ "updatetoken", 1, "inputs"},
{ "destroytoken", 1, "inputs" },
{ "listtokens", 0, "pagination" },
{ "listtokens", 1, "verbose" },
{ "minttokens", 1, "inputs"},
Expand Down
19 changes: 0 additions & 19 deletions src/test/storage_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,25 +250,6 @@ BOOST_AUTO_TEST_CASE(tokens)
BOOST_REQUIRE(token->symbol == "DCT3");
}

// destroy token
// BOOST_REQUIRE(pcustomcsview->DestroyToken(0, uint256S("0x2222"), 999) == false); // stable coin!
BOOST_REQUIRE(pcustomcsview->DestroyToken(uint256S("0x3333"), uint256S("0xaaaa"), 999).ok == false); // nonexist
BOOST_REQUIRE(pcustomcsview->DestroyToken(uint256S("0x2222"), uint256S("0xaaaa"), 999).ok); // ok
BOOST_REQUIRE(pcustomcsview->DestroyToken(uint256S("0x2222"), uint256S("0xbbbb"), 999).ok == false); // already destroyed
{ // search by id
auto token = pcustomcsview->GetToken(DCT_ID{129});
BOOST_REQUIRE(token);
auto tokenImpl = static_cast<CTokenImplementation &>(*token);
BOOST_REQUIRE(tokenImpl.destructionHeight == 999);
BOOST_REQUIRE(tokenImpl.destructionTx == uint256S("0xaaaa"));
}

// revert destroy token
// BOOST_REQUIRE(pcustomcsview->RevertDestroyToken(0, uint256S("0x2222")) == false); // stable coin!
BOOST_REQUIRE(pcustomcsview->RevertDestroyToken(uint256S("0x3333"), uint256S("0xaaaa")) == false); // nonexist
BOOST_REQUIRE(pcustomcsview->RevertDestroyToken(uint256S("0x1111"), uint256S("0xaaaa")) == false); // not destroyed, active
BOOST_REQUIRE(pcustomcsview->RevertDestroyToken(uint256S("0x2222"), uint256S("0xbbbb")) == false); // destroyed, but wrong tx for revert
BOOST_REQUIRE(pcustomcsview->RevertDestroyToken(uint256S("0x2222"), uint256S("0xaaaa"))); // ok!
{ // search by id
auto token = pcustomcsview->GetToken(DCT_ID{129});
BOOST_REQUIRE(token);
Expand Down
26 changes: 2 additions & 24 deletions test/functional/feature_tokens_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,18 +150,6 @@ def run_test(self):
fundingTx = self.nodes[0].sendtoaddress(collateral0, 1)
self.nodes[0].generate(1)

print ("Destroy token...")
destroyTx = self.nodes[0].destroytoken("GOLD#128", [])
self.nodes[0].generate(1)
assert_equal(self.nodes[0].listtokens()['128']['destructionTx'], destroyTx)

# Try to mint destroyed token ('minting' is not the task of current test, but let's check it here)
try:
self.nodes[0].minttokens("100@GOLD#128", [])
except JSONRPCException as e:
errorString = e.error['message']
assert("already destroyed" in errorString)

# Spend unlocked collateral
# This checks two cases at once:
# 1) Finally, we should not fail on accept to mempool
Expand All @@ -170,20 +158,10 @@ def run_test(self):
# Don't mine here, check mempool after reorg!
# self.nodes[0].generate(1)


# REVERTING:
#========================
print ("Reverting...")
# Revert token destruction!
self.start_node(1)
self.nodes[1].generate(5)
# Check that collateral spending tx is still in the mempool
assert_equal(sendedTxHash, self.nodes[0].getrawmempool()[0])

connect_nodes_bi(self.nodes, 0, 1)
self.sync_blocks(self.nodes[0:2])

assert_equal(sorted(self.nodes[0].getrawmempool()), sorted([fundingTx, destroyTx, newGoldTx]))
assert_equal(sorted(self.nodes[0].getrawmempool()), sorted([fundingTx, newGoldTx]))
assert_equal(self.nodes[0].listtokens()['128']['destructionHeight'], -1)
assert_equal(self.nodes[0].listtokens()['128']['destructionTx'], '0000000000000000000000000000000000000000000000000000000000000000')

Expand All @@ -194,7 +172,7 @@ def run_test(self):
connect_nodes_bi(self.nodes, 0, 2)
self.sync_blocks(self.nodes[0:3])
assert_equal(len(self.nodes[0].listtokens()), 1)
assert_equal(sorted(self.nodes[0].getrawmempool()), sorted([createTokenTx, fundingTx, destroyTx, newGoldTx]))
assert_equal(sorted(self.nodes[0].getrawmempool()), sorted([createTokenTx, fundingTx, newGoldTx]))

if __name__ == '__main__':
TokensBasicTest ().main ()

0 comments on commit b7459d0

Please sign in to comment.