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 <charon@lethean.io>
This commit is contained in:
Claude 2026-02-21 00:53:51 +00:00
parent 83a715cdb8
commit 7abac5e011
No known key found for this signature in database
GPG key ID: AF404715446AEB41
2 changed files with 73 additions and 0 deletions

View file

@ -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
}

View file

@ -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)
}