From 663e9bd32965008a43a08d1d26ea09cbb14e83aa Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 11 May 2015 15:52:17 -0700 Subject: [PATCH] FULLFEATURE: Separate serialization tx witness and the rest --- src/bitcoin-tx.cpp | 8 +-- src/main.cpp | 3 ++ src/merkleblock.cpp | 4 +- src/primitives/block.cpp | 8 ++- src/primitives/block.h | 2 + src/primitives/transaction.cpp | 41 +++++++++++--- src/primitives/transaction.h | 99 +++++++++++++++++++++++++++------- src/script/interpreter.cpp | 44 +++++++++------ src/script/script.cpp | 4 +- src/wallet.cpp | 2 +- 10 files changed, 164 insertions(+), 51 deletions(-) diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index bab53b4485..a7092e8555 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -585,19 +585,19 @@ static void MutateTxWithdrawSign(CMutableTransaction& tx, const string& flagStr) merkleBlock.header.SetBitcoinBlock(); ssProof >> merkleBlock; - CDataStream ssTx(txData, SER_NETWORK, PROTOCOL_VERSION); + CDataStream ssTx(txData, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_VERSION_MASK_BITCOIN_TX); CTransaction txBTC; ssTx >> txBTC; - CDataStream ssCoinbaseTx(coinbaseTxData, SER_NETWORK, PROTOCOL_VERSION); + CDataStream ssCoinbaseTx(coinbaseTxData, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_VERSION_MASK_BITCOIN_TX); CTransaction coinbaseTxBTC; ssCoinbaseTx >> coinbaseTxBTC; vector transactionHashes; if (merkleBlock.txn.ExtractMatches(transactionHashes) != merkleBlock.header.hashMerkleRoot || transactionHashes.size() != 2 || - transactionHashes[0] != coinbaseTxBTC.GetHash() || - transactionHashes[1] != txBTC.GetHash()) + transactionHashes[0] != coinbaseTxBTC.GetBitcoinHash() || + transactionHashes[1] != txBTC.GetBitcoinHash()) throw runtime_error("txoutproof is invalid or did not match tx"); if (nOut < 0 || (unsigned int) nOut >= txBTC.vout.size()) diff --git a/src/main.cpp b/src/main.cpp index 83991dc6a9..b295839d62 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1971,6 +1971,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin dsMerkleBlockDS << (CMerkleBlock(block, txSet)); scriptSig.PushWithdraw(std::vector(dsMerkleBlockDS.begin(), dsMerkleBlockDS.end())); + uint256 witnessHash(dsTx.GetWitnessHash()); + scriptSig << std::vector(witnessHash.begin(), witnessHash.end()); + scriptSig << OP_1 << OP_1; proofTx.vout.push_back(CTxOut(0, CScript())); diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp index 5727a1263d..9e36e8e729 100644 --- a/src/merkleblock.cpp +++ b/src/merkleblock.cpp @@ -23,7 +23,7 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter) for (unsigned int i = 0; i < block.vtx.size(); i++) { - const uint256& hash = block.vtx[i].GetHash(); + const uint256& hash = block.vtx[i].GetFullHash(); if (filter.IsRelevantAndUpdate(block.vtx[i])) { vMatch.push_back(true); @@ -54,7 +54,7 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, const std::set& txids) vMatch.push_back(true); else vMatch.push_back(false); - vHashes.push_back(hash); + vHashes.push_back(block.vtx[i].GetFullHash()); } txn = CPartialMerkleTree(vHashes, vMatch); diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index db4cafb21b..310abddb7f 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -65,8 +65,12 @@ uint256 CBlock::BuildMerkleTree(bool* fMutated) const */ vMerkleTree.clear(); vMerkleTree.reserve(vtx.size() * 2 + 16); // Safe upper bound for the number of total nodes. - for (std::vector::const_iterator it(vtx.begin()); it != vtx.end(); ++it) - vMerkleTree.push_back(it->GetHash()); + for (std::vector::const_iterator it(vtx.begin()); it != vtx.end(); ++it) { + if (IsBitcoinBlock()) + vMerkleTree.push_back(it->GetBitcoinHash()); + else + vMerkleTree.push_back(it->GetFullHash()); + } int j = 0; bool mutated = false; for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) diff --git a/src/primitives/block.h b/src/primitives/block.h index 8ac201d444..7ac09473ab 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -185,6 +185,8 @@ public: template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(*(CBlockHeader*)this); + if (IsBitcoinBlock()) + nVersion |= SERIALIZE_VERSION_MASK_BITCOIN_TX; READWRITE(vtx); } diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index dc561fb74c..58e432a6b8 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -18,10 +18,7 @@ CTxOutValue::CTxOutValue() CTxOutValue::CTxOutValue(CAmount nAmountIn) { vchCommitment.resize(nCommitmentSize); - assert(vchCommitment.size() > sizeof(nAmountIn) + 1); - memset(&vchCommitment[0], 0, vchCommitment.size() - sizeof(nAmountIn)); - for (size_t i = 0; i < sizeof(nAmountIn); ++i) - vchCommitment[vchCommitment.size() - (i + 1)] = ((nAmountIn >> (i * 8)) & 0xff); + SetToAmount(nAmountIn); } CTxOutValue::CTxOutValue(const std::vector& vchValueCommitmentIn, const std::vector& vchRangeproofIn) @@ -62,6 +59,14 @@ bool CTxOutValue::IsAmount() const return !vchCommitment[0]; } +void CTxOutValue::SetToAmount(CAmount nAmount) +{ + assert(vchCommitment.size() > sizeof(nAmount) + 1); + memset(&vchCommitment[0], 0, vchCommitment.size() - sizeof(nAmount)); + for (size_t i = 0; i < sizeof(nAmount); ++i) + vchCommitment[vchCommitment.size() - (i + 1)] = ((nAmount >> (i * 8)) & 0xff); +} + CAmount CTxOutValue::GetAmount() const { assert(IsAmount()); @@ -132,15 +137,36 @@ CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.n uint256 CMutableTransaction::GetHash() const { - return SerializeHash(*this); + if (IsCoinBase()) { + return SerializeHash(*this, SER_GETHASH, PROTOCOL_VERSION); + } else { + return SerializeHash(*this, SER_GETHASH, PROTOCOL_VERSION | SERIALIZE_VERSION_MASK_NO_WITNESS); + } } void CTransaction::UpdateHash() const { - *const_cast(&hash) = SerializeHash(*this); + bool maybeBitcoinTx = true; + for (unsigned int i = 0; i < vout.size(); i++) + if (!vout[i].nValue.IsAmount()) + maybeBitcoinTx = false; + if (maybeBitcoinTx) + *const_cast(&hashBitcoin) = SerializeHash(*this, SER_GETHASH, PROTOCOL_VERSION | SERIALIZE_VERSION_MASK_BITCOIN_TX); + + if (IsCoinBase()) { + *const_cast(&hash) = SerializeHash(*this, SER_GETHASH, PROTOCOL_VERSION); + } else { + *const_cast(&hash) = SerializeHash(*this, SER_GETHASH, PROTOCOL_VERSION | SERIALIZE_VERSION_MASK_NO_WITNESS); + } + *const_cast(&hashWitness) = SerializeHash(*this, SER_GETHASH, PROTOCOL_VERSION | SERIALIZE_VERSION_MASK_ONLY_WITNESS); + // Update full hash combining the normalized txid with the hash of the witness + CHash256 hasher; + hasher.Write((unsigned char*)&hash, hash.size()); + hasher.Write((unsigned char*)&hashWitness, hashWitness.size()); + hasher.Finalize((unsigned char*)&hashFull); } -CTransaction::CTransaction() : hash(0), nVersion(CTransaction::CURRENT_VERSION), vin(), nTxFee(0), vout(), nLockTime(0) { } +CTransaction::CTransaction() : hash(0), hashFull(0), nVersion(CTransaction::CURRENT_VERSION), vin(), nTxFee(0), vout(), nLockTime(0) { } CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), nTxFee(tx.nTxFee), vout(tx.vout), nLockTime(tx.nLockTime) { UpdateHash(); @@ -153,6 +179,7 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) { *const_cast*>(&vout) = tx.vout; *const_cast(&nLockTime) = tx.nLockTime; *const_cast(&hash) = tx.hash; + *const_cast(&hashFull) = tx.hashFull; return *this; } diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index ea8ca2dcd7..4cc9d7db3b 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -7,10 +7,16 @@ #define BITCOIN_PRIMITIVES_TRANSACTION_H #include "amount.h" +#include "hash.h" #include "script/script.h" #include "serialize.h" #include "uint256.h" +#define SERIALIZE_VERSION_MASK_NO_WITNESS 0x40000000 +#define SERIALIZE_VERSION_MASK_ONLY_WITNESS 0x80000000 +#define SERIALIZE_VERSION_MASK_BITCOIN_TX 0x20000000 +#define SERIALIZE_VERSION_MASK_PREHASH 0x10000000 + class CTxOutValue { public: @@ -29,9 +35,29 @@ public: template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(REF(CFlatData(&vchCommitment[0], &vchCommitment[nCommitmentSize]))); - READWRITE(vchRangeproof); - READWRITE(vchNonceCommitment); + bool fBitcoinTx = nVersion & SERIALIZE_VERSION_MASK_BITCOIN_TX; + bool fWitness = (nVersion & SERIALIZE_VERSION_MASK_NO_WITNESS) == 0; + bool fOnlyWitness = nVersion & SERIALIZE_VERSION_MASK_ONLY_WITNESS; + assert(!fBitcoinTx || IsAmount() || (IsNull() && ser_action.ForRead())); + if (fBitcoinTx) { + CAmount amount = 0; + if (!ser_action.ForRead()) + amount = GetAmount(); + READWRITE(amount); + if (ser_action.ForRead()) + SetToAmount(amount); + } else { + if (!fOnlyWitness) READWRITE(REF(CFlatData(&vchCommitment[0], &vchCommitment[nCommitmentSize]))); + if (fWitness) { + if (nVersion & SERIALIZE_VERSION_MASK_PREHASH) { + uint256 prehash = (CHashWriter(nType, nVersion) << vchRangeproof << vchNonceCommitment).GetHash(); + READWRITE(prehash); + } else { + READWRITE(vchRangeproof); + READWRITE(vchNonceCommitment); + } + } + } } bool IsValid() const; @@ -39,6 +65,7 @@ public: bool IsAmount() const; CAmount GetAmount() const; + void SetToAmount(CAmount nAmount); friend bool operator==(const CTxOutValue& a, const CTxOutValue& b); friend bool operator!=(const CTxOutValue& a, const CTxOutValue& b); @@ -105,9 +132,12 @@ public: template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(prevout); - READWRITE(scriptSig); - READWRITE(nSequence); + bool fWitness = (nVersion & SERIALIZE_VERSION_MASK_NO_WITNESS) == 0; + bool fOnlyWitness = nVersion & SERIALIZE_VERSION_MASK_ONLY_WITNESS; + assert((nType != SER_GETHASH && !fOnlyWitness && fWitness) || nType == SER_GETHASH); + if (!fOnlyWitness) READWRITE(prevout); + if (fWitness) READWRITE(scriptSig); + if (!fOnlyWitness) READWRITE(nSequence); } friend bool operator==(const CTxIn& a, const CTxIn& b) @@ -201,6 +231,9 @@ class CTransaction private: /** Memory only. */ const uint256 hash; + const uint256 hashWitness; // Just witness + const uint256 hashFull; // Including witness + const uint256 hashBitcoin; // For Bitcoin Transactions void UpdateHash() const; public: @@ -229,12 +262,16 @@ public: template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(*const_cast(&this->nVersion)); - nVersion = this->nVersion; - READWRITE(*const_cast*>(&vin)); - READWRITE(*const_cast(&nTxFee)); - READWRITE(*const_cast*>(&vout)); - READWRITE(*const_cast(&nLockTime)); + bool fWitness = (nVersion & SERIALIZE_VERSION_MASK_NO_WITNESS) == 0; + bool fOnlyWitness = nVersion & SERIALIZE_VERSION_MASK_ONLY_WITNESS; + bool fBitcoinTx = nVersion & SERIALIZE_VERSION_MASK_BITCOIN_TX; + assert(!fBitcoinTx || (fBitcoinTx && fWitness && !fOnlyWitness)); + assert((nType != SER_GETHASH && !fOnlyWitness && fWitness) || nType == SER_GETHASH); + if (!fOnlyWitness) READWRITE(*const_cast(&this->nVersion)); + READWRITE(*const_cast*>(&vin)); + if (!fBitcoinTx && !fOnlyWitness) READWRITE(*const_cast(&nTxFee)); + if (!fOnlyWitness) READWRITE(*const_cast*>(&vout)); + if (!fOnlyWitness) READWRITE(*const_cast(&nLockTime)); if (ser_action.ForRead()) UpdateHash(); } @@ -243,10 +280,27 @@ public: return vin.empty() && vout.empty(); } + /* Transaction hash without witness information */ const uint256& GetHash() const { return hash; } + /* Transaction hash including witness information */ + const uint256& GetFullHash() const { + return hashFull; + } + + /* Hash of just witness information */ + const uint256& GetWitnessHash() const { + return hashWitness; + } + + /* Transaction hash including witness information */ + const uint256& GetBitcoinHash() const { + assert(hashBitcoin != 0); + return hashBitcoin; + } + // Compute priority, given priority of inputs and (optionally) tx size double ComputePriority(double dPriorityInputs, unsigned int nTxSize=0) const; @@ -287,12 +341,21 @@ struct CMutableTransaction template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(this->nVersion); - nVersion = this->nVersion; - READWRITE(vin); - READWRITE(nTxFee); - READWRITE(vout); - READWRITE(nLockTime); + bool fWitness = (nVersion & SERIALIZE_VERSION_MASK_NO_WITNESS) == 0; + bool fOnlyWitness = nVersion & SERIALIZE_VERSION_MASK_ONLY_WITNESS; + bool fBitcoinTx = nVersion & SERIALIZE_VERSION_MASK_BITCOIN_TX; + assert(!fBitcoinTx); + assert((nType != SER_GETHASH && !fOnlyWitness && fWitness) || nType == SER_GETHASH); + if (!fOnlyWitness) READWRITE(this->nVersion); + READWRITE(vin); + if (!fOnlyWitness) READWRITE(nTxFee); + if (!fOnlyWitness) READWRITE(vout); + if (!fOnlyWitness) READWRITE(nLockTime); + } + + bool IsCoinBase() const + { + return (vin.size() == 1 && vin[0].prevout.IsNull()); } /** Compute the hash of this CMutableTransaction. This is computed on the diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 41aa495d79..2377c8339c 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1416,7 +1416,7 @@ bool EvalScript(vector >& stack, const CScript& script, un #endif CTransaction locktx; - CDataStream locktxStream(vlockTx, SER_NETWORK, PROTOCOL_VERSION); + CDataStream locktxStream(vlockTx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_VERSION_MASK_BITCOIN_TX); locktxStream >> locktx; if (!locktxStream.empty()) return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_LOCKTX); @@ -1425,16 +1425,16 @@ bool EvalScript(vector >& stack, const CScript& script, un if (nlocktxOut < 0 || (unsigned int)nlocktxOut >= locktx.vout.size()) return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_LOCKTX); - if (locktx.GetHash() != txHashes[1]) + if (locktx.GetBitcoinHash() != txHashes[1]) return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_LOCKTX); CTransaction coinbasetx; - CDataStream coinbasetxStream(vlockCoinbaseTx, SER_NETWORK, PROTOCOL_VERSION); + CDataStream coinbasetxStream(vlockCoinbaseTx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_VERSION_MASK_BITCOIN_TX); coinbasetxStream >> coinbasetx; if (!coinbasetxStream.empty()) return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_BLOCK); - if (coinbasetx.GetHash() != txHashes[0] || !coinbasetx.IsCoinBase()) + if (coinbasetx.GetBitcoinHash() != txHashes[0] || !coinbasetx.IsCoinBase()) return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_BLOCK); valtype vcoinbaseHeight; CScript::const_iterator coinbasepc = coinbasetx.vin[0].scriptSig.begin(); @@ -1501,7 +1501,7 @@ bool EvalScript(vector >& stack, const CScript& script, un if (withdrawOutput.nValue.GetAmount() < withdrawVal) return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_OUTPUT); - uint256 locktxHash = locktx.GetHash(); + uint256 locktxHash = locktx.GetBitcoinHash(); std::vector vlocktxHash(locktxHash.begin(), locktxHash.end()); CScript expectedWithdrawScriptPubKeyStart = CScript() << OP_IF << nLockHeight << std::vector(vlocktxHash.rbegin(), vlocktxHash.rend()) << nlocktxOut @@ -1621,11 +1621,18 @@ bool EvalScript(vector >& stack, const CScript& script, un if (proofType == 1) { // Double-spent withdraw // We need to have complete proof that two transactions double-spent each other: // So we read the following from the stack: - // 9. merkle block with tx we are proving against (ie our input tx) - // 10. the full transaction of the original withdraw - // 11. the input index in the transaction above which shows the double-spend - // 12. the transaction which is spent in the above input - // 13. merkle block containing the original withdraw tx, required only if they are not in the same block + // 9. witness hash of the tx we are proving against (ie our input tx) + // 10. merkle block with tx we are proving against (ie our input tx) + // 11. the full transaction of the original withdraw + // 12. the input index in the transaction above which shows the double-spend + // 13. the transaction which is spent in the above input + // 14. merkle block containing the original withdraw tx, required only if they are not in the same block + if (stack.size() < size_t(-(stackReadPos))) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + valtype vInputTxWitnessHash = stacktop(stackReadPos--); + if (vInputTxWitnessHash.size() != 32) + return set_error(serror, SCRIPT_ERR_REORG_VERIFY_FORMAT); + valtype vmerkleBlockInputTx; if (!WithdrawProofReadStackItem(stack, fRequireMinimal, &stackReadPos, vmerkleBlockInputTx)) return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); @@ -1655,7 +1662,14 @@ bool EvalScript(vector >& stack, const CScript& script, un if (inputTxHashes.size() != 1 && inputTxHashes.size() != 2) return set_error(serror, SCRIPT_ERR_REORG_VERIFY_FRAUD_BLOCK); - if (inputTxHashes[0] != checker.GetPrevOut().hash) + uint256 inputTxWitnessHash(vInputTxWitnessHash); + uint256 inputTxFullHash(checker.GetPrevOut().hash); + CHash256 hasher; + hasher.Write(inputTxFullHash.begin(), inputTxFullHash.size()); + hasher.Write(inputTxWitnessHash.begin(), inputTxWitnessHash.size()); + hasher.Finalize(inputTxFullHash.begin()); + + if (inputTxHashes[0] != inputTxFullHash) return set_error(serror, SCRIPT_ERR_REORG_VERIFY_FRAUD_BLOCK); CTransaction originalWithdrawTx; @@ -1681,9 +1695,9 @@ bool EvalScript(vector >& stack, const CScript& script, un if (originalWithdrawHashes.size() != 1 || merkleBlockOriginalWithdrawTx.header.GetHash() == merkleBlockInputTx.header.GetHash()) return set_error(serror, SCRIPT_ERR_REORG_VERIFY_FRAUD_ORIG_BLOCK); - if (originalWithdrawHashes[0] != originalWithdrawTx.GetHash()) + if (originalWithdrawHashes[0] != originalWithdrawTx.GetFullHash()) return set_error(serror, SCRIPT_ERR_REORG_VERIFY_FRAUD_ORIG_BLOCK); - } else if (inputTxHashes[1] != originalWithdrawTx.GetHash()) + } else if (inputTxHashes[1] != originalWithdrawTx.GetFullHash()) return set_error(serror, SCRIPT_ERR_REORG_VERIFY_FRAUD_BLOCK); int noriginalWithdrawTxIn = CScriptNum(voriginalWithdrawTxInIndex, fRequireMinimal).getint(); @@ -1830,9 +1844,9 @@ public: void SerializeOutput(S &s, unsigned int nOutput, int nType, int nVersion) const { if (fHashSingle && nOutput != nIn) // Do not lock-in the txout payee at other indices as txin - ::Serialize(s, CTxOut(), nType, nVersion); + ::Serialize(s, CTxOut(), nType, nVersion | SERIALIZE_VERSION_MASK_PREHASH); else - ::Serialize(s, txTo.vout[nOutput], nType, nVersion); + ::Serialize(s, txTo.vout[nOutput], nType, nVersion | SERIALIZE_VERSION_MASK_PREHASH); } /** Serialize txTo */ diff --git a/src/script/script.cpp b/src/script/script.cpp index cfd7fd30c4..470fc18334 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -473,12 +473,12 @@ COutPoint CScript::GetWithdrawSpent() const if (!PopWithdrawPush(pushes, &vTx)) return COutPoint(); CTransaction tx; - CDataStream(vTx, SER_NETWORK, PROTOCOL_VERSION) >> tx; + CDataStream(vTx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_VERSION_MASK_BITCOIN_TX) >> tx; if (ntxOut < 0 || (unsigned int)ntxOut >= tx.vout.size()) return COutPoint(); - return COutPoint(tx.GetHash(), ntxOut); + return COutPoint(tx.GetBitcoinHash(), ntxOut); } catch (std::exception& e) { return COutPoint(); } diff --git a/src/wallet.cpp b/src/wallet.cpp index ef2a4cb0e0..4cfdfa02b0 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -2488,7 +2488,7 @@ int CMerkleTx::GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const // Make sure the merkle branch connects to this block if (!fMerkleVerified) { - if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) + if (CBlock::CheckMerkleBranch(GetFullHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) return 0; fMerkleVerified = true; }