Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Support custom chain presets #123

Merged
merged 20 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ require (
github.com/holiman/uint256 v1.2.4
github.com/huandu/go-clone v1.6.0
github.com/huandu/go-clone/generic v1.6.0
github.com/pk910/dynamic-ssz v0.0.2
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.16.0
github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7
github.com/r3labs/sse/v2 v2.10.0
github.com/rs/zerolog v1.32.0
github.com/stretchr/testify v1.8.4
go.opentelemetry.io/otel v1.16.0
go.opentelemetry.io/otel/trace v1.16.0
golang.org/x/crypto v0.20.0
golang.org/x/sync v0.2.0
)
Expand All @@ -41,11 +43,11 @@ require (
github.com/prometheus/procfs v0.10.1 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
go.opentelemetry.io/otel/metric v1.16.0 // indirect
go.opentelemetry.io/otel/trace v1.16.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/Knetic/govaluate.v3 v3.0.0 // indirect
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dz
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pk910/dynamic-ssz v0.0.2 h1:W8PUqjcM7WeLABtQduqthqqaCK5n4g3VzBukI21tmyk=
github.com/pk910/dynamic-ssz v0.0.2/go.mod h1:PpHTmUfKQKTG5X8NJP9uo14eqmZ0akC9AitgrdjeJmY=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down Expand Up @@ -130,6 +132,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/Knetic/govaluate.v3 v3.0.0 h1:18mUyIt4ZlRlFZAAfVetz4/rzlJs9yhN+U02F4u1AOc=
gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E=
gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y=
gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
56 changes: 46 additions & 10 deletions http/beaconstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/attestantio/go-eth2-client/spec/capella"
"github.com/attestantio/go-eth2-client/spec/deneb"
"github.com/attestantio/go-eth2-client/spec/phase0"
dynssz "github.com/pk910/dynamic-ssz"
)

// BeaconState fetches a beacon state.
Expand Down Expand Up @@ -70,31 +71,66 @@ func (s *Service) beaconStateFromSSZ(res *httpResponse) (*api.Response[*spec.Ver
Metadata: metadataFromHeaders(res.headers),
}

var dynSSZ *dynssz.DynSsz
if s.useDynamicSSZ {
dynSSZ = dynssz.NewDynSsz(s.spec)
pk910 marked this conversation as resolved.
Show resolved Hide resolved
}

switch res.consensusVersion {
case spec.DataVersionPhase0:
response.Data.Phase0 = &phase0.BeaconState{}
if err := response.Data.Phase0.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode phase0 beacon state"), err)
if s.useDynamicSSZ {
if err := dynSSZ.UnmarshalSSZ(response.Data.Phase0, res.body); err != nil {
return nil, errors.Join(errors.New("failed to dynamic decode phase0 beacon state"), err)
}
} else {
if err := response.Data.Phase0.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode phase0 beacon state"), err)
}
}
case spec.DataVersionAltair:
response.Data.Altair = &altair.BeaconState{}
if err := response.Data.Altair.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode altair beacon state"), err)
if s.useDynamicSSZ {
if err := dynSSZ.UnmarshalSSZ(response.Data.Altair, res.body); err != nil {
return nil, errors.Join(errors.New("failed to dynamic decode altair beacon state"), err)
}
} else {
if err := response.Data.Altair.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode altair beacon state"), err)
}
pk910 marked this conversation as resolved.
Show resolved Hide resolved
}
case spec.DataVersionBellatrix:
response.Data.Bellatrix = &bellatrix.BeaconState{}
if err := response.Data.Bellatrix.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode bellatrix beacon state"), err)
if s.useDynamicSSZ {
if err := dynSSZ.UnmarshalSSZ(response.Data.Bellatrix, res.body); err != nil {
return nil, errors.Join(errors.New("failed to dynamic decode bellatrix beacon state"), err)
}
} else {
if err := response.Data.Bellatrix.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode bellatrix beacon state"), err)
}
}
case spec.DataVersionCapella:
response.Data.Capella = &capella.BeaconState{}
if err := response.Data.Capella.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode capella beacon state"), err)
if s.useDynamicSSZ {
if err := dynSSZ.UnmarshalSSZ(response.Data.Capella, res.body); err != nil {
return nil, errors.Join(errors.New("failed to dynamic decode capella beacon state"), err)
}
} else {
if err := response.Data.Capella.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode capella beacon state"), err)
}
}
case spec.DataVersionDeneb:
response.Data.Deneb = &deneb.BeaconState{}
if err := response.Data.Deneb.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode deneb beacon state"), err)
if s.useDynamicSSZ {
if err := dynSSZ.UnmarshalSSZ(response.Data.Deneb, res.body); err != nil {
return nil, errors.Join(errors.New("failed to dynamic decode deneb beacon state"), err)
}
} else {
if err := response.Data.Deneb.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode deneb beacon state"), err)
}
}
default:
return nil, fmt.Errorf("unhandled state version %s", res.consensusVersion)
Expand Down
9 changes: 9 additions & 0 deletions http/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type parameters struct {
allowDelayedStart bool
hooks *Hooks
reducedMemoryUsage bool
dynamicSSZ bool
}

// Parameter is the interface for service parameters.
Expand Down Expand Up @@ -124,6 +125,14 @@ func WithReducedMemoryUsage(reducedMemoryUsage bool) Parameter {
})
}

// WithDynamicSSZ use dynamic SSZ library, which is able to handle non-mainnet presets.
// Dynamic SSZ en-/decoding is much slower than the static one. Use only if required.
func WithDynamicSSZ(dynamicSSZ bool) Parameter {
return parameterFunc(func(p *parameters) {
p.dynamicSSZ = dynamicSSZ
})
}

// parseAndCheckParameters parses and checks parameters to ensure that mandatory parameters are present and correct.
func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
parameters := parameters{
Expand Down
55 changes: 47 additions & 8 deletions http/proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/attestantio/go-eth2-client/spec/bellatrix"
"github.com/attestantio/go-eth2-client/spec/capella"
"github.com/attestantio/go-eth2-client/spec/phase0"
dynssz "github.com/pk910/dynamic-ssz"
"go.opentelemetry.io/otel"
)

Expand Down Expand Up @@ -120,6 +121,7 @@ func (s *Service) Proposal(ctx context.Context,
return response, nil
}

//nolint:nestif
func (s *Service) beaconBlockProposalFromSSZ(res *httpResponse) (*api.Response[*api.VersionedProposal], error) {
response := &api.Response[*api.VersionedProposal]{
Data: &api.VersionedProposal{
Expand All @@ -134,37 +136,74 @@ func (s *Service) beaconBlockProposalFromSSZ(res *httpResponse) (*api.Response[*
return nil, err
}

var dynSSZ *dynssz.DynSsz
if s.useDynamicSSZ {
dynSSZ = dynssz.NewDynSsz(s.spec)
}

var err error
switch res.consensusVersion {
case spec.DataVersionPhase0:
response.Data.Phase0 = &phase0.BeaconBlock{}
err = response.Data.Phase0.UnmarshalSSZ(res.body)
if s.useDynamicSSZ {
err = dynSSZ.UnmarshalSSZ(response.Data.Phase0, res.body)
} else {
err = response.Data.Phase0.UnmarshalSSZ(res.body)
}
case spec.DataVersionAltair:
response.Data.Altair = &altair.BeaconBlock{}
err = response.Data.Altair.UnmarshalSSZ(res.body)
if s.useDynamicSSZ {
err = dynSSZ.UnmarshalSSZ(response.Data.Altair, res.body)
} else {
err = response.Data.Altair.UnmarshalSSZ(res.body)
}
case spec.DataVersionBellatrix:
if response.Data.Blinded {
response.Data.BellatrixBlinded = &apiv1bellatrix.BlindedBeaconBlock{}
err = response.Data.BellatrixBlinded.UnmarshalSSZ(res.body)
if s.useDynamicSSZ {
err = dynSSZ.UnmarshalSSZ(response.Data.BellatrixBlinded, res.body)
} else {
err = response.Data.BellatrixBlinded.UnmarshalSSZ(res.body)
}
} else {
response.Data.Bellatrix = &bellatrix.BeaconBlock{}
err = response.Data.Bellatrix.UnmarshalSSZ(res.body)
if s.useDynamicSSZ {
err = dynSSZ.UnmarshalSSZ(response.Data.Bellatrix, res.body)
} else {
err = response.Data.Bellatrix.UnmarshalSSZ(res.body)
}
}
case spec.DataVersionCapella:
if response.Data.Blinded {
response.Data.CapellaBlinded = &apiv1capella.BlindedBeaconBlock{}
err = response.Data.CapellaBlinded.UnmarshalSSZ(res.body)
if s.useDynamicSSZ {
err = dynSSZ.UnmarshalSSZ(response.Data.CapellaBlinded, res.body)
} else {
err = response.Data.CapellaBlinded.UnmarshalSSZ(res.body)
}
} else {
response.Data.Capella = &capella.BeaconBlock{}
err = response.Data.Capella.UnmarshalSSZ(res.body)
if s.useDynamicSSZ {
err = dynSSZ.UnmarshalSSZ(response.Data.Capella, res.body)
} else {
err = response.Data.Capella.UnmarshalSSZ(res.body)
}
}
case spec.DataVersionDeneb:
if response.Data.Blinded {
response.Data.DenebBlinded = &apiv1deneb.BlindedBeaconBlock{}
err = response.Data.DenebBlinded.UnmarshalSSZ(res.body)
if s.useDynamicSSZ {
err = dynSSZ.UnmarshalSSZ(response.Data.DenebBlinded, res.body)
} else {
err = response.Data.DenebBlinded.UnmarshalSSZ(res.body)
}
} else {
response.Data.Deneb = &apiv1deneb.BlockContents{}
err = response.Data.Deneb.UnmarshalSSZ(res.body)
if s.useDynamicSSZ {
err = dynSSZ.UnmarshalSSZ(response.Data.Deneb, res.body)
} else {
err = response.Data.Deneb.UnmarshalSSZ(res.body)
}
}
default:
return nil, fmt.Errorf("unhandled block proposal version %s", res.consensusVersion)
Expand Down
2 changes: 2 additions & 0 deletions http/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ type Service struct {
enforceJSON bool
connectedToDVTMiddleware bool
reducedMemoryUsage bool
useDynamicSSZ bool
}

// New creates a new Ethereum 2 client service, connecting with a standard HTTP.
Expand Down Expand Up @@ -126,6 +127,7 @@ func New(ctx context.Context, params ...Parameter) (client.Service, error) {
pingSem: semaphore.NewWeighted(1),
hooks: parameters.hooks,
reducedMemoryUsage: parameters.reducedMemoryUsage,
useDynamicSSZ: parameters.dynamicSSZ,
}

// Ping the client to see if it is ready to serve requests.
Expand Down
56 changes: 46 additions & 10 deletions http/signedbeaconblock.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/attestantio/go-eth2-client/spec/capella"
"github.com/attestantio/go-eth2-client/spec/deneb"
"github.com/attestantio/go-eth2-client/spec/phase0"
dynssz "github.com/pk910/dynamic-ssz"
)

// SignedBeaconBlock fetches a signed beacon block given a block ID.
Expand Down Expand Up @@ -73,31 +74,66 @@ func (s *Service) signedBeaconBlockFromSSZ(res *httpResponse) (*api.Response[*sp
Metadata: metadataFromHeaders(res.headers),
}

var dynSSZ *dynssz.DynSsz
if s.useDynamicSSZ {
dynSSZ = dynssz.NewDynSsz(s.spec)
}

switch res.consensusVersion {
case spec.DataVersionPhase0:
response.Data.Phase0 = &phase0.SignedBeaconBlock{}
if err := response.Data.Phase0.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode phase0 signed beacon block"), err)
if s.useDynamicSSZ {
if err := dynSSZ.UnmarshalSSZ(response.Data.Phase0, res.body); err != nil {
return nil, errors.Join(errors.New("failed to dynamic decode phase0 signed beacon block"), err)
}
} else {
if err := response.Data.Phase0.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode phase0 signed beacon block"), err)
}
}
case spec.DataVersionAltair:
response.Data.Altair = &altair.SignedBeaconBlock{}
if err := response.Data.Altair.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode altair signed beacon block"), err)
if s.useDynamicSSZ {
if err := dynSSZ.UnmarshalSSZ(response.Data.Altair, res.body); err != nil {
return nil, errors.Join(errors.New("failed to dynamic decode altair signed beacon block"), err)
}
} else {
if err := response.Data.Altair.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode altair signed beacon block"), err)
}
}
case spec.DataVersionBellatrix:
response.Data.Bellatrix = &bellatrix.SignedBeaconBlock{}
if err := response.Data.Bellatrix.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode bellatrix signed beacon block"), err)
if s.useDynamicSSZ {
if err := dynSSZ.UnmarshalSSZ(response.Data.Bellatrix, res.body); err != nil {
return nil, errors.Join(errors.New("failed to dynamic decode bellatrix signed beacon block"), err)
}
} else {
if err := response.Data.Bellatrix.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode bellatrix signed beacon block"), err)
}
}
case spec.DataVersionCapella:
response.Data.Capella = &capella.SignedBeaconBlock{}
if err := response.Data.Capella.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode capella signed beacon block"), err)
if s.useDynamicSSZ {
if err := dynSSZ.UnmarshalSSZ(response.Data.Capella, res.body); err != nil {
return nil, errors.Join(errors.New("failed to dynamic decode capella signed beacon block"), err)
}
} else {
if err := response.Data.Capella.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode capella signed beacon block"), err)
}
}
case spec.DataVersionDeneb:
response.Data.Deneb = &deneb.SignedBeaconBlock{}
if err := response.Data.Deneb.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode deneb signed block contents"), err)
if s.useDynamicSSZ {
if err := dynSSZ.UnmarshalSSZ(response.Data.Deneb, res.body); err != nil {
return nil, errors.Join(errors.New("failed to dynamic decode deneb signed beacon block"), err)
}
} else {
if err := response.Data.Deneb.UnmarshalSSZ(res.body); err != nil {
return nil, errors.Join(errors.New("failed to decode deneb signed block contents"), err)
}
}
default:
return nil, fmt.Errorf("unhandled block version %s", res.consensusVersion)
Expand Down
10 changes: 5 additions & 5 deletions spec/altair/beaconstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@ type BeaconState struct {
Slot phase0.Slot
Fork *phase0.Fork
LatestBlockHeader *phase0.BeaconBlockHeader
BlockRoots []phase0.Root `ssz-size:"8192,32"`
StateRoots []phase0.Root `ssz-size:"8192,32"`
HistoricalRoots []phase0.Root `ssz-max:"16777216" ssz-size:"?,32"`
BlockRoots []phase0.Root `dynssz-size:"SLOTS_PER_HISTORICAL_ROOT,32" ssz-size:"8192,32"`
StateRoots []phase0.Root `dynssz-size:"SLOTS_PER_HISTORICAL_ROOT,32" ssz-size:"8192,32"`
HistoricalRoots []phase0.Root `ssz-max:"16777216" ssz-size:"?,32"`
ETH1Data *phase0.ETH1Data
ETH1DataVotes []*phase0.ETH1Data `ssz-max:"2048"`
ETH1DepositIndex uint64
Validators []*phase0.Validator `ssz-max:"1099511627776"`
Balances []phase0.Gwei `ssz-max:"1099511627776"`
RANDAOMixes []phase0.Root `ssz-size:"65536,32"`
Slashings []phase0.Gwei `ssz-size:"8192"`
RANDAOMixes []phase0.Root `dynssz-size:"EPOCHS_PER_HISTORICAL_VECTOR,32" ssz-size:"65536,32"`
Slashings []phase0.Gwei `dynssz-size:"EPOCHS_PER_SLASHINGS_VECTOR" ssz-size:"8192"`
PreviousEpochParticipation []ParticipationFlags `ssz-max:"1099511627776"`
CurrentEpochParticipation []ParticipationFlags `ssz-max:"1099511627776"`
JustificationBits bitfield.Bitvector4 `ssz-size:"1"`
Expand Down
8 changes: 1 addition & 7 deletions spec/altair/syncaggregate.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (

// SyncAggregate is the Ethereum 2 sync aggregate structure.
type SyncAggregate struct {
SyncCommitteeBits bitfield.Bitvector512 `ssz-size:"64"`
SyncCommitteeBits bitfield.Bitvector512 `dynssz-size:"SYNC_COMMITTEE_SIZE/8" ssz-size:"64"`
SyncCommitteeSignature phase0.BLSSignature `ssz-size:"96"`
}

Expand Down Expand Up @@ -70,12 +70,6 @@ func (s *SyncAggregate) unpack(syncAggregateJSON *syncAggregateJSON) error {
if err != nil {
return errors.Wrap(err, "invalid value for sync committee bits")
}
if len(syncCommitteeBits) < syncCommitteeSize/8 {
return errors.New("sync committee bits too short")
}
if len(syncCommitteeBits) > syncCommitteeSize/8 {
return errors.New("sync committee bits too long")
}
mcdee marked this conversation as resolved.
Show resolved Hide resolved
s.SyncCommitteeBits = syncCommitteeBits

if syncAggregateJSON.SyncCommitteeSignature == "" {
Expand Down
Loading
Loading