From 1ce74e07f35452e6c4512bbdfa1cecf5bc064f6b Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Tue, 22 Oct 2019 18:32:03 -0400 Subject: [PATCH] Be able to fund transactions with peg-ins --- src/wallet/coincontrol.h | 10 +++++++ src/wallet/rpcwallet.cpp | 55 +++++++++++++++++++++++++++++++++++-- test/functional/rpc_psbt.py | 5 ---- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h index 0041852ec9..7ee3a8d72b 100644 --- a/src/wallet/coincontrol.h +++ b/src/wallet/coincontrol.h @@ -83,6 +83,16 @@ public: m_external_txouts.emplace(outpoint, txout); } + void Select(const COutPoint& outpoint, const Sidechain::Bitcoin::CTxOut& txout_in) + { + setSelected.insert(outpoint); + CTxOut txout; + txout.scriptPubKey = txout_in.scriptPubKey; + txout.nValue.SetToAmount(txout_in.nValue); + txout.nAsset.SetToAsset(Params().GetConsensus().pegged_asset); + m_external_txouts.emplace(outpoint, txout); + } + void UnSelect(const COutPoint& output) { setSelected.erase(output); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 8afa6cad0e..b2485651b5 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3381,10 +3381,22 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f setSubtractFeeFromOutputs.insert(pos); } + // Check any existing inputs for peg-in data and add to external txouts if so // Fetch specified UTXOs from the UTXO set + const auto& fedpegscripts = GetValidFedpegScripts(chainActive.Tip(), Params().GetConsensus(), true /* nextblock_validation */); std::map coins; - for (const CTxIn& txin : tx.vin) { + for (unsigned int i = 0; i < tx.vin.size(); ++i ) { + const CTxIn& txin = tx.vin[i]; coins[txin.prevout]; // Create empty map entry keyed by prevout. + if (txin.m_is_pegin) { + std::string err; + if (tx.witness.vtxinwit.size() != tx.vin.size() || !IsValidPeginWitness(tx.witness.vtxinwit[i].m_pegin_witness, fedpegscripts, txin.prevout, err, false)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Transaction contains invalid peg-in input: %s", err)); + } + CScriptWitness& pegin_witness = tx.witness.vtxinwit[i].m_pegin_witness; + CTxOut txout = GetPeginOutputFromWitness(pegin_witness); + coinControl.SelectExternal(txin.prevout, txout); + } } CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); @@ -4671,6 +4683,9 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"}, {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"}, {"sequence", RPCArg::Type::NUM, RPCArg::Optional::NO, "The sequence number"}, + {"pegin_bitcoin_tx", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The raw bitcoin transaction (in hex) depositing bitcoin to the mainchain_address generated by getpeginaddress"}, + {"pegin_txout_proof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A rawtxoutproof (in hex) generated by the mainchain daemon's `gettxoutproof` containing a proof of only bitcoin_tx"}, + {"pegin_claim_script", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The witness program generated by getpeginaddress."}, }, }, }, @@ -4768,7 +4783,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) // It's hard to control the behavior of FundTransaction, so we will wait // until after it's done, then extract the blinding keys from the output // nonces. - CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]["replaceable"], NullUniValue /* CA: assets_in */, nullptr /* output_pubkeys_out */, false /* allow_peg_in */); + CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]["replaceable"], NullUniValue /* CA: assets_in */, nullptr /* output_pubkeys_out */, true /* allow_peg_in */); FundTransaction(pwallet, rawTx, fee, change_position, request.params[3], request.params[5]); // Make a blank psbt @@ -4789,6 +4804,42 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) throw JSONRPCTransactionError(err); } + // Add peg-in stuff if it's there + for (unsigned int i = 0; i < rawTx.vin.size(); ++i) { + if (psbtx.tx->vin[i].m_is_pegin) { + CScriptWitness& pegin_witness = psbtx.tx->witness.vtxinwit[i].m_pegin_witness; + CAmount val; + VectorReader vr_val(SER_NETWORK, PROTOCOL_VERSION, pegin_witness.stack[0], 0); + vr_val >> val; + psbtx.inputs[i].value = val; + VectorReader vr_asset(SER_NETWORK, PROTOCOL_VERSION, pegin_witness.stack[1], 0); + vr_asset >> psbtx.inputs[i].asset; + VectorReader vr_genesis(SER_NETWORK, PROTOCOL_VERSION, pegin_witness.stack[2], 0); + vr_genesis >> psbtx.inputs[i].genesis_hash; + psbtx.inputs[i].claim_script.assign(pegin_witness.stack[3].begin(), pegin_witness.stack[3].end()); + + VectorReader vr_tx(SER_NETWORK, PROTOCOL_VERSION, pegin_witness.stack[4], 0); + VectorReader vr_proof(SER_NETWORK, PROTOCOL_VERSION, pegin_witness.stack[5], 0); + if (Params().GetConsensus().ParentChainHasPow()) { + Sidechain::Bitcoin::CTransactionRef tx_btc; + vr_tx >> tx_btc; + psbtx.inputs[i].peg_in_tx = tx_btc; + Sidechain::Bitcoin::CMerkleBlock tx_proof; + vr_proof >> tx_proof; + psbtx.inputs[i].txout_proof = tx_proof; + } else { + CTransactionRef tx_btc; + vr_tx >> tx_btc; + psbtx.inputs[i].peg_in_tx = tx_btc; + CMerkleBlock tx_proof; + vr_proof >> tx_proof; + psbtx.inputs[i].txout_proof = tx_proof; + } + pegin_witness.SetNull(); + psbtx.tx->vin[i].m_is_pegin = false; + } + } + // Serialize the PSBT CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << psbtx; diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index 11a0631721..7a0f9f6d0f 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -524,11 +524,6 @@ class PSBTTest(BitcoinTestFramework): # Some Confidential-Assets-specific tests self.run_ca_tests() - # Check that peg-ins are disallowed for walletcreatefundedpsbt - assert_raises_rpc_error(-8, 'pegin_ arguments provided but this command does not support peg-ins', self.nodes[0].walletcreatefundedpsbt, [{"txid": "0000000000000000000000000000000000000000000000000000000000000000", "vout": 0, "pegin_bitcoin_tx": "00"}], [{self.nodes[0].getnewaddress(): 1}]) - assert_raises_rpc_error(-8, 'pegin_ arguments provided but this command does not support peg-ins', self.nodes[0].walletcreatefundedpsbt, [{"txid": "0000000000000000000000000000000000000000000000000000000000000000", "vout": 0, "pegin_txout_proof": "00"}], [{self.nodes[0].getnewaddress(): 1}]) - assert_raises_rpc_error(-8, 'pegin_ arguments provided but this command does not support peg-ins', self.nodes[0].walletcreatefundedpsbt, [{"txid": "0000000000000000000000000000000000000000000000000000000000000000", "vout": 0, "pegin_claim_script": "00"}], [{self.nodes[0].getnewaddress(): 1}]) - # Tests added in the 0.18 rebase don't pass on Elements yet. """