| Age | Commit message (Collapse) | Author |
|
v1.2.1 disabled the unbounded opcodes and capped a single pushed element at
520 bytes. What it did not add was a ceiling on how many items there can be,
so a script still pays for itself once, in bytes, and then multiplies: 520
bytes per stack entry means a script of N bytes made almost entirely of
OP_DUP occupies roughly 520N in memory, on every node that validates it.
Two limits, both from Bitcoin:
- at most 201 operations per script, counted outside the fExec test so
the cost of a script cannot depend on which branch a signature happens
to take. Pushes are exempt, being already bounded by the script length
and by MAX_SCRIPT_ELEMENT_SIZE.
- at most 1000 entries across both stacks together, counted together
because OP_TOALTSTACK would otherwise move the problem rather than
solve it.
This tightens validation, so a node that does not upgrade accepts a
superset of what this one accepts and is not stranded.
Verified against the live chain: a node with these rules synced all 3910
blocks from genesis and accepted every one, so nothing in use relies on
scripts these limits would reject.
|
|
* script: fix OP_RETURN spend bypass, disable unbounded opcodes
OP_RETURN let anyone spend anyone else's outputs.
VerifySignature evaluates scriptSig and scriptPubKey as one concatenated
script. OP_RETURN was implemented as 'pc = pend', which jumps past
everything that follows, and EvalScript ends with
CastToBool(stack.back()). So a scriptSig of <OP_TRUE OP_RETURN> skipped
the entire scriptPubKey -- OP_CHECKSIG included -- and the final check
read the attacker's own OP_TRUE. No signature required, any output
spendable. This is the 0.1.0 behaviour Bitcoin fixed in 0.3.5.
OP_RETURN now fails the script outright.
Also disables the opcodes Bitcoin removed for being unbounded, and caps
pushed elements at 520 bytes:
OP_CAT grows a stack element with no limit -- <x> OP_DUP OP_CAT
repeated doubles it each time, so a script that fits in a transaction
exhausts memory on every node that validates it. OP_LSHIFT shifts by
an attacker-supplied amount with no ceiling, same effect. There were
no element, stack or opcode-count limits of any kind.
Nothing on this chain uses these opcodes -- all outputs are standard
pay-to-pubkey-hash or coinbase pay-to-pubkey -- so no existing
transaction changes validity.
* main: reject blocks containing a duplicate transaction (CVE-2012-2459)
BuildMerkleTree duplicates the trailing hash whenever a tree level has
an odd width. Appending a copy of the last transaction therefore
produces a block whose merkle root -- and whose block hash -- match a
legitimate block exactly.
A node handed the forgery rejects it (the duplicated inputs are already
spent) and remembers that hash as bad. When the genuine block arrives
carrying the same hash, it is refused too, and the node stops following
the real chain.
CheckBlock now rejects any block that carries the same transaction
twice, which is what the mutation requires.
* main: halve the subsidy on the block's height, not the node's tip
GetBlockValue derived the halving from nBestHeight -- the validating
node's own chain tip -- rather than from the height of the block being
valued. The subsidy a block may pay is a property of that block, so
reading it off local state makes validity depend on how far the node has
synced: across a halving boundary a node still catching up would allow
50 BTF where a synced node allows 25, and the two would follow different
chains. Reorgs have the same problem in miniature.
Harmless today (every node computes nHeight/210000 == 0 at height ~2400)
and it changes no existing block's validity, but it splits the network
at height 210000.
The height is now passed in explicitly: pindex->nHeight when connecting,
tip + 1 when mining or building pool work.
|
|
Satoshi's bitcoin-0.1.0 reborn with RandomX CPU proof of work, anonymous .btf
addressing over Nostr with CGNAT rendezvous, automatic peer discovery, a
headless Linux node, and Windows GUI wallet. Fair launch, no premine, 21M cap.
|