diff --git a/src/callrpc.cpp b/src/callrpc.cpp index 1c07573ce2..357cd2099d 100644 --- a/src/callrpc.cpp +++ b/src/callrpc.cpp @@ -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; diff --git a/src/callrpc.h b/src/callrpc.h index 8557aa23ca..3ba091325e 100644 --- a/src/callrpc.h +++ b/src/callrpc.h @@ -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 diff --git a/src/merkleblock.h b/src/merkleblock.h index 73cbf670ee..5726b6f492 100644 --- a/src/merkleblock.h +++ b/src/merkleblock.h @@ -115,6 +115,11 @@ public: * returns the merkle root, or 0 in case of failure */ uint256 ExtractMatches(std::vector &vMatch, std::vector &vnIndex); + + /** Get number of transactions the merkle proof is indicating for cross-reference with + * local blockchain knowledge. + */ + unsigned int GetNumTransactions() const { return nTransactions; }; }; diff --git a/src/primitives/bitcoin/merkleblock.h b/src/primitives/bitcoin/merkleblock.h index 7dcb4411cb..8ea5120d8d 100644 --- a/src/primitives/bitcoin/merkleblock.h +++ b/src/primitives/bitcoin/merkleblock.h @@ -120,6 +120,11 @@ public: * returns the merkle root, or 0 in case of failure */ uint256 ExtractMatches(std::vector &vMatch, std::vector &vnIndex); + + /** Get number of transactions the merkle proof is indicating for cross-reference with + * local blockchain knowledge. + */ + unsigned int GetNumTransactions() const { return nTransactions; }; }; diff --git a/src/validation.cpp b/src/validation.cpp index c64540bc45..fc6860501f 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -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; } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index acfd861315..1adae5c678 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -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;