Browse Source

Move CTxInWitness inside CTxIn

0.14
Pieter Wuille 8 years ago
parent
commit
f6fb7acda4
  1. 5
      src/bench/verify_script.cpp
  2. 2
      src/bitcoin-tx.cpp
  3. 28
      src/core_memusage.h
  4. 4
      src/core_write.cpp
  5. 4
      src/miner.cpp
  6. 4
      src/net_processing.cpp
  7. 8
      src/policy/policy.cpp
  8. 10
      src/primitives/transaction.cpp
  9. 99
      src/primitives/transaction.h
  10. 11
      src/rpc/rawtransaction.cpp
  11. 3
      src/script/bitcoinconsensus.cpp
  12. 2
      src/script/script.h
  13. 9
      src/script/sign.cpp
  14. 3
      src/test/script_tests.cpp
  15. 32
      src/test/sigopcount_tests.cpp
  16. 17
      src/test/transaction_tests.cpp
  17. 23
      src/validation.cpp
  18. 6
      src/wallet/wallet.cpp

5
src/bench/verify_script.cpp

@ -36,7 +36,6 @@ static CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, co
txSpend.nLockTime = 0; txSpend.nLockTime = 0;
txSpend.vin.resize(1); txSpend.vin.resize(1);
txSpend.vout.resize(1); txSpend.vout.resize(1);
txSpend.wit.vtxinwit.resize(1);
txSpend.vin[0].prevout.hash = txCredit.GetHash(); txSpend.vin[0].prevout.hash = txCredit.GetHash();
txSpend.vin[0].prevout.n = 0; txSpend.vin[0].prevout.n = 0;
txSpend.vin[0].scriptSig = scriptSig; txSpend.vin[0].scriptSig = scriptSig;
@ -68,7 +67,7 @@ static void VerifyScriptBench(benchmark::State& state)
CScript witScriptPubkey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkeyHash) << OP_EQUALVERIFY << OP_CHECKSIG; CScript witScriptPubkey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkeyHash) << OP_EQUALVERIFY << OP_CHECKSIG;
CTransaction txCredit = BuildCreditingTransaction(scriptPubKey); CTransaction txCredit = BuildCreditingTransaction(scriptPubKey);
CMutableTransaction txSpend = BuildSpendingTransaction(scriptSig, txCredit); CMutableTransaction txSpend = BuildSpendingTransaction(scriptSig, txCredit);
CScriptWitness& witness = txSpend.wit.vtxinwit[0].scriptWitness; CScriptWitness& witness = txSpend.vin[0].scriptWitness;
witness.stack.emplace_back(); witness.stack.emplace_back();
key.Sign(SignatureHash(witScriptPubkey, txSpend, 0, SIGHASH_ALL, txCredit.vout[0].nValue, SIGVERSION_WITNESS_V0), witness.stack.back(), 0); key.Sign(SignatureHash(witScriptPubkey, txSpend, 0, SIGHASH_ALL, txCredit.vout[0].nValue, SIGVERSION_WITNESS_V0), witness.stack.back(), 0);
witness.stack.back().push_back(static_cast<unsigned char>(SIGHASH_ALL)); witness.stack.back().push_back(static_cast<unsigned char>(SIGHASH_ALL));
@ -80,7 +79,7 @@ static void VerifyScriptBench(benchmark::State& state)
bool success = VerifyScript( bool success = VerifyScript(
txSpend.vin[0].scriptSig, txSpend.vin[0].scriptSig,
txCredit.vout[0].scriptPubKey, txCredit.vout[0].scriptPubKey,
&txSpend.wit.vtxinwit[0].scriptWitness, &txSpend.vin[0].scriptWitness,
flags, flags,
MutableTransactionSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue), MutableTransactionSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue),
&err); &err);

2
src/bitcoin-tx.cpp

@ -494,7 +494,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i)); sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i));
UpdateTransaction(mergedTx, i, sigdata); UpdateTransaction(mergedTx, i, sigdata);
if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount))) if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount)))
fComplete = false; fComplete = false;
} }

28
src/core_memusage.h

@ -18,35 +18,19 @@ static inline size_t RecursiveDynamicUsage(const COutPoint& out) {
} }
static inline size_t RecursiveDynamicUsage(const CTxIn& in) { static inline size_t RecursiveDynamicUsage(const CTxIn& in) {
return RecursiveDynamicUsage(in.scriptSig) + RecursiveDynamicUsage(in.prevout); size_t mem = RecursiveDynamicUsage(in.scriptSig) + RecursiveDynamicUsage(in.prevout) + memusage::DynamicUsage(in.scriptWitness.stack);
} for (std::vector<std::vector<unsigned char> >::const_iterator it = in.scriptWitness.stack.begin(); it != in.scriptWitness.stack.end(); it++) {
static inline size_t RecursiveDynamicUsage(const CTxOut& out) {
return RecursiveDynamicUsage(out.scriptPubKey);
}
static inline size_t RecursiveDynamicUsage(const CScriptWitness& scriptWit) {
size_t mem = memusage::DynamicUsage(scriptWit.stack);
for (std::vector<std::vector<unsigned char> >::const_iterator it = scriptWit.stack.begin(); it != scriptWit.stack.end(); it++) {
mem += memusage::DynamicUsage(*it); mem += memusage::DynamicUsage(*it);
} }
return mem; return mem;
} }
static inline size_t RecursiveDynamicUsage(const CTxInWitness& txinwit) { static inline size_t RecursiveDynamicUsage(const CTxOut& out) {
return RecursiveDynamicUsage(txinwit.scriptWitness); return RecursiveDynamicUsage(out.scriptPubKey);
}
static inline size_t RecursiveDynamicUsage(const CTxWitness& txwit) {
size_t mem = memusage::DynamicUsage(txwit.vtxinwit);
for (std::vector<CTxInWitness>::const_iterator it = txwit.vtxinwit.begin(); it != txwit.vtxinwit.end(); it++) {
mem += RecursiveDynamicUsage(*it);
}
return mem;
} }
static inline size_t RecursiveDynamicUsage(const CTransaction& tx) { static inline size_t RecursiveDynamicUsage(const CTransaction& tx) {
size_t mem = memusage::DynamicUsage(tx.vin) + memusage::DynamicUsage(tx.vout) + RecursiveDynamicUsage(tx.wit); size_t mem = memusage::DynamicUsage(tx.vin) + memusage::DynamicUsage(tx.vout);
for (std::vector<CTxIn>::const_iterator it = tx.vin.begin(); it != tx.vin.end(); it++) { for (std::vector<CTxIn>::const_iterator it = tx.vin.begin(); it != tx.vin.end(); it++) {
mem += RecursiveDynamicUsage(*it); mem += RecursiveDynamicUsage(*it);
} }
@ -57,7 +41,7 @@ static inline size_t RecursiveDynamicUsage(const CTransaction& tx) {
} }
static inline size_t RecursiveDynamicUsage(const CMutableTransaction& tx) { static inline size_t RecursiveDynamicUsage(const CMutableTransaction& tx) {
size_t mem = memusage::DynamicUsage(tx.vin) + memusage::DynamicUsage(tx.vout) + RecursiveDynamicUsage(tx.wit); size_t mem = memusage::DynamicUsage(tx.vin) + memusage::DynamicUsage(tx.vout);
for (std::vector<CTxIn>::const_iterator it = tx.vin.begin(); it != tx.vin.end(); it++) { for (std::vector<CTxIn>::const_iterator it = tx.vin.begin(); it != tx.vin.end(); it++) {
mem += RecursiveDynamicUsage(*it); mem += RecursiveDynamicUsage(*it);
} }

4
src/core_write.cpp

@ -168,9 +168,9 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry)
o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true)); o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true));
o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())); o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
in.pushKV("scriptSig", o); in.pushKV("scriptSig", o);
if (!tx.wit.IsNull() && i < tx.wit.vtxinwit.size() && !tx.wit.vtxinwit[i].IsNull()) { if (!tx.vin[i].scriptWitness.IsNull()) {
UniValue txinwitness(UniValue::VARR); UniValue txinwitness(UniValue::VARR);
for (const auto& item : tx.wit.vtxinwit[i].scriptWitness.stack) { for (const auto& item : tx.vin[i].scriptWitness.stack) {
txinwitness.push_back(HexStr(item.begin(), item.end())); txinwitness.push_back(HexStr(item.begin(), item.end()));
} }
in.pushKV("txinwitness", txinwitness); in.pushKV("txinwitness", txinwitness);

4
src/miner.cpp

@ -245,7 +245,7 @@ bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& packa
BOOST_FOREACH (const CTxMemPool::txiter it, package) { BOOST_FOREACH (const CTxMemPool::txiter it, package) {
if (!IsFinalTx(it->GetTx(), nHeight, nLockTimeCutoff)) if (!IsFinalTx(it->GetTx(), nHeight, nLockTimeCutoff))
return false; return false;
if (!fIncludeWitness && !it->GetTx().wit.IsNull()) if (!fIncludeWitness && it->GetTx().HasWitness())
return false; return false;
if (fNeedSizeAccounting) { if (fNeedSizeAccounting) {
uint64_t nTxSize = ::GetSerializeSize(it->GetTx(), SER_NETWORK, PROTOCOL_VERSION); uint64_t nTxSize = ::GetSerializeSize(it->GetTx(), SER_NETWORK, PROTOCOL_VERSION);
@ -554,7 +554,7 @@ void BlockAssembler::addPriorityTxs()
} }
// cannot accept witness transactions into a non-witness block // cannot accept witness transactions into a non-witness block
if (!fIncludeWitness && !iter->GetTx().wit.IsNull()) if (!fIncludeWitness && iter->GetTx().HasWitness())
continue; continue;
// If tx is dependent on other mempool txs which haven't yet been included // If tx is dependent on other mempool txs which haven't yet been included

4
src/net_processing.cpp

@ -1666,7 +1666,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// Probably non-standard or insufficient fee/priority // Probably non-standard or insufficient fee/priority
LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString()); LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString());
vEraseQueue.push_back(orphanHash); vEraseQueue.push_back(orphanHash);
if (orphanTx.wit.IsNull() && !stateDummy.CorruptionPossible()) { if (!orphanTx.HasWitness() && !stateDummy.CorruptionPossible()) {
// Do not use rejection cache for witness transactions or // Do not use rejection cache for witness transactions or
// witness-stripped transactions, as they can have been malleated. // witness-stripped transactions, as they can have been malleated.
// See https://github.com/bitcoin/bitcoin/issues/8279 for details. // See https://github.com/bitcoin/bitcoin/issues/8279 for details.
@ -1708,7 +1708,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
LogPrint("mempool", "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString()); LogPrint("mempool", "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString());
} }
} else { } else {
if (tx.wit.IsNull() && !state.CorruptionPossible()) { if (!tx.HasWitness() && !state.CorruptionPossible()) {
// Do not use rejection cache for witness transactions or // Do not use rejection cache for witness transactions or
// witness-stripped transactions, as they can have been malleated. // witness-stripped transactions, as they can have been malleated.
// See https://github.com/bitcoin/bitcoin/issues/8279 for details. // See https://github.com/bitcoin/bitcoin/issues/8279 for details.

8
src/policy/policy.cpp

@ -163,7 +163,7 @@ 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. // 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 the script is invalid without witness, it would be caught sooner or later during validation.
if (tx.wit.vtxinwit[i].IsNull()) if (tx.vin[i].scriptWitness.IsNull())
continue; continue;
const CTxOut &prev = mapInputs.GetOutputFor(tx.vin[i]); const CTxOut &prev = mapInputs.GetOutputFor(tx.vin[i]);
@ -192,13 +192,13 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
// Check P2WSH standard limits // Check P2WSH standard limits
if (witnessversion == 0 && witnessprogram.size() == 32) { if (witnessversion == 0 && witnessprogram.size() == 32) {
if (tx.wit.vtxinwit[i].scriptWitness.stack.back().size() > MAX_STANDARD_P2WSH_SCRIPT_SIZE) if (tx.vin[i].scriptWitness.stack.back().size() > MAX_STANDARD_P2WSH_SCRIPT_SIZE)
return false; return false;
size_t sizeWitnessStack = tx.wit.vtxinwit[i].scriptWitness.stack.size() - 1; size_t sizeWitnessStack = tx.vin[i].scriptWitness.stack.size() - 1;
if (sizeWitnessStack > MAX_STANDARD_P2WSH_STACK_ITEMS) if (sizeWitnessStack > MAX_STANDARD_P2WSH_STACK_ITEMS)
return false; return false;
for (unsigned int j = 0; j < sizeWitnessStack; j++) { for (unsigned int j = 0; j < sizeWitnessStack; j++) {
if (tx.wit.vtxinwit[i].scriptWitness.stack[j].size() > MAX_STANDARD_P2WSH_STACK_ITEM_SIZE) if (tx.vin[i].scriptWitness.stack[j].size() > MAX_STANDARD_P2WSH_STACK_ITEM_SIZE)
return false; return false;
} }
} }

10
src/primitives/transaction.cpp

@ -55,7 +55,7 @@ std::string CTxOut::ToString() const
} }
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {} CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {}
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), wit(tx.wit), nLockTime(tx.nLockTime) {} CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) {}
uint256 CMutableTransaction::GetHash() const uint256 CMutableTransaction::GetHash() const
{ {
@ -74,8 +74,8 @@ uint256 CTransaction::GetWitnessHash() const
/* For backward compatibility, the hash is initialized to 0. TODO: remove the need for this default constructor entirely. */ /* For backward compatibility, the hash is initialized to 0. TODO: remove the need for this default constructor entirely. */
CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0), hash() {} CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0), hash() {}
CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), wit(tx.wit), nLockTime(tx.nLockTime), hash(ComputeHash()) {} CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), hash(ComputeHash()) {}
CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), vin(std::move(tx.vin)), vout(std::move(tx.vout)), wit(std::move(tx.wit)), nLockTime(tx.nLockTime), hash(ComputeHash()) {} CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), vin(std::move(tx.vin)), vout(std::move(tx.vout)), nLockTime(tx.nLockTime), hash(ComputeHash()) {}
CAmount CTransaction::GetValueOut() const CAmount CTransaction::GetValueOut() const
{ {
@ -131,8 +131,8 @@ std::string CTransaction::ToString() const
nLockTime); nLockTime);
for (unsigned int i = 0; i < vin.size(); i++) for (unsigned int i = 0; i < vin.size(); i++)
str += " " + vin[i].ToString() + "\n"; str += " " + vin[i].ToString() + "\n";
for (unsigned int i = 0; i < wit.vtxinwit.size(); i++) for (unsigned int i = 0; i < vin.size(); i++)
str += " " + wit.vtxinwit[i].scriptWitness.ToString() + "\n"; str += " " + vin[i].scriptWitness.ToString() + "\n";
for (unsigned int i = 0; i < vout.size(); i++) for (unsigned int i = 0; i < vout.size(); i++)
str += " " + vout[i].ToString() + "\n"; str += " " + vout[i].ToString() + "\n";
return str; return str;

99
src/primitives/transaction.h

@ -65,6 +65,7 @@ public:
COutPoint prevout; COutPoint prevout;
CScript scriptSig; CScript scriptSig;
uint32_t nSequence; uint32_t nSequence;
CScriptWitness scriptWitness; //! Only serialized through CTransaction
/* Setting nSequence to this value for every input in a transaction /* Setting nSequence to this value for every input in a transaction
* disables nLockTime. */ * disables nLockTime. */
@ -211,62 +212,6 @@ public:
std::string ToString() const; std::string ToString() const;
}; };
class CTxInWitness
{
public:
CScriptWitness scriptWitness;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(scriptWitness.stack);
}
bool IsNull() const { return scriptWitness.IsNull(); }
CTxInWitness() { }
};
class CTxWitness
{
public:
/** In case vtxinwit is missing, all entries are treated as if they were empty CTxInWitnesses */
std::vector<CTxInWitness> vtxinwit;
ADD_SERIALIZE_METHODS;
bool IsEmpty() const { return vtxinwit.empty(); }
bool IsNull() const
{
for (size_t n = 0; n < vtxinwit.size(); n++) {
if (!vtxinwit[n].IsNull()) {
return false;
}
}
return true;
}
void SetNull()
{
vtxinwit.clear();
}
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
for (size_t n = 0; n < vtxinwit.size(); n++) {
READWRITE(vtxinwit[n]);
}
if (IsNull()) {
/* It's illegal to encode a witness when all vtxinwit entries are empty. */
throw std::ios_base::failure("Superfluous witness record");
}
}
};
struct CMutableTransaction; struct CMutableTransaction;
/** /**
@ -294,7 +239,6 @@ inline void UnserializeTransaction(TxType& tx, Stream& s) {
unsigned char flags = 0; unsigned char flags = 0;
tx.vin.clear(); tx.vin.clear();
tx.vout.clear(); tx.vout.clear();
tx.wit.SetNull();
/* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */ /* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */
s >> tx.vin; s >> tx.vin;
if (tx.vin.size() == 0 && fAllowWitness) { if (tx.vin.size() == 0 && fAllowWitness) {
@ -311,8 +255,9 @@ inline void UnserializeTransaction(TxType& tx, Stream& s) {
if ((flags & 1) && fAllowWitness) { if ((flags & 1) && fAllowWitness) {
/* The witness flag is present, and we support witnesses. */ /* The witness flag is present, and we support witnesses. */
flags ^= 1; flags ^= 1;
tx.wit.vtxinwit.resize(tx.vin.size()); for (size_t i = 0; i < tx.vin.size(); i++) {
s >> tx.wit; s >> tx.vin[i].scriptWitness.stack;
}
} }
if (flags) { if (flags) {
/* Unknown flag in the serialization */ /* Unknown flag in the serialization */
@ -328,10 +273,9 @@ inline void SerializeTransaction(const TxType& tx, Stream& s) {
s << tx.nVersion; s << tx.nVersion;
unsigned char flags = 0; unsigned char flags = 0;
// Consistency check // Consistency check
assert(tx.wit.vtxinwit.size() <= tx.vin.size());
if (fAllowWitness) { if (fAllowWitness) {
/* Check whether witnesses need to be serialized. */ /* Check whether witnesses need to be serialized. */
if (!tx.wit.IsNull()) { if (tx.HasWitness()) {
flags |= 1; flags |= 1;
} }
} }
@ -345,11 +289,7 @@ inline void SerializeTransaction(const TxType& tx, Stream& s) {
s << tx.vout; s << tx.vout;
if (flags & 1) { if (flags & 1) {
for (size_t i = 0; i < tx.vin.size(); i++) { for (size_t i = 0; i < tx.vin.size(); i++) {
if (i < tx.wit.vtxinwit.size()) { s << tx.vin[i].scriptWitness.stack;
s << tx.wit.vtxinwit[i];
} else {
s << CTxInWitness();
}
} }
} }
s << tx.nLockTime; s << tx.nLockTime;
@ -379,7 +319,6 @@ public:
const int32_t nVersion; const int32_t nVersion;
const std::vector<CTxIn> vin; const std::vector<CTxIn> vin;
const std::vector<CTxOut> vout; const std::vector<CTxOut> vout;
CTxWitness wit; // Not const: can change without invalidating the txid cache
const uint32_t nLockTime; const uint32_t nLockTime;
private: private:
@ -451,6 +390,16 @@ public:
} }
std::string ToString() const; std::string ToString() const;
bool HasWitness() const
{
for (size_t i = 0; i < vin.size(); i++) {
if (!vin[i].scriptWitness.IsNull()) {
return true;
}
}
return false;
}
}; };
/** A mutable version of CTransaction. */ /** A mutable version of CTransaction. */
@ -459,7 +408,6 @@ struct CMutableTransaction
int32_t nVersion; int32_t nVersion;
std::vector<CTxIn> vin; std::vector<CTxIn> vin;
std::vector<CTxOut> vout; std::vector<CTxOut> vout;
CTxWitness wit;
uint32_t nLockTime; uint32_t nLockTime;
CMutableTransaction(); CMutableTransaction();
@ -485,6 +433,21 @@ struct CMutableTransaction
* fly, as opposed to GetHash() in CTransaction, which uses a cached result. * fly, as opposed to GetHash() in CTransaction, which uses a cached result.
*/ */
uint256 GetHash() const; uint256 GetHash() const;
friend bool operator==(const CMutableTransaction& a, const CMutableTransaction& b)
{
return a.GetHash() == b.GetHash();
}
bool HasWitness() const
{
for (size_t i = 0; i < vin.size(); i++) {
if (!vin[i].scriptWitness.IsNull()) {
return true;
}
}
return false;
}
}; };
typedef std::shared_ptr<const CTransaction> CTransactionRef; typedef std::shared_ptr<const CTransaction> CTransactionRef;

11
src/rpc/rawtransaction.cpp

@ -82,17 +82,14 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
in.push_back(Pair("scriptSig", o)); in.push_back(Pair("scriptSig", o));
} }
if (!tx.wit.IsNull()) { if (tx.HasWitness()) {
if (!tx.wit.vtxinwit[i].IsNull()) {
UniValue txinwitness(UniValue::VARR); UniValue txinwitness(UniValue::VARR);
for (unsigned int j = 0; j < tx.wit.vtxinwit[i].scriptWitness.stack.size(); j++) { for (unsigned int j = 0; j < tx.vin[i].scriptWitness.stack.size(); j++) {
std::vector<unsigned char> item = tx.wit.vtxinwit[i].scriptWitness.stack[j]; std::vector<unsigned char> item = tx.vin[i].scriptWitness.stack[j];
txinwitness.push_back(HexStr(item.begin(), item.end())); txinwitness.push_back(HexStr(item.begin(), item.end()));
} }
in.push_back(Pair("txinwitness", txinwitness)); in.push_back(Pair("txinwitness", txinwitness));
} }
}
in.push_back(Pair("sequence", (int64_t)txin.nSequence)); in.push_back(Pair("sequence", (int64_t)txin.nSequence));
vin.push_back(in); vin.push_back(in);
} }
@ -840,7 +837,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
UpdateTransaction(mergedTx, i, sigdata); UpdateTransaction(mergedTx, i, sigdata);
ScriptError serror = SCRIPT_ERR_OK; ScriptError serror = SCRIPT_ERR_OK;
if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) { if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
} }
} }

3
src/script/bitcoinconsensus.cpp

@ -93,8 +93,9 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP
// Regardless of the verification result, the tx did not error. // Regardless of the verification result, the tx did not error.
set_error(err, bitcoinconsensus_ERR_OK); set_error(err, bitcoinconsensus_ERR_OK);
PrecomputedTransactionData txdata(tx); PrecomputedTransactionData txdata(tx);
return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata), NULL); return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), &tx.vin[nIn].scriptWitness, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata), NULL);
} catch (const std::exception&) { } catch (const std::exception&) {
return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing
} }

2
src/script/script.h

@ -656,6 +656,8 @@ struct CScriptWitness
bool IsNull() const { return stack.empty(); } bool IsNull() const { return stack.empty(); }
void SetNull() { stack.clear(); stack.shrink_to_fit(); }
std::string ToString() const; std::string ToString() const;
}; };

9
src/script/sign.cpp

@ -194,9 +194,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
SignatureData data; SignatureData data;
assert(tx.vin.size() > nIn); assert(tx.vin.size() > nIn);
data.scriptSig = tx.vin[nIn].scriptSig; data.scriptSig = tx.vin[nIn].scriptSig;
if (tx.wit.vtxinwit.size() > nIn) { data.scriptWitness = tx.vin[nIn].scriptWitness;
data.scriptWitness = tx.wit.vtxinwit[nIn].scriptWitness;
}
return data; return data;
} }
@ -204,10 +202,7 @@ void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const Signatur
{ {
assert(tx.vin.size() > nIn); assert(tx.vin.size() > nIn);
tx.vin[nIn].scriptSig = data.scriptSig; tx.vin[nIn].scriptSig = data.scriptSig;
if (!data.scriptWitness.IsNull() || tx.wit.vtxinwit.size() > nIn) { tx.vin[nIn].scriptWitness = data.scriptWitness;
tx.wit.vtxinwit.resize(tx.vin.size());
tx.wit.vtxinwit[nIn].scriptWitness = data.scriptWitness;
}
} }
bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType) bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType)

3
src/test/script_tests.cpp

@ -145,8 +145,7 @@ CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CSc
txSpend.nLockTime = 0; txSpend.nLockTime = 0;
txSpend.vin.resize(1); txSpend.vin.resize(1);
txSpend.vout.resize(1); txSpend.vout.resize(1);
txSpend.wit.vtxinwit.resize(1); txSpend.vin[0].scriptWitness = scriptWitness;
txSpend.wit.vtxinwit[0].scriptWitness = scriptWitness;
txSpend.vin[0].prevout.hash = txCredit.GetHash(); txSpend.vin[0].prevout.hash = txCredit.GetHash();
txSpend.vin[0].prevout.n = 0; txSpend.vin[0].prevout.n = 0;
txSpend.vin[0].scriptSig = scriptSig; txSpend.vin[0].scriptSig = scriptSig;

32
src/test/sigopcount_tests.cpp

@ -73,7 +73,7 @@ ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTransaction
{ {
ScriptError error; ScriptError error;
CTransaction inputi(input); CTransaction inputi(input);
bool ret = VerifyScript(inputi.vin[0].scriptSig, output.vout[0].scriptPubKey, inputi.wit.vtxinwit.size() > 0 ? &inputi.wit.vtxinwit[0].scriptWitness : NULL, flags, TransactionSignatureChecker(&inputi, 0, output.vout[0].nValue), &error); bool ret = VerifyScript(inputi.vin[0].scriptSig, output.vout[0].scriptPubKey, &inputi.vin[0].scriptWitness, flags, TransactionSignatureChecker(&inputi, 0, output.vout[0].nValue), &error);
BOOST_CHECK((ret == true) == (error == SCRIPT_ERR_OK)); BOOST_CHECK((ret == true) == (error == SCRIPT_ERR_OK));
return error; return error;
@ -84,13 +84,12 @@ ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTransaction
* and witness such that spendingTx spends output zero of creationTx. * and witness such that spendingTx spends output zero of creationTx.
* Also inserts creationTx's output into the coins view. * Also inserts creationTx's output into the coins view.
*/ */
void BuildTxs(CMutableTransaction& spendingTx, CCoinsViewCache& coins, CMutableTransaction& creationTx, const CScript& scriptPubKey, const CScript& scriptSig, const CTxInWitness& witness) void BuildTxs(CMutableTransaction& spendingTx, CCoinsViewCache& coins, CMutableTransaction& creationTx, const CScript& scriptPubKey, const CScript& scriptSig, const CScriptWitness& witness)
{ {
creationTx.nVersion = 1; creationTx.nVersion = 1;
creationTx.vin.resize(1); creationTx.vin.resize(1);
creationTx.vin[0].prevout.SetNull(); creationTx.vin[0].prevout.SetNull();
creationTx.vin[0].scriptSig = CScript(); creationTx.vin[0].scriptSig = CScript();
creationTx.wit.vtxinwit.resize(1);
creationTx.vout.resize(1); creationTx.vout.resize(1);
creationTx.vout[0].nValue = 1; creationTx.vout[0].nValue = 1;
creationTx.vout[0].scriptPubKey = scriptPubKey; creationTx.vout[0].scriptPubKey = scriptPubKey;
@ -100,8 +99,7 @@ void BuildTxs(CMutableTransaction& spendingTx, CCoinsViewCache& coins, CMutableT
spendingTx.vin[0].prevout.hash = creationTx.GetHash(); spendingTx.vin[0].prevout.hash = creationTx.GetHash();
spendingTx.vin[0].prevout.n = 0; spendingTx.vin[0].prevout.n = 0;
spendingTx.vin[0].scriptSig = scriptSig; spendingTx.vin[0].scriptSig = scriptSig;
spendingTx.wit.vtxinwit.resize(1); spendingTx.vin[0].scriptWitness = witness;
spendingTx.wit.vtxinwit[0] = witness;
spendingTx.vout.resize(1); spendingTx.vout.resize(1);
spendingTx.vout[0].nValue = 1; spendingTx.vout[0].nValue = 1;
spendingTx.vout[0].scriptPubKey = CScript(); spendingTx.vout[0].scriptPubKey = CScript();
@ -133,7 +131,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
// Do not use a valid signature to avoid using wallet operations. // Do not use a valid signature to avoid using wallet operations.
CScript scriptSig = CScript() << OP_0 << OP_0; CScript scriptSig = CScript() << OP_0 << OP_0;
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, CTxInWitness()); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, CScriptWitness());
// Legacy counting only includes signature operations in scriptSigs and scriptPubKeys // Legacy counting only includes signature operations in scriptSigs and scriptPubKeys
// of a transaction and does not take the actual executed sig operations into account. // of a transaction and does not take the actual executed sig operations into account.
// spendingTx in itself does not contain a signature operation. // spendingTx in itself does not contain a signature operation.
@ -151,7 +149,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
CScript scriptPubKey = GetScriptForDestination(CScriptID(redeemScript)); CScript scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
CScript scriptSig = CScript() << OP_0 << OP_0 << ToByteVector(redeemScript); CScript scriptSig = CScript() << OP_0 << OP_0 << ToByteVector(redeemScript);
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, CTxInWitness()); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, CScriptWitness());
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2 * WITNESS_SCALE_FACTOR); assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2 * WITNESS_SCALE_FACTOR);
assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY); assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);
} }
@ -161,14 +159,12 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
CScript p2pk = CScript() << ToByteVector(pubkey) << OP_CHECKSIG; CScript p2pk = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
CScript scriptPubKey = GetScriptForWitness(p2pk); CScript scriptPubKey = GetScriptForWitness(p2pk);
CScript scriptSig = CScript(); CScript scriptSig = CScript();
CTxInWitness witness;
CScriptWitness scriptWitness; CScriptWitness scriptWitness;
scriptWitness.stack.push_back(vector<unsigned char>(0)); scriptWitness.stack.push_back(vector<unsigned char>(0));
scriptWitness.stack.push_back(vector<unsigned char>(0)); scriptWitness.stack.push_back(vector<unsigned char>(0));
witness.scriptWitness = scriptWitness;
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, witness); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 1); assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 1);
// No signature operations if we don't verify the witness. // No signature operations if we don't verify the witness.
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags & ~SCRIPT_VERIFY_WITNESS) == 0); assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags & ~SCRIPT_VERIFY_WITNESS) == 0);
@ -177,10 +173,10 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
// The sig op cost for witness version != 0 is zero. // The sig op cost for witness version != 0 is zero.
assert(scriptPubKey[0] == 0x00); assert(scriptPubKey[0] == 0x00);
scriptPubKey[0] = 0x51; scriptPubKey[0] = 0x51;
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, witness); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 0); assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 0);
scriptPubKey[0] = 0x00; scriptPubKey[0] = 0x00;
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, witness); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);
// The witness of a coinbase transaction is not taken into account. // The witness of a coinbase transaction is not taken into account.
spendingTx.vin[0].prevout.SetNull(); spendingTx.vin[0].prevout.SetNull();
@ -193,13 +189,11 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
CScript scriptSig = GetScriptForWitness(p2pk); CScript scriptSig = GetScriptForWitness(p2pk);
CScript scriptPubKey = GetScriptForDestination(CScriptID(scriptSig)); CScript scriptPubKey = GetScriptForDestination(CScriptID(scriptSig));
scriptSig = CScript() << ToByteVector(scriptSig); scriptSig = CScript() << ToByteVector(scriptSig);
CTxInWitness witness;
CScriptWitness scriptWitness; CScriptWitness scriptWitness;
scriptWitness.stack.push_back(vector<unsigned char>(0)); scriptWitness.stack.push_back(vector<unsigned char>(0));
scriptWitness.stack.push_back(vector<unsigned char>(0)); scriptWitness.stack.push_back(vector<unsigned char>(0));
witness.scriptWitness = scriptWitness;
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, witness); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 1); assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 1);
assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_EQUALVERIFY); assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_EQUALVERIFY);
} }
@ -209,14 +203,12 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
CScript witnessScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY; CScript witnessScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;
CScript scriptPubKey = GetScriptForWitness(witnessScript); CScript scriptPubKey = GetScriptForWitness(witnessScript);
CScript scriptSig = CScript(); CScript scriptSig = CScript();
CTxInWitness witness;
CScriptWitness scriptWitness; CScriptWitness scriptWitness;
scriptWitness.stack.push_back(vector<unsigned char>(0)); scriptWitness.stack.push_back(vector<unsigned char>(0));
scriptWitness.stack.push_back(vector<unsigned char>(0)); scriptWitness.stack.push_back(vector<unsigned char>(0));
scriptWitness.stack.push_back(vector<unsigned char>(witnessScript.begin(), witnessScript.end())); scriptWitness.stack.push_back(vector<unsigned char>(witnessScript.begin(), witnessScript.end()));
witness.scriptWitness = scriptWitness;
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, witness); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2); assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2);
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags & ~SCRIPT_VERIFY_WITNESS) == 0); assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags & ~SCRIPT_VERIFY_WITNESS) == 0);
assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY); assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);
@ -228,14 +220,12 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
CScript redeemScript = GetScriptForWitness(witnessScript); CScript redeemScript = GetScriptForWitness(witnessScript);
CScript scriptPubKey = GetScriptForDestination(CScriptID(redeemScript)); CScript scriptPubKey = GetScriptForDestination(CScriptID(redeemScript));
CScript scriptSig = CScript() << ToByteVector(redeemScript); CScript scriptSig = CScript() << ToByteVector(redeemScript);
CTxInWitness witness;
CScriptWitness scriptWitness; CScriptWitness scriptWitness;
scriptWitness.stack.push_back(vector<unsigned char>(0)); scriptWitness.stack.push_back(vector<unsigned char>(0));
scriptWitness.stack.push_back(vector<unsigned char>(0)); scriptWitness.stack.push_back(vector<unsigned char>(0));
scriptWitness.stack.push_back(vector<unsigned char>(witnessScript.begin(), witnessScript.end())); scriptWitness.stack.push_back(vector<unsigned char>(witnessScript.begin(), witnessScript.end()));
witness.scriptWitness = scriptWitness;
BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, witness); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);
assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2); assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2);
assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY); assert(VerifyWithFlag(creationTx, spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);
} }

17
src/test/transaction_tests.cpp

@ -170,7 +170,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
amount = mapprevOutValues[tx.vin[i].prevout]; amount = mapprevOutValues[tx.vin[i].prevout];
} }
unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
const CScriptWitness *witness = (i < tx.wit.vtxinwit.size()) ? &tx.wit.vtxinwit[i].scriptWitness : NULL; const CScriptWitness *witness = &tx.vin[i].scriptWitness;
BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
witness, verify_flags, TransactionSignatureChecker(&tx, i, amount, txdata), &err), witness, verify_flags, TransactionSignatureChecker(&tx, i, amount, txdata), &err),
strTest); strTest);
@ -254,7 +254,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
if (mapprevOutValues.count(tx.vin[i].prevout)) { if (mapprevOutValues.count(tx.vin[i].prevout)) {
amount = mapprevOutValues[tx.vin[i].prevout]; amount = mapprevOutValues[tx.vin[i].prevout];
} }
const CScriptWitness *witness = (i < tx.wit.vtxinwit.size()) ? &tx.wit.vtxinwit[i].scriptWitness : NULL; const CScriptWitness *witness = &tx.vin[i].scriptWitness;
fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
witness, verify_flags, TransactionSignatureChecker(&tx, i, amount, txdata), &err); witness, verify_flags, TransactionSignatureChecker(&tx, i, amount, txdata), &err);
} }
@ -351,7 +351,6 @@ void CreateCreditAndSpend(const CKeyStore& keystore, const CScript& outscript, C
outputm.vin.resize(1); outputm.vin.resize(1);
outputm.vin[0].prevout.SetNull(); outputm.vin[0].prevout.SetNull();
outputm.vin[0].scriptSig = CScript(); outputm.vin[0].scriptSig = CScript();
outputm.wit.vtxinwit.resize(1);
outputm.vout.resize(1); outputm.vout.resize(1);
outputm.vout[0].nValue = 1; outputm.vout[0].nValue = 1;
outputm.vout[0].scriptPubKey = outscript; outputm.vout[0].scriptPubKey = outscript;
@ -362,14 +361,12 @@ void CreateCreditAndSpend(const CKeyStore& keystore, const CScript& outscript, C
assert(output->vin[0] == outputm.vin[0]); assert(output->vin[0] == outputm.vin[0]);
assert(output->vout.size() == 1); assert(output->vout.size() == 1);
assert(output->vout[0] == outputm.vout[0]); assert(output->vout[0] == outputm.vout[0]);
assert(output->wit.vtxinwit.size() == 0);
CMutableTransaction inputm; CMutableTransaction inputm;
inputm.nVersion = 1; inputm.nVersion = 1;
inputm.vin.resize(1); inputm.vin.resize(1);
inputm.vin[0].prevout.hash = output->GetHash(); inputm.vin[0].prevout.hash = output->GetHash();
inputm.vin[0].prevout.n = 0; inputm.vin[0].prevout.n = 0;
inputm.wit.vtxinwit.resize(1);
inputm.vout.resize(1); inputm.vout.resize(1);
inputm.vout[0].nValue = 1; inputm.vout[0].nValue = 1;
inputm.vout[0].scriptPubKey = CScript(); inputm.vout[0].scriptPubKey = CScript();
@ -382,20 +379,14 @@ void CreateCreditAndSpend(const CKeyStore& keystore, const CScript& outscript, C
assert(input.vin[0] == inputm.vin[0]); assert(input.vin[0] == inputm.vin[0]);
assert(input.vout.size() == 1); assert(input.vout.size() == 1);
assert(input.vout[0] == inputm.vout[0]); assert(input.vout[0] == inputm.vout[0]);
if (inputm.wit.IsNull()) { assert(input.vin[0].scriptWitness.stack == inputm.vin[0].scriptWitness.stack);
assert(input.wit.IsNull());
} else {
assert(!input.wit.IsNull());
assert(input.wit.vtxinwit.size() == 1);
assert(input.wit.vtxinwit[0].scriptWitness.stack == inputm.wit.vtxinwit[0].scriptWitness.stack);
}
} }
void CheckWithFlag(const CTransactionRef& output, const CMutableTransaction& input, int flags, bool success) void CheckWithFlag(const CTransactionRef& output, const CMutableTransaction& input, int flags, bool success)
{ {
ScriptError error; ScriptError error;
CTransaction inputi(input); CTransaction inputi(input);
bool ret = VerifyScript(inputi.vin[0].scriptSig, output->vout[0].scriptPubKey, inputi.wit.vtxinwit.size() > 0 ? &inputi.wit.vtxinwit[0].scriptWitness : NULL, flags, TransactionSignatureChecker(&inputi, 0, output->vout[0].nValue), &error); bool ret = VerifyScript(inputi.vin[0].scriptSig, output->vout[0].scriptPubKey, &inputi.vin[0].scriptWitness, flags, TransactionSignatureChecker(&inputi, 0, output->vout[0].nValue), &error);
assert(ret == success); assert(ret == success);
} }

23
src/validation.cpp

@ -446,7 +446,7 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i
for (unsigned int i = 0; i < tx.vin.size(); i++) for (unsigned int i = 0; i < tx.vin.size(); i++)
{ {
const CTxOut &prevout = inputs.GetOutputFor(tx.vin[i]); const CTxOut &prevout = inputs.GetOutputFor(tx.vin[i]);
nSigOps += CountWitnessSigOps(tx.vin[i].scriptSig, prevout.scriptPubKey, i < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[i].scriptWitness : NULL, flags); nSigOps += CountWitnessSigOps(tx.vin[i].scriptSig, prevout.scriptPubKey, &tx.vin[i].scriptWitness, flags);
} }
return nSigOps; return nSigOps;
} }
@ -550,7 +550,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
// Reject transactions with witness before segregated witness activates (override with -prematurewitness) // Reject transactions with witness before segregated witness activates (override with -prematurewitness)
bool witnessEnabled = IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus()); bool witnessEnabled = IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus());
if (!GetBoolArg("-prematurewitness",false) && !tx.wit.IsNull() && !witnessEnabled) { if (!GetBoolArg("-prematurewitness",false) && tx.HasWitness() && !witnessEnabled) {
return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true); return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true);
} }
@ -672,7 +672,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs"); return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
// Check for non-standard witness in P2WSH // Check for non-standard witness in P2WSH
if (!tx.wit.IsNull() && fRequireStandard && !IsWitnessStandard(tx, view)) if (tx.HasWitness() && fRequireStandard && !IsWitnessStandard(tx, view))
return state.DoS(0, false, REJECT_NONSTANDARD, "bad-witness-nonstandard", true); return state.DoS(0, false, REJECT_NONSTANDARD, "bad-witness-nonstandard", true);
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS); int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS);
@ -912,7 +912,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
// SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we // SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we
// need to turn both off, and compare against just turning off CLEANSTACK // need to turn both off, and compare against just turning off CLEANSTACK
// to see if the failure is specifically due to witness validation. // to see if the failure is specifically due to witness validation.
if (tx.wit.IsNull() && CheckInputs(tx, state, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) && if (!tx.HasWitness() && CheckInputs(tx, state, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) &&
!CheckInputs(tx, state, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, txdata)) { !CheckInputs(tx, state, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, txdata)) {
// Only the witness is missing, so the transaction itself may be fine. // Only the witness is missing, so the transaction itself may be fine.
state.SetCorruptionPossible(); state.SetCorruptionPossible();
@ -1304,7 +1304,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
bool CScriptCheck::operator()() { bool CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
const CScriptWitness *witness = (nIn < ptxTo->wit.vtxinwit.size()) ? &ptxTo->wit.vtxinwit[nIn].scriptWitness : NULL; const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness;
if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), &error)) { if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), &error)) {
return false; return false;
} }
@ -2846,11 +2846,10 @@ void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPr
{ {
int commitpos = GetWitnessCommitmentIndex(block); int commitpos = GetWitnessCommitmentIndex(block);
static const std::vector<unsigned char> nonce(32, 0x00); static const std::vector<unsigned char> nonce(32, 0x00);
if (commitpos != -1 && IsWitnessEnabled(pindexPrev, consensusParams) && block.vtx[0]->wit.IsEmpty()) { if (commitpos != -1 && IsWitnessEnabled(pindexPrev, consensusParams) && !block.vtx[0]->HasWitness()) {
CMutableTransaction tx(*block.vtx[0]); CMutableTransaction tx(*block.vtx[0]);
tx.wit.vtxinwit.resize(1); tx.vin[0].scriptWitness.stack.resize(1);
tx.wit.vtxinwit[0].scriptWitness.stack.resize(1); tx.vin[0].scriptWitness.stack[0] = nonce;
tx.wit.vtxinwit[0].scriptWitness.stack[0] = nonce;
block.vtx[0] = MakeTransactionRef(std::move(tx)); block.vtx[0] = MakeTransactionRef(std::move(tx));
} }
} }
@ -2958,10 +2957,10 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co
// The malleation check is ignored; as the transaction tree itself // The malleation check is ignored; as the transaction tree itself
// already does not permit it, it is impossible to trigger in the // already does not permit it, it is impossible to trigger in the
// witness tree. // witness tree.
if (block.vtx[0]->wit.vtxinwit.size() != 1 || block.vtx[0]->wit.vtxinwit[0].scriptWitness.stack.size() != 1 || block.vtx[0]->wit.vtxinwit[0].scriptWitness.stack[0].size() != 32) { if (block.vtx[0]->vin[0].scriptWitness.stack.size() != 1 || block.vtx[0]->vin[0].scriptWitness.stack[0].size() != 32) {
return state.DoS(100, false, REJECT_INVALID, "bad-witness-nonce-size", true, strprintf("%s : invalid witness nonce size", __func__)); return state.DoS(100, false, REJECT_INVALID, "bad-witness-nonce-size", true, strprintf("%s : invalid witness nonce size", __func__));
} }
CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0]->wit.vtxinwit[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin()); CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0]->vin[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin());
if (memcmp(hashWitness.begin(), &block.vtx[0]->vout[commitpos].scriptPubKey[6], 32)) { if (memcmp(hashWitness.begin(), &block.vtx[0]->vout[commitpos].scriptPubKey[6], 32)) {
return state.DoS(100, false, REJECT_INVALID, "bad-witness-merkle-match", true, strprintf("%s : witness merkle commitment mismatch", __func__)); return state.DoS(100, false, REJECT_INVALID, "bad-witness-merkle-match", true, strprintf("%s : witness merkle commitment mismatch", __func__));
} }
@ -2972,7 +2971,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co
// No witness data is allowed in blocks that don't commit to witness data, as this would otherwise leave room for spam // No witness data is allowed in blocks that don't commit to witness data, as this would otherwise leave room for spam
if (!fHaveWitness) { if (!fHaveWitness) {
for (size_t i = 0; i < block.vtx.size(); i++) { for (size_t i = 0; i < block.vtx.size(); i++) {
if (!block.vtx[i]->wit.IsNull()) { if (block.vtx[i]->HasWitness()) {
return state.DoS(100, false, REJECT_INVALID, "unexpected-witness", true, strprintf("%s : unexpected witness data found", __func__)); return state.DoS(100, false, REJECT_INVALID, "unexpected-witness", true, strprintf("%s : unexpected witness data found", __func__));
} }
} }

6
src/wallet/wallet.cpp

@ -2301,7 +2301,6 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
nChangePosInOut = nChangePosRequest; nChangePosInOut = nChangePosRequest;
txNew.vin.clear(); txNew.vin.clear();
txNew.vout.clear(); txNew.vout.clear();
txNew.wit.SetNull();
wtxNew.fFromMe = true; wtxNew.fFromMe = true;
bool fFirst = true; bool fFirst = true;
@ -2488,9 +2487,10 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// Remove scriptSigs if we used dummy signatures for fee calculation // Remove scriptSigs if we used dummy signatures for fee calculation
if (!sign) { if (!sign) {
BOOST_FOREACH (CTxIn& vin, txNew.vin) BOOST_FOREACH (CTxIn& vin, txNew.vin) {
vin.scriptSig = CScript(); vin.scriptSig = CScript();
txNew.wit.SetNull(); vin.scriptWitness.SetNull();
}
} }
// Embed the constructed transaction data in wtxNew. // Embed the constructed transaction data in wtxNew.

Loading…
Cancel
Save