From dfc6c6483fe16ee3548255451bd50e95c0a0c0eb Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Wed, 31 May 2017 12:14:54 -0400 Subject: [PATCH] Use wallet to find compatible pegin sidechain_address by default during claimpegin --- qa/rpc-tests/pegging.py | 81 +++++++++++++++++++++++++--------------- src/wallet/rpcwallet.cpp | 65 +++++++++++++++++++++----------- 2 files changed, 93 insertions(+), 53 deletions(-) diff --git a/qa/rpc-tests/pegging.py b/qa/rpc-tests/pegging.py index 747a4cd5f0..f8dbf7f7ca 100755 --- a/qa/rpc-tests/pegging.py +++ b/qa/rpc-tests/pegging.py @@ -1,6 +1,6 @@ #!/usr/bin/env python2 -from test_framework.authproxy import AuthServiceProxy +from test_framework.authproxy import AuthServiceProxy, JSONRPCException import os import random import sys @@ -38,6 +38,7 @@ with open(os.path.join(bitcoin_datadir, "bitcoin.conf"), 'w') as f: f.write("testnet=0\n") f.write("txindex=1\n") f.write("daemon=1\n") + f.write("listen=0\n") with open(os.path.join(sidechain_datadir, "elements.conf"), 'w') as f: f.write("regtest=1\n") @@ -56,49 +57,67 @@ with open(os.path.join(sidechain_datadir, "elements.conf"), 'w') as f: f.write("mainchainrpcpassword="+bitcoin_pass+"\n") f.write("validatepegin=1\n") f.write("validatepegout=0\n") + f.write("listen=0\n") -# Start daemons -print("Starting daemons at "+bitcoin_datadir+" and "+sidechain_datadir) -bitcoindstart = sys.argv[1]+"/bitcoind -datadir="+bitcoin_datadir -subprocess.Popen(bitcoindstart.split(), stdout=subprocess.PIPE) +try: -sidechainstart = sys.argv[2]+"/elementsd -datadir="+sidechain_datadir -subprocess.Popen(sidechainstart.split(), stdout=subprocess.PIPE) + # Start daemons + print("Starting daemons at "+bitcoin_datadir+" and "+sidechain_datadir) + bitcoindstart = sys.argv[1]+"/bitcoind -datadir="+bitcoin_datadir + subprocess.Popen(bitcoindstart.split(), stdout=subprocess.PIPE) -print("Daemons started") -time.sleep(2) + sidechainstart = sys.argv[2]+"/elementsd -datadir="+sidechain_datadir + subprocess.Popen(sidechainstart.split(), stdout=subprocess.PIPE) -bitcoin = AuthServiceProxy("http://bitcoinrpc:"+bitcoin_pass+"@127.0.0.1:"+str(bitcoin_port)) -sidechain = AuthServiceProxy("http://sidechainrpc:"+sidechain_pass+"@127.0.0.1:"+str(sidechain_port)) -print("Daemons started, making blocks to get funds") + print("Daemons started") + time.sleep(2) -bitcoin.generate(101) -sidechain.generate(101) + bitcoin = AuthServiceProxy("http://bitcoinrpc:"+bitcoin_pass+"@127.0.0.1:"+str(bitcoin_port)) + sidechain = AuthServiceProxy("http://sidechainrpc:"+sidechain_pass+"@127.0.0.1:"+str(sidechain_port)) + print("Daemons started, making blocks to get funds") -addr = bitcoin.getnewaddress() + bitcoin.generate(101) + sidechain.generate(101) -# Lockup some funds to unlock later -sidechain.sendtomainchain(addr, 50) -sidechain.generate(101) + addr = bitcoin.getnewaddress() -addrs = sidechain.getpeginaddress() -txid = bitcoin.sendtoaddress(addrs["mainchain_address"], 49) -bitcoin.generate(10) -proof = bitcoin.gettxoutproof([txid]) -raw = bitcoin.getrawtransaction(txid) + # Lockup some funds to unlock later + sidechain.sendtomainchain(addr, 50) + sidechain.generate(101) -print("Attempting peg-in") -pegtxid = sidechain.claimpegin(addrs["sidechain_address"], raw, proof) -sidechain.generate(1) + addrs = sidechain.getpeginaddress() + txid = bitcoin.sendtoaddress(addrs["mainchain_address"], 49) + bitcoin.generate(10) + proof = bitcoin.gettxoutproof([txid]) + raw = bitcoin.getrawtransaction(txid) -tx = sidechain.gettransaction(pegtxid) + print("Attempting peg-in") -if "confirmations" in tx and tx["confirmations"] > 0: - print("Peg-in is confirmed: Success!") -else: - print("Peg-in has failed.") + # Should fail due to non-matching address + try: + pegtxid = sidechain.claimpegin(raw, proof, sidechain.getnewaddress()) + raise Exception("Peg-in with non-matching address should fail.") + except JSONRPCException: + pass + # Should succeed via wallet lookup for address match + pegtxid = sidechain.claimpegin(raw, proof) + sidechain.generate(1) + + tx = sidechain.gettransaction(pegtxid) + + if "confirmations" in tx and tx["confirmations"] > 0: + print("Peg-in is confirmed: Success!") + else: + raise Exception("Peg-in confirmation has failed.") + +except JSONRPCException as e: + print("Pegging testing failed, aborting:") + print(e.error) +except Exception as e: + print("Pegging testing failed, aborting:") + print(e) print("Stopping daemons and cleaning up") bitcoin.stop() diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index af5953c492..9e1935086e 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3175,19 +3175,32 @@ UniValue sendtomainchain(const UniValue& params, bool fHelp) extern UniValue sendrawtransaction(const UniValue& params, bool fHelp); +unsigned int GetPeginTxnOutputIndex(const Sidechain::Bitcoin::CTransaction& txn, const CBitcoinAddress& sidechainAddress, unsigned char* fullcontract) +{ + unsigned char nonce[16]; + memset(nonce, 0, sizeof(nonce)); + unsigned int nOut = 0; + //Call contracthashtool + CScript mainchain_script = GetScriptForDestination(calculate_contract(Params().GetConsensus().fedpegScript, sidechainAddress, &nonce[0], fullcontract)); + for (; nOut < txn.vout.size(); nOut++) + if (txn.vout[nOut].scriptPubKey == mainchain_script) + break; + return nOut; +} + UniValue claimpegin(const UniValue& params, bool fHelp) { - if (fHelp || params.size() != 3) + if (fHelp || params.size() < 2 || params.size() > 3) throw runtime_error( - "claimpegin sidechainaddress bitcoinTx txoutproof\n" + "claimpegin bitcoinTx txoutproof ( sidechain_address )\n" "\nClaim coins from the main chain by creating a withdraw transaction with the necessary metadata after the corresponding Bitcoin transaction.\n" - "Note that the transaction will not be mined or relayed unless it is buried at least 10 blocks deep.\n" + "Note that the transaction will not be relayed unless it is buried at least 102 blocks deep.\n" "If a transaction is not relayed it may require manual addition to a functionary mempool in order for it to be mined.\n" "\nArguments:\n" - "1. \"sidechain_address\" (string, required) The sidechain_address address generated by getpeginaddress\n" - "2. \"bitcoinTx\" (string, required) The raw bitcoin transaction (in hex) depositing bitcoin to the mainchain_address generated by getpeginaddress\n" - "3. \"txoutproof\" (string, required) A rawtxoutproof (in hex) generated by bitcoind's `gettxoutproof` containing a proof of only bitcoinTx\n" + "1. \"bitcoinTx\" (string, required) The raw bitcoin transaction (in hex) depositing bitcoin to the mainchain_address generated by getpeginaddress\n" + "2. \"txoutproof\" (string, required) A rawtxoutproof (in hex) generated by bitcoind's `gettxoutproof` containing a proof of only bitcoinTx\n" + "3. \"sidechain_address\" (string, optional) The sidechain_address generated by getpeginaddress. Only needed if not in wallet.\n" "\nResult:\n" "\"txid\" (string) Txid of the resulting sidechain transaction\n" "\nExamples:\n" @@ -3200,14 +3213,12 @@ UniValue claimpegin(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_WALLET_ERROR, "Peg-ins cannot be completed during initial sync or reindexing."); } - CBitcoinAddress sidechainAddress(params[0].get_str()); - if (!sidechainAddress.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid sidechain_address"); + if (!IsHex(params[0].get_str()) || !IsHex(params[1].get_str())) { + throw JSONRPCError(RPC_TYPE_ERROR, "the first two arguments must be hex strings"); + } - if (!IsHex(params[1].get_str()) || !IsHex(params[2].get_str())) - throw JSONRPCError(RPC_TYPE_ERROR, "the last two arguments must be hex strings"); - std::vector txData = ParseHex(params[1].get_str()); + std::vector txData = ParseHex(params[0].get_str()); CDataStream ssTx(txData, SER_NETWORK, PROTOCOL_VERSION); Sidechain::Bitcoin::CTransaction txBTC; try { @@ -3217,7 +3228,7 @@ UniValue claimpegin(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_TYPE_ERROR, "The included bitcoinTx is malformed. Are you sure that is the whole string?"); } - std::vector txOutProofData = ParseHex(params[2].get_str()); + std::vector txOutProofData = ParseHex(params[1].get_str()); CDataStream ssTxOutProof(txOutProofData, SER_NETWORK, PROTOCOL_VERSION); Sidechain::Bitcoin::CMerkleBlock merkleBlock; try { @@ -3237,16 +3248,26 @@ UniValue claimpegin(const UniValue& params, bool fHelp) if (txHashes.size() != 1 || txHashes[0] != txBTC.GetHash()) throw JSONRPCError(RPC_INVALID_PARAMETER, "The txoutproof must contain bitcoinTx and only bitcoinTx"); - //Call contracthashtool - unsigned char nonce[16]; - memset(nonce, 0, sizeof(nonce)); - unsigned char fullcontract[40]; - CScript mainchain_script = GetScriptForDestination(calculate_contract(Params().GetConsensus().fedpegScript, sidechainAddress, &nonce[0], fullcontract)); - unsigned int nOut = 0; - for (; nOut < txBTC.vout.size(); nOut++) - if (txBTC.vout[nOut].scriptPubKey == mainchain_script) - break; + unsigned char fullcontract[40]; + CBitcoinAddress sidechainAddress; + if (params.size() > 2) { + sidechainAddress = CBitcoinAddress(params[2].get_str()); + if (!sidechainAddress.IsValid()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Given sidechain_address is invalid."); + } + nOut = GetPeginTxnOutputIndex(txBTC, sidechainAddress, fullcontract); + } + else { + // Look through address book for pegin contract value + for (std::map::const_iterator iter = pwalletMain->mapAddressBook.begin(); iter != pwalletMain->mapAddressBook.end(); ++iter) { + sidechainAddress = CBitcoinAddress(iter->first); + nOut = GetPeginTxnOutputIndex(txBTC, sidechainAddress, fullcontract); + if (nOut != txBTC.vout.size()) { + break; + } + } + } if (nOut == txBTC.vout.size()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Failed to find output in bitcoinTx to the mainchain_address from getpeginaddress"); CAmount value = txBTC.vout[nOut].nValue;