Skip to content

Commit 6508ba8

Browse files
ajtownsTheBlueMatt
andcommitted
Make 64 byte txs consensus invalid
The 64-byte transaction check is executed in a new ContextualBlockPreCheck which must be run before CheckBlock (at least in the final checking before writing the block to disk). This function is a bit awkward but is seemingly the simplest way to implement the new check, with the caveat that, because the new function is called before CheckBlock, it can never return a non-CorruptionPossible error state. Co-Authored-By: Matt Corallo <[email protected]>
1 parent 42d8eb3 commit 6508ba8

File tree

8 files changed

+67
-3
lines changed

8 files changed

+67
-3
lines changed

src/consensus/params.h

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ enum DeploymentPos : uint16_t {
3535
DEPLOYMENT_CHECKTEMPLATEVERIFY, // Deployment of CTV (BIP 119)
3636
DEPLOYMENT_ANYPREVOUT,
3737
DEPLOYMENT_OP_CAT,
38+
DEPLOYMENT_64BYTETX,
3839
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp
3940
MAX_VERSION_BITS_DEPLOYMENTS
4041
};

src/deploymentinfo.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B
2727
/*.name =*/ "opcat",
2828
/*.gbt_force =*/ true,
2929
},
30+
{
31+
/*.name =*/ "64bytetx",
32+
/*.gbt_force =*/ true,
33+
},
3034
};
3135

3236
std::string DeploymentName(Consensus::BuriedDeployment dep)

src/kernel/chainparams.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,8 @@ class CRegTestParams : public CChainParams
472472
consensus.vDeployments[Consensus::DEPLOYMENT_CHECKTEMPLATEVERIFY] = SetupDeployment{.activate = 0x60007700, .abandon = 0x40007700, .always = true};
473473
consensus.vDeployments[Consensus::DEPLOYMENT_ANYPREVOUT] = SetupDeployment{.activate = 0x60007600, .abandon = 0x40007600, .always = true};
474474
consensus.vDeployments[Consensus::DEPLOYMENT_OP_CAT] = SetupDeployment{.activate = 0x62000100, .abandon = 0x42000100, .always = true};
475+
consensus.vDeployments[Consensus::DEPLOYMENT_64BYTETX] = SetupDeployment{.activate = 0x60000001, .abandon = 0x40000001, .always = true}; // needs BIN assigned
476+
475477
consensus.nMinimumChainWork = uint256{};
476478
consensus.defaultAssumeValid = uint256{};
477479

src/rpc/blockchain.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1334,6 +1334,7 @@ UniValue DeploymentInfo(const CBlockIndex* blockindex, const ChainstateManager&
13341334
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_CHECKTEMPLATEVERIFY);
13351335
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_ANYPREVOUT);
13361336
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_OP_CAT);
1337+
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_64BYTETX);
13371338
return softforks;
13381339
}
13391340
} // anon namespace

src/validation.cpp

+36-1
Original file line numberDiff line numberDiff line change
@@ -2175,6 +2175,7 @@ unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const Chainstat
21752175
return flags;
21762176
}
21772177

2178+
static bool ContextualBlockPreCheck(const CBlock& block, BlockValidationState& state, const ChainstateManager& chainman, const CBlockIndex* pindexPrev);
21782179

21792180
static SteadyClock::duration time_check{};
21802181
static SteadyClock::duration time_forks{};
@@ -2201,6 +2202,10 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
22012202
const auto time_start{SteadyClock::now()};
22022203
const CChainParams& params{m_chainman.GetParams()};
22032204

2205+
if (!ContextualBlockPreCheck(block, state, m_chainman, pindex->pprev)) {
2206+
return error("%s: Consensus::ContextualBlockPreCheck: %s", __func__, state.ToString());
2207+
}
2208+
22042209
// Check it again in case a previous version let a bad block in
22052210
// NOTE: We don't currently (re-)invoke ContextualCheckBlock() or
22062211
// ContextualCheckBlockHeader() here. This means that if we add a new
@@ -3925,6 +3930,28 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
39253930
return true;
39263931
}
39273932

3933+
/**
3934+
* We want to enforce certain rules (specifically the 64-byte transaction check)
3935+
* before we call CheckBlock to check the merkle root. This allows us to enforce
3936+
* malleability checks which may interact with other CheckBlock checks.
3937+
* This is currently called both in AcceptBlock prior to writing the block to
3938+
* disk and in ConnectBlock.
3939+
* Note that as this is called before merkle-tree checks so must never return a
3940+
* non-malleable error condition.
3941+
*/
3942+
static bool ContextualBlockPreCheck(const CBlock& block, BlockValidationState& state, const ChainstateManager& chainman, const CBlockIndex* pindexPrev)
3943+
{
3944+
if (DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_64BYTETX)) {
3945+
for (const auto& tx : block.vtx) {
3946+
if (::GetSerializeSize(TX_NO_WITNESS(tx)) == 64) {
3947+
return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "64-byte-transaction", strprintf("size of tx %s without witness is 64 bytes", tx->GetHash().ToString()));
3948+
}
3949+
}
3950+
}
3951+
3952+
return true;
3953+
}
3954+
39283955
/** NOTE: This function is not currently invoked by ConnectBlock(), so we
39293956
* should consider upgrade issues if we change which consensus rules are
39303957
* enforced in this function (eg by adding a new consensus rule). See comment
@@ -4205,7 +4232,8 @@ bool ChainstateManager::AcceptBlock(const std::shared_ptr<const CBlock>& pblock,
42054232

42064233
const CChainParams& params{GetParams()};
42074234

4208-
if (!CheckBlock(block, state, params.GetConsensus()) ||
4235+
if (!ContextualBlockPreCheck(block, state, *this, pindex->pprev) ||
4236+
!CheckBlock(block, state, params.GetConsensus()) ||
42094237
!ContextualCheckBlock(block, state, *this, pindex->pprev)) {
42104238
if (state.IsInvalid() && state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
42114239
pindex->nStatus |= BLOCK_FAILED_VALID;
@@ -4325,6 +4353,8 @@ bool TestBlockValidity(BlockValidationState& state,
43254353
// NOTE: CheckBlockHeader is called by CheckBlock
43264354
if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainstate.m_chainman, pindexPrev))
43274355
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, state.ToString());
4356+
if (!ContextualBlockPreCheck(block, state, chainstate.m_chainman, pindexPrev))
4357+
return error("%s: Consensus::ContextualBlockPreCheck: %s", __func__, state.ToString());
43284358
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
43294359
return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString());
43304360
if (!ContextualCheckBlock(block, state, chainstate.m_chainman, pindexPrev))
@@ -4441,6 +4471,11 @@ VerifyDBResult CVerifyDB::VerifyDB(
44414471
return VerifyDBResult::CORRUPTED_BLOCK_DB;
44424472
}
44434473
// check level 1: verify block validity
4474+
if (nCheckLevel >= 1 && !ContextualBlockPreCheck(block, state, chainstate.m_chainman, pindex->pprev)) {
4475+
LogPrintf("Verification error: found bad block at %d due to soft-fork, hash=%s (%s)\n",
4476+
pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
4477+
return VerifyDBResult::CORRUPTED_BLOCK_DB;
4478+
}
44444479
if (nCheckLevel >= 1 && !CheckBlock(block, state, consensus_params)) {
44454480
LogPrintf("Verification error: found bad block at %d, hash=%s (%s)\n",
44464481
pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());

test/functional/data/invalid_txs.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ class BadTxTemplate:
7676
# the mempool (i.e. does it violate policy but not consensus)?
7777
valid_in_block = False
7878

79+
# Do we need a signature for this tx
80+
wants_signature = True
81+
7982
def __init__(self, *, spend_tx=None, spend_block=None):
8083
self.spend_tx = spend_block.vtx[0] if spend_block else spend_tx
8184
self.spend_avail = sum(o.nValue for o in self.spend_tx.vout)
@@ -101,6 +104,7 @@ def get_tx(self):
101104
class InputMissing(BadTxTemplate):
102105
reject_reason = "bad-txns-vin-empty"
103106
expect_disconnect = True
107+
wants_signature = False
104108

105109
# We use a blank transaction here to make sure
106110
# it is interpreted as a non-witness transaction.
@@ -118,7 +122,9 @@ def get_tx(self):
118122
class SizeExactly64(BadTxTemplate):
119123
reject_reason = "tx-size-small"
120124
expect_disconnect = False
121-
valid_in_block = True
125+
valid_in_block = False
126+
wants_signature = False
127+
block_reject_reason = "64-byte-transaction"
122128

123129
def get_tx(self):
124130
tx = CTransaction()
@@ -133,6 +139,7 @@ class SizeSub64(BadTxTemplate):
133139
reject_reason = "tx-size-small"
134140
expect_disconnect = False
135141
valid_in_block = True
142+
wants_signature = False
136143

137144
def get_tx(self):
138145
tx = CTransaction()

test/functional/feature_block.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def run_test(self):
160160
blockname = f"for_invalid.{TxTemplate.__name__}"
161161
self.next_block(blockname)
162162
badtx = template.get_tx()
163-
if TxTemplate != invalid_txs.InputMissing:
163+
if template.wants_signature:
164164
self.sign_tx(badtx, attempt_spend_tx)
165165
badtx.rehash()
166166
badblock = self.update_block(blockname, [badtx])

test/functional/rpc_blockchain.py

+14
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,20 @@ def check_signalling_deploymentinfo_result(self, gdi_result, height, blockhash):
261261
'height': 0,
262262
'active': True,
263263
},
264+
'64bytetx': {
265+
'type': 'heretical',
266+
'heretical': {
267+
'binana-id': 'BIN-2016-0000-001',
268+
'start_time': -1,
269+
'timeout': 0x7fffffffffffffff, # testdummy does not have a timeout so is set to the max int64 value
270+
'period': 144,
271+
'status': 'active',
272+
'status_next': 'active',
273+
'since': 0,
274+
},
275+
'active': True,
276+
'height': 0,
277+
},
264278
}
265279
})
266280

0 commit comments

Comments
 (0)