Merge #370: Refactor: Validation: Make parts of IsValidPeginWitness templated

a20986b 2WP: Refactor out GetAmountFromParentChainPegin (Jorge Timón)
2be9cae Refactor: Validation: Make parts of IsValidPeginWitness templated (Jorge Timón)
This commit is contained in:
Gregory Sanders 2018-06-25 20:28:05 -04:00
commit 146a95724e
No known key found for this signature in database
GPG key ID: F3F68E2D86A48FDB
2 changed files with 88 additions and 43 deletions

View file

@ -2368,6 +2368,78 @@ CScript calculate_contract(const CScript& federationRedeemScript, const CScript&
return scriptDestination;
}
bool GetAmountFromParentChainPegin(CAmount& amount, const Sidechain::Bitcoin::CTransaction& txBTC, unsigned int nOut)
{
amount = txBTC.vout[nOut].nValue;
return true;
}
template<typename T>
static bool GetBlockAndTxFromMerkleBlock(uint256& block_hash, uint256& tx_hash, T& merkle_block, const std::vector<unsigned char>& merkle_block_raw)
{
try {
std::vector<uint256> tx_hashes;
std::vector<unsigned int> tx_indices;
CDataStream merkle_block_stream(merkle_block_raw, SER_NETWORK, PROTOCOL_VERSION);
merkle_block_stream >> merkle_block;
block_hash = merkle_block.header.GetHash();
if (!merkle_block_stream.empty()) {
return false;
}
if (merkle_block.txn.ExtractMatches(tx_hashes, tx_indices) != merkle_block.header.hashMerkleRoot || tx_hashes.size() != 1) {
return false;
}
tx_hash = tx_hashes[0];
} catch (std::exception& e) {
// Invalid encoding of merkle block
return false;
}
return true;
}
template<typename T>
static bool CheckPeginTx(const std::vector<unsigned char>& tx_data, T& pegtx, const COutPoint& prevout, const CAmount claim_amount, const CScript& claim_script)
{
try {
CDataStream pegtx_stream(tx_data, SER_NETWORK, PROTOCOL_VERSION);
pegtx_stream >> pegtx;
if (!pegtx_stream.empty()) {
return false;
}
} catch (std::exception& e) {
// Invalid encoding of transaction
return false;
}
// Check that transaction matches txid
if (pegtx->GetHash() != prevout.hash) {
return false;
}
if (prevout.n >= pegtx->vout.size()) {
return false;
}
CAmount amount = 0;
if (!GetAmountFromParentChainPegin(amount, *pegtx, prevout.n)) {
return false;
}
// Check the transaction nout/value matches
if (claim_amount != amount) {
return false;
}
// Check that the witness program matches the p2ch on the p2sh-p2wsh transaction output
CScript tweaked_fedpegscript = calculate_contract(Params().GetConsensus().fedpegScript, claim_script);
CScript witness_output(GetScriptForWitness(tweaked_fedpegscript));
CScript expected_script(CScript() << OP_HASH160 << ToByteVector(CScriptID(witness_output)) << OP_EQUAL);
if (pegtx->vout[prevout.n].scriptPubKey != expected_script) {
return false;
}
return true;
}
bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& prevout, bool check_depth) {
// Format on stack is as follows:
@ -2418,45 +2490,26 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p
return false;
}
// Get serialized transaction
Sidechain::Bitcoin::CTransactionRef pegtx;
try {
CDataStream pegtx_stream(stack[4], SER_NETWORK, PROTOCOL_VERSION);
pegtx_stream >> pegtx;
if (!pegtx_stream.empty()) {
return false;
}
} catch (std::exception& e) {
// Invalid encoding of transaction
return false;
}
uint256 block_hash;
uint256 tx_hash;
// Get txout proof
Sidechain::Bitcoin::CMerkleBlock merkle_block;
std::vector<uint256> txHashes;
std::vector<unsigned int> txIndices;
try {
CDataStream merkleBlockStream(stack[5], SER_NETWORK, PROTOCOL_VERSION);
merkleBlockStream >> merkle_block;
if (!merkleBlockStream.empty() || !CheckBitcoinProof(merkle_block.header.GetHash(), merkle_block.header.nBits)) {
return false;
}
if (merkle_block.txn.ExtractMatches(txHashes, txIndices) != merkle_block.header.hashMerkleRoot || txHashes.size() != 1) {
return false;
}
} catch (std::exception& e) {
// Invalid encoding of merkle block
if (!GetBlockAndTxFromMerkleBlock(block_hash, tx_hash, merkle_block, stack[5])) {
return false;
}
if (!CheckBitcoinProof(block_hash, merkle_block.header.nBits)) {
return false;
}
// Check that transaction matches txid
if (pegtx->GetHash() != prevout.hash) {
// Get serialized transaction
Sidechain::Bitcoin::CTransactionRef pegtx;
if (!CheckPeginTx(stack[4], pegtx, prevout, value, claim_script)) {
return false;
}
// Check that the merkle proof corresponds to the txid
if (prevout.hash != txHashes[0]) {
if (prevout.hash != tx_hash) {
return false;
}
@ -2470,22 +2523,9 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p
return false;
}
// Check the transaction nout/value matches
if (prevout.n >= pegtx->vout.size() || value != pegtx->vout[prevout.n].nValue) {
return false;
}
// Check that the witness program matches the p2ch on the p2sh-p2wsh transaction output
CScript tweaked_fedpegscript = calculate_contract(Params().GetConsensus().fedpegScript, claim_script);
CScript witness_output(GetScriptForWitness(tweaked_fedpegscript));
CScript expected_script(CScript() << OP_HASH160 << ToByteVector(CScriptID(witness_output)) << OP_EQUAL);
if (pegtx->vout[prevout.n].scriptPubKey != expected_script) {
return false;
}
// Finally, validate peg-in via rpc call
if (check_depth && GetBoolArg("-validatepegin", DEFAULT_VALIDATE_PEGIN)) {
return IsConfirmedBitcoinBlock(merkle_block.header.GetHash(), Params().GetConsensus().pegin_min_depth);
return IsConfirmedBitcoinBlock(block_hash, Params().GetConsensus().pegin_min_depth);
}
return true;
}