2WP: Verify number of transactions in SPV proof

Use the getblockheader RPC from Bitcoin Core to verify if the number of
transactions mentioned in the SPV proof matches the number for the
block known by the Core daemon.

Inspired by 6b9dc8ceae
This commit is contained in:
Steven Roose 2018-08-09 14:16:30 +01:00 committed by Steven Roose
parent 621b52f80b
commit 85eb5026de
6 changed files with 42 additions and 6 deletions

View file

@ -178,7 +178,7 @@ UniValue CallRPC(const std::string& strMethod, const UniValue& params, bool conn
return reply;
}
bool IsConfirmedBitcoinBlock(const uint256& hash, int nMinConfirmationDepth)
bool IsConfirmedBitcoinBlock(const uint256& hash, const int nMinConfirmationDepth, const int nbTxs)
{
try {
UniValue params(UniValue::VARR);
@ -189,8 +189,21 @@ bool IsConfirmedBitcoinBlock(const uint256& hash, int nMinConfirmationDepth)
UniValue result = find_value(reply, "result");
if (!result.isObject())
return false;
result = find_value(result.get_obj(), "confirmations");
return result.isNum() && result.get_int64() >= nMinConfirmationDepth;
UniValue confirmations = find_value(result.get_obj(), "confirmations");
if (!confirmations.isNum() || confirmations.get_int64() < nMinConfirmationDepth) {
return false;
}
// Only perform extra test if nbTxs has been provided (non-zero).
if (nbTxs != 0) {
UniValue nTx = find_value(result.get_obj(), "nTx");
if (!nTx.isNum() || nTx.get_int64() != nbTxs) {
LogPrintf("ERROR: Invalid number of transactions in merkle block for %s",
hash.GetHex());
return false;
}
}
} catch (CConnectionFailed& e) {
LogPrintf("ERROR: Lost connection to bitcoind RPC, you will want to restart after fixing this!\n");
return false;

View file

@ -35,6 +35,11 @@ public:
};
UniValue CallRPC(const std::string& strMethod, const UniValue& params, bool connectToMainchain=false);
bool IsConfirmedBitcoinBlock(const uint256& hash, int nMinConfirmationDepth);
// Verify if the block with given hash has at least the specified minimum number
// of confirmations.
// For validating merkle blocks, you can provide the nbTxs parameter to verify if
// it equals the number of transactions in the block.
bool IsConfirmedBitcoinBlock(const uint256& hash, int nMinConfirmationDepth, int nbTxs);
#endif // BITCOIN_CALLRPC_H

View file

@ -115,6 +115,11 @@ public:
* returns the merkle root, or 0 in case of failure
*/
uint256 ExtractMatches(std::vector<uint256> &vMatch, std::vector<unsigned int> &vnIndex);
/** Get number of transactions the merkle proof is indicating for cross-reference with
* local blockchain knowledge.
*/
unsigned int GetNumTransactions() const { return nTransactions; };
};

View file

@ -120,6 +120,11 @@ public:
* returns the merkle root, or 0 in case of failure
*/
uint256 ExtractMatches(std::vector<uint256> &vMatch, std::vector<unsigned int> &vnIndex);
/** Get number of transactions the merkle proof is indicating for cross-reference with
* local blockchain knowledge.
*/
unsigned int GetNumTransactions() const { return nTransactions; };
};

View file

@ -2531,6 +2531,7 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p
uint256 block_hash;
uint256 tx_hash;
int num_txs;
// Get txout proof
if (Params().GetConsensus().ParentChainHasPow()) {
@ -2546,6 +2547,8 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p
if (!CheckPeginTx(stack[4], pegtx, prevout, value, claim_script)) {
return false;
}
num_txs = merkle_block_pow.txn.GetNumTransactions();
} else {
CMerkleBlock merkle_block;
@ -2561,6 +2564,8 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p
if (!CheckPeginTx(stack[4], pegtx, prevout, value, claim_script)) {
return false;
}
num_txs = merkle_block.txn.GetNumTransactions();
}
// Check that the merkle proof corresponds to the txid
@ -2580,7 +2585,9 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p
// Finally, validate peg-in via rpc call
if (check_depth && GetBoolArg("-validatepegin", DEFAULT_VALIDATE_PEGIN)) {
return IsConfirmedBitcoinBlock(block_hash, Params().GetConsensus().pegin_min_depth);
if (!IsConfirmedBitcoinBlock(block_hash, Params().GetConsensus().pegin_min_depth, num_txs)) {
return false;
}
}
return true;
}

View file

@ -3702,7 +3702,8 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef
// Additional block lee-way to avoid bitcoin block races
if (GetBoolArg("-validatepegin", DEFAULT_VALIDATE_PEGIN)) {
ret.push_back(Pair("mature", IsConfirmedBitcoinBlock(merkleBlock.header.GetHash(), Params().GetConsensus().pegin_min_depth+2)));
ret.push_back(Pair("mature", IsConfirmedBitcoinBlock(merkleBlock.header.GetHash(),
Params().GetConsensus().pegin_min_depth+2, merkleBlock.txn.GetNumTransactions())));
}
return ret;