From 7abac5e011ea7c9bb4b629f87ba7a49b36cd6577 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 21 Feb 2026 00:53:51 +0000 Subject: [PATCH] feat(consensus): full block validation orchestrator ValidateBlock combines timestamp, miner tx, and reward checks into a single entry point for block-level consensus validation. Co-Authored-By: Charon --- consensus/block.go | 25 +++++++++++++++++++++ consensus/block_test.go | 48 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/consensus/block.go b/consensus/block.go index 31cd7d9..8bd8687 100644 --- a/consensus/block.go +++ b/consensus/block.go @@ -124,3 +124,28 @@ func ValidateBlockReward(minerTx *types.Transaction, height, blockSize, medianSi return nil } + +// ValidateBlock performs full consensus validation on a block. It checks +// the timestamp, miner transaction structure, and reward. Transaction +// semantic validation for regular transactions should be done separately +// via ValidateTransaction for each tx in the block. +func ValidateBlock(blk *types.Block, height, blockSize, medianSize, totalFees, adjustedTime uint64, + recentTimestamps []uint64, forks []config.HardFork) error { + + // Timestamp validation. + if err := CheckTimestamp(blk.Timestamp, blk.Flags, adjustedTime, recentTimestamps); err != nil { + return err + } + + // Miner transaction structure. + if err := ValidateMinerTx(&blk.MinerTx, height, forks); err != nil { + return err + } + + // Block reward. + if err := ValidateBlockReward(&blk.MinerTx, height, blockSize, medianSize, totalFees, forks); err != nil { + return err + } + + return nil +} diff --git a/consensus/block_test.go b/consensus/block_test.go index 0fd1ba1..efff152 100644 --- a/consensus/block_test.go +++ b/consensus/block_test.go @@ -150,3 +150,51 @@ func TestValidateBlockReward_Good_WithFees(t *testing.T) { err := ValidateBlockReward(tx, height, 1000, config.BlockGrantedFullRewardZone, fees, config.MainnetForks) require.NoError(t, err) } + +func TestValidateBlock_Good(t *testing.T) { + now := uint64(time.Now().Unix()) + height := uint64(100) + blk := &types.Block{ + BlockHeader: types.BlockHeader{ + MajorVersion: 1, + Timestamp: now, + Flags: 0, // PoW + }, + MinerTx: *validMinerTx(height), + } + + err := ValidateBlock(blk, height, 1000, config.BlockGrantedFullRewardZone, 0, now, nil, config.MainnetForks) + require.NoError(t, err) +} + +func TestValidateBlock_Bad_Timestamp(t *testing.T) { + now := uint64(time.Now().Unix()) + height := uint64(100) + blk := &types.Block{ + BlockHeader: types.BlockHeader{ + MajorVersion: 1, + Timestamp: now + config.BlockFutureTimeLimit + 100, + Flags: 0, + }, + MinerTx: *validMinerTx(height), + } + + err := ValidateBlock(blk, height, 1000, config.BlockGrantedFullRewardZone, 0, now, nil, config.MainnetForks) + assert.ErrorIs(t, err, ErrTimestampFuture) +} + +func TestValidateBlock_Bad_MinerTx(t *testing.T) { + now := uint64(time.Now().Unix()) + height := uint64(100) + blk := &types.Block{ + BlockHeader: types.BlockHeader{ + MajorVersion: 1, + Timestamp: now, + Flags: 0, + }, + MinerTx: *validMinerTx(200), // wrong height + } + + err := ValidateBlock(blk, height, 1000, config.BlockGrantedFullRewardZone, 0, now, nil, config.MainnetForks) + assert.ErrorIs(t, err, ErrMinerTxHeight) +}