diff --git a/mod/beacon/blockchain/process.go b/mod/beacon/blockchain/process.go index 46c2e4cf05..10b524cc07 100644 --- a/mod/beacon/blockchain/process.go +++ b/mod/beacon/blockchain/process.go @@ -46,15 +46,13 @@ func (s *Service[ ]) ProcessGenesisData( ctx context.Context, genesisData *genesis.Genesis[ - DepositT, *types.ExecutionPayloadHeaderDeneb, + DepositT, *types.ExecutionPayloadHeader, ], ) ([]*transition.ValidatorUpdate, error) { return s.sp.InitializePreminedBeaconStateFromEth1( s.sb.StateFromContext(ctx), genesisData.Deposits, - &types.ExecutionPayloadHeader{ - InnerExecutionPayloadHeader: genesisData.ExecutionPayloadHeader, - }, + genesisData.ExecutionPayloadHeader, genesisData.ForkVersion, ) } diff --git a/mod/cli/pkg/commands/genesis/collect.go b/mod/cli/pkg/commands/genesis/collect.go index 719db97564..09f2fc62cd 100644 --- a/mod/cli/pkg/commands/genesis/collect.go +++ b/mod/cli/pkg/commands/genesis/collect.go @@ -73,7 +73,7 @@ func CollectGenesisDepositsCmd() *cobra.Command { genesisInfo := &genesis.Genesis[ *types.Deposit, - *types.ExecutionPayloadHeaderDeneb, + *types.ExecutionPayloadHeader, ]{} if err = json.Unmarshal( diff --git a/mod/cli/pkg/commands/genesis/payload.go b/mod/cli/pkg/commands/genesis/payload.go index 64560ba1d9..15a2c5dd61 100644 --- a/mod/cli/pkg/commands/genesis/payload.go +++ b/mod/cli/pkg/commands/genesis/payload.go @@ -32,6 +32,7 @@ import ( "github.com/berachain/beacon-kit/mod/primitives" "github.com/berachain/beacon-kit/mod/primitives/pkg/constants" "github.com/berachain/beacon-kit/mod/primitives/pkg/math" + "github.com/berachain/beacon-kit/mod/primitives/pkg/version" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" @@ -87,9 +88,9 @@ func AddExecutionPayloadCmd() *cobra.Command { } genesisInfo := &genesis.Genesis[ - *types.Deposit, - *types.ExecutionPayloadHeaderDeneb, + *types.Deposit, *types.ExecutionPayloadHeader, ]{} + if err = json.Unmarshal( appGenesisState["beacon"], genesisInfo, ); err != nil { @@ -97,14 +98,17 @@ func AddExecutionPayloadCmd() *cobra.Command { } // Inject the execution payload. - genesisInfo.ExecutionPayloadHeader, err = - executableDataToExecutionPayloadHeader(payload) + header, err := executableDataToExecutionPayloadHeader( + version.ToUint32(genesisInfo.ForkVersion), + payload, + ) if err != nil { return errors.Wrap( err, "failed to convert executable data to execution payload header", ) } + genesisInfo.ExecutionPayloadHeader = header appGenesisState["beacon"], err = json.Marshal(genesisInfo) if err != nil { @@ -127,77 +131,91 @@ func AddExecutionPayloadCmd() *cobra.Command { // Converts the eth executable data type to the beacon execution payload header // interface. func executableDataToExecutionPayloadHeader( + forkVersion uint32, data *ethengineprimitives.ExecutableData, -) (*types.ExecutionPayloadHeaderDeneb, error) { - withdrawals := make([]*engineprimitives.Withdrawal, len(data.Withdrawals)) - for i, withdrawal := range data.Withdrawals { - // #nosec:G103 // primitives.Withdrawals is data.Withdrawals with hard - // types. - withdrawals[i] = (*engineprimitives.Withdrawal)( - unsafe.Pointer(withdrawal), +) (*types.ExecutionPayloadHeader, error) { + var executionPayloadHeader *types.ExecutionPayloadHeader + switch forkVersion { + case version.Deneb: + withdrawals := make( + []*engineprimitives.Withdrawal, + len(data.Withdrawals), + ) + for i, withdrawal := range data.Withdrawals { + // #nosec:G103 // primitives.Withdrawals is data.Withdrawals with + // hard + // types. + withdrawals[i] = (*engineprimitives.Withdrawal)( + unsafe.Pointer(withdrawal), + ) + } + + if len(data.ExtraData) > constants.ExtraDataLength { + data.ExtraData = data.ExtraData[:constants.ExtraDataLength] + } + + var blobGasUsed uint64 + if data.BlobGasUsed != nil { + blobGasUsed = *data.BlobGasUsed + } + + var excessBlobGas uint64 + if data.ExcessBlobGas != nil { + excessBlobGas = *data.ExcessBlobGas + } + + // Get the merkle roots of transactions and withdrawals in parallel. + var ( + g, _ = errgroup.WithContext(context.Background()) + txsRoot primitives.Root + withdrawalsRoot primitives.Root ) - } - - if len(data.ExtraData) > constants.ExtraDataLength { - data.ExtraData = data.ExtraData[:constants.ExtraDataLength] - } - - var blobGasUsed uint64 - if data.BlobGasUsed != nil { - blobGasUsed = *data.BlobGasUsed - } - - var excessBlobGas uint64 - if data.ExcessBlobGas != nil { - excessBlobGas = *data.ExcessBlobGas - } - - // Get the merkle roots of transactions and withdrawals in parallel. - var ( - g, _ = errgroup.WithContext(context.Background()) - txsRoot primitives.Root - withdrawalsRoot primitives.Root - ) - - g.Go(func() error { - var txsRootErr error - txsRoot, txsRootErr = engineprimitives.Transactions( - data.Transactions, - ).HashTreeRoot() - return txsRootErr - }) - - g.Go(func() error { - var withdrawalsRootErr error - withdrawalsRoot, withdrawalsRootErr = engineprimitives.Withdrawals( - withdrawals, - ).HashTreeRoot() - return withdrawalsRootErr - }) - - // If deriving either of the roots fails, return the error. - if err := g.Wait(); err != nil { - return nil, err - } - executionPayloadHeader := &types.ExecutionPayloadHeaderDeneb{ - ParentHash: data.ParentHash, - FeeRecipient: data.FeeRecipient, - StateRoot: primitives.Bytes32(data.StateRoot), - ReceiptsRoot: primitives.Bytes32(data.ReceiptsRoot), - LogsBloom: data.LogsBloom, - Random: primitives.Bytes32(data.Random), - Number: math.U64(data.Number), - GasLimit: math.U64(data.GasLimit), - GasUsed: math.U64(data.GasUsed), - Timestamp: math.U64(data.Timestamp), - ExtraData: data.ExtraData, - BaseFeePerGas: math.MustNewU256LFromBigInt(data.BaseFeePerGas), - BlockHash: data.BlockHash, - TransactionsRoot: txsRoot, - WithdrawalsRoot: withdrawalsRoot, - BlobGasUsed: math.U64(blobGasUsed), - ExcessBlobGas: math.U64(excessBlobGas), + g.Go(func() error { + var txsRootErr error + txsRoot, txsRootErr = engineprimitives.Transactions( + data.Transactions, + ).HashTreeRoot() + return txsRootErr + }) + + g.Go(func() error { + var withdrawalsRootErr error + withdrawalsRoot, withdrawalsRootErr = engineprimitives.Withdrawals( + withdrawals, + ).HashTreeRoot() + return withdrawalsRootErr + }) + + // If deriving either of the roots fails, return the error. + if err := g.Wait(); err != nil { + return nil, err + } + + executionPayloadHeader = &types.ExecutionPayloadHeader{ + InnerExecutionPayloadHeader: &types.ExecutionPayloadHeaderDeneb{ + ParentHash: data.ParentHash, + FeeRecipient: data.FeeRecipient, + StateRoot: primitives.Bytes32(data.StateRoot), + ReceiptsRoot: primitives.Bytes32(data.ReceiptsRoot), + LogsBloom: data.LogsBloom, + Random: primitives.Bytes32(data.Random), + Number: math.U64(data.Number), + GasLimit: math.U64(data.GasLimit), + GasUsed: math.U64(data.GasUsed), + Timestamp: math.U64(data.Timestamp), + ExtraData: data.ExtraData, + BaseFeePerGas: math.MustNewU256LFromBigInt( + data.BaseFeePerGas, + ), + BlockHash: data.BlockHash, + TransactionsRoot: txsRoot, + WithdrawalsRoot: withdrawalsRoot, + BlobGasUsed: math.U64(blobGasUsed), + ExcessBlobGas: math.U64(excessBlobGas), + }} + default: + return nil, errors.Newf("unsupported fork version %d", forkVersion) } return executionPayloadHeader, nil diff --git a/mod/consensus-types/pkg/genesis/genesis.go b/mod/consensus-types/pkg/genesis/genesis.go index e28e5cf5e7..be7715e499 100644 --- a/mod/consensus-types/pkg/genesis/genesis.go +++ b/mod/consensus-types/pkg/genesis/genesis.go @@ -22,6 +22,7 @@ package genesis import ( "context" + "encoding/json" "math/big" "github.com/berachain/beacon-kit/mod/consensus-types/pkg/types" @@ -36,9 +37,13 @@ import ( // Genesis is a struct that contains the genesis information // need to start the beacon chain. +// +//nolint:lll type Genesis[ DepositT any, - ExecutonPayloadHeaderT any, + ExecutionPayloadHeaderT interface { + NewFromJSON([]byte, uint32) (ExecutionPayloadHeaderT, error) + }, ] struct { // ForkVersion is the fork version of the genesis slot. ForkVersion primitives.Version `json:"fork_version"` @@ -49,12 +54,44 @@ type Genesis[ // ExecutionPayloadHeader is the header of the execution payload // in the genesis. - ExecutionPayloadHeader ExecutonPayloadHeaderT `json:"execution_payload_header"` + ExecutionPayloadHeader ExecutionPayloadHeaderT `json:"execution_payload_header"` +} + +// UnmarshalJSON for Genesis. +func (g *Genesis[DepositT, ExecutionPayloadHeaderT]) UnmarshalJSON( + data []byte, +) error { + type genesisMarshalable[Deposit any] struct { + ForkVersion primitives.Version `json:"fork_version"` + Deposits []DepositT `json:"deposits"` + ExecutionPayloadHeader json.RawMessage `json:"execution_payload_header"` + } + var g2 genesisMarshalable[DepositT] + if err := json.Unmarshal(data, &g2); err != nil { + return err + } + + var ( + payloadHeader ExecutionPayloadHeaderT + err error + ) + payloadHeader, err = payloadHeader.NewFromJSON( + g2.ExecutionPayloadHeader, + version.ToUint32(g2.ForkVersion), + ) + if err != nil { + return err + } + + g.Deposits = g2.Deposits + g.ForkVersion = g2.ForkVersion + g.ExecutionPayloadHeader = payloadHeader + return nil } // DefaultGenesis returns a the default genesis. func DefaultGenesisDeneb() *Genesis[ - *types.Deposit, *types.ExecutionPayloadHeaderDeneb, + *types.Deposit, *types.ExecutionPayloadHeader, ] { defaultHeader, err := DefaultGenesisExecutionPayloadHeaderDeneb() @@ -63,12 +100,14 @@ func DefaultGenesisDeneb() *Genesis[ } // TODO: Uncouple from deneb. - return &Genesis[*types.Deposit, *types.ExecutionPayloadHeaderDeneb]{ + return &Genesis[*types.Deposit, *types.ExecutionPayloadHeader]{ ForkVersion: version.FromUint32[primitives.Version]( version.Deneb, ), - Deposits: make([]*types.Deposit, 0), - ExecutionPayloadHeader: defaultHeader, + Deposits: make([]*types.Deposit, 0), + ExecutionPayloadHeader: &types.ExecutionPayloadHeader{ + InnerExecutionPayloadHeader: defaultHeader, + }, } } diff --git a/mod/consensus-types/pkg/genesis/genesis_test.go b/mod/consensus-types/pkg/genesis/genesis_test.go index 165373ed85..5d0c60776f 100644 --- a/mod/consensus-types/pkg/genesis/genesis_test.go +++ b/mod/consensus-types/pkg/genesis/genesis_test.go @@ -52,15 +52,15 @@ func TestDefaultGenesisDeneb(t *testing.T) { t.Errorf("Expected ExecutionPayloadHeader to be non-nil") } - require.Equal(t, common.ZeroHash, payloadHeader.ParentHash, + require.Equal(t, common.ZeroHash, payloadHeader.GetParentHash(), "Unexpected ParentHash") - require.Equal(t, common.ZeroAddress, payloadHeader.FeeRecipient, + require.Equal(t, common.ZeroAddress, payloadHeader.GetFeeRecipient(), "Unexpected FeeRecipient") - require.Equal(t, math.U64(30000000), payloadHeader.GasLimit, + require.Equal(t, math.U64(30000000), payloadHeader.GetGasLimit(), "Unexpected GasLimit") - require.Equal(t, math.U64(0), payloadHeader.GasUsed, + require.Equal(t, math.U64(0), payloadHeader.GetGasUsed(), "Unexpected GasUsed") - require.Equal(t, math.U64(0), payloadHeader.Timestamp, + require.Equal(t, math.U64(0), payloadHeader.GetTimestamp(), "Unexpected Timestamp") } diff --git a/mod/consensus-types/pkg/types/payload_header.go b/mod/consensus-types/pkg/types/payload_header.go index 9d639b6587..99ca0f6c0d 100644 --- a/mod/consensus-types/pkg/types/payload_header.go +++ b/mod/consensus-types/pkg/types/payload_header.go @@ -71,11 +71,15 @@ func (e *ExecutionPayloadHeader) NewFromSSZ( return e, nil } -// UnmarshalJSON unmarshals the JSON bytes into the ExecutionPayloadHeader. -func (e *ExecutionPayloadHeader) UnmarshalJSON(bz []byte) error { - // TODO: Generalize somehow. - e = e.Empty(version.Deneb) - return e.InnerExecutionPayloadHeader.UnmarshalJSON(bz) +// NewFromJSON returns a new ExecutionPayloadHeader from the given JSON bytes. +func (e *ExecutionPayloadHeader) NewFromJSON( + bz []byte, forkVersion uint32, +) (*ExecutionPayloadHeader, error) { + e = e.Empty(forkVersion) + if err := e.UnmarshalJSON(bz); err != nil { + return nil, err + } + return e, nil } // ExecutionPayloadHeaderDeneb is the execution header payload of Deneb. diff --git a/mod/consensus-types/pkg/types/payload_header.ssz.go b/mod/consensus-types/pkg/types/payload_header.ssz.go index 1305f1563b..41199e1798 100644 --- a/mod/consensus-types/pkg/types/payload_header.ssz.go +++ b/mod/consensus-types/pkg/types/payload_header.ssz.go @@ -1,5 +1,5 @@ // Code generated by fastssz. DO NOT EDIT. -// Hash: 967200916e521170b5cfcef5a3039c35938502308eb18bbb444530e15fffa144 +// Hash: 94dab153fb2c42c9e9d3dc6371223045ee27f627f8e9d1c0d53167dcc00bfe07 // Version: 0.1.3 package types diff --git a/mod/consensus-types/pkg/types/payload_header_test.go b/mod/consensus-types/pkg/types/payload_header_test.go index 578a01bd35..23971dd0c3 100644 --- a/mod/consensus-types/pkg/types/payload_header_test.go +++ b/mod/consensus-types/pkg/types/payload_header_test.go @@ -480,22 +480,29 @@ func TestExecutionPayloadHeader_NewFromSSZ(t *testing.T) { } } -func TestUnmarshalJSON(t *testing.T) { +func TestExecutionPayloadHeader_NewFromJSON(t *testing.T) { t.Helper() - testCases := []struct { + type testCase struct { name string data []byte + header *types.ExecutionPayloadHeader expectedError error - }{ - { - name: "Valid JSON", - data: func() []byte { - header := generateExecutionPayloadHeaderDeneb() - data, err := json.Marshal(header) - require.NoError(t, err) - return data - }(), - }, + } + testCases := []testCase{ + func() testCase { + header := &types.ExecutionPayloadHeader{ + InnerExecutionPayloadHeader: generateExecutionPayloadHeaderDeneb(), + } + return testCase{ + name: "Valid JSON", + header: header, + data: func() []byte { + data, err := json.Marshal(header) + require.NoError(t, err) + return data + }(), + } + }(), { name: "Invalid JSON", data: []byte{}, @@ -505,14 +512,19 @@ func TestUnmarshalJSON(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - header := new(types.ExecutionPayloadHeader) - err := header.UnmarshalJSON(tc.data) + header, err := new(types.ExecutionPayloadHeader).NewFromJSON( + tc.data, + version.Deneb, + ) if tc.expectedError != nil { require.Error(t, err) require.Contains(t, err.Error(), tc.expectedError.Error()) } else { require.NoError(t, err) } + if tc.header != nil { + require.Equal(t, tc.header, header) + } }) } } diff --git a/mod/node-core/pkg/components/module/module.go b/mod/node-core/pkg/components/module/module.go index 6f76bf6c73..919f215a7f 100644 --- a/mod/node-core/pkg/components/module/module.go +++ b/mod/node-core/pkg/components/module/module.go @@ -105,7 +105,7 @@ func (am AppModule) ExportGenesis( ) (json.RawMessage, error) { return json.Marshal( &genesis.Genesis[ - *types.Deposit, *types.ExecutionPayloadHeaderDeneb, + *types.Deposit, *types.ExecutionPayloadHeader, ]{}, ) } diff --git a/mod/runtime/pkg/middleware/finalize.go b/mod/runtime/pkg/middleware/finalize.go index e116a295cb..fb7d43b517 100644 --- a/mod/runtime/pkg/middleware/finalize.go +++ b/mod/runtime/pkg/middleware/finalize.go @@ -92,11 +92,12 @@ func (h *FinalizeBlockMiddleware[ bz []byte, ) ([]appmodulev2.ValidatorUpdate, error) { data := new( - genesis.Genesis[*types.Deposit, *types.ExecutionPayloadHeaderDeneb], + genesis.Genesis[*types.Deposit, *types.ExecutionPayloadHeader], ) if err := json.Unmarshal(bz, data); err != nil { return nil, err } + updates, err := h.chainService.ProcessGenesisData( ctx, data, diff --git a/mod/runtime/pkg/middleware/types.go b/mod/runtime/pkg/middleware/types.go index 17f60cb0f2..9d51dd84ae 100644 --- a/mod/runtime/pkg/middleware/types.go +++ b/mod/runtime/pkg/middleware/types.go @@ -58,7 +58,7 @@ type BlockchainService[ ProcessGenesisData( context.Context, *genesis.Genesis[ - *types.Deposit, *types.ExecutionPayloadHeaderDeneb, + *types.Deposit, *types.ExecutionPayloadHeader, ], ) ([]*transition.ValidatorUpdate, error) // ProcessBlockAndBlobs processes the given beacon block and associated