Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Beacon root check #529

Merged
merged 11 commits into from
Apr 25, 2024
23 changes: 16 additions & 7 deletions consensus/dummy/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,13 +244,22 @@ func (self *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header
}
// Verify the existence / non-existence of excessBlobGas
cancun := chain.Config().IsCancun(header.Number, header.Time)
if !cancun && header.ExcessBlobGas != nil {
return fmt.Errorf("invalid excessBlobGas: have %d, expected nil", header.ExcessBlobGas)
}
if !cancun && header.BlobGasUsed != nil {
return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", header.BlobGasUsed)
}
if cancun {
if !cancun {
switch {
case header.ExcessBlobGas != nil:
return fmt.Errorf("invalid excessBlobGas: have %d, expected nil", *header.ExcessBlobGas)
case header.BlobGasUsed != nil:
return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", *header.BlobGasUsed)
case header.ParentBeaconRoot != nil:
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", *header.ParentBeaconRoot)
}
} else {
if header.ParentBeaconRoot == nil {
return errors.New("header is missing beaconRoot")
}
if *header.ParentBeaconRoot != (common.Hash{}) {
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected empty", *header.ParentBeaconRoot)
}
if err := eip4844.VerifyEIP4844Header(parent, header); err != nil {
return err
}
Expand Down
3 changes: 1 addition & 2 deletions core/state_processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,7 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
header.ExcessBlobGas = &excess
header.BlobGasUsed = &used

beaconRoot := common.HexToHash("0xbeac00")
header.ParentBeaconRoot = &beaconRoot
header.ParentBeaconRoot = new(common.Hash)
}
// Assemble and return the final block for sealing
return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil))
Expand Down
3 changes: 2 additions & 1 deletion miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ type worker struct {
mu sync.RWMutex // The lock used to protect the coinbase and extra fields
coinbase common.Address
clock *mockable.Clock // Allows us mock the clock for testing
beaconRoot *common.Hash // TODO: not set anywhere, retained for upstream compatibility and future use
beaconRoot *common.Hash // TODO: set to empty hash, retained for upstream compatibility and future use
}

func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, clock *mockable.Clock) *worker {
Expand All @@ -117,6 +117,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
mux: mux,
coinbase: config.Etherbase,
clock: clock,
beaconRoot: &common.Hash{},
}

return worker
Expand Down
17 changes: 15 additions & 2 deletions plugin/evm/block_verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,16 +264,29 @@ func (v blockValidator) SyntacticVerify(b *Block, rules params.Rules) error {
// Verify the existence / non-existence of excessBlobGas
cancun := rules.IsCancun
if !cancun && ethHeader.ExcessBlobGas != nil {
return fmt.Errorf("invalid excessBlobGas: have %d, expected nil", ethHeader.ExcessBlobGas)
return fmt.Errorf("invalid excessBlobGas: have %d, expected nil", *ethHeader.ExcessBlobGas)
}
if !cancun && ethHeader.BlobGasUsed != nil {
return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", ethHeader.BlobGasUsed)
return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", *ethHeader.BlobGasUsed)
}
if cancun && ethHeader.ExcessBlobGas == nil {
return errors.New("header is missing excessBlobGas")
}
if cancun && ethHeader.BlobGasUsed == nil {
return errors.New("header is missing blobGasUsed")
}
if !cancun && ethHeader.ParentBeaconRoot != nil {
return fmt.Errorf("invalid parentBeaconRoot: have %x, expected nil", *ethHeader.ParentBeaconRoot)
}
// TODO: decide what to do after Cancun
// currently we are enforcing it to be empty hash
if cancun {
switch {
case ethHeader.ParentBeaconRoot == nil:
return errors.New("header is missing parentBeaconRoot")
case *ethHeader.ParentBeaconRoot != (common.Hash{}):
return fmt.Errorf("invalid parentBeaconRoot: have %x, expected empty hash", ethHeader.ParentBeaconRoot)
}
}
return nil
}
119 changes: 119 additions & 0 deletions plugin/evm/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ var (
genesisJSONDurango = `{"config":{"chainId":43111,"homesteadBlock":0,"daoForkBlock":0,"daoForkSupport":true,"eip150Block":0,"eip150Hash":"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0","eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"muirGlacierBlock":0,"apricotPhase1BlockTimestamp":0,"apricotPhase2BlockTimestamp":0,"apricotPhase3BlockTimestamp":0,"apricotPhase4BlockTimestamp":0,"apricotPhase5BlockTimestamp":0,"apricotPhasePre6BlockTimestamp":0,"apricotPhase6BlockTimestamp":0,"apricotPhasePost6BlockTimestamp":0,"banffBlockTimestamp":0,"cortinaBlockTimestamp":0,"durangoBlockTimestamp":0},"nonce":"0x0","timestamp":"0x0","extraData":"0x00","gasLimit":"0x5f5e100","difficulty":"0x0","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"0x99b9DEA54C48Dfea6aA9A4Ca4623633EE04ddbB5":{"balance":"0x56bc75e2d63100000"},"0100000000000000000000000000000000000000":{"code":"0x7300000000000000000000000000000000000000003014608060405260043610603d5760003560e01c80631e010439146042578063b6510bb314606e575b600080fd5b605c60048036036020811015605657600080fd5b503560b1565b60408051918252519081900360200190f35b818015607957600080fd5b5060af60048036036080811015608e57600080fd5b506001600160a01b03813516906020810135906040810135906060013560b6565b005b30cd90565b836001600160a01b031681836108fc8690811502906040516000604051808303818888878c8acf9550505050505015801560f4573d6000803e3d6000fd5b505050505056fea26469706673582212201eebce970fe3f5cb96bf8ac6ba5f5c133fc2908ae3dcd51082cfee8f583429d064736f6c634300060a0033","balance":"0x0"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}`
genesisJSONLatest = genesisJSONDurango

genesisJSONCancun = `{"config":{"chainId":43111,"cancunTime":0,"homesteadBlock":0,"daoForkBlock":0,"daoForkSupport":true,"eip150Block":0,"eip150Hash":"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0","eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"muirGlacierBlock":0,"apricotPhase1BlockTimestamp":0,"apricotPhase2BlockTimestamp":0,"apricotPhase3BlockTimestamp":0,"apricotPhase4BlockTimestamp":0,"apricotPhase5BlockTimestamp":0,"apricotPhasePre6BlockTimestamp":0,"apricotPhase6BlockTimestamp":0,"apricotPhasePost6BlockTimestamp":0,"banffBlockTimestamp":0,"cortinaBlockTimestamp":0,"durangoBlockTimestamp":0},"nonce":"0x0","timestamp":"0x0","extraData":"0x00","gasLimit":"0x5f5e100","difficulty":"0x0","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"0x99b9DEA54C48Dfea6aA9A4Ca4623633EE04ddbB5":{"balance":"0x56bc75e2d63100000"},"0100000000000000000000000000000000000000":{"code":"0x7300000000000000000000000000000000000000003014608060405260043610603d5760003560e01c80631e010439146042578063b6510bb314606e575b600080fd5b605c60048036036020811015605657600080fd5b503560b1565b60408051918252519081900360200190f35b818015607957600080fd5b5060af60048036036080811015608e57600080fd5b506001600160a01b03813516906020810135906040810135906060013560b6565b005b30cd90565b836001600160a01b031681836108fc8690811502906040516000604051808303818888878c8acf9550505050505015801560f4573d6000803e3d6000fd5b505050505056fea26469706673582212201eebce970fe3f5cb96bf8ac6ba5f5c133fc2908ae3dcd51082cfee8f583429d064736f6c634300060a0033","balance":"0x0"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}`

apricotRulesPhase0 = params.Rules{}
apricotRulesPhase1 = params.Rules{IsApricotPhase1: true}
apricotRulesPhase2 = params.Rules{IsApricotPhase1: true, IsApricotPhase2: true}
Expand Down Expand Up @@ -4094,3 +4096,120 @@ func TestSkipChainConfigCheckCompatible(t *testing.T) {
require.NoError(t, err)
require.NoError(t, reinitVM.Shutdown(context.Background()))
}

func TestParentBeaconRootBlock(t *testing.T) {
tests := []struct {
name string
genesisJSON string
beaconRoot *common.Hash
expectedError bool
errString string
}{
{
name: "non-empty parent beacon root in Durango",
genesisJSON: genesisJSONDurango,
beaconRoot: &common.Hash{0x01},
expectedError: true,
// err string wont work because it will also fail with blob gas is non-empty (zeroed)
},
{
name: "empty parent beacon root in Durango",
genesisJSON: genesisJSONDurango,
beaconRoot: &common.Hash{},
expectedError: true,
},
{
name: "nil parent beacon root in Durango",
genesisJSON: genesisJSONDurango,
beaconRoot: nil,
expectedError: false,
},
{
name: "non-empty parent beacon root in Cancun",
genesisJSON: genesisJSONCancun,
beaconRoot: &common.Hash{0x01},
expectedError: true,
errString: "expected empty hash",
},
{
name: "empty parent beacon root in Cancun",
genesisJSON: genesisJSONCancun,
beaconRoot: &common.Hash{},
expectedError: false,
},
{
name: "nil parent beacon root in Cancun",
genesisJSON: genesisJSONCancun,
beaconRoot: nil,
expectedError: true,
errString: "header is missing parentBeaconRoot",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
importAmount := uint64(1000000000)
issuer, vm, _, _, _ := GenesisVMWithUTXOs(t, true, test.genesisJSON, "", "", map[ids.ShortID]uint64{
testShortIDAddrs[0]: importAmount,
})

defer func() {
if err := vm.Shutdown(context.Background()); err != nil {
t.Fatal(err)
}
}()

importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]})
if err != nil {
t.Fatal(err)
}

if err := vm.mempool.AddLocalTx(importTx); err != nil {
t.Fatal(err)
}

<-issuer

blk, err := vm.BuildBlock(context.Background())
if err != nil {
t.Fatalf("Failed to build block with import transaction: %s", err)
}

// Modify the block to have a parent beacon root
ethBlock := blk.(*chain.BlockWrapper).Block.(*Block).ethBlock
header := types.CopyHeader(ethBlock.Header())
header.ParentBeaconRoot = test.beaconRoot
parentBeaconEthBlock := types.NewBlockWithExtData(
header,
nil,
nil,
nil,
new(trie.Trie),
ethBlock.ExtData(),
false,
)

parentBeaconBlock, err := vm.newBlock(parentBeaconEthBlock)
if err != nil {
t.Fatal(err)
}

errCheck := func(err error) {
if test.expectedError {
if test.errString != "" {
require.ErrorContains(t, err, test.errString)
} else {
require.Error(t, err)
}
} else {
require.NoError(t, err)
}
}

_, err = vm.ParseBlock(context.Background(), parentBeaconBlock.Bytes())
errCheck(err)
err = parentBeaconBlock.Verify(context.Background())
errCheck(err)
})
}
}
Loading