mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-13 12:33:42 +02:00
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:
parent
621b52f80b
commit
85eb5026de
6 changed files with 42 additions and 6 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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; };
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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; };
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue