haskoin-node-2.5.0: src/Haskoin/Node/Block.hs
-- |
-- Module : Haskoin.Node.Block
-- Copyright : (c) Haskoin Developers
-- License : MIT
-- Maintainer : jprupp@protonmail.ch
-- Stability : experimental
-- Portability : POSIX
--
-- Whether the transactions of a block are the ones its header commits to.
--
-- A block on the wire is a header and a list of transactions. The proof of
-- work covers the header only. The header commits to the transactions
-- through its Merkle root, and since SegWit the coinbase commits to their
-- witness data. A peer can send a real header, with real proof of work,
-- together with transactions of its choice. Nothing else stops it.
--
-- 'blockMutation' is the check that catches this. It is a port of
-- @IsBlockMutated@ in Bitcoin Core 32.1 (@src/validation.cpp@), which Core
-- runs on every block a peer sends before it does anything else with it. A
-- block that fails it is called mutated.
--
-- = Why it works this way
--
-- * __The same check as Bitcoin Core.__ The rules, their order and the
-- reasons are those of @CheckMerkleRoot@, @IsBlockMutated@ and
-- @CheckWitnessMalleation@. A stricter check would refuse valid blocks
-- that some peer serves. A laxer one would let a peer change what a block
-- holds without changing its hash.
--
-- * __Not block validation.__ The check says nothing about whether the
-- transactions are valid, spend existing coins, or pay the right subsidy.
-- It only says that the block is the one the header names. A node that
-- needs more must validate blocks itself.
--
-- * __The header stays good.__ A mutated block does not make its header
-- invalid. The same header with the right transactions is still a valid
-- block (CVE-2012-2459). The node blames the peer that sent the mutated
-- block and leaves the header chain alone.
--
-- * __SegWit depends on the height.__ Core checks the witness commitment
-- only for a block at or above the SegWit activation height of the
-- network. Before it, miners already put commitments in coinbases without
-- the matching witness data, and those blocks are valid. So the witness
-- rule needs the height of the parent block, which 'witnessCheck' turns
-- into a 'WitnessCheck'. When the parent is not known, Core does not check
-- the witness either, and neither does this module.
module Haskoin.Node.Block
( BlockMutation (..),
WitnessCheck (..),
blockMutation,
witnessCheck,
segWitHeight,
)
where
import Data.ByteString (ByteString)
import Data.ByteString qualified as B
import Data.Serialize (encode)
import Haskoin
-- | Why a block is not the one its header commits to. The names follow the
-- reasons that Bitcoin Core 32.1 gives.
data BlockMutation
= -- | the transactions do not hash to the Merkle root of the header, or
-- there are none (@bad-txnmrklroot@)
BadMerkleRoot
| -- | the Merkle tree hashes two identical nodes together, so the list of
-- transactions repeats some to keep the root (@bad-txns-duplicate@,
-- CVE-2012-2459)
DuplicateTransactions
| -- | the first transaction is not a coinbase and some transaction is 64
-- bytes long without its witness, so it may be an inner node of the
-- Merkle tree posing as a transaction
SixtyFourByteTransaction
| -- | the coinbase commits to witness data, but its witness is not a single
-- item of 32 bytes (@bad-witness-nonce-size@)
BadWitnessNonceSize
| -- | the witness data does not hash to the commitment in the coinbase
-- (@bad-witness-merkle-match@)
BadWitnessCommitment
| -- | a transaction carries witness data, but the block does not commit to
-- any (@unexpected-witness@)
UnexpectedWitness
deriving stock (Eq, Show)
-- | Which witness rule applies to a block.
data WitnessCheck
= -- | SegWit is active for the block. If the coinbase has a witness
-- commitment, the witness data must match it. If not, no transaction may
-- have witness data.
SegWitActive
| -- | SegWit is not active for the block, or the network has no SegWit. No
-- transaction may have witness data.
SegWitInactive
| -- | The height of the block is not known, so its witness data is not
-- checked. Its Merkle root still is.
WitnessUnknown
deriving stock (Eq, Show)
-- | Height of the first block whose witness data is checked, on networks
-- with SegWit. The values are @consensus.SegwitHeight@ in the chain
-- parameters of Bitcoin Core 32.1 (@src/kernel/chainparams.cpp@). Networks
-- without SegWit, and networks this module does not know, give 'Nothing'.
segWitHeight :: Network -> Maybe BlockHeight
segWitHeight net
| not net.segWit = Nothing
| otherwise = case net.name of
"btc" -> Just 481824
"btctest" -> Just 834624
"btctest4" -> Just 1
"btcreg" -> Just 0
_ -> Nothing
-- | The witness rule for a block, given the header of its parent if the node
-- has it. This is @DeploymentActiveAfter(prev_block, DEPLOYMENT_SEGWIT)@ in
-- Bitcoin Core: SegWit is active when the height of the block, one more than
-- its parent's, is at least 'segWitHeight'.
witnessCheck :: Network -> Maybe BlockNode -> WitnessCheck
witnessCheck net parent
| not net.segWit = SegWitInactive
| otherwise = case (parent, segWitHeight net) of
(Just p, Just h)
| p.height + 1 >= h -> SegWitActive
| otherwise -> SegWitInactive
_ -> WitnessUnknown
-- | Why a block is not the one its header commits to, or 'Nothing' if it is.
--
-- The checks come in the order of @IsBlockMutated@ in Bitcoin Core 32.1:
--
-- 1. The transaction ids hash to the Merkle root of the header
-- ('BadMerkleRoot'), and no level of the tree hashes two identical nodes
-- together ('DuplicateTransactions').
--
-- 2. If the first transaction is not a coinbase, no transaction is 64 bytes
-- long without its witness ('SixtyFourByteTransaction'). Such a block is
-- invalid anyway, and the witness is not looked at.
--
-- 3. The witness rule that 'WitnessCheck' names.
--
-- A block without transactions has no Merkle root and fails the first
-- check. Bitcoin Core refuses it as @bad-blk-length@.
blockMutation :: WitnessCheck -> Block -> Maybe BlockMutation
blockMutation rule b = case b.txs of
[] -> Just BadMerkleRoot
first : rest
| root /= Just b.header.merkle -> Just BadMerkleRoot
| mutated -> Just DuplicateTransactions
| not (isCoinbase first) ->
if any ((== 64) . strippedSize) b.txs
then Just SixtyFourByteTransaction
else Nothing
| otherwise -> witnessMutation rule first rest
where
txids = map txHash b.txs
-- haskoin-core gives the root of a tree that is not mutated, and refuses
-- to build a mutated one. Only then is the root built a second time, to
-- tell a wrong root from a mutated tree, in Bitcoin Core's order.
(root, mutated) = case buildMerkleRoot txids of
Right r -> (Just r, False)
Left _ -> (merkleRoot txids, True)
-- | The witness part of the check, @CheckWitnessMalleation@ in Bitcoin Core,
-- given the coinbase and the other transactions of the block.
witnessMutation :: WitnessCheck -> Tx -> [Tx] -> Maybe BlockMutation
witnessMutation rule coinbase rest = case rule of
WitnessUnknown -> Nothing
SegWitInactive -> noWitness
SegWitActive -> case witnessCommitment coinbase of
Nothing -> noWitness
Just commitment -> case coinbaseStack of
[reserved]
| B.length reserved == 32 ->
if committed reserved == Just commitment
then Nothing
else Just BadWitnessCommitment
_ -> Just BadWitnessNonceSize
where
noWitness
| any hasWitness (coinbase : rest) = Just UnexpectedWitness
| otherwise = Nothing
-- The witness stack of the only input of the coinbase.
coinbaseStack = case coinbase.witness of
stack : _ -> stack
[] -> []
-- The witness id of the coinbase counts as zero.
zero = "0000000000000000000000000000000000000000000000000000000000000000"
witnessRoot = merkleRoot (zero : map txWitnessHash rest)
committed reserved = do
root <- witnessRoot
return (encode (doubleSHA256 (encode root <> reserved)))
-- | The 32 bytes of the last witness commitment among the outputs of a
-- coinbase, as @GetWitnessCommitmentIndex@ in Bitcoin Core finds it: an
-- output script of at least 38 bytes that starts with @OP_RETURN@, a push of
-- 36 bytes, and the tag @aa21a9ed@.
witnessCommitment :: Tx -> Maybe ByteString
witnessCommitment tx =
case filter isCommitment (map (.script) tx.outputs) of
[] -> Nothing
scripts -> Just (B.take 32 (B.drop 6 (last scripts)))
where
isCommitment s =
B.length s >= 38 && B.take 6 s == B.pack [0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed]
-- | The root of the Merkle tree over the given leaves, whether or not the
-- tree is mutated, as @ComputeMerkleRoot@ in Bitcoin Core computes it.
-- 'Nothing' for no leaves.
merkleRoot :: [TxHash] -> Maybe Hash256
merkleRoot leaves = calcHash (calcTreeHeight (length leaves)) 0 leaves
-- | Whether a transaction is a coinbase, as @IsCoinBase@ in Bitcoin Core
-- tells: it has one input, and that input spends the null outpoint.
isCoinbase :: Tx -> Bool
isCoinbase tx = case tx.inputs of
[i] -> i.outpoint == OutPoint {hash = zeroTx, index = 0xffffffff}
_ -> False
where
zeroTx = "0000000000000000000000000000000000000000000000000000000000000000"
-- | Size of a transaction serialized without its witness.
strippedSize :: Tx -> Int
strippedSize tx = B.length (encode tx {witness = []})