Repository navigation
blockchain: validate BLAKE2b (header v2) proof of work - #1
Conversation
Mirror Bitcoin Knots v29.4.2 so a light client can check BLAKE2b-chain headers itself: - chaincfg: Blake2bHeight and Blake2bTargetShift (mainnet 961640/22, testnet4 150308/20). Zero height means not configured. - checkProofOfWork: a v2 block id is compared with its target like a SHA256d one; reserved flag bits 0xc0 must be clear (bad-flags-highbits). - CheckBlockHeaderContext: v1 below Blake2bHeight (bad-version-sha256d), v2 from it on (bad-version-blake2b), committed height = height (bad-header-height). Without Blake2bHeight a v2 header is still refused with ErrUnsupportedProofOfWork. - calcNextRequiredDifficulty: the activation block's target is shifted once by Blake2bTargetShift (ApplyBlake2bTargetShift), not on the no-retarget or min-difficulty paths, as in GetNextWorkRequired. Tests use real mainnet headers: every v2 header's PoW, the activation target and the 7 retargets since (testdata/blake2b_retargets.json).
|
Thank you for this, it's careful work. I checked it rule by rule against Knots v29.4.2 (tag One correction, about the comment in I've added one commit on top of yours (
I've merged your commit with that one and tagged This also fixes something in my current release that I hadn't noticed. Two small notes, nothing to change now:
|
|
Thanks for the review and for d1bd696, agreed on the no-retarget path. Some field data: Kilowallet 1.0 (Android) now ships LND with neutrino built against v0.25.1-blake2b.6 (lightning-fork#3). Phones on mainnet follow the chain from compact filters and check the v2 headers' PoW themselves; real channels are open since today, including a 2M one funded through PSBT. On your first note: happy to send a small follow-up so the sanity check refuses a v2 header that commits to a height below activation, as Knots does in CheckBlockHeader, if you want it. neutrino-blake2b#1 and lightning-fork#3 build on this and are ready for review whenever you have time. |
|
Good to hear Kilowallet is running it on mainnet, and thanks for the field report. Yes please on the follow-up: refusing a v2 header that commits to a height below activation, as Knots does in One thing worth knowing for Kilowallet: Knots is about to change the coinbase maturity rules (bitcoinknots/bitcoin#429, flag day at block 979920). A neutrino node does no transaction validation, so it can't enforce that rule itself either way, but its wallet will need to treat coinbase outputs from block 973440 on as unspendable for longer. I'm working out how to express that in btcd and lnd before the flag day and will share it. |
This lets the blockchain package check BLAKE2b (header v2) headers itself, following Bitcoin Knots v29.4.2, so a light client (neutrino) can follow the chain without trusting a backend. Today
checkProofOfWorkrefuses every v2 header.Rules, with the Knots reject reason each one mirrors:
chaincfg: newBlake2bHeightandBlake2bTargetShiftfields, set to 961640/22 on mainnet and 150308/20 on testnet4. A height of zero means not configured, which is what every otherParamsgets.checkProofOfWork: a v2 block id is compared against its target in the same way as a SHA256d id (Knots callsCheckProofOfWork(GetHash(), nBits)for both). The reserved flag bits 0xc0 must be clear (bad-flags-highbits).CheckBlockHeaderContext: belowBlake2bHeighta header must be v1 (bad-version-sha256d). From that height on it must be v2 (bad-version-blake2b) and commit to its own height (bad-header-height). On a network with noBlake2bHeight, a v2 header is still refused withErrUnsupportedProofOfWork, as it is now.calcNextRequiredDifficulty: the target of the block atBlake2bHeightis shifted once byBlake2bTargetShift, capped at the PoW limit (ApplyBlake2bTargetShift). The shift is skipped on the no-retarget and min-difficulty paths, as inGetNextWorkRequired.Out of scope: the activation headline lives in the coinbase, so it is a block rule and not a header rule. It is left to full validation.
Tests (
blockchain/blake2b_test.go) use real mainnet headers from a Knots v29.4.2 node:wire/testdatapasses PoW, and fails with another nonce;testdata/blake2b_retargets.json).I also replayed the whole mainnet header chain (heights 0 to 976738, 15,099 of them v2) through
CheckBlockHeaderSanityandCheckBlockHeaderContext, and every header validated. The existing blockchain, wire and chaincfg tests pass.Related: a neutrino change to store 164-byte headers (Kilombino/neutrino, branch
blake2b) and a lightning-fork PR that enables the neutrino backend on top of both. With the three together, neutrino lnd on regtest syncs across activation, opens a channel to a bitcoind-backed lnd, pays both ways and closes cooperatively.