use VersionedUnmarshaller

This commit is contained in:
rkapka
2022-05-10 19:35:33 +02:00
parent 584ffb87be
commit 7388eeb963
7 changed files with 107 additions and 62 deletions

View File

@@ -44,6 +44,7 @@ go_library(
"//consensus-types/wrapper:go_default_library",
"//crypto/bls:go_default_library",
"//encoding/bytesutil:go_default_library",
"//encoding/ssz/detect:go_default_library",
"//network/forks:go_default_library",
"//proto/eth/v1:go_default_library",
"//proto/eth/v2:go_default_library",

View File

@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"strconv"
"strings"
"github.com/golang/protobuf/ptypes/empty"
"github.com/pkg/errors"
@@ -19,6 +18,8 @@ import (
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/encoding/ssz/detect"
"github.com/prysmaticlabs/prysm/network/forks"
ethpbv1 "github.com/prysmaticlabs/prysm/proto/eth/v1"
ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2"
"github.com/prysmaticlabs/prysm/proto/migration"
@@ -244,56 +245,24 @@ func (bs *Server) SubmitBlockSSZ(ctx context.Context, req *ethpbv2.SignedBeaconB
if len(ver) == 0 {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not read eth-consensus-version header")
}
switch strings.ToLower(ver[0]) {
case strings.ToLower(ethpbv2.Version_PHASE0.String()):
block := &ethpbv1.SignedBeaconBlock{}
if err := block.UnmarshalSSZ(req.Data); err != nil {
return &emptypb.Empty{},
status.Errorf(
codes.InvalidArgument,
"could not unmarshal data into %s block: %v",
strings.ToLower(ethpbv2.Version_PHASE0.String()),
err,
)
}
if err := bs.submitPhase0Block(ctx, block.Block, block.Signature); err != nil {
return nil, err
}
case strings.ToLower(ethpbv2.Version_ALTAIR.String()):
block := &ethpbv2.SignedBeaconBlockAltair{}
if err := block.UnmarshalSSZ(req.Data); err != nil {
return &emptypb.Empty{},
status.Errorf(
codes.InvalidArgument,
"could not unmarshal data into %s block: %v",
strings.ToLower(ethpbv2.Version_ALTAIR.String()),
err,
)
}
if err := bs.submitAltairBlock(ctx, block.Message, block.Signature); err != nil {
return nil, err
}
case strings.ToLower(ethpbv2.Version_BELLATRIX.String()):
block := &ethpbv2.SignedBeaconBlockBellatrix{}
if err := block.UnmarshalSSZ(req.Data); err != nil {
return &emptypb.Empty{},
status.Errorf(
codes.InvalidArgument,
"could not unmarshal data into %s block: %v",
strings.ToLower(ethpbv2.Version_BELLATRIX.String()),
err,
)
}
if err := bs.submitBellatrixBlock(ctx, block.Message, block.Signature); err != nil {
return nil, err
}
default:
return &emptypb.Empty{}, status.Errorf(codes.InvalidArgument, "Unsupported Eth-Consensus-Version %s", ver)
schedule := forks.NewOrderedSchedule(params.BeaconConfig())
forkVer, err := schedule.VersionForName(ver[0])
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not determine fork version: %v", err)
}
return &emptypb.Empty{}, nil
unmarshaler, err := detect.FromForkVersion(forkVer)
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not create unmarshaler: %v", err)
}
block, err := unmarshaler.UnmarshalBeaconBlock(req.Data)
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not unmarshal request data into block: %v", err)
}
root, err := block.Block().HashTreeRoot()
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not compute block's hash tree root: %v", err)
}
return &emptypb.Empty{}, bs.submitBlock(ctx, root, block)
}
// SubmitBlindedBlock instructs the beacon node to use the components of the `SignedBlindedBeaconBlock` to construct

View File

@@ -144,6 +144,7 @@ type BeaconChainConfig struct {
ShardingForkVersion []byte `yaml:"SHARDING_FORK_VERSION" spec:"true"` // ShardingForkVersion is used to represent the fork version for sharding.
ShardingForkEpoch types.Epoch `yaml:"SHARDING_FORK_EPOCH" spec:"true"` // ShardingForkEpoch is used to represent the assigned fork epoch for sharding.
ForkVersionSchedule map[[fieldparams.VersionLength]byte]types.Epoch // Schedule of fork epochs by version.
ForkVersionNames map[[fieldparams.VersionLength]byte]string // Human-readable names of fork versions.
// Weak subjectivity values.
SafetyDecay uint64 // SafetyDecay is defined as the loss in the 1/3 consensus safety margin of the casper FFG mechanism.
@@ -195,6 +196,7 @@ type BeaconChainConfig struct {
func (b *BeaconChainConfig) InitializeForkSchedule() {
// Reset Fork Version Schedule.
b.ForkVersionSchedule = configForkSchedule(b)
b.ForkVersionNames = configForkNames(b)
}
func configForkSchedule(b *BeaconChainConfig) map[[fieldparams.VersionLength]byte]types.Epoch {
@@ -207,3 +209,14 @@ func configForkSchedule(b *BeaconChainConfig) map[[fieldparams.VersionLength]byt
fvs[bytesutil.ToBytes4(b.BellatrixForkVersion)] = b.BellatrixForkEpoch
return fvs
}
func configForkNames(b *BeaconChainConfig) map[[fieldparams.VersionLength]byte]string {
fvn := map[[fieldparams.VersionLength]byte]string{}
// Set Genesis fork data.
fvn[bytesutil.ToBytes4(b.GenesisForkVersion)] = "phase0"
// Set Altair fork data.
fvn[bytesutil.ToBytes4(b.AltairForkVersion)] = "altair"
// Set Bellatrix fork data.
fvn[bytesutil.ToBytes4(b.BellatrixForkVersion)] = "bellatrix"
return fvn
}

View File

@@ -128,7 +128,10 @@ var beaconBlockSlot = fieldSpec{
t: typeUint64,
}
func slotFromBlock(marshaled []byte) (types.Slot, error) {
// SlotFromBlock exploits the fixed-size lower-order bytes in a beacon block
// as a heuristic to obtain the value of the block.slot field without first
// unmarshaling the block.
func SlotFromBlock(marshaled []byte) (types.Slot, error) {
slot, err := beaconBlockSlot.uint64(marshaled)
if err != nil {
return 0, err
@@ -136,12 +139,12 @@ func slotFromBlock(marshaled []byte) (types.Slot, error) {
return types.Slot(slot), nil
}
var errBlockForkMismatch = errors.New("fork or config detected from state is different than block")
var errBlockForkMismatch = errors.New("fork or config detected in unmarshaler is different than block")
// UnmarshalBeaconBlock uses internal knowledge in the VersionedUnmarshaler to pick the right concrete SignedBeaconBlock type,
// then Unmarshal()s the type and returns an instance of block.SignedBeaconBlock if successful.
func (cf *VersionedUnmarshaler) UnmarshalBeaconBlock(marshaled []byte) (interfaces.SignedBeaconBlock, error) {
slot, err := slotFromBlock(marshaled)
slot, err := SlotFromBlock(marshaled)
if err != nil {
return nil, err
}

View File

@@ -27,7 +27,7 @@ func TestSlotFromBlock(t *testing.T) {
b.Block.Slot = slot
bb, err := b.MarshalSSZ()
require.NoError(t, err)
sfb, err := slotFromBlock(bb)
sfb, err := SlotFromBlock(bb)
require.NoError(t, err)
require.Equal(t, slot, sfb)
@@ -35,7 +35,7 @@ func TestSlotFromBlock(t *testing.T) {
ba.Block.Slot = slot
bab, err := ba.MarshalSSZ()
require.NoError(t, err)
sfba, err := slotFromBlock(bab)
sfba, err := SlotFromBlock(bab)
require.NoError(t, err)
require.Equal(t, slot, sfba)
@@ -43,7 +43,7 @@ func TestSlotFromBlock(t *testing.T) {
bm.Block.Slot = slot
bmb, err := ba.MarshalSSZ()
require.NoError(t, err)
sfbm, err := slotFromBlock(bmb)
sfbm, err := SlotFromBlock(bmb)
require.NoError(t, err)
require.Equal(t, slot, sfbm)
}

View File

@@ -2,6 +2,7 @@ package forks
import (
"sort"
"strings"
"github.com/pkg/errors"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
@@ -13,10 +14,11 @@ import (
type ForkScheduleEntry struct {
Version [fieldparams.VersionLength]byte
Epoch types.Epoch
Name string
}
// OrderedSchedule provides a type that can be used to sort the fork schedule and find the Version
// the chain should be at for a given epoch (via VersionForEpoch).
// the chain should be at for a given epoch (via VersionForEpoch) or name (via VersionForName).
type OrderedSchedule []ForkScheduleEntry
// Len implements the Len method of sort.Interface
@@ -38,6 +40,17 @@ func (o OrderedSchedule) VersionForEpoch(epoch types.Epoch) ([fieldparams.Versio
return [fieldparams.VersionLength]byte{}, errors.Wrapf(ErrVersionNotFound, "no epoch in list <= %d", epoch)
}
// VersionForName finds the Version corresponding to the lowercase version of the provided name.
func (o OrderedSchedule) VersionForName(name string) ([fieldparams.VersionLength]byte, error) {
lower := strings.ToLower(name)
for _, e := range o {
if e.Name == lower {
return e.Version, nil
}
}
return [4]byte{}, errors.Wrapf(ErrVersionNotFound, "no version with name %s", lower)
}
func (o OrderedSchedule) Previous(version [fieldparams.VersionLength]byte) ([fieldparams.VersionLength]byte, error) {
for i := len(o) - 1; i >= 0; i-- {
if o[i].Version == version {
@@ -51,7 +64,7 @@ func (o OrderedSchedule) Previous(version [fieldparams.VersionLength]byte) ([fie
return [fieldparams.VersionLength]byte{}, errors.Wrapf(ErrVersionNotFound, "no version in list == %#x", version)
}
// Converts the ForkVersionSchedule map into a list of Version+Epoch values, ordered by Epoch from lowest to highest.
// NewOrderedSchedule Converts fork version maps into a list of Version+Epoch+Name values, ordered by Epoch from lowest to highest.
// See docs for OrderedSchedule for more detail on what you can do with this type.
func NewOrderedSchedule(b *params.BeaconChainConfig) OrderedSchedule {
ofs := make(OrderedSchedule, 0)
@@ -59,6 +72,7 @@ func NewOrderedSchedule(b *params.BeaconChainConfig) OrderedSchedule {
fse := ForkScheduleEntry{
Version: version,
Epoch: epoch,
Name: b.ForkVersionNames[version],
}
ofs = append(ofs, fse)
}

View File

@@ -35,7 +35,7 @@ func TestOrderedConfigSchedule(t *testing.T) {
})
}
bc := testForkVersionScheduleBCC()
bc := testForkVersionBCC()
ofs := NewOrderedSchedule(bc)
for i := range ofs {
if ofs[i].Epoch != types.Epoch(math.Pow(2, float64(i))) {
@@ -45,7 +45,7 @@ func TestOrderedConfigSchedule(t *testing.T) {
}
func TestVersionForEpoch(t *testing.T) {
bc := testForkVersionScheduleBCC()
bc := testForkVersionBCC()
ofs := NewOrderedSchedule(bc)
testCases := []struct {
name string
@@ -92,7 +92,45 @@ func TestVersionForEpoch(t *testing.T) {
}
}
func testForkVersionScheduleBCC() *params.BeaconChainConfig {
func TestVersionForName(t *testing.T) {
bc := testForkVersionBCC()
ofs := NewOrderedSchedule(bc)
testCases := []struct {
testName string
version [4]byte
versionName string
err error
}{
{
testName: "found",
version: [4]byte{2, 1, 2, 3},
versionName: "third",
},
{
testName: "found lowercase",
version: [4]byte{4, 1, 2, 3},
versionName: "FiFtH",
},
{
testName: "not found",
versionName: "nonexistent",
err: ErrVersionNotFound,
},
}
for _, tc := range testCases {
t.Run(tc.testName, func(t *testing.T) {
v, err := ofs.VersionForName(tc.versionName)
if tc.err == nil {
require.NoError(t, err)
} else {
require.ErrorIs(t, err, tc.err)
}
require.Equal(t, tc.version, v)
})
}
}
func testForkVersionBCC() *params.BeaconChainConfig {
return &params.BeaconChainConfig{
ForkVersionSchedule: map[[4]byte]types.Epoch{
{1, 1, 2, 3}: types.Epoch(2),
@@ -101,11 +139,18 @@ func testForkVersionScheduleBCC() *params.BeaconChainConfig {
{3, 1, 2, 3}: types.Epoch(8),
{2, 1, 2, 3}: types.Epoch(4),
},
ForkVersionNames: map[[4]byte]string{
{1, 1, 2, 3}: "second",
{0, 1, 2, 3}: "first",
{4, 1, 2, 3}: "fifth",
{3, 1, 2, 3}: "fourth",
{2, 1, 2, 3}: "third",
},
}
}
func TestPrevious(t *testing.T) {
cfg := testForkVersionScheduleBCC()
cfg := testForkVersionBCC()
os := NewOrderedSchedule(cfg)
unreal := [4]byte{255, 255, 255, 255}
_, err := os.Previous(unreal)