Skip to content

Commit f79cc05

Browse files
committed
Merge 0d2832a into merged_master (Elements PR ElementsProject#741)
To resolve the `ConstructTransaction` conflict I reverted to the existing code then manually applied the diff (it adds a parameter then adds a giant pile of code to the input loop).
2 parents 8c4eec5 + 0d2832a commit f79cc05

9 files changed

Lines changed: 271 additions & 122 deletions

File tree

src/pegins.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,3 +496,45 @@ std::vector<std::pair<CScript, CScript>> GetValidFedpegScripts(const CBlockIndex
496496
fedpegscripts.resize(std::min(fedpegscripts.size(), params.total_valid_epochs));
497497
return fedpegscripts;
498498
}
499+
500+
template<typename T_tx_ref, typename T_merkle_block>
501+
CScriptWitness CreatePeginWitnessInner(const CAmount& value, const CAsset& asset, const uint256& genesis_hash, const CScript& claim_script, const T_tx_ref& tx_ref, const T_merkle_block& merkle_block)
502+
{
503+
std::vector<unsigned char> value_bytes;
504+
CVectorWriter ss_val(0, 0, value_bytes, 0);
505+
try {
506+
ss_val << value;
507+
} catch (...) {
508+
throw std::ios_base::failure("Amount serialization is invalid.");
509+
}
510+
511+
// Strip witness data for proof inclusion since only TXID-covered fields matters
512+
CDataStream ss_tx(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
513+
ss_tx << tx_ref;
514+
std::vector<unsigned char> tx_data_stripped(ss_tx.begin(), ss_tx.end());
515+
516+
// Serialize merkle block
517+
CDataStream ss_txout_proof(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
518+
ss_txout_proof << merkle_block;
519+
std::vector<unsigned char> txout_proof_bytes(ss_txout_proof.begin(), ss_txout_proof.end());
520+
521+
// Construct pegin proof
522+
CScriptWitness pegin_witness;
523+
std::vector<std::vector<unsigned char> >& stack = pegin_witness.stack;
524+
stack.push_back(value_bytes);
525+
stack.push_back(std::vector<unsigned char>(asset.begin(), asset.end()));
526+
stack.push_back(std::vector<unsigned char>(genesis_hash.begin(), genesis_hash.end()));
527+
stack.push_back(std::vector<unsigned char>(claim_script.begin(), claim_script.end()));
528+
stack.push_back(tx_data_stripped);
529+
stack.push_back(txout_proof_bytes);
530+
return pegin_witness;
531+
}
532+
533+
CScriptWitness CreatePeginWitness(const CAmount& value, const CAsset& asset, const uint256& genesis_hash, const CScript& claim_script, const CTransactionRef& tx_ref, const CMerkleBlock& merkle_block)
534+
{
535+
return CreatePeginWitnessInner(value, asset, genesis_hash, claim_script, tx_ref, merkle_block);
536+
}
537+
CScriptWitness CreatePeginWitness(const CAmount& value, const CAsset& asset, const uint256& genesis_hash, const CScript& claim_script, const Sidechain::Bitcoin::CTransactionRef& tx_ref, const Sidechain::Bitcoin::CMerkleBlock& merkle_block)
538+
{
539+
return CreatePeginWitnessInner(value, asset, genesis_hash, claim_script, tx_ref, merkle_block);
540+
}

src/pegins.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

88
#include <amount.h>
99
#include <consensus/params.h>
10+
#include <merkleblock.h>
1011
#include <primitives/bitcoin/transaction.h>
12+
#include <primitives/bitcoin/merkleblock.h>
1113
#include <primitives/transaction.h>
1214
#include <script/script.h>
1315
#include <chain.h>
@@ -38,4 +40,9 @@ bool MatchLiquidWatchman(const CScript& script);
3840
* scriptPubKey for the script, the second is the witnessScript. */
3941
std::vector<std::pair<CScript, CScript>> GetValidFedpegScripts(const CBlockIndex* pblockindex, const Consensus::Params& params, bool nextblock_validation);
4042

43+
/** Create the peg-in witness stack */
44+
CScriptWitness CreatePeginWitness(const CAmount& value, const CAsset& asset, const uint256& genesis_hash, const CScript& claim_script, const CTransactionRef& tx_ref, const CMerkleBlock& merkle_block);
45+
CScriptWitness CreatePeginWitness(const CAmount& value, const CAsset& asset, const uint256& genesis_hash, const CScript& claim_script, const Sidechain::Bitcoin::CTransactionRef& tx_ref, const Sidechain::Bitcoin::CMerkleBlock& merkle_block);
46+
47+
4148
#endif // BITCOIN_PEGINS_H

src/rpc/rawtransaction.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
55

66
#include <asset.h>
7+
#include <block_proof.h>
78
#include <chain.h>
89
#include <coins.h>
910
#include <compat/byteswap.h>
@@ -20,6 +21,8 @@
2021
#include <policy/policy.h>
2122
#include <policy/rbf.h>
2223
#include <primitives/transaction.h>
24+
#include <primitives/bitcoin/merkleblock.h>
25+
#include <primitives/bitcoin/transaction.h>
2326
#include <psbt.h>
2427
#include <random.h>
2528
#include <rpc/rawtransaction_util.h>
@@ -377,6 +380,9 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
377380
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
378381
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
379382
{"sequence", RPCArg::Type::NUM, /* default */ "depends on the value of the 'replaceable' and 'locktime' arguments", "The sequence number"},
383+
{"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"},
384+
{"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"},
385+
{"pegin_claim_script", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The claim script generated by getpeginaddress."},
380386
},
381387
},
382388
},
@@ -1617,7 +1623,7 @@ UniValue createpsbt(const JSONRPCRequest& request)
16171623
if (!request.params[3].isNull()) {
16181624
rbf = request.params[3].isTrue();
16191625
}
1620-
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf, request.params[4], &output_pubkeys);
1626+
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf, request.params[4], &output_pubkeys, false /* allow_peg_in */);
16211627

16221628
// Make a blank psbt
16231629
PartiallySignedTransaction psbtx(rawTx);

src/rpc/rawtransaction_util.cpp

Lines changed: 159 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55

66
#include <rpc/rawtransaction_util.h>
77

8+
#include <block_proof.h>
89
#include <coins.h>
910
#include <core_io.h>
1011
#include <key_io.h>
1112
#include <pegins.h>
1213
#include <policy/policy.h>
1314
#include <primitives/transaction.h>
15+
#include <primitives/bitcoin/merkleblock.h>
16+
#include <primitives/bitcoin/transaction.h>
1417
#include <rpc/request.h>
1518
#include <rpc/util.h>
1619
#include <script/sign.h>
@@ -21,7 +24,126 @@
2124
#include <util/strencodings.h>
2225
#include <validation.h>
2326

24-
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf, const UniValue& assets_in, std::vector<CPubKey>* output_pubkeys_out)
27+
template<typename T_tx>
28+
unsigned int GetPeginTxnOutputIndex(const T_tx& txn, const CScript& witnessProgram, const std::vector<std::pair<CScript, CScript>>& fedpegscripts)
29+
{
30+
for (const auto & scripts : fedpegscripts) {
31+
CScript mainchain_script = GetScriptForWitness(calculate_contract(scripts.second, witnessProgram));
32+
if (scripts.first.IsPayToScriptHash()) {
33+
mainchain_script = GetScriptForDestination(ScriptHash(mainchain_script));
34+
}
35+
for (unsigned int nOut = 0; nOut < txn.vout.size(); nOut++) {
36+
if (txn.vout[nOut].scriptPubKey == mainchain_script) {
37+
return nOut;
38+
}
39+
}
40+
}
41+
return txn.vout.size();
42+
}
43+
44+
// Modifies an existing transaction input in-place to be a valid peg-in input, and inserts the witness if deemed valid.
45+
template<typename T_tx_ref, typename T_merkle_block>
46+
static void CreatePegInInputInner(CMutableTransaction& mtx, uint32_t input_idx, T_tx_ref& txBTCRef, T_merkle_block& merkleBlock, const std::set<CScript>& claim_scripts, const std::vector<unsigned char>& txData, const std::vector<unsigned char>& txOutProofData)
47+
{
48+
if ((mtx.vin.size() > input_idx && !mtx.vin[input_idx].scriptSig.empty()) || (mtx.witness.vtxinwit.size() > input_idx && !mtx.witness.vtxinwit[input_idx].IsNull())) {
49+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Attempting to add a peg-in to an input that already has a scriptSig or witness");
50+
}
51+
52+
CDataStream ssTx(txData, SER_NETWORK, PROTOCOL_VERSION);
53+
try {
54+
ssTx >> txBTCRef;
55+
}
56+
catch (...) {
57+
throw JSONRPCError(RPC_TYPE_ERROR, "The included bitcoinTx is malformed. Are you sure that is the whole string?");
58+
}
59+
60+
CDataStream ssTxOutProof(txOutProofData, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
61+
try {
62+
ssTxOutProof >> merkleBlock;
63+
}
64+
catch (...) {
65+
throw JSONRPCError(RPC_TYPE_ERROR, "The included txoutproof is malformed. Are you sure that is the whole string?");
66+
}
67+
68+
if (!ssTxOutProof.empty()) {
69+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx out proof");
70+
}
71+
72+
std::vector<uint256> txHashes;
73+
std::vector<unsigned int> txIndices;
74+
if (merkleBlock.txn.ExtractMatches(txHashes, txIndices) != merkleBlock.header.hashMerkleRoot)
75+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx out proof");
76+
77+
if (txHashes.size() != 1 || txHashes[0] != txBTCRef->GetHash())
78+
throw JSONRPCError(RPC_INVALID_PARAMETER, "The txoutproof must contain bitcoinTx and only bitcoinTx");
79+
80+
CScript witness_script;
81+
unsigned int nOut = txBTCRef->vout.size();
82+
const auto fedpegscripts = GetValidFedpegScripts(::ChainActive().Tip(), Params().GetConsensus(), true /* nextblock_validation */);
83+
for (const CScript& script : claim_scripts) {
84+
nOut = GetPeginTxnOutputIndex(*txBTCRef, script, fedpegscripts);
85+
if (nOut != txBTCRef->vout.size()) {
86+
witness_script = script;
87+
break;
88+
}
89+
}
90+
if (nOut == txBTCRef->vout.size()) {
91+
if (claim_scripts.size() == 1) {
92+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Given claim_script does not match the given Bitcoin transaction.");
93+
} else {
94+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Failed to find output in bitcoinTx to the mainchain_address from getpeginaddress");
95+
}
96+
}
97+
assert(witness_script != CScript());
98+
99+
int version = -1;
100+
std::vector<unsigned char> witness_program;
101+
if (!witness_script.IsWitnessProgram(version, witness_program) || version != 0) {
102+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Given or recovered script is not a v0 witness program.");
103+
}
104+
105+
CAmount value = 0;
106+
if (!GetAmountFromParentChainPegin(value, *txBTCRef, nOut)) {
107+
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Amounts to pegin must be explicit and asset must be %s", Params().GetConsensus().parent_pegged_asset.GetHex()));
108+
}
109+
110+
// Add/replace input in mtx
111+
if (mtx.vin.size() <= input_idx) {
112+
mtx.vin.resize(input_idx + 1);
113+
}
114+
mtx.vin[input_idx] = CTxIn(COutPoint(txHashes[0], nOut), CScript(), ~(uint32_t)0);
115+
116+
// Construct pegin proof
117+
CScriptWitness pegin_witness = CreatePeginWitness(value, Params().GetConsensus().pegged_asset, Params().ParentGenesisBlockHash(), witness_script, txBTCRef, merkleBlock);
118+
119+
// Peg-in witness isn't valid, even though the block header is(without depth check)
120+
// We re-check depth before returning with more descriptive result
121+
std::string err;
122+
if (!IsValidPeginWitness(pegin_witness, fedpegscripts, mtx.vin[input_idx].prevout, err, false)) {
123+
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Constructed peg-in witness is invalid: %s", err));
124+
}
125+
126+
// Put input witness in transaction
127+
mtx.vin[input_idx].m_is_pegin = true;
128+
CTxInWitness txinwit;
129+
txinwit.m_pegin_witness = pegin_witness;
130+
131+
if (mtx.witness.vtxinwit.size() <= input_idx) {
132+
mtx.witness.vtxinwit.resize(input_idx + 1);
133+
}
134+
mtx.witness.vtxinwit[input_idx] = txinwit;
135+
}
136+
137+
void CreatePegInInput(CMutableTransaction& mtx, uint32_t input_idx, CTransactionRef& tx_btc, CMerkleBlock& merkle_block, const std::set<CScript>& claim_scripts, const std::vector<unsigned char>& txData, const std::vector<unsigned char>& txOutProofData)
138+
{
139+
CreatePegInInputInner(mtx, input_idx, tx_btc, merkle_block, claim_scripts, txData, txOutProofData);
140+
}
141+
void CreatePegInInput(CMutableTransaction& mtx, uint32_t input_idx, Sidechain::Bitcoin::CTransactionRef& tx_btc, Sidechain::Bitcoin::CMerkleBlock& merkle_block, const std::set<CScript>& claim_scripts, const std::vector<unsigned char>& txData, const std::vector<unsigned char>& txOutProofData)
142+
{
143+
CreatePegInInputInner(mtx, input_idx, tx_btc, merkle_block, claim_scripts, txData, txOutProofData);
144+
}
145+
146+
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf, const UniValue& assets_in, std::vector<CPubKey>* output_pubkeys_out, bool allow_peg_in)
25147
{
26148
if (inputs_in.isNull() || outputs_in.isNull())
27149
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
@@ -78,8 +200,43 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
78200
}
79201

80202
CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
81-
82203
rawTx.vin.push_back(in);
204+
205+
// Get the pegin stuff if it's there
206+
const UniValue& pegin_tx = find_value(o, "pegin_bitcoin_tx");
207+
const UniValue& pegin_tx_proof = find_value(o, "pegin_txout_proof");
208+
const UniValue& pegin_script = find_value(o, "pegin_claim_script");
209+
if (!pegin_tx.isNull() && !pegin_tx_proof.isNull() && !pegin_script.isNull() && allow_peg_in) {
210+
if (!IsHex(pegin_script.get_str())) {
211+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Given claim_script is not hex.");
212+
}
213+
// If given manually, no need for it to be a witness script
214+
std::vector<unsigned char> claim_script_bytes(ParseHex(pegin_script.get_str()));
215+
CScript claim_script(claim_script_bytes.begin(), claim_script_bytes.end());
216+
std::set<CScript> claim_scripts;
217+
claim_scripts.insert(std::move(claim_script));
218+
if (Params().GetConsensus().ParentChainHasPow()) {
219+
Sidechain::Bitcoin::CTransactionRef tx_btc;
220+
Sidechain::Bitcoin::CMerkleBlock merkle_block;
221+
CreatePegInInput(rawTx, idx, tx_btc, merkle_block, claim_scripts, ParseHex(pegin_tx.get_str()), ParseHex(pegin_tx_proof.get_str()));
222+
if (!CheckParentProofOfWork(merkle_block.header.GetHash(), merkle_block.header.nBits, Params().GetConsensus())) {
223+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx out proof");
224+
}
225+
} else {
226+
CTransactionRef tx_btc;
227+
CMerkleBlock merkle_block;
228+
CreatePegInInput(rawTx, idx, tx_btc, merkle_block, claim_scripts, ParseHex(pegin_tx.get_str()), ParseHex(pegin_tx_proof.get_str()));
229+
if (!CheckProofSignedParent(merkle_block.header, Params().GetConsensus())) {
230+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx out proof");
231+
}
232+
}
233+
} else if (!pegin_tx.isNull() || !pegin_tx_proof.isNull() || !pegin_script.isNull()) {
234+
if (allow_peg_in) {
235+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Some but not all pegin_ arguments provided");
236+
} else {
237+
throw JSONRPCError(RPC_INVALID_PARAMETER, "pegin_ arguments provided but this command does not support peg-ins");
238+
}
239+
}
83240
}
84241

85242
if (!outputs_is_obj) {

src/rpc/rawtransaction_util.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
#include <map>
99
#include <vector>
1010

11+
#include <merkleblock.h>
12+
#include <primitives/bitcoin/merkleblock.h>
13+
#include <primitives/bitcoin/transaction.h>
14+
#include <primitives/transaction.h>
1115
#include <pubkey.h>
1216

1317
class FillableSigningProvider;
@@ -37,7 +41,13 @@ UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keysto
3741
*/
3842
void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins);
3943

40-
/** Create a transaction from univalue parameters */
41-
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf, const UniValue& assets_in, std::vector<CPubKey>* output_pubkeys_out = nullptr);
44+
/** Create a transaction from univalue parameters. If (and only if)
45+
output_pubkeys_out is null, the "nonce hack" of storing Confidential
46+
Assets output pubkeys in nonces will be used. */
47+
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf, const UniValue& assets_in, std::vector<CPubKey>* output_pubkeys_out = nullptr, bool allow_peg_in = true);
48+
49+
/** Create a peg-in input */
50+
void CreatePegInInput(CMutableTransaction& mtx, uint32_t input_idx, CTransactionRef& tx_btc, CMerkleBlock& merkle_block, const std::set<CScript>& claim_scripts, const std::vector<unsigned char>& txData, const std::vector<unsigned char>& txOutProofData);
51+
void CreatePegInInput(CMutableTransaction& mtx, uint32_t input_idx, Sidechain::Bitcoin::CTransactionRef& tx_btc, Sidechain::Bitcoin::CMerkleBlock& merkle_block, const std::set<CScript>& claim_scripts, const std::vector<unsigned char>& txData, const std::vector<unsigned char>& txOutProofData);
4252

4353
#endif // BITCOIN_RPC_RAWTRANSACTION_UTIL_H

0 commit comments

Comments
 (0)