From 8b49040854be2e26b66366aeae1cba4716f93d93 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 6 Nov 2015 01:42:38 +0100 Subject: [PATCH] BIP141: Commitment structure and deployment Includes a fix by Suhas Daftuar and LongShao007 --- src/chainparams.cpp | 14 ++++ src/consensus/merkle.cpp | 11 +++ src/consensus/merkle.h | 6 ++ src/consensus/params.h | 1 + src/consensus/validation.h | 3 + src/main.cpp | 146 +++++++++++++++++++++++++++++++++-- src/main.h | 9 +++ src/miner.cpp | 18 +++++ src/miner.h | 2 + src/primitives/transaction.h | 3 +- src/rpc/blockchain.cpp | 1 + src/rpc/mining.cpp | 20 ++++- src/versionbits.cpp | 4 + 13 files changed, 225 insertions(+), 13 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 8c27a578b..2a198e855 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -92,6 +92,11 @@ public: consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1462060800; // May 1st, 2016 consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1493596800; // May 1st, 2017 + // Deployment of SegWit (BIP141 and BIP143) + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1; + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 0; // Never / undefined + /** * The message start string is designed to be unlikely to occur in normal data. * The characters are rarely used upper ASCII, not valid as UTF-8, and produce @@ -183,6 +188,11 @@ public: consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1456790400; // March 1st, 2016 consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1493596800; // May 1st, 2017 + // Deployment of SegWit (BIP141 and BIP143) + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1; + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 2000000000; // Far in the future + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 2100000000; + pchMessageStart[0] = 0x0b; pchMessageStart[1] = 0x11; pchMessageStart[2] = 0x09; @@ -255,6 +265,9 @@ public: consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0; consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 0; consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1; + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 999999999999ULL; pchMessageStart[0] = 0xfa; pchMessageStart[1] = 0xbf; @@ -317,3 +330,4 @@ void SelectParams(const std::string& network) SelectBaseParams(network); pCurrentParams = &Params(network); } + diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp index 22eb7159a..35f7d2e05 100644 --- a/src/consensus/merkle.cpp +++ b/src/consensus/merkle.cpp @@ -165,6 +165,17 @@ uint256 BlockMerkleRoot(const CBlock& block, bool* mutated) return ComputeMerkleRoot(leaves, mutated); } +uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated) +{ + std::vector leaves; + 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(); + } + return ComputeMerkleRoot(leaves, mutated); +} + std::vector BlockMerkleBranch(const CBlock& block, uint32_t position) { std::vector leaves; diff --git a/src/consensus/merkle.h b/src/consensus/merkle.h index 6ef59745a..194aea9b7 100644 --- a/src/consensus/merkle.h +++ b/src/consensus/merkle.h @@ -22,6 +22,12 @@ uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::vectorpprev, chainparams.GetConsensus())) { + flags |= SCRIPT_VERIFY_WITNESS; + } + int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1; LogPrint("bench", " - Fork checks: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), nTimeForks * 0.000001); @@ -3441,6 +3462,71 @@ static bool CheckIndexAgainstCheckpoint(const CBlockIndex* pindexPrev, CValidati return true; } +bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params) +{ + LOCK(cs_main); + return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == THRESHOLD_ACTIVE); +} + +// Compute at which vout of the block's coinbase transaction the witness +// commitment occurs, or -1 if not found. +static int GetWitnessCommitmentIndex(const CBlock& block) +{ + int commitpos = -1; + for (size_t o = 0; o < block.vtx[0].vout.size(); o++) { + if (block.vtx[0].vout[o].scriptPubKey.size() >= 38 && block.vtx[0].vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0].vout[o].scriptPubKey[1] == 0x24 && block.vtx[0].vout[o].scriptPubKey[2] == 0xaa && block.vtx[0].vout[o].scriptPubKey[3] == 0x21 && block.vtx[0].vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0].vout[o].scriptPubKey[5] == 0xed) { + commitpos = o; + } + } + return commitpos; +} + +void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams) +{ + int commitpos = GetWitnessCommitmentIndex(block); + static const std::vector nonce(32, 0x00); + if (commitpos != -1 && IsWitnessEnabled(pindexPrev, consensusParams) && block.vtx[0].wit.IsEmpty()) { + block.vtx[0].wit.vtxinwit.resize(1); + block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.resize(1); + block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0] = nonce; + } +} + +std::vector GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams) +{ + std::vector commitment; + int commitpos = GetWitnessCommitmentIndex(block); + bool fHaveWitness = false; + for (size_t t = 1; t < block.vtx.size(); t++) { + if (!block.vtx[t].wit.IsNull()) { + fHaveWitness = true; + break; + } + } + std::vector ret(32, 0x00); + if (fHaveWitness && IsWitnessEnabled(pindexPrev, consensusParams)) { + if (commitpos == -1) { + uint256 witnessroot = BlockWitnessMerkleRoot(block, NULL); + CHash256().Write(witnessroot.begin(), 32).Write(&ret[0], 32).Finalize(witnessroot.begin()); + CTxOut out; + out.nValue = 0; + out.scriptPubKey.resize(38); + out.scriptPubKey[0] = OP_RETURN; + out.scriptPubKey[1] = 0x24; + out.scriptPubKey[2] = 0xaa; + out.scriptPubKey[3] = 0x21; + out.scriptPubKey[4] = 0xa9; + out.scriptPubKey[5] = 0xed; + memcpy(&out.scriptPubKey[6], witnessroot.begin(), 32); + commitment = std::vector(out.scriptPubKey.begin(), out.scriptPubKey.end()); + const_cast*>(&block.vtx[0].vout)->push_back(out); + block.vtx[0].UpdateHash(); + } + } + UpdateUncommittedBlockStructures(block, pindexPrev, consensusParams); + return commitment; +} + bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex * const pindexPrev, int64_t nAdjustedTime) { // Check proof of work @@ -3497,6 +3583,43 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn } } + // Validation for witness commitments. + // * We compute the witness hash (which is the hash including witnesses) of all the block's transactions, except the + // coinbase (where 0x0000....0000 is used instead). + // * The coinbase scriptWitness is a stack of a single 32-byte vector, containing a witness nonce (unconstrained). + // * We build a merkle tree with all those witness hashes as leaves (similar to the hashMerkleRoot in the block header). + // * There must be at least one output whose scriptPubKey is a single 36-byte push, the first 4 bytes of which are + // {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness nonce). In case there are + // multiple, the last one is used. + bool fHaveWitness = false; + if (IsWitnessEnabled(pindexPrev, consensusParams)) { + int commitpos = GetWitnessCommitmentIndex(block); + if (commitpos != -1) { + bool malleated = false; + uint256 hashWitness = BlockWitnessMerkleRoot(block, &malleated); + // The malleation check is ignored; as the transaction tree itself + // already does not permit it, it is impossible to trigger in the + // 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) { + return state.DoS(100, error("%s : invalid witness nonce size", __func__), REJECT_INVALID, "bad-witness-nonce-size", true); + } + CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin()); + if (memcmp(hashWitness.begin(), &block.vtx[0].vout[commitpos].scriptPubKey[6], 32)) { + return state.DoS(100, error("%s : witness merkle commitment mismatch", __func__), REJECT_INVALID, "bad-witness-merkle-match", true); + } + fHaveWitness = true; + } + } + + // No witness data is allowed in blocks that don't commit to witness data, as this would otherwise leave room for spam + if (!fHaveWitness) { + for (size_t i = 0; i < block.vtx.size(); i++) { + if (!block.vtx[i].wit.IsNull()) { + return state.DoS(100, error("%s : unexpected witness data found", __func__), REJECT_INVALID, "unexpected-witness", true); + } + } + } + return true; } @@ -5278,7 +5401,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (!fMissingInputs2) { int nDos = 0; - if (stateDummy.IsInvalid(nDos) && nDos > 0) + if (stateDummy.IsInvalid(nDos) && nDos > 0 && (!state.CorruptionPossible() || State(fromPeer)->fHaveWitness)) { // Punish peer that gave us an invalid orphan tx Misbehaving(fromPeer, nDos); @@ -5289,8 +5412,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Probably non-standard or insufficient fee/priority LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString()); vEraseQueue.push_back(orphanHash); - assert(recentRejects); - recentRejects->insert(orphanHash); + if (!stateDummy.CorruptionPossible()) { + assert(recentRejects); + recentRejects->insert(orphanHash); + } } mempool.check(pcoinsTip); } @@ -5325,8 +5450,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("mempool", "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString()); } } else { - assert(recentRejects); - recentRejects->insert(tx.GetHash()); + if (!state.CorruptionPossible()) { + assert(recentRejects); + recentRejects->insert(tx.GetHash()); + } if (pfrom->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { // Always relay transactions received from whitelisted peers, even @@ -5355,8 +5482,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); - if (nDoS > 0) + if (nDoS > 0 && (!state.CorruptionPossible() || State(pfrom->id)->fHaveWitness)) { + // When a non-witness-supporting peer gives us a transaction that would + // be accepted if witness validation was off, we can't blame them for it. Misbehaving(pfrom->GetId(), nDoS); + } } FlushStateToDisk(state, FLUSH_STATE_PERIODIC); } diff --git a/src/main.h b/src/main.h index f004a29bb..d0dce7068 100644 --- a/src/main.h +++ b/src/main.h @@ -453,6 +453,15 @@ bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockI /** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true); +/** Check whether witness commitments are required for block. */ +bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params); + +/** Update uncommitted block structures (currently: only the witness nonce). This is safe for submitted blocks. */ +void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams); + +/** Produce the necessary coinbase commitment for a block (modifies the hash, don't call for mined blocks). */ +std::vector GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams); + /** RAII wrapper for VerifyDB: Verify consistency of the block and coin databases */ class CVerifyDB { public: diff --git a/src/miner.cpp b/src/miner.cpp index 989ad11a2..a7bf9ae84 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -93,6 +93,7 @@ void BlockAssembler::resetBlock() // Reserve space for coinbase tx nBlockSize = 1000; nBlockSigOps = 100; + fIncludeWitness = false; // These counters do not include coinbase tx nBlockTx = 0; @@ -134,6 +135,14 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) ? nMedianTimePast : pblock->GetBlockTime(); + // Decide whether to include witness transactions + // This is only needed in case the witness softfork activation is reverted + // (which would require a very deep reorganization) or when + // -promiscuousmempoolflags is used. + // TODO: replace this with a call to main to assess validity of a mempool + // transaction (which in most cases can be a no-op). + fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus()); + addPriorityTxs(); addPackageTxs(); @@ -150,6 +159,7 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; pblock->vtx[0] = coinbaseTx; + pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus()); pblocktemplate->vTxFees[0] = -nFees; // Fill in header @@ -299,6 +309,10 @@ void BlockAssembler::addScoreTxs() continue; } + // cannot accept witness transactions into a non-witness block + if (!fIncludeWitness && !iter->GetTx().wit.IsNull()) + continue; + // If tx is dependent on other mempool txs which haven't yet been included // then put it in the waitSet if (isStillDependent(iter)) { @@ -543,6 +557,10 @@ void BlockAssembler::addPriorityTxs() continue; } + // cannot accept witness transactions into a non-witness block + if (!fIncludeWitness && !iter->GetTx().wit.IsNull()) + continue; + // If tx is dependent on other mempool txs which haven't yet been included // then put it in the waitSet if (isStillDependent(iter)) { diff --git a/src/miner.h b/src/miner.h index a9fea8530..8bfc1493d 100644 --- a/src/miner.h +++ b/src/miner.h @@ -29,6 +29,7 @@ struct CBlockTemplate CBlock block; std::vector vTxFees; std::vector vTxSigOps; + std::vector vchCoinbaseCommitment; }; // Container for tracking updates to ancestor feerate as we include (parent) @@ -139,6 +140,7 @@ private: CBlock* pblock; // Configuration parameters for the block size + bool fIncludeWitness; unsigned int nBlockMaxSize, nBlockMinSize; // Information on the current status of the block diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index d8ae41ad7..5a5824134 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -335,7 +335,6 @@ class CTransaction private: /** Memory only. */ const uint256 hash; - void UpdateHash() const; public: // Default transaction version. @@ -414,6 +413,8 @@ public: } std::string ToString() const; + + void UpdateHash() const; }; /** A mutable version of CTransaction. */ diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 1bb365d36..dae283fb6 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -930,6 +930,7 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); bip9_softforks.push_back(Pair("csv", BIP9SoftForkDesc(consensusParams, Consensus::DEPLOYMENT_CSV))); + bip9_softforks.push_back(Pair("segwit", BIP9SoftForkDesc(consensusParams, Consensus::DEPLOYMENT_SEGWIT))); obj.push_back(Pair("softforks", softforks)); obj.push_back(Pair("bip9_softforks", bip9_softforks)); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 94eeea91f..291314b8b 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -348,7 +348,8 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) " \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n" " {\n" " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n" - " \"hash\" : \"xxxx\", (string) hash/id encoded in little-endian hexadecimal\n" + " \"txid\" : \"xxxx\", (string) transaction id encoded in little-endian hexadecimal\n" + " \"hash\" : \"xxxx\", (string) hash encoded in little-endian hexadecimal\n" " \"depends\" : [ (array) array of numbers \n" " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n" " ,...\n" @@ -546,7 +547,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) UniValue transactions(UniValue::VARR); map setTxIndex; int i = 0; - BOOST_FOREACH (const CTransaction& tx, pblock->vtx) { + BOOST_FOREACH (CTransaction& tx, pblock->vtx) { uint256 txHash = tx.GetHash(); setTxIndex[txHash] = i++; @@ -556,8 +557,8 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) UniValue entry(UniValue::VOBJ); entry.push_back(Pair("data", EncodeHexTx(tx))); - - entry.push_back(Pair("hash", txHash.GetHex())); + entry.push_back(Pair("txid", txHash.GetHex())); + entry.push_back(Pair("hash", tx.GetWitnessHash().GetHex())); UniValue deps(UniValue::VARR); BOOST_FOREACH (const CTxIn &in, tx.vin) @@ -656,6 +657,9 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) result.push_back(Pair("curtime", pblock->GetBlockTime())); result.push_back(Pair("bits", strprintf("%08x", pblock->nBits))); result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); + if (!pblocktemplate->vchCoinbaseCommitment.empty()) { + result.push_back(Pair("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end()))); + } return result; } @@ -719,6 +723,14 @@ UniValue submitblock(const UniValue& params, bool fHelp) } } + { + LOCK(cs_main); + BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); + if (mi != mapBlockIndex.end()) { + UpdateUncommittedBlockStructures(block, mi->second, Params().GetConsensus()); + } + } + CValidationState state; submitblock_StateCatcher sc(block.GetHash()); RegisterValidationInterface(&sc); diff --git a/src/versionbits.cpp b/src/versionbits.cpp index 043819c65..bf32ae662 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -14,6 +14,10 @@ const struct BIP9DeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION { /*.name =*/ "csv", /*.gbt_force =*/ true, + }, + { + /*.name =*/ "segwit", + /*.gbt_force =*/ false, } };