feat(consensus): enforce block version in chain sync
This commit is contained in:
parent
01f4e5cd0a
commit
0ba5bbe49c
5 changed files with 60 additions and 31 deletions
|
|
@ -173,7 +173,7 @@ func (c *Chain) processBlockBlobs(blockBlob []byte, txBlobs [][]byte,
|
|||
}
|
||||
|
||||
// Validate header.
|
||||
if err := c.ValidateHeader(&blk, height); err != nil {
|
||||
if err := c.ValidateHeader(&blk, height, opts.Forks); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,13 +12,14 @@ import (
|
|||
coreerr "dappco.re/go/core/log"
|
||||
|
||||
"dappco.re/go/core/blockchain/config"
|
||||
"dappco.re/go/core/blockchain/consensus"
|
||||
"dappco.re/go/core/blockchain/types"
|
||||
"dappco.re/go/core/blockchain/wire"
|
||||
)
|
||||
|
||||
// ValidateHeader checks a block header before storage.
|
||||
// expectedHeight is the height at which this block would be stored.
|
||||
func (c *Chain) ValidateHeader(b *types.Block, expectedHeight uint64) error {
|
||||
func (c *Chain) ValidateHeader(b *types.Block, expectedHeight uint64, forks []config.HardFork) error {
|
||||
currentHeight, err := c.Height()
|
||||
if err != nil {
|
||||
return coreerr.E("Chain.ValidateHeader", "validate: get height", err)
|
||||
|
|
@ -34,6 +35,9 @@ func (c *Chain) ValidateHeader(b *types.Block, expectedHeight uint64) error {
|
|||
if !b.PrevID.IsZero() {
|
||||
return coreerr.E("Chain.ValidateHeader", "validate: genesis block has non-zero prev_id", nil)
|
||||
}
|
||||
if err := consensus.CheckBlockVersion(b.MajorVersion, forks, expectedHeight); err != nil {
|
||||
return coreerr.E("Chain.ValidateHeader", "validate: block version", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -46,6 +50,11 @@ func (c *Chain) ValidateHeader(b *types.Block, expectedHeight uint64) error {
|
|||
return coreerr.E("Chain.ValidateHeader", fmt.Sprintf("validate: prev_id %s does not match top block %s", b.PrevID, topMeta.Hash), nil)
|
||||
}
|
||||
|
||||
// Block major version check.
|
||||
if err := consensus.CheckBlockVersion(b.MajorVersion, forks, expectedHeight); err != nil {
|
||||
return coreerr.E("Chain.ValidateHeader", "validate: block version", err)
|
||||
}
|
||||
|
||||
// Block size check.
|
||||
var buf bytes.Buffer
|
||||
enc := wire.NewEncoder(&buf)
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@ package chain
|
|||
import (
|
||||
"testing"
|
||||
|
||||
store "dappco.re/go/core/store"
|
||||
"dappco.re/go/core/blockchain/config"
|
||||
"dappco.re/go/core/blockchain/types"
|
||||
store "dappco.re/go/core/store"
|
||||
)
|
||||
|
||||
func TestValidateHeader_Good_Genesis(t *testing.T) {
|
||||
|
|
@ -19,13 +20,13 @@ func TestValidateHeader_Good_Genesis(t *testing.T) {
|
|||
|
||||
blk := &types.Block{
|
||||
BlockHeader: types.BlockHeader{
|
||||
MajorVersion: 1,
|
||||
MajorVersion: 0,
|
||||
Timestamp: 1770897600,
|
||||
},
|
||||
MinerTx: testCoinbaseTx(0),
|
||||
}
|
||||
|
||||
err := c.ValidateHeader(blk, 0)
|
||||
err := c.ValidateHeader(blk, 0, config.MainnetForks)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateHeader genesis: %v", err)
|
||||
}
|
||||
|
|
@ -38,7 +39,7 @@ func TestValidateHeader_Good_Sequential(t *testing.T) {
|
|||
|
||||
// Store block 0.
|
||||
blk0 := &types.Block{
|
||||
BlockHeader: types.BlockHeader{MajorVersion: 1, Timestamp: 1770897600},
|
||||
BlockHeader: types.BlockHeader{MajorVersion: 0, Timestamp: 1770897600},
|
||||
MinerTx: testCoinbaseTx(0),
|
||||
}
|
||||
hash0 := types.Hash{0x01}
|
||||
|
|
@ -47,14 +48,14 @@ func TestValidateHeader_Good_Sequential(t *testing.T) {
|
|||
// Validate block 1.
|
||||
blk1 := &types.Block{
|
||||
BlockHeader: types.BlockHeader{
|
||||
MajorVersion: 1,
|
||||
MajorVersion: 0,
|
||||
Timestamp: 1770897720,
|
||||
PrevID: hash0,
|
||||
},
|
||||
MinerTx: testCoinbaseTx(1),
|
||||
}
|
||||
|
||||
err := c.ValidateHeader(blk1, 1)
|
||||
err := c.ValidateHeader(blk1, 1, config.MainnetForks)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateHeader block 1: %v", err)
|
||||
}
|
||||
|
|
@ -66,21 +67,21 @@ func TestValidateHeader_Bad_WrongPrevID(t *testing.T) {
|
|||
c := New(s)
|
||||
|
||||
blk0 := &types.Block{
|
||||
BlockHeader: types.BlockHeader{MajorVersion: 1, Timestamp: 1770897600},
|
||||
BlockHeader: types.BlockHeader{MajorVersion: 0, Timestamp: 1770897600},
|
||||
MinerTx: testCoinbaseTx(0),
|
||||
}
|
||||
c.PutBlock(blk0, &BlockMeta{Hash: types.Hash{0x01}, Height: 0})
|
||||
|
||||
blk1 := &types.Block{
|
||||
BlockHeader: types.BlockHeader{
|
||||
MajorVersion: 1,
|
||||
MajorVersion: 0,
|
||||
Timestamp: 1770897720,
|
||||
PrevID: types.Hash{0xFF}, // wrong
|
||||
},
|
||||
MinerTx: testCoinbaseTx(1),
|
||||
}
|
||||
|
||||
err := c.ValidateHeader(blk1, 1)
|
||||
err := c.ValidateHeader(blk1, 1, config.MainnetForks)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for wrong prev_id")
|
||||
}
|
||||
|
|
@ -92,12 +93,12 @@ func TestValidateHeader_Bad_WrongHeight(t *testing.T) {
|
|||
c := New(s)
|
||||
|
||||
blk := &types.Block{
|
||||
BlockHeader: types.BlockHeader{MajorVersion: 1, Timestamp: 1770897600},
|
||||
BlockHeader: types.BlockHeader{MajorVersion: 0, Timestamp: 1770897600},
|
||||
MinerTx: testCoinbaseTx(0),
|
||||
}
|
||||
|
||||
// Chain is empty (height 0), but we pass expectedHeight=5.
|
||||
err := c.ValidateHeader(blk, 5)
|
||||
err := c.ValidateHeader(blk, 5, config.MainnetForks)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for wrong height")
|
||||
}
|
||||
|
|
@ -110,14 +111,33 @@ func TestValidateHeader_Bad_GenesisNonZeroPrev(t *testing.T) {
|
|||
|
||||
blk := &types.Block{
|
||||
BlockHeader: types.BlockHeader{
|
||||
MajorVersion: 1,
|
||||
MajorVersion: 0,
|
||||
PrevID: types.Hash{0xFF}, // genesis must have zero prev_id
|
||||
},
|
||||
MinerTx: testCoinbaseTx(0),
|
||||
}
|
||||
|
||||
err := c.ValidateHeader(blk, 0)
|
||||
err := c.ValidateHeader(blk, 0, config.MainnetForks)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for genesis with non-zero prev_id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateHeader_Bad_WrongVersion(t *testing.T) {
|
||||
s, _ := store.New(":memory:")
|
||||
defer s.Close()
|
||||
c := New(s)
|
||||
|
||||
blk := &types.Block{
|
||||
BlockHeader: types.BlockHeader{
|
||||
MajorVersion: 1,
|
||||
Timestamp: 1770897600,
|
||||
},
|
||||
MinerTx: testCoinbaseTx(0),
|
||||
}
|
||||
|
||||
err := c.ValidateHeader(blk, 0, config.MainnetForks)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for wrong block version")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,14 +127,14 @@ func ValidateBlockReward(minerTx *types.Transaction, height, blockSize, medianSi
|
|||
return nil
|
||||
}
|
||||
|
||||
// expectedBlockMajorVersion returns the expected block major version for a
|
||||
// ExpectedBlockMajorVersion returns the expected block major version for a
|
||||
// given height and fork schedule. This maps hardfork eras to block versions:
|
||||
//
|
||||
// HF0 (genesis) -> 0
|
||||
// HF1 -> 1
|
||||
// HF3 -> 2
|
||||
// HF4+ -> 3
|
||||
func expectedBlockMajorVersion(forks []config.HardFork, height uint64) uint8 {
|
||||
func ExpectedBlockMajorVersion(forks []config.HardFork, height uint64) uint8 {
|
||||
if config.IsHardForkActive(forks, config.HF4Zarcanum, height) {
|
||||
return config.CurrentBlockMajorVersion // 3
|
||||
}
|
||||
|
|
@ -147,13 +147,13 @@ func expectedBlockMajorVersion(forks []config.HardFork, height uint64) uint8 {
|
|||
return config.BlockMajorVersionInitial // 0
|
||||
}
|
||||
|
||||
// checkBlockVersion validates that the block's major version matches
|
||||
// CheckBlockVersion validates that the block's major version matches
|
||||
// what is expected at the given height in the fork schedule.
|
||||
func checkBlockVersion(blk *types.Block, forks []config.HardFork, height uint64) error {
|
||||
expected := expectedBlockMajorVersion(forks, height)
|
||||
if blk.MajorVersion != expected {
|
||||
return coreerr.E("checkBlockVersion", fmt.Sprintf("got %d, want %d at height %d",
|
||||
blk.MajorVersion, expected, height), ErrBlockMajorVersion)
|
||||
func CheckBlockVersion(majorVersion uint8, forks []config.HardFork, height uint64) error {
|
||||
expected := ExpectedBlockMajorVersion(forks, height)
|
||||
if majorVersion != expected {
|
||||
return coreerr.E("CheckBlockVersion", fmt.Sprintf("got %d, want %d at height %d",
|
||||
majorVersion, expected, height), ErrBlockMajorVersion)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -166,7 +166,7 @@ func ValidateBlock(blk *types.Block, height, blockSize, medianSize, totalFees, a
|
|||
recentTimestamps []uint64, forks []config.HardFork) error {
|
||||
|
||||
// Block major version check.
|
||||
if err := checkBlockVersion(blk, forks, height); err != nil {
|
||||
if err := CheckBlockVersion(blk.MajorVersion, forks, height); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -263,9 +263,9 @@ func TestExpectedBlockMajorVersion_Good(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := expectedBlockMajorVersion(tt.forks, tt.height)
|
||||
got := ExpectedBlockMajorVersion(tt.forks, tt.height)
|
||||
if got != tt.want {
|
||||
t.Errorf("expectedBlockMajorVersion(%d) = %d, want %d", tt.height, got, tt.want)
|
||||
t.Errorf("ExpectedBlockMajorVersion(%d) = %d, want %d", tt.height, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -294,7 +294,7 @@ func TestCheckBlockVersion_Good(t *testing.T) {
|
|||
Flags: 0,
|
||||
},
|
||||
}
|
||||
err := checkBlockVersion(blk, tt.forks, tt.height)
|
||||
err := CheckBlockVersion(blk.MajorVersion, tt.forks, tt.height)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
|
@ -323,7 +323,7 @@ func TestCheckBlockVersion_Bad(t *testing.T) {
|
|||
Flags: 0,
|
||||
},
|
||||
}
|
||||
err := checkBlockVersion(blk, tt.forks, tt.height)
|
||||
err := CheckBlockVersion(blk.MajorVersion, tt.forks, tt.height)
|
||||
assert.ErrorIs(t, err, ErrBlockVersion)
|
||||
})
|
||||
}
|
||||
|
|
@ -336,17 +336,17 @@ func TestCheckBlockVersion_Ugly(t *testing.T) {
|
|||
blk := &types.Block{
|
||||
BlockHeader: types.BlockHeader{MajorVersion: 255, Timestamp: now},
|
||||
}
|
||||
err := checkBlockVersion(blk, config.MainnetForks, 0)
|
||||
err := CheckBlockVersion(blk.MajorVersion, config.MainnetForks, 0)
|
||||
assert.ErrorIs(t, err, ErrBlockVersion)
|
||||
|
||||
err = checkBlockVersion(blk, config.MainnetForks, 10081)
|
||||
err = CheckBlockVersion(blk.MajorVersion, config.MainnetForks, 10081)
|
||||
assert.ErrorIs(t, err, ErrBlockVersion)
|
||||
|
||||
// Version 0 at the exact HF1 boundary (height 10080 -- fork not yet active).
|
||||
blk0 := &types.Block{
|
||||
BlockHeader: types.BlockHeader{MajorVersion: config.BlockMajorVersionInitial, Timestamp: now},
|
||||
}
|
||||
err = checkBlockVersion(blk0, config.MainnetForks, 10080)
|
||||
err = CheckBlockVersion(blk0.MajorVersion, config.MainnetForks, 10080)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue