Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions blockchain/blake2b_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,35 @@ func TestBlake2bHeaderContext(t *testing.T) {
t.Fatalf("classic header without BLAKE2b configured: %v", err)
}
}

// TestCheckHeaderV2Sanity pins the context-free rule Knots applies in
// CheckBlockHeader: a v2 header committing to the activation height is
// accepted, one committing to the block below it is refused (bad-version-
// sha256d), and a network without BLAKE2b configured takes no v2 header.
func TestCheckHeaderV2Sanity(t *testing.T) {
params := &chaincfg.MainNetParams
v2 := mustHeader(t, activationHeaderRaw)
if v2.Height != 961640 {
t.Fatalf("the activation header commits to %d", v2.Height)
}

if err := CheckHeaderV2Sanity(v2, params); err != nil {
t.Fatalf("v2 header committing to the activation height: %v", err)
}
below := *v2
below.Height = 961639
if code, _ := ruleCode(CheckHeaderV2Sanity(&below, params)); code != ErrBadHeaderV2 {
t.Fatalf("v2 header committing to the block below the activation: got %v, want ErrBadHeaderV2", code)
}

classic := chaincfg.MainNetParams.GenesisBlock.Header
if err := CheckHeaderV2Sanity(&classic, params); err != nil {
t.Fatalf("classic header: %v", err)
}

none := chaincfg.MainNetParams
none.Blake2bHeight = 0
if code, _ := ruleCode(CheckHeaderV2Sanity(v2, &none)); code != ErrUnsupportedProofOfWork {
t.Fatalf("v2 header without BLAKE2b configured: got %v, want ErrUnsupportedProofOfWork", code)
}
}
24 changes: 24 additions & 0 deletions blockchain/blake2bpow.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,30 @@ func checkHeaderV2Context(header *wire.BlockHeader, height int32,
return nil
}

// CheckHeaderV2Sanity is the context-free part of Knots' CheckBlockHeader for
// BLAKE2b headers: a v2 header must commit to a height at which BLAKE2b proof
// of work is required (Knots' bad-version-sha256d), and a network without
// BLAKE2b proof of work configured takes no v2 header. It needs only the
// header, so it runs where the block's real height is not known yet (an
// orphan, a header out of order); checkHeaderV2Context then checks the
// committed height against the real one.
func CheckHeaderV2Sanity(header *wire.BlockHeader, params *chaincfg.Params) error {
if !header.IsV2() {
return nil
}
if !params.Blake2bPowDeployed() {
return ruleError(ErrUnsupportedProofOfWork, "BLAKE2b (header v2) "+
"proof of work is not configured for network "+params.Name)
}
if !params.IsBlake2bHeight(header.Height) {
str := fmt.Sprintf("header v2 commits to height %d: blocks require "+
"SHA256d proof of work until height %d", header.Height,
params.Blake2bHeight)
return ruleError(ErrBadHeaderV2, str)
}
return nil
}

// applyBlake2bTargetShift is Knots' ApplyBlake2bTargetShift: the target of the
// first block mined under BLAKE2b, from the one the usual rules give.
func applyBlake2bTargetShift(bits uint32, params *chaincfg.Params) uint32 {
Expand Down
4 changes: 4 additions & 0 deletions blockchain/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo
if err != nil {
return false, false, err
}
err = CheckHeaderV2Sanity(&block.MsgBlock().Header, b.chainParams)
if err != nil {
return false, false, err
}

// Find the previous checkpoint and perform some additional checks based
// on the checkpoint. This provides a few nice properties such as
Expand Down
4 changes: 4 additions & 0 deletions blockchain/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -1394,6 +1394,10 @@ func (b *BlockChain) CheckConnectBlockTemplate(block *btcutil.Block) error {
if err != nil {
return err
}
err = CheckHeaderV2Sanity(&header, b.chainParams)
if err != nil {
return err
}

err = b.checkBlockContext(block, tip, flags)
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -3720,6 +3720,11 @@ func verifyChain(s *rpcServer, level, depth int32) error {
if level > 0 {
err := blockchain.CheckBlockSanity(block,
s.cfg.ChainParams.PowLimit, s.cfg.TimeSource)
if err == nil {
err = blockchain.CheckHeaderV2Sanity(
&block.MsgBlock().Header, s.cfg.ChainParams,
)
}
if err != nil {
rpcsLog.Errorf("Verify is unable to validate "+
"block at hash %v height %d: %v",
Expand Down