|
|
@ -293,6 +293,8 @@ struct CNodeState { |
|
|
|
bool fPreferHeaderAndIDs; |
|
|
|
bool fPreferHeaderAndIDs; |
|
|
|
//! Whether this peer will send us cmpctblocks if we request them
|
|
|
|
//! Whether this peer will send us cmpctblocks if we request them
|
|
|
|
bool fProvidesHeaderAndIDs; |
|
|
|
bool fProvidesHeaderAndIDs; |
|
|
|
|
|
|
|
//! Whether this peer can give us witnesses
|
|
|
|
|
|
|
|
bool fHaveWitness; |
|
|
|
|
|
|
|
|
|
|
|
CNodeState() { |
|
|
|
CNodeState() { |
|
|
|
fCurrentlyConnected = false; |
|
|
|
fCurrentlyConnected = false; |
|
|
@ -1119,6 +1121,11 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C |
|
|
|
return state.DoS(0, false, REJECT_NONSTANDARD, "premature-version2-tx"); |
|
|
|
return state.DoS(0, false, REJECT_NONSTANDARD, "premature-version2-tx"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Don't accept witness transactions before the final threshold passes
|
|
|
|
|
|
|
|
if (!tx.wit.IsNull() && !IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus())) { |
|
|
|
|
|
|
|
return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Only accept nLockTime-using transactions that can be mined in the next
|
|
|
|
// Only accept nLockTime-using transactions that can be mined in the next
|
|
|
|
// block; we don't want our mempool filled up with transactions that can't
|
|
|
|
// block; we don't want our mempool filled up with transactions that can't
|
|
|
|
// be mined yet.
|
|
|
|
// be mined yet.
|
|
|
@ -1459,8 +1466,17 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C |
|
|
|
|
|
|
|
|
|
|
|
// Check against previous transactions
|
|
|
|
// Check against previous transactions
|
|
|
|
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
|
|
|
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
|
|
|
if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true)) |
|
|
|
if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true)) { |
|
|
|
return false; // state filled in by CheckInputs
|
|
|
|
// SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we
|
|
|
|
|
|
|
|
// need to turn both off, and compare against just turning off CLEANSTACK
|
|
|
|
|
|
|
|
// to see if the failure is specifically due to witness validation.
|
|
|
|
|
|
|
|
if (CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true) && |
|
|
|
|
|
|
|
!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS & ~SCRIPT_VERIFY_CLEANSTACK, true)) { |
|
|
|
|
|
|
|
// Only the witness is wrong, so the transaction itself may be fine.
|
|
|
|
|
|
|
|
state.SetCorruptionPossible(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Check again against just the consensus-critical mandatory script
|
|
|
|
// Check again against just the consensus-critical mandatory script
|
|
|
|
// verification flags, in case of bugs in the standard flags that cause
|
|
|
|
// verification flags, in case of bugs in the standard flags that cause
|
|
|
@ -2406,6 +2422,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin |
|
|
|
nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE; |
|
|
|
nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Start enforcing WITNESS rules using versionbits logic.
|
|
|
|
|
|
|
|
if (IsWitnessEnabled(pindex->pprev, chainparams.GetConsensus())) { |
|
|
|
|
|
|
|
flags |= SCRIPT_VERIFY_WITNESS; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1; |
|
|
|
int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1; |
|
|
|
LogPrint("bench", " - Fork checks: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), nTimeForks * 0.000001); |
|
|
|
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; |
|
|
|
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<unsigned char> 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<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
std::vector<unsigned char> 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<unsigned char> 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<unsigned char>(out.scriptPubKey.begin(), out.scriptPubKey.end()); |
|
|
|
|
|
|
|
const_cast<std::vector<CTxOut>*>(&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) |
|
|
|
bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex * const pindexPrev, int64_t nAdjustedTime) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// Check proof of work
|
|
|
|
// 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; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -5278,7 +5401,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, |
|
|
|
else if (!fMissingInputs2) |
|
|
|
else if (!fMissingInputs2) |
|
|
|
{ |
|
|
|
{ |
|
|
|
int nDos = 0; |
|
|
|
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
|
|
|
|
// Punish peer that gave us an invalid orphan tx
|
|
|
|
Misbehaving(fromPeer, nDos); |
|
|
|
Misbehaving(fromPeer, nDos); |
|
|
@ -5289,9 +5412,11 @@ 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 (!stateDummy.CorruptionPossible()) { |
|
|
|
assert(recentRejects); |
|
|
|
assert(recentRejects); |
|
|
|
recentRejects->insert(orphanHash); |
|
|
|
recentRejects->insert(orphanHash); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
mempool.check(pcoinsTip); |
|
|
|
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()); |
|
|
|
LogPrint("mempool", "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString()); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
|
|
|
|
if (!state.CorruptionPossible()) { |
|
|
|
assert(recentRejects); |
|
|
|
assert(recentRejects); |
|
|
|
recentRejects->insert(tx.GetHash()); |
|
|
|
recentRejects->insert(tx.GetHash()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (pfrom->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { |
|
|
|
if (pfrom->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { |
|
|
|
// Always relay transactions received from whitelisted peers, even
|
|
|
|
// Always relay transactions received from whitelisted peers, even
|
|
|
@ -5355,9 +5482,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, |
|
|
|
if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P
|
|
|
|
if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P
|
|
|
|
pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), |
|
|
|
pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), |
|
|
|
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); |
|
|
|
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); |
|
|
|
Misbehaving(pfrom->GetId(), nDoS); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
FlushStateToDisk(state, FLUSH_STATE_PERIODIC); |
|
|
|
FlushStateToDisk(state, FLUSH_STATE_PERIODIC); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|