diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp index bc99b8cdcd..46d2bb4642 100644 --- a/src/bench/block_assemble.cpp +++ b/src/bench/block_assemble.cpp @@ -93,7 +93,8 @@ static void AssembleBlock(benchmark::State& state) for (size_t b{0}; b < NUM_BLOCKS; ++b) { CMutableTransaction tx; tx.vin.push_back(MineBlock(SCRIPT_PUB)); - tx.vin.back().scriptWitness = witness; + tx.witness.vtxinwit.resize(1); + tx.witness.vtxinwit.back().scriptWitness = witness; tx.vout.emplace_back(1337, SCRIPT_PUB); if (NUM_BLOCKS - b >= COINBASE_MATURITY) txs.at(b) = MakeTransactionRef(tx); diff --git a/src/bench/mempool_eviction.cpp b/src/bench/mempool_eviction.cpp index 96b7c469e2..c53070b372 100644 --- a/src/bench/mempool_eviction.cpp +++ b/src/bench/mempool_eviction.cpp @@ -8,6 +8,7 @@ #include #include +#include static void AddTx(const CTransactionRef& tx, const CAmount& nFee, CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs) { @@ -30,7 +31,8 @@ static void MempoolEviction(benchmark::State& state) CMutableTransaction tx1 = CMutableTransaction(); tx1.vin.resize(1); tx1.vin[0].scriptSig = CScript() << OP_1; - tx1.vin[0].scriptWitness.stack.push_back({1}); + tx1.witness.vtxinwit.resize(1); + tx1.witness.vtxinwit[0].scriptWitness.stack.push_back({1}); tx1.vout.resize(1); tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL; tx1.vout[0].nValue = 10 * COIN; @@ -38,7 +40,8 @@ static void MempoolEviction(benchmark::State& state) CMutableTransaction tx2 = CMutableTransaction(); tx2.vin.resize(1); tx2.vin[0].scriptSig = CScript() << OP_2; - tx2.vin[0].scriptWitness.stack.push_back({2}); + tx2.witness.vtxinwit.resize(1); + tx2.witness.vtxinwit[0].scriptWitness.stack.push_back({2}); tx2.vout.resize(1); tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL; tx2.vout[0].nValue = 10 * COIN; @@ -47,7 +50,8 @@ static void MempoolEviction(benchmark::State& state) tx3.vin.resize(1); tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0); tx3.vin[0].scriptSig = CScript() << OP_2; - tx3.vin[0].scriptWitness.stack.push_back({3}); + tx3.witness.vtxinwit.resize(1); + tx3.witness.vtxinwit[0].scriptWitness.stack.push_back({3}); tx3.vout.resize(1); tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL; tx3.vout[0].nValue = 10 * COIN; @@ -56,10 +60,12 @@ static void MempoolEviction(benchmark::State& state) tx4.vin.resize(2); tx4.vin[0].prevout.SetNull(); tx4.vin[0].scriptSig = CScript() << OP_4; - tx4.vin[0].scriptWitness.stack.push_back({4}); + tx4.witness.vtxinwit.resize(1); + tx4.witness.vtxinwit[0].scriptWitness.stack.push_back({4}); tx4.vin[1].prevout.SetNull(); tx4.vin[1].scriptSig = CScript() << OP_4; - tx4.vin[1].scriptWitness.stack.push_back({4}); + tx4.witness.vtxinwit.resize(2); + tx4.witness.vtxinwit[1].scriptWitness.stack.push_back({4}); tx4.vout.resize(2); tx4.vout[0].scriptPubKey = CScript() << OP_4 << OP_EQUAL; tx4.vout[0].nValue = 10 * COIN; @@ -70,10 +76,12 @@ static void MempoolEviction(benchmark::State& state) tx5.vin.resize(2); tx5.vin[0].prevout = COutPoint(tx4.GetHash(), 0); tx5.vin[0].scriptSig = CScript() << OP_4; - tx5.vin[0].scriptWitness.stack.push_back({4}); + tx5.witness.vtxinwit.resize(1); + tx5.witness.vtxinwit[0].scriptWitness.stack.push_back({4}); tx5.vin[1].prevout.SetNull(); tx5.vin[1].scriptSig = CScript() << OP_5; - tx5.vin[1].scriptWitness.stack.push_back({5}); + tx5.witness.vtxinwit.resize(2); + tx5.witness.vtxinwit[1].scriptWitness.stack.push_back({5}); tx5.vout.resize(2); tx5.vout[0].scriptPubKey = CScript() << OP_5 << OP_EQUAL; tx5.vout[0].nValue = 10 * COIN; @@ -84,10 +92,12 @@ static void MempoolEviction(benchmark::State& state) tx6.vin.resize(2); tx6.vin[0].prevout = COutPoint(tx4.GetHash(), 1); tx6.vin[0].scriptSig = CScript() << OP_4; - tx6.vin[0].scriptWitness.stack.push_back({4}); + tx6.witness.vtxinwit.resize(1); + tx6.witness.vtxinwit[0].scriptWitness.stack.push_back({4}); tx6.vin[1].prevout.SetNull(); tx6.vin[1].scriptSig = CScript() << OP_6; - tx6.vin[1].scriptWitness.stack.push_back({6}); + tx6.witness.vtxinwit.resize(2); + tx6.witness.vtxinwit[1].scriptWitness.stack.push_back({6}); tx6.vout.resize(2); tx6.vout[0].scriptPubKey = CScript() << OP_6 << OP_EQUAL; tx6.vout[0].nValue = 10 * COIN; @@ -98,10 +108,12 @@ static void MempoolEviction(benchmark::State& state) tx7.vin.resize(2); tx7.vin[0].prevout = COutPoint(tx5.GetHash(), 0); tx7.vin[0].scriptSig = CScript() << OP_5; - tx7.vin[0].scriptWitness.stack.push_back({5}); + tx7.witness.vtxinwit.resize(1); + tx7.witness.vtxinwit[0].scriptWitness.stack.push_back({5}); tx7.vin[1].prevout = COutPoint(tx6.GetHash(), 0); tx7.vin[1].scriptSig = CScript() << OP_6; - tx7.vin[1].scriptWitness.stack.push_back({6}); + tx7.witness.vtxinwit.resize(2); + tx7.witness.vtxinwit[1].scriptWitness.stack.push_back({6}); tx7.vout.resize(2); tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL; tx7.vout[0].nValue = 10 * COIN; diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp index 312b66e38a..07810507fa 100644 --- a/src/bench/verify_script.cpp +++ b/src/bench/verify_script.cpp @@ -74,7 +74,8 @@ static void VerifyScriptBench(benchmark::State& state) CScript witScriptPubkey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkeyHash) << OP_EQUALVERIFY << OP_CHECKSIG; const CMutableTransaction& txCredit = BuildCreditingTransaction(scriptPubKey); CMutableTransaction txSpend = BuildSpendingTransaction(scriptSig, txCredit); - CScriptWitness& witness = txSpend.vin[0].scriptWitness; + txSpend.witness.vtxinwit.resize(1); + CScriptWitness& witness = txSpend.witness.vtxinwit[0].scriptWitness; witness.stack.emplace_back(); key.Sign(SignatureHash(witScriptPubkey, txSpend, 0, SIGHASH_ALL, txCredit.vout[0].nValue, SigVersion::WITNESS_V0), witness.stack.back()); witness.stack.back().push_back(static_cast(SIGHASH_ALL)); @@ -86,7 +87,7 @@ static void VerifyScriptBench(benchmark::State& state) bool success = VerifyScript( txSpend.vin[0].scriptSig, txCredit.vout[0].scriptPubKey, - &txSpend.vin[0].scriptWitness, + &txSpend.witness.vtxinwit[0].scriptWitness, flags, MutableTransactionSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue), &err); diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 74f114e8ab..0783303030 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -649,7 +649,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) if (!fHashSingle || (i < mergedTx.vout.size())) ProduceSignature(keystore, MutableTransactionSignatureCreator(&mergedTx, i, amount, nHashType), prevPubKey, sigdata); - UpdateInput(txin, sigdata); + UpdateTransaction(mergedTx, i, sigdata); } tx = mergedTx; diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp index 2a87a936b1..4d7df48f24 100644 --- a/src/consensus/merkle.cpp +++ b/src/consensus/merkle.cpp @@ -79,8 +79,11 @@ uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated) leaves.resize(block.vtx.size()); leaves[0].SetNull(); // The witness hash of the coinbase is 0. for (size_t s = 1; s < block.vtx.size(); s++) { - leaves[s] = block.vtx[s]->GetWitnessHash(); + if (g_con_elementswitness) { + leaves[s] = block.vtx[s]->GetWitnessOnlyHash(); + } else { + leaves[s] = block.vtx[s]->GetWitnessHash(); + } } return ComputeMerkleRoot(std::move(leaves), mutated); } - diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index 6e1633073e..5fbf5153d3 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -159,20 +159,21 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i for (unsigned int i = 0; i < tx.vin.size(); i++) { - std::string err; - if (tx.vin[i].m_is_pegin && !IsValidPeginWitness(tx.vin[i].m_pegin_witness, tx.vin[i].prevout, err, true)) { - continue; - } - CTxOut prevout; if (tx.vin[i].m_is_pegin) { - prevout = GetPeginOutputFromWitness(tx.vin[i].m_pegin_witness); + std::string err; + if (tx.witness.vtxinwit.size() <= i || !IsValidPeginWitness(tx.witness.vtxinwit[i].m_pegin_witness, tx.vin[i].prevout, err, true)) { + continue; + } + prevout = GetPeginOutputFromWitness(tx.witness.vtxinwit[i].m_pegin_witness); } else { const Coin& coin = inputs.AccessCoin(tx.vin[i].prevout); assert(!coin.IsSpent()); prevout = coin.out; } - nSigOps += CountWitnessSigOps(tx.vin[i].scriptSig, prevout.scriptPubKey, &tx.vin[i].scriptWitness, flags); + + const CScriptWitness* pScriptWitness = tx.witness.vtxinwit.size() > i ? &tx.witness.vtxinwit[i].scriptWitness : NULL; + nSigOps += CountWitnessSigOps(tx.vin[i].scriptSig, prevout.scriptPubKey, pScriptWitness, flags); } return nSigOps; } @@ -241,10 +242,10 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins if (tx.vin[i].m_is_pegin) { // Check existence and validity of pegin witness std::string err; - if (!IsValidPeginWitness(tx.vin[i].m_pegin_witness, prevout, err, true)) { + if (tx.witness.vtxinwit.size() <= i || !IsValidPeginWitness(tx.witness.vtxinwit[i].m_pegin_witness, prevout, err, true)) { return state.DoS(0, false, REJECT_PEGIN, "bad-pegin-witness"); } - std::pair pegin = std::make_pair(uint256(tx.vin[i].m_pegin_witness.stack[2]), prevout); + std::pair pegin = std::make_pair(uint256(tx.witness.vtxinwit[i].m_pegin_witness.stack[2]), prevout); if (inputs.IsPeginSpent(pegin)) { return state.Invalid(false, REJECT_INVALID, "bad-txns-double-pegin", strprintf("Double-pegin of %s:%d", prevout.hash.ToString(), prevout.n)); } @@ -255,7 +256,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins setPeginsSpent.insert(pegin); // Tally the input amount. - const CTxOut out = GetPeginOutputFromWitness(tx.vin[i].m_pegin_witness); + const CTxOut out = GetPeginOutputFromWitness(tx.witness.vtxinwit[i].m_pegin_witness); if (!MoneyRange(out.nValue)) { return state.DoS(100, false, REJECT_INVALID, "bad-txns-pegin-inputvalue-outofrange"); } diff --git a/src/consensus/validation.h b/src/consensus/validation.h index 7c65b787bf..8c1fa5cdbb 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -103,10 +103,16 @@ static inline int64_t GetBlockWeight(const CBlock& block) { return ::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(block, PROTOCOL_VERSION); } -static inline int64_t GetTransactionInputWeight(const CTxIn& txin) + +static inline int64_t GetTransactionInputWeight(const CTransaction& tx, const size_t nIn) { // scriptWitness size is added here because witnesses and txins are split up in segwit serialization. - return ::GetSerializeSize(txin, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(txin, PROTOCOL_VERSION) + ::GetSerializeSize(txin.scriptWitness.stack, PROTOCOL_VERSION); + assert(tx.witness.vtxinwit.size() > nIn); + //TODO(rebase) only count CA/CT witnesses when g_con_elementswitness is true + return ::GetSerializeSize(tx.vin[nIn], PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + + ::GetSerializeSize(tx.vin[nIn], PROTOCOL_VERSION) + + ::GetSerializeSize(tx.witness.vtxinwit[nIn].scriptWitness.stack, PROTOCOL_VERSION) + + ::GetSerializeSize(tx.witness.vtxinwit[nIn].m_pegin_witness.stack, PROTOCOL_VERSION); } #endif // BITCOIN_CONSENSUS_VALIDATION_H diff --git a/src/core_memusage.h b/src/core_memusage.h index 6f1ace9980..8c6a5ea2ba 100644 --- a/src/core_memusage.h +++ b/src/core_memusage.h @@ -18,9 +18,37 @@ static inline size_t RecursiveDynamicUsage(const COutPoint& out) { } static inline size_t RecursiveDynamicUsage(const CTxIn& in) { - size_t mem = RecursiveDynamicUsage(in.scriptSig) + RecursiveDynamicUsage(in.prevout) + memusage::DynamicUsage(in.scriptWitness.stack); - for (std::vector >::const_iterator it = in.scriptWitness.stack.begin(); it != in.scriptWitness.stack.end(); it++) { - mem += memusage::DynamicUsage(*it); + size_t mem = RecursiveDynamicUsage(in.scriptSig) + RecursiveDynamicUsage(in.prevout); + return mem; +} + +static inline size_t RecursiveDynamicUsage(const CScriptWitness& scriptWit) { + size_t mem = memusage::DynamicUsage(scriptWit.stack); + for (std::vector >::const_iterator it = scriptWit.stack.begin(); it != scriptWit.stack.end(); it++) { + mem += memusage::DynamicUsage(*it); + } + return mem; +} + +static inline size_t RecursiveDynamicUsage(const CTxInWitness& txInWit) { + size_t mem = RecursiveDynamicUsage(txInWit.scriptWitness); + mem += RecursiveDynamicUsage(txInWit.m_pegin_witness); + return mem; +} + +static inline size_t RecursiveDynamicUsage(const CTxOutWitness& txOutWit) { + size_t mem = memusage::DynamicUsage(txOutWit.vchRangeproof); + mem += memusage::DynamicUsage(txOutWit.vchSurjectionproof); + return mem; +} + +static inline size_t RecursiveDynamicUsage(const CTxWitness& wit) { + size_t mem = memusage::DynamicUsage(wit.vtxinwit) + memusage::DynamicUsage(wit.vtxoutwit); + for (const auto& txInWit: wit.vtxinwit) { + mem += RecursiveDynamicUsage(txInWit); + } + for (const auto& txOutWit: wit.vtxoutwit) { + mem += RecursiveDynamicUsage(txOutWit); } return mem; } diff --git a/src/core_write.cpp b/src/core_write.cpp index b158310db5..c48130ecde 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -200,6 +200,10 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, { entry.pushKV("txid", tx.GetHash().GetHex()); entry.pushKV("hash", tx.GetWitnessHash().GetHex()); + if (g_con_elementswitness) { + entry.pushKV("wtxid", tx.GetWitnessHash().GetHex()); + entry.pushKV("withash", tx.GetWitnessOnlyHash().GetHex()); + } entry.pushKV("version", tx.nVersion); entry.pushKV("size", (int)::GetSerializeSize(tx, PROTOCOL_VERSION)); entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR); @@ -220,19 +224,22 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())); in.pushKV("scriptSig", o); - if (!tx.vin[i].scriptWitness.IsNull()) { - UniValue txinwitness(UniValue::VARR); - for (const auto& item : tx.vin[i].scriptWitness.stack) { - txinwitness.push_back(HexStr(item.begin(), item.end())); + if (tx.witness.vtxinwit.size() > i) { + const CScriptWitness &scriptWitness = tx.witness.vtxinwit[i].scriptWitness; + if (!scriptWitness.IsNull()) { + UniValue txinwitness(UniValue::VARR); + for (const auto &item : scriptWitness.stack) { + txinwitness.push_back(HexStr(item.begin(), item.end())); + } + in.pushKV("txinwitness", txinwitness); } - in.pushKV("txinwitness", txinwitness); } // ELEMENTS: in.pushKV("is_pegin", txin.m_is_pegin); - if (!tx.vin[i].m_pegin_witness.IsNull()) { + if (tx.witness.vtxinwit.size() > i && !tx.witness.vtxinwit[i].m_pegin_witness.IsNull()) { UniValue pegin_witness(UniValue::VARR); - for (const auto& item : tx.vin[i].m_pegin_witness.stack) { + for (const auto& item : tx.witness.vtxinwit[i].m_pegin_witness.stack) { pegin_witness.push_back(HexStr(item.begin(), item.end())); } in.pushKV("pegin_witness", pegin_witness); diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 3507c68bf8..2e6acdd6fb 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -218,10 +218,11 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) { // We don't care if witness for this input is empty, since it must not be bloated. // If the script is invalid without witness, it would be caught sooner or later during validation. - if (tx.vin[i].scriptWitness.IsNull()) + if (tx.witness.vtxinwit.size() <= i || tx.witness.vtxinwit[i].scriptWitness.IsNull()) { continue; + } - const CTxOut &prev = tx.vin[i].m_is_pegin ? GetPeginOutputFromWitness(tx.vin[i].m_pegin_witness) : mapInputs.AccessCoin(tx.vin[i].prevout).out; + const CTxOut &prev = tx.vin[i].m_is_pegin ? GetPeginOutputFromWitness(tx.witness.vtxinwit[i].m_pegin_witness) : mapInputs.AccessCoin(tx.vin[i].prevout).out; // get the scriptPubKey corresponding to this input: CScript prevScript = prev.scriptPubKey; @@ -247,13 +248,14 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) // Check P2WSH standard limits if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) { - if (tx.vin[i].scriptWitness.stack.back().size() > MAX_STANDARD_P2WSH_SCRIPT_SIZE) + const CScriptWitness& scriptWitness = tx.witness.vtxinwit[i].scriptWitness; + if (scriptWitness.stack.back().size() > MAX_STANDARD_P2WSH_SCRIPT_SIZE) return false; - size_t sizeWitnessStack = tx.vin[i].scriptWitness.stack.size() - 1; + size_t sizeWitnessStack = scriptWitness.stack.size() - 1; if (sizeWitnessStack > MAX_STANDARD_P2WSH_STACK_ITEMS) return false; for (unsigned int j = 0; j < sizeWitnessStack; j++) { - if (tx.vin[i].scriptWitness.stack[j].size() > MAX_STANDARD_P2WSH_STACK_ITEM_SIZE) + if (scriptWitness.stack[j].size() > MAX_STANDARD_P2WSH_STACK_ITEM_SIZE) return false; } } @@ -275,7 +277,7 @@ int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost) return GetVirtualTransactionSize(GetTransactionWeight(tx), nSigOpCost); } -int64_t GetVirtualTransactionInputSize(const CTxIn& txin, int64_t nSigOpCost) +int64_t GetVirtualTransactionInputSize(const CTransaction& tx, const size_t nIn, int64_t nSigOpCost) { - return GetVirtualTransactionSize(GetTransactionInputWeight(txin), nSigOpCost); + return GetVirtualTransactionSize(GetTransactionInputWeight(tx, nIn), nSigOpCost); } diff --git a/src/policy/policy.h b/src/policy/policy.h index 3d47ac1267..5b12d97af5 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -105,6 +105,6 @@ extern unsigned int nBytesPerSigOp; /** Compute the virtual transaction size (weight reinterpreted as bytes). */ int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost); int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost = 0); -int64_t GetVirtualTransactionInputSize(const CTxIn& tx, int64_t nSigOpCost = 0); +int64_t GetVirtualTransactionInputSize(const CTransaction& tx, const size_t nIn, int64_t nSigOpCost = 0); #endif // BITCOIN_POLICY_POLICY_H diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 8d3c83e15f..d5c677de09 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -57,7 +57,8 @@ std::string CTxOut::ToString() const } CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {} -CMutableTransaction::CMutableTransaction(const CTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime) {} +CMutableTransaction::CMutableTransaction(const CTransaction& tx) : + vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime), witness(tx.witness) {} uint256 CMutableTransaction::GetHash() const { @@ -77,10 +78,39 @@ uint256 CTransaction::ComputeWitnessHash() const return SerializeHash(*this, SER_GETHASH, 0); } +// ELEMENTS ONLY +uint256 CTransaction::GetWitnessOnlyHash() const +{ + assert(g_con_elementswitness); + + std::vector leaves; + leaves.reserve(std::max(vin.size(), vout.size())); + /* Inputs */ + for (size_t i = 0; i < vin.size(); ++i) { + const CTxInWitness& txinwit = witness.vtxinwit.size() <= i || vin[i].prevout.IsNull() ? CTxInWitness() : witness.vtxinwit[i]; + leaves.push_back(txinwit.GetHash()); + } + uint256 hashIn = ComputeFastMerkleRoot(leaves); + leaves.clear(); + /* Outputs */ + for (size_t i = 0; i < vout.size(); ++i) { + const CTxOutWitness& txoutwit = witness.vtxoutwit.size() <= i ? CTxOutWitness() : witness.vtxoutwit[i]; + leaves.push_back(txoutwit.GetHash()); + } + uint256 hashOut = ComputeFastMerkleRoot(leaves); + leaves.clear(); + /* Combined */ + leaves.push_back(hashIn); + leaves.push_back(hashOut); + return ComputeFastMerkleRoot(leaves); +} + /* For backward compatibility, the hash is initialized to 0. TODO: remove the need for this default constructor entirely. */ CTransaction::CTransaction() : vin(), vout(), nVersion(CTransaction::CURRENT_VERSION), nLockTime(0), hash{}, m_witness_hash{} {} -CTransaction::CTransaction(const CMutableTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {} -CTransaction::CTransaction(CMutableTransaction&& tx) : vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {} +CTransaction::CTransaction(const CMutableTransaction& tx) : + vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime), witness(tx.witness), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {} +CTransaction::CTransaction(CMutableTransaction&& tx) : + vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), witness(std::move(tx.witness)),hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {} CAmount CTransaction::GetValueOut() const { @@ -109,7 +139,7 @@ std::string CTransaction::ToString() const nLockTime); for (const auto& tx_in : vin) str += " " + tx_in.ToString() + "\n"; - for (const auto& tx_in : vin) + for (const auto& tx_in : witness.vtxinwit) str += " " + tx_in.scriptWitness.ToString() + "\n"; for (const auto& tx_out : vout) str += " " + tx_out.ToString() + "\n"; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 3b0636dde1..687682abb2 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -11,6 +11,7 @@ #include