Skip to content

Commit

Permalink
Add Elements-style witness serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
stevenroose committed Feb 13, 2019
1 parent 6ed8fd8 commit fb72ee8
Show file tree
Hide file tree
Showing 50 changed files with 872 additions and 413 deletions.
3 changes: 2 additions & 1 deletion src/bench/block_assemble.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
34 changes: 23 additions & 11 deletions src/bench/mempool_eviction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <list>
#include <vector>
#include <primitives/transaction.h>

static void AddTx(const CTransactionRef& tx, const CAmount& nFee, CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
{
Expand All @@ -30,15 +31,17 @@ 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;

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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions src/bench/verify_script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<unsigned char>(SIGHASH_ALL));
Expand 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);
Expand Down
2 changes: 1 addition & 1 deletion src/bitcoin-tx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 5 additions & 2 deletions src/consensus/merkle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

21 changes: 11 additions & 10 deletions src/consensus/tx_verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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<uint256, COutPoint> pegin = std::make_pair(uint256(tx.vin[i].m_pegin_witness.stack[2]), prevout);
std::pair<uint256, COutPoint> 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));
}
Expand All @@ -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");
}
Expand Down
10 changes: 8 additions & 2 deletions src/consensus/validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
34 changes: 31 additions & 3 deletions src/core_memusage.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::vector<unsigned char> >::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<std::vector<unsigned char> >::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;
}
Expand Down
21 changes: 14 additions & 7 deletions src/core_write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
16 changes: 9 additions & 7 deletions src/policy/policy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
}
Expand All @@ -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);
}
2 changes: 1 addition & 1 deletion src/policy/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading

0 comments on commit fb72ee8

Please sign in to comment.