diff --git a/beacon-chain/db/kv/blocks.go b/beacon-chain/db/kv/blocks.go index 9e07c423d0..9b94098af8 100644 --- a/beacon-chain/db/kv/blocks.go +++ b/beacon-chain/db/kv/blocks.go @@ -789,6 +789,16 @@ func unmarshalBlock(_ context.Context, enc []byte) (interfaces.SignedBeaconBlock if err := rawBlock.UnmarshalSSZ(enc[len(bellatrixBlindKey):]); err != nil { return nil, errors.Wrap(err, "could not unmarshal blinded Bellatrix block") } + case hasCapellaKey(enc): + rawBlock = ðpb.SignedBeaconBlockCapella{} + if err := rawBlock.UnmarshalSSZ(enc[len(capellaKey):]); err != nil { + return nil, errors.Wrap(err, "could not unmarshal Capella block") + } + case hasCapellaBlindKey(enc): + rawBlock = ðpb.SignedBlindedBeaconBlockCapella{} + if err := rawBlock.UnmarshalSSZ(enc[len(capellaBlindKey):]); err != nil { + return nil, errors.Wrap(err, "could not unmarshal blinded Capella block") + } default: // Marshal block bytes to phase 0 beacon block. rawBlock = ðpb.SignedBeaconBlock{} @@ -828,6 +838,11 @@ func marshalBlock(_ context.Context, blk interfaces.SignedBeaconBlock) ([]byte, } } switch blockToSave.Version() { + case version.Capella: + if blockToSave.IsBlinded() { + return snappy.Encode(nil, append(capellaBlindKey, encodedBlock...)), nil + } + return snappy.Encode(nil, append(capellaKey, encodedBlock...)), nil case version.Bellatrix: if blockToSave.IsBlinded() { return snappy.Encode(nil, append(bellatrixBlindKey, encodedBlock...)), nil diff --git a/beacon-chain/db/kv/blocks_test.go b/beacon-chain/db/kv/blocks_test.go index 46d1e81bda..35a52b0d40 100644 --- a/beacon-chain/db/kv/blocks_test.go +++ b/beacon-chain/db/kv/blocks_test.go @@ -67,6 +67,28 @@ var blockTests = []struct { return blocks.NewSignedBeaconBlock(b) }, }, + { + name: "capella", + newBlock: func(slot types.Slot, root []byte) (interfaces.SignedBeaconBlock, error) { + b := util.NewBeaconBlockCapella() + b.Block.Slot = slot + if root != nil { + b.Block.ParentRoot = root + } + return blocks.NewSignedBeaconBlock(b) + }, + }, + { + name: "capella blind", + newBlock: func(slot types.Slot, root []byte) (interfaces.SignedBeaconBlock, error) { + b := util.NewBlindedBeaconBlockCapella() + b.Block.Slot = slot + if root != nil { + b.Block.ParentRoot = root + } + return blocks.NewSignedBeaconBlock(b) + }, + }, } func TestStore_SaveBackfillBlockRoot(t *testing.T) { @@ -332,6 +354,10 @@ func TestStore_BlocksCRUD_NoCache(t *testing.T) { wanted, err = blk.ToBlinded() require.NoError(t, err) } + if _, err := blk.PbCapellaBlock(); err == nil { + wanted, err = blk.ToBlinded() + require.NoError(t, err) + } wantedPb, err := wanted.Proto() require.NoError(t, err) retrievedPb, err := retrievedBlock.Proto() @@ -551,6 +577,10 @@ func TestStore_SaveBlock_CanGetHighestAt(t *testing.T) { wanted, err = wanted.ToBlinded() require.NoError(t, err) } + if _, err := block1.PbCapellaBlock(); err == nil { + wanted, err = wanted.ToBlinded() + require.NoError(t, err) + } wantedPb, err := wanted.Proto() require.NoError(t, err) bPb, err := b.Proto() @@ -569,6 +599,10 @@ func TestStore_SaveBlock_CanGetHighestAt(t *testing.T) { wanted2, err = block2.ToBlinded() require.NoError(t, err) } + if _, err := block2.PbCapellaBlock(); err == nil { + wanted2, err = block2.ToBlinded() + require.NoError(t, err) + } wanted2Pb, err := wanted2.Proto() require.NoError(t, err) bPb, err = b.Proto() @@ -587,6 +621,10 @@ func TestStore_SaveBlock_CanGetHighestAt(t *testing.T) { wanted, err = wanted.ToBlinded() require.NoError(t, err) } + if _, err := block3.PbCapellaBlock(); err == nil { + wanted, err = wanted.ToBlinded() + require.NoError(t, err) + } wantedPb, err = wanted.Proto() require.NoError(t, err) bPb, err = b.Proto() @@ -623,6 +661,10 @@ func TestStore_GenesisBlock_CanGetHighestAt(t *testing.T) { wanted, err = block1.ToBlinded() require.NoError(t, err) } + if _, err := block1.PbCapellaBlock(); err == nil { + wanted, err = block1.ToBlinded() + require.NoError(t, err) + } wantedPb, err := wanted.Proto() require.NoError(t, err) bPb, err := b.Proto() @@ -640,6 +682,10 @@ func TestStore_GenesisBlock_CanGetHighestAt(t *testing.T) { wanted, err = genesisBlock.ToBlinded() require.NoError(t, err) } + if _, err := genesisBlock.PbCapellaBlock(); err == nil { + wanted, err = genesisBlock.ToBlinded() + require.NoError(t, err) + } wantedPb, err = wanted.Proto() require.NoError(t, err) bPb, err = b.Proto() @@ -657,6 +703,10 @@ func TestStore_GenesisBlock_CanGetHighestAt(t *testing.T) { wanted, err = genesisBlock.ToBlinded() require.NoError(t, err) } + if _, err := genesisBlock.PbCapellaBlock(); err == nil { + wanted, err = genesisBlock.ToBlinded() + require.NoError(t, err) + } wantedPb, err = wanted.Proto() require.NoError(t, err) bPb, err = b.Proto() @@ -753,6 +803,10 @@ func TestStore_BlocksBySlot_BlockRootsBySlot(t *testing.T) { wanted, err = b1.ToBlinded() require.NoError(t, err) } + if _, err := b1.PbCapellaBlock(); err == nil { + wanted, err = b1.ToBlinded() + require.NoError(t, err) + } retrieved0Pb, err := retrievedBlocks[0].Proto() require.NoError(t, err) wantedPb, err := wanted.Proto() @@ -769,6 +823,10 @@ func TestStore_BlocksBySlot_BlockRootsBySlot(t *testing.T) { wanted, err = b2.ToBlinded() require.NoError(t, err) } + if _, err := b2.PbCapellaBlock(); err == nil { + wanted, err = b2.ToBlinded() + require.NoError(t, err) + } retrieved0Pb, err = retrievedBlocks[0].Proto() require.NoError(t, err) wantedPb, err = wanted.Proto() @@ -779,6 +837,10 @@ func TestStore_BlocksBySlot_BlockRootsBySlot(t *testing.T) { wanted, err = b3.ToBlinded() require.NoError(t, err) } + if _, err := b3.PbCapellaBlock(); err == nil { + wanted, err = b3.ToBlinded() + require.NoError(t, err) + } retrieved1Pb, err := retrievedBlocks[1].Proto() require.NoError(t, err) wantedPb, err = wanted.Proto() diff --git a/beacon-chain/db/kv/key.go b/beacon-chain/db/kv/key.go index ffec270bbb..65c4cfa887 100644 --- a/beacon-chain/db/kv/key.go +++ b/beacon-chain/db/kv/key.go @@ -30,3 +30,10 @@ func hasCapellaKey(enc []byte) bool { } return bytes.Equal(enc[:len(capellaKey)], capellaKey) } + +func hasCapellaBlindKey(enc []byte) bool { + if len(capellaBlindKey) >= len(enc) { + return false + } + return bytes.Equal(enc[:len(capellaBlindKey)], capellaBlindKey) +} diff --git a/beacon-chain/db/kv/schema.go b/beacon-chain/db/kv/schema.go index 43e7610187..49e5724215 100644 --- a/beacon-chain/db/kv/schema.go +++ b/beacon-chain/db/kv/schema.go @@ -53,6 +53,8 @@ var ( bellatrixKey = []byte("merge") bellatrixBlindKey = []byte("blind-bellatrix") capellaKey = []byte("capella") + capellaBlindKey = []byte("blind-capella") + // block root included in the beacon state used by weak subjectivity initial sync originCheckpointBlockRootKey = []byte("origin-checkpoint-block-root") // block root tracking the progress of backfill, or pointing at genesis if backfill has not been initiated diff --git a/testing/util/block.go b/testing/util/block.go index 450e2e47a4..97c5200fa4 100644 --- a/testing/util/block.go +++ b/testing/util/block.go @@ -889,6 +889,141 @@ func HydrateV2BlindedBeaconBlockBodyBellatrix(b *v2.BlindedBeaconBlockBodyBellat return b } +// HydrateSignedBeaconBlockCapella hydrates a signed beacon block with correct field length sizes +// to comply with fssz marshalling and unmarshalling rules. +func HydrateSignedBeaconBlockCapella(b *ethpb.SignedBeaconBlockCapella) *ethpb.SignedBeaconBlockCapella { + if b.Signature == nil { + b.Signature = make([]byte, fieldparams.BLSSignatureLength) + } + b.Block = HydrateBeaconBlockCapella(b.Block) + return b +} + +// HydrateBeaconBlockCapella hydrates a beacon block with correct field length sizes +// to comply with fssz marshalling and unmarshalling rules. +func HydrateBeaconBlockCapella(b *ethpb.BeaconBlockCapella) *ethpb.BeaconBlockCapella { + if b == nil { + b = ðpb.BeaconBlockCapella{} + } + if b.ParentRoot == nil { + b.ParentRoot = make([]byte, fieldparams.RootLength) + } + if b.StateRoot == nil { + b.StateRoot = make([]byte, fieldparams.RootLength) + } + b.Body = HydrateBeaconBlockBodyCapella(b.Body) + return b +} + +// HydrateBeaconBlockBodyCapella hydrates a beacon block body with correct field length sizes +// to comply with fssz marshalling and unmarshalling rules. +func HydrateBeaconBlockBodyCapella(b *ethpb.BeaconBlockBodyCapella) *ethpb.BeaconBlockBodyCapella { + if b == nil { + b = ðpb.BeaconBlockBodyCapella{} + } + if b.RandaoReveal == nil { + b.RandaoReveal = make([]byte, fieldparams.BLSSignatureLength) + } + if b.Graffiti == nil { + b.Graffiti = make([]byte, fieldparams.RootLength) + } + if b.Eth1Data == nil { + b.Eth1Data = ðpb.Eth1Data{ + DepositRoot: make([]byte, fieldparams.RootLength), + BlockHash: make([]byte, fieldparams.RootLength), + } + } + if b.SyncAggregate == nil { + b.SyncAggregate = ðpb.SyncAggregate{ + SyncCommitteeBits: make([]byte, fieldparams.SyncAggregateSyncCommitteeBytesLength), + SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength), + } + } + if b.ExecutionPayload == nil { + b.ExecutionPayload = &enginev1.ExecutionPayloadCapella{ + ParentHash: make([]byte, fieldparams.RootLength), + FeeRecipient: make([]byte, 20), + StateRoot: make([]byte, fieldparams.RootLength), + ReceiptsRoot: make([]byte, fieldparams.RootLength), + LogsBloom: make([]byte, 256), + PrevRandao: make([]byte, fieldparams.RootLength), + BaseFeePerGas: make([]byte, fieldparams.RootLength), + BlockHash: make([]byte, fieldparams.RootLength), + Transactions: make([][]byte, 0), + ExtraData: make([]byte, 0), + } + } + return b +} + +// HydrateSignedBlindedBeaconBlockCapella hydrates a signed blinded beacon block with correct field length sizes +// to comply with fssz marshalling and unmarshalling rules. +func HydrateSignedBlindedBeaconBlockCapella(b *ethpb.SignedBlindedBeaconBlockCapella) *ethpb.SignedBlindedBeaconBlockCapella { + if b.Signature == nil { + b.Signature = make([]byte, fieldparams.BLSSignatureLength) + } + b.Block = HydrateBlindedBeaconBlockCapella(b.Block) + return b +} + +// HydrateBlindedBeaconBlockCapella hydrates a blinded beacon block with correct field length sizes +// to comply with fssz marshalling and unmarshalling rules. +func HydrateBlindedBeaconBlockCapella(b *ethpb.BlindedBeaconBlockCapella) *ethpb.BlindedBeaconBlockCapella { + if b == nil { + b = ðpb.BlindedBeaconBlockCapella{} + } + if b.ParentRoot == nil { + b.ParentRoot = make([]byte, fieldparams.RootLength) + } + if b.StateRoot == nil { + b.StateRoot = make([]byte, fieldparams.RootLength) + } + b.Body = HydrateBlindedBeaconBlockBodyCapella(b.Body) + return b +} + +// HydrateBlindedBeaconBlockBodyCapella hydrates a blinded beacon block body with correct field length sizes +// to comply with fssz marshalling and unmarshalling rules. +func HydrateBlindedBeaconBlockBodyCapella(b *ethpb.BlindedBeaconBlockBodyCapella) *ethpb.BlindedBeaconBlockBodyCapella { + if b == nil { + b = ðpb.BlindedBeaconBlockBodyCapella{} + } + if b.RandaoReveal == nil { + b.RandaoReveal = make([]byte, fieldparams.BLSSignatureLength) + } + if b.Graffiti == nil { + b.Graffiti = make([]byte, 32) + } + if b.Eth1Data == nil { + b.Eth1Data = ðpb.Eth1Data{ + DepositRoot: make([]byte, fieldparams.RootLength), + BlockHash: make([]byte, 32), + } + } + if b.SyncAggregate == nil { + b.SyncAggregate = ðpb.SyncAggregate{ + SyncCommitteeBits: make([]byte, fieldparams.SyncAggregateSyncCommitteeBytesLength), + SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength), + } + } + if b.ExecutionPayloadHeader == nil { + b.ExecutionPayloadHeader = &enginev1.ExecutionPayloadHeaderCapella{ + ParentHash: make([]byte, 32), + FeeRecipient: make([]byte, 20), + StateRoot: make([]byte, fieldparams.RootLength), + ReceiptsRoot: make([]byte, fieldparams.RootLength), + LogsBloom: make([]byte, 256), + PrevRandao: make([]byte, 32), + BaseFeePerGas: make([]byte, 32), + BlockHash: make([]byte, 32), + TransactionsRoot: make([]byte, fieldparams.RootLength), + ExtraData: make([]byte, 0), + WithdrawalsRoot: make([]byte, fieldparams.RootLength), + } + } + return b +} + func SaveBlock(tb assertions.AssertionTestingTB, ctx context.Context, db iface.NoHeadAccessDatabase, b interface{}) interfaces.SignedBeaconBlock { wsb, err := blocks.NewSignedBeaconBlock(b) require.NoError(tb, err) diff --git a/testing/util/block_test.go b/testing/util/block_test.go index b0741e2acd..0441922a97 100644 --- a/testing/util/block_test.go +++ b/testing/util/block_test.go @@ -303,3 +303,57 @@ func TestHydrateV2BlindedBeaconBlockBodyBellatrix_NoError(t *testing.T) { _, err := b.HashTreeRoot() require.NoError(t, err) } + +func TestHydrateSignedBeaconBlockCapella_NoError(t *testing.T) { + b := ðpbalpha.SignedBeaconBlockCapella{} + b = HydrateSignedBeaconBlockCapella(b) + _, err := b.HashTreeRoot() + require.NoError(t, err) + _, err = b.Block.HashTreeRoot() + require.NoError(t, err) + _, err = b.Block.Body.HashTreeRoot() + require.NoError(t, err) +} + +func TestHydrateBeaconBlockCapella_NoError(t *testing.T) { + b := ðpbalpha.BeaconBlockCapella{} + b = HydrateBeaconBlockCapella(b) + _, err := b.HashTreeRoot() + require.NoError(t, err) + _, err = b.Body.HashTreeRoot() + require.NoError(t, err) +} + +func TestHydrateBeaconBlockBodyCapella_NoError(t *testing.T) { + b := ðpbalpha.BeaconBlockBodyCapella{} + b = HydrateBeaconBlockBodyCapella(b) + _, err := b.HashTreeRoot() + require.NoError(t, err) +} + +func TestHydrateSignedBlindedBeaconBlockCapella_NoError(t *testing.T) { + b := ðpbalpha.SignedBlindedBeaconBlockCapella{} + b = HydrateSignedBlindedBeaconBlockCapella(b) + _, err := b.HashTreeRoot() + require.NoError(t, err) + _, err = b.Block.HashTreeRoot() + require.NoError(t, err) + _, err = b.Block.Body.HashTreeRoot() + require.NoError(t, err) +} + +func TestHydrateBlindedBeaconBlockCapella_NoError(t *testing.T) { + b := ðpbalpha.BlindedBeaconBlockCapella{} + b = HydrateBlindedBeaconBlockCapella(b) + _, err := b.HashTreeRoot() + require.NoError(t, err) + _, err = b.Body.HashTreeRoot() + require.NoError(t, err) +} + +func TestHydrateBlindedBeaconBlockBodyCapella_NoError(t *testing.T) { + b := ðpbalpha.BlindedBeaconBlockBodyCapella{} + b = HydrateBlindedBeaconBlockBodyCapella(b) + _, err := b.HashTreeRoot() + require.NoError(t, err) +} diff --git a/testing/util/merge.go b/testing/util/merge.go index bd47370bd8..0ae7a1ba15 100644 --- a/testing/util/merge.go +++ b/testing/util/merge.go @@ -19,3 +19,13 @@ func NewBlindedBeaconBlockBellatrix() *ethpb.SignedBlindedBeaconBlockBellatrix { func NewBlindedBeaconBlockBellatrixV2() *v2.SignedBlindedBeaconBlockBellatrix { return HydrateV2SignedBlindedBeaconBlockBellatrix(&v2.SignedBlindedBeaconBlockBellatrix{}) } + +// NewBeaconBlockCapella creates a beacon block with minimum marshalable fields. +func NewBeaconBlockCapella() *ethpb.SignedBeaconBlockCapella { + return HydrateSignedBeaconBlockCapella(ðpb.SignedBeaconBlockCapella{}) +} + +// NewBlindedBeaconBlockCapella creates a blinded beacon block with minimum marshalable fields. +func NewBlindedBeaconBlockCapella() *ethpb.SignedBlindedBeaconBlockCapella { + return HydrateSignedBlindedBeaconBlockCapella(ðpb.SignedBlindedBeaconBlockCapella{}) +}