From 0328059a69e938c22b46592ea838a570862a3246 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Mon, 2 Apr 2018 16:40:17 -0700 Subject: [PATCH 1/2] add optional subtract fee argument to sendtomainchain --- src/rpc/client.cpp | 1 + src/wallet/rpcwallet.cpp | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 0fbf8ef97e..137f8e84f9 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -137,6 +137,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getmempooldescendants", 1, "verbose" }, { "bumpfee", 1, "options" }, { "testproposedblock", 1, "acceptnonstd" }, + { "sendtomainchain", 2, "subtractfeefromamount"}, // Echo with conversion (For testing only) { "echojson", 0, "arg0" }, { "echojson", 1, "arg1" }, diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index a3ad9b0144..b8f7a26352 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3446,14 +3446,15 @@ UniValue sendtomainchain(const JSONRPCRequest& request) if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (request.fHelp || request.params.size() != 2) + if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) throw runtime_error( - "sendtomainchain mainchainaddress amount\n" + "sendtomainchain mainchainaddress amount ( subtractfeefromamount )\n" "\nSends sidechain funds to the given mainchain address, through the federated withdraw mechanism\n" + HelpRequiringPassphrase() + "\nArguments:\n" "1. \"address\" (string, required) The destination address on Bitcoin mainchain\n" "2. \"amount\" (numeric, required) The amount being sent to Bitcoin mainchain\n" + "3. \"subtractfeefromamount\" (boolean, optional, default=false) The fee will be deducted from the amount being pegged-out.\n" "\nResult:\n" "\"txid\" (string) Transaction ID of the resulting sidechain transaction\n" "\nExamples:\n" @@ -3473,6 +3474,11 @@ UniValue sendtomainchain(const JSONRPCRequest& request) if (nAmount <= 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); + bool subtract_fee = false; + if (request.params.size() > 2) { + subtract_fee = request.params[2].get_bool(); + } + // Parse Bitcoin address for destination, embed script CScript scriptPubKeyMainchain(GetScriptForDestination(address.Get())); @@ -3487,7 +3493,7 @@ UniValue sendtomainchain(const JSONRPCRequest& request) EnsureWalletIsUnlocked(); CWalletTx wtxNew; - SendMoney(scriptPubKey, nAmount, Params().GetConsensus().pegged_asset, false, CPubKey(), wtxNew, true); + SendMoney(scriptPubKey, nAmount, Params().GetConsensus().pegged_asset, subtract_fee, CPubKey(), wtxNew, true); std::string blinds; for (unsigned int i=0; ivout.size(); i++) { @@ -4118,7 +4124,7 @@ static const CRPCCommand commands[] = { "wallet", "setaccount", &setaccount, true, {"address","account"} }, { "wallet", "reissueasset", &reissueasset, true, {"asset", "assetamount"} }, { "wallet", "signblock", &signblock, true, {} }, - { "wallet", "sendtomainchain", &sendtomainchain, false, {} }, + { "wallet", "sendtomainchain", &sendtomainchain, false, {"address", "amount", "subtractfeefromamount"} }, { "wallet", "destroyamount", &destroyamount, false, {"asset", "amount", "comment"} }, { "wallet", "settxfee", &settxfee, true, {"amount"} }, { "wallet", "signmessage", &signmessage, true, {"address","message"} }, From ed6354754d205b488c79c3ae663288ee61222d88 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Mon, 2 Apr 2018 16:41:29 -0700 Subject: [PATCH 2/2] add basic peg-out functional testing --- qa/rpc-tests/pegging.py | 49 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/qa/rpc-tests/pegging.py b/qa/rpc-tests/pegging.py index 502cda29f8..3a186b009e 100755 --- a/qa/rpc-tests/pegging.py +++ b/qa/rpc-tests/pegging.py @@ -173,6 +173,13 @@ try: addr = bitcoin.getnewaddress() + # First, blackhole all 21M bitcoin that already exist(and test subtractfrom) + assert(sidechain.getwalletinfo()["balance"]["bitcoin"] == 21000000) + sidechain.sendtomainchain(addr, 21000000, True) + assert("bitcoin" not in sidechain.getwalletinfo()["balance"]) + + sidechain.generate(101) + addrs = sidechain.getpeginaddress() txid1 = bitcoin.sendtoaddress(addrs["mainchain_address"], 24) # 10+2 confirms required to get into mempool and confirm @@ -296,6 +303,48 @@ try: # is awaiting further validation, nodes reject subsequent blocks # even ones they create sync_all(sidechain, sidechain2, False) + print("Now send funds out in two stages, partial, and full") + some_btc_addr = bitcoin.getnewaddress() + bal_1 = sidechain.getwalletinfo()["balance"]["bitcoin"] + try: + sidechain.sendtomainchain(some_btc_addr, bal_1 + 1) + raise Exception("Sending out too much; should have failed") + except JSONRPCException as e: + assert("Insufficient funds" in e.error["message"]) + pass + + assert(sidechain.getwalletinfo()["balance"]["bitcoin"] == bal_1) + try: + sidechain.sendtomainchain(some_btc_addr+"b", bal_1 - 1) + raise Exception("Sending to invalid address; should have failed") + except JSONRPCException as e: + assert("Invalid Bitcoin address" in e.error["message"]) + pass + + assert(sidechain.getwalletinfo()["balance"]["bitcoin"] == bal_1) + try: + sidechain.sendtomainchain("1Nro9WkpaKm9axmcfPVp79dAJU1Gx7VmMZ", bal_1 - 1) + raise Exception("Sending to mainchain address when should have been testnet; should have failed") + except JSONRPCException as e: + assert("Invalid Bitcoin address" in e.error["message"]) + pass + + assert(sidechain.getwalletinfo()["balance"]["bitcoin"] == bal_1) + + peg_out_txid = sidechain.sendtomainchain(some_btc_addr, 1) + + peg_out_details = sidechain.decoderawtransaction(sidechain.getrawtransaction(peg_out_txid)) + # peg-out, change, fee(which is the last) + assert(len(peg_out_details["vout"]) == 3) + assert(peg_out_details["vout"][0]["value"] == 1 or peg_out_details["vout"][1]["value"] == 1) + + bal_2 = sidechain.getwalletinfo()["balance"]["bitcoin"] + # Make sure balance went down + assert(bal_2 + 1 < bal_1) + + sidechain.sendtomainchain(some_btc_addr, bal_2, True) + + assert("bitcoin" not in sidechain.getwalletinfo()["balance"]) print("Success!")