diff --git a/examples/berad/pkg/consensus-types/validator.go b/examples/berad/pkg/consensus-types/validator.go index 0dd44859a1..c15d3d0571 100644 --- a/examples/berad/pkg/consensus-types/validator.go +++ b/examples/berad/pkg/consensus-types/validator.go @@ -37,15 +37,15 @@ type Validator struct { // Pubkey is the validator's 48-byte BLS public key. Pubkey crypto.BLSPubkey `json:"pubkey"` // WithdrawalCredentials are an address that controls the validator. - WithdrawalCredentials types.WithdrawalCredentials `json:"withdrawalCredentials"` + WithdrawalCredentials types.WithdrawalCredentials `json:"withdrawal_credentials"` // EffectiveBalance is the validator's current effective balance in gwei. - EffectiveBalance math.Gwei `json:"effectiveBalance"` + EffectiveBalance math.Gwei `json:"effective_balance"` // ActivationEpoch is the epoch in which the validator activated. - ActivationEpoch math.Epoch `json:"activationEpoch"` + ActivationEpoch math.Epoch `json:"activation_epoch"` // ExitEpoch is the epoch in which the validator exited. - ExitEpoch math.Epoch `json:"exitEpoch"` + ExitEpoch math.Epoch `json:"exit_epoch"` // WithdrawableEpoch is the epoch in which the validator can withdraw. - WithdrawableEpoch math.Epoch `json:"withdrawableEpoch"` + WithdrawableEpoch math.Epoch `json:"withdrawable_epoch"` } /* -------------------------------------------------------------------------- */ @@ -233,3 +233,13 @@ func (v Validator) GetWithdrawableEpoch() math.Epoch { func (v Validator) GetWithdrawalCredentials() types.WithdrawalCredentials { return v.WithdrawalCredentials } + +// GetActivationEpoch returns the activation epoch of the validator. +func (v *Validator) GetActivationEpoch() math.Epoch { + return v.ActivationEpoch +} + +// GetExitEpoch returns the exit epoch of the validator. +func (v *Validator) GetExitEpoch() math.Epoch { + return v.ExitEpoch +} diff --git a/mod/consensus-types/go.mod b/mod/consensus-types/go.mod index e271d2a9fb..a4737e60fa 100644 --- a/mod/consensus-types/go.mod +++ b/mod/consensus-types/go.mod @@ -4,7 +4,7 @@ go 1.23.0 require ( github.com/berachain/beacon-kit/mod/engine-primitives v0.0.0-20240808194557-e72e74f58197 - github.com/berachain/beacon-kit/mod/errors v0.0.0-20240705193247-d464364483df + github.com/berachain/beacon-kit/mod/errors v0.0.0-20240806211103-d1105603bfc0 github.com/berachain/beacon-kit/mod/primitives v0.0.0-20240911165923-82f71ec86570 github.com/ferranbt/fastssz v0.1.4-0.20240629094022-eac385e6ee79 github.com/holiman/uint256 v1.3.1 diff --git a/mod/consensus-types/go.sum b/mod/consensus-types/go.sum index f0261566a0..11200af898 100644 --- a/mod/consensus-types/go.sum +++ b/mod/consensus-types/go.sum @@ -12,8 +12,8 @@ github.com/berachain/beacon-kit/mod/chain-spec v0.0.0-20240705193247-d464364483d github.com/berachain/beacon-kit/mod/chain-spec v0.0.0-20240705193247-d464364483df/go.mod h1:bTFB4Rdvm7D/WdwPYkqQ+8T0XOMBv0pzXfp1E46BFX8= github.com/berachain/beacon-kit/mod/engine-primitives v0.0.0-20240808194557-e72e74f58197 h1:wVWkiiERY/7kaXvE/VNPPUtYp/l8ky6QSuKM3ThVMXU= github.com/berachain/beacon-kit/mod/engine-primitives v0.0.0-20240808194557-e72e74f58197/go.mod h1:LiOiqrJhhLH/GPo0XE5fel3EYyi7X6dwBOyTqZakTeQ= -github.com/berachain/beacon-kit/mod/errors v0.0.0-20240705193247-d464364483df h1:6MJllcmMFt6dtvftM5zmdl1WVDpqZkNy3hFXVZtNV0s= -github.com/berachain/beacon-kit/mod/errors v0.0.0-20240705193247-d464364483df/go.mod h1:yRD7rmnyaaqgq/6+eIVqvSkFJXuLXpBddUu59HUOrtc= +github.com/berachain/beacon-kit/mod/errors v0.0.0-20240806211103-d1105603bfc0 h1:kCSrkb/uVXfMKJPKjf0c7nlJkwn5cNwMxtzRW4zNq2A= +github.com/berachain/beacon-kit/mod/errors v0.0.0-20240806211103-d1105603bfc0/go.mod h1:og0jtHZosPDTyhge9tMBlRItoZ4Iv3aZFM9n4QDpcdo= github.com/berachain/beacon-kit/mod/geth-primitives v0.0.0-20240806160829-cde2d1347e7e h1:0/FDBXtagMkpta/f4J2uAah2NM1G+0dqxngzMzrmbw4= github.com/berachain/beacon-kit/mod/geth-primitives v0.0.0-20240806160829-cde2d1347e7e/go.mod h1:7/SXz8S5VpFl2thcKuBdu1OId+SgI1o4N+S1FB92Zw8= github.com/berachain/beacon-kit/mod/primitives v0.0.0-20240911165923-82f71ec86570 h1:w0Gkg31VQRFDv0EJjYgVtlpza7kSaJq7U28zxZjfZeE= diff --git a/mod/consensus-types/pkg/types/fork.go b/mod/consensus-types/pkg/types/fork.go index 2f0bc087b1..c6361731dc 100644 --- a/mod/consensus-types/pkg/types/fork.go +++ b/mod/consensus-types/pkg/types/fork.go @@ -129,7 +129,7 @@ func (f *Fork) HashTreeRootWith(hh fastssz.HashWalker) error { hh.PutBytes(f.CurrentVersion[:]) // Field (2) 'Epoch' - hh.PutUint64(uint64(f.Epoch)) + hh.PutUint64(f.Epoch.Unwrap()) hh.Merkleize(indx) return nil @@ -139,3 +139,22 @@ func (f *Fork) HashTreeRootWith(hh fastssz.HashWalker) error { func (f *Fork) GetTree() (*fastssz.Node, error) { return fastssz.ProofTree(f) } + +/* -------------------------------------------------------------------------- */ +/* Getters */ +/* -------------------------------------------------------------------------- */ + +// GetPreviousVersion returns the previous version of the fork. +func (f *Fork) GetPreviousVersion() common.Version { + return f.PreviousVersion +} + +// GetCurrentVersion returns the current version of the fork. +func (f *Fork) GetCurrentVersion() common.Version { + return f.CurrentVersion +} + +// GetEpoch returns the epoch at which the fork occurred. +func (f *Fork) GetEpoch() math.Epoch { + return f.Epoch +} diff --git a/mod/consensus-types/pkg/types/state_test.go b/mod/consensus-types/pkg/types/state_test.go index cece1489cb..c54037cf69 100644 --- a/mod/consensus-types/pkg/types/state_test.go +++ b/mod/consensus-types/pkg/types/state_test.go @@ -78,8 +78,10 @@ func generateValidBeaconState() *types.BeaconState[ }, Validators: []*types.Validator{ { - Pubkey: [48]byte{0x01}, - WithdrawalCredentials: [32]byte{0x02}, + Pubkey: [48]byte{0x01}, + WithdrawalCredentials: types.NewCredentialsFromExecutionAddress( + common.ExecutionAddress{0x02}, + ), EffectiveBalance: 32000000000, Slashed: false, ActivationEligibilityEpoch: 1, @@ -88,8 +90,10 @@ func generateValidBeaconState() *types.BeaconState[ WithdrawableEpoch: 18446744073709551615, }, { - Pubkey: [48]byte{0x03}, - WithdrawalCredentials: [32]byte{0x04}, + Pubkey: [48]byte{0x03}, + WithdrawalCredentials: types.NewCredentialsFromExecutionAddress( + common.ExecutionAddress{0x04}, + ), EffectiveBalance: 31000000000, Slashed: true, ActivationEligibilityEpoch: 3, diff --git a/mod/consensus-types/pkg/types/validator.go b/mod/consensus-types/pkg/types/validator.go index 21c25287aa..37c8bdecfe 100644 --- a/mod/consensus-types/pkg/types/validator.go +++ b/mod/consensus-types/pkg/types/validator.go @@ -47,20 +47,20 @@ type Validator struct { // Pubkey is the validator's 48-byte BLS public key. Pubkey crypto.BLSPubkey `json:"pubkey"` // WithdrawalCredentials are an address that controls the validator. - WithdrawalCredentials WithdrawalCredentials `json:"withdrawalCredentials"` + WithdrawalCredentials WithdrawalCredentials `json:"withdrawal_credentials"` // EffectiveBalance is the validator's current effective balance in gwei. - EffectiveBalance math.Gwei `json:"effectiveBalance"` + EffectiveBalance math.Gwei `json:"effective_balance"` // Slashed indicates whether the validator has been slashed. Slashed bool `json:"slashed"` // ActivationEligibilityEpoch is the epoch in which the validator became // eligible for activation. - ActivationEligibilityEpoch math.Epoch `json:"activationEligibilityEpoch"` + ActivationEligibilityEpoch math.Epoch `json:"activation_eligibility_epoch"` // ActivationEpoch is the epoch in which the validator activated. - ActivationEpoch math.Epoch `json:"activationEpoch"` + ActivationEpoch math.Epoch `json:"activation_epoch"` // ExitEpoch is the epoch in which the validator exited. - ExitEpoch math.Epoch `json:"exitEpoch"` + ExitEpoch math.Epoch `json:"exit_epoch"` // WithdrawableEpoch is the epoch in which the validator can withdraw. - WithdrawableEpoch math.Epoch `json:"withdrawableEpoch"` + WithdrawableEpoch math.Epoch `json:"withdrawable_epoch"` } /* -------------------------------------------------------------------------- */ @@ -226,7 +226,7 @@ func (v *Validator) GetEffectiveBalance() math.Gwei { // https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#is_active_validator // //nolint:lll -func (v Validator) IsActive(epoch math.Epoch) bool { +func (v *Validator) IsActive(epoch math.Epoch) bool { return v.ActivationEpoch <= epoch && epoch < v.ExitEpoch } @@ -234,7 +234,7 @@ func (v Validator) IsActive(epoch math.Epoch) bool { // https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#is_eligible_for_activation_queue // //nolint:lll -func (v Validator) IsEligibleForActivation( +func (v *Validator) IsEligibleForActivation( finalizedEpoch math.Epoch, ) bool { return v.ActivationEligibilityEpoch <= finalizedEpoch && @@ -245,7 +245,7 @@ func (v Validator) IsEligibleForActivation( // https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#is_eligible_for_activation_queue // //nolint:lll -func (v Validator) IsEligibleForActivationQueue( +func (v *Validator) IsEligibleForActivationQueue( maxEffectiveBalance math.Gwei, ) bool { return v.ActivationEligibilityEpoch == math.Epoch( @@ -258,13 +258,13 @@ func (v Validator) IsEligibleForActivationQueue( // https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#is_slashable_validator // //nolint:lll -func (v Validator) IsSlashable(epoch math.Epoch) bool { +func (v *Validator) IsSlashable(epoch math.Epoch) bool { return !v.Slashed && v.ActivationEpoch <= epoch && epoch < v.WithdrawableEpoch } // IsSlashed returns whether the validator has been slashed. -func (v Validator) IsSlashed() bool { +func (v *Validator) IsSlashed() bool { return v.Slashed } @@ -272,7 +272,7 @@ func (v Validator) IsSlashed() bool { // https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#is_fully_withdrawable_validator // //nolint:lll -func (v Validator) IsFullyWithdrawable( +func (v *Validator) IsFullyWithdrawable( balance math.Gwei, epoch math.Epoch, ) bool { @@ -284,7 +284,7 @@ func (v Validator) IsFullyWithdrawable( // https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#is_partially_withdrawable_validator // //nolint:lll -func (v Validator) IsPartiallyWithdrawable( +func (v *Validator) IsPartiallyWithdrawable( balance, maxEffectiveBalance math.Gwei, ) bool { hasExcessBalance := balance > maxEffectiveBalance @@ -296,13 +296,13 @@ func (v Validator) IsPartiallyWithdrawable( // https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#has_eth1_withdrawal_credential // //nolint:lll -func (v Validator) HasEth1WithdrawalCredentials() bool { +func (v *Validator) HasEth1WithdrawalCredentials() bool { return v.WithdrawalCredentials[0] == EthSecp256k1CredentialPrefix } // HasMaxEffectiveBalance determines if the validator has the maximum effective // balance. -func (v Validator) HasMaxEffectiveBalance( +func (v *Validator) HasMaxEffectiveBalance( maxEffectiveBalance math.Gwei, ) bool { return v.EffectiveBalance == maxEffectiveBalance @@ -314,11 +314,27 @@ func (v *Validator) SetEffectiveBalance(balance math.Gwei) { } // GetWithdrawableEpoch returns the epoch when the validator can withdraw. -func (v Validator) GetWithdrawableEpoch() math.Epoch { +func (v *Validator) GetWithdrawableEpoch() math.Epoch { return v.WithdrawableEpoch } // GetWithdrawalCredentials returns the withdrawal credentials of the validator. -func (v Validator) GetWithdrawalCredentials() WithdrawalCredentials { +func (v *Validator) GetWithdrawalCredentials() WithdrawalCredentials { return v.WithdrawalCredentials } + +// GetActivationEligibilityEpoch returns the activation eligibility +// epoch of the validator. +func (v *Validator) GetActivationEligibilityEpoch() math.Epoch { + return v.ActivationEligibilityEpoch +} + +// GetActivationEpoch returns the activation epoch of the validator. +func (v *Validator) GetActivationEpoch() math.Epoch { + return v.ActivationEpoch +} + +// GetExitEpoch returns the exit epoch of the validator. +func (v *Validator) GetExitEpoch() math.Epoch { + return v.ExitEpoch +} diff --git a/mod/consensus-types/pkg/types/withdrawal_credentials.go b/mod/consensus-types/pkg/types/withdrawal_credentials.go index 1956de94c6..1ce97da7fd 100644 --- a/mod/consensus-types/pkg/types/withdrawal_credentials.go +++ b/mod/consensus-types/pkg/types/withdrawal_credentials.go @@ -81,3 +81,8 @@ func (wc WithdrawalCredentials) MarshalText() ([]byte, error) { func (wc *WithdrawalCredentials) UnmarshalText(text []byte) error { return (*common.Bytes32)(wc).UnmarshalText(text) } + +// Bytes returns the raw byte representation of the withdrawal credentials. +func (wc WithdrawalCredentials) Bytes() []byte { + return wc[:] +} diff --git a/mod/node-api/backend/mocks/validator.mock.go b/mod/node-api/backend/mocks/validator.mock.go index 73404aa0d7..6649c07394 100644 --- a/mod/node-api/backend/mocks/validator.mock.go +++ b/mod/node-api/backend/mocks/validator.mock.go @@ -4,6 +4,8 @@ package mocks import ( backend "github.com/berachain/beacon-kit/mod/node-api/backend" + crypto "github.com/berachain/beacon-kit/mod/primitives/pkg/crypto" + math "github.com/berachain/beacon-kit/mod/primitives/pkg/math" mock "github.com/stretchr/testify/mock" @@ -22,109 +24,334 @@ func (_m *Validator[WithdrawalCredentialsT]) EXPECT() *Validator_Expecter[Withdr return &Validator_Expecter[WithdrawalCredentialsT]{mock: &_m.Mock} } -// GetWithdrawalCredentials provides a mock function with given fields: -func (_m *Validator[WithdrawalCredentialsT]) GetWithdrawalCredentials() WithdrawalCredentialsT { +// GetActivationEligibilityEpoch provides a mock function with given fields: +func (_m *Validator[WithdrawalCredentialsT]) GetActivationEligibilityEpoch() math.U64 { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for GetWithdrawalCredentials") + panic("no return value specified for GetActivationEligibilityEpoch") } - var r0 WithdrawalCredentialsT - if rf, ok := ret.Get(0).(func() WithdrawalCredentialsT); ok { + var r0 math.U64 + if rf, ok := ret.Get(0).(func() math.U64); ok { r0 = rf() } else { - r0 = ret.Get(0).(WithdrawalCredentialsT) + r0 = ret.Get(0).(math.U64) } return r0 } -// Validator_GetWithdrawalCredentials_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWithdrawalCredentials' -type Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT backend.WithdrawalCredentials] struct { +// Validator_GetActivationEligibilityEpoch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetActivationEligibilityEpoch' +type Validator_GetActivationEligibilityEpoch_Call[WithdrawalCredentialsT backend.WithdrawalCredentials] struct { *mock.Call } -// GetWithdrawalCredentials is a helper method to define mock.On call -func (_e *Validator_Expecter[WithdrawalCredentialsT]) GetWithdrawalCredentials() *Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT] { - return &Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT]{Call: _e.mock.On("GetWithdrawalCredentials")} +// GetActivationEligibilityEpoch is a helper method to define mock.On call +func (_e *Validator_Expecter[WithdrawalCredentialsT]) GetActivationEligibilityEpoch() *Validator_GetActivationEligibilityEpoch_Call[WithdrawalCredentialsT] { + return &Validator_GetActivationEligibilityEpoch_Call[WithdrawalCredentialsT]{Call: _e.mock.On("GetActivationEligibilityEpoch")} } -func (_c *Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT]) Run(run func()) *Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT] { +func (_c *Validator_GetActivationEligibilityEpoch_Call[WithdrawalCredentialsT]) Run(run func()) *Validator_GetActivationEligibilityEpoch_Call[WithdrawalCredentialsT] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT]) Return(_a0 WithdrawalCredentialsT) *Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT] { +func (_c *Validator_GetActivationEligibilityEpoch_Call[WithdrawalCredentialsT]) Return(_a0 math.U64) *Validator_GetActivationEligibilityEpoch_Call[WithdrawalCredentialsT] { _c.Call.Return(_a0) return _c } -func (_c *Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT]) RunAndReturn(run func() WithdrawalCredentialsT) *Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT] { +func (_c *Validator_GetActivationEligibilityEpoch_Call[WithdrawalCredentialsT]) RunAndReturn(run func() math.U64) *Validator_GetActivationEligibilityEpoch_Call[WithdrawalCredentialsT] { _c.Call.Return(run) return _c } -// IsFullyWithdrawable provides a mock function with given fields: amount, epoch -func (_m *Validator[WithdrawalCredentialsT]) IsFullyWithdrawable(amount math.U64, epoch math.U64) bool { - ret := _m.Called(amount, epoch) +// GetActivationEpoch provides a mock function with given fields: +func (_m *Validator[WithdrawalCredentialsT]) GetActivationEpoch() math.U64 { + ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for IsFullyWithdrawable") + panic("no return value specified for GetActivationEpoch") } - var r0 bool - if rf, ok := ret.Get(0).(func(math.U64, math.U64) bool); ok { - r0 = rf(amount, epoch) + var r0 math.U64 + if rf, ok := ret.Get(0).(func() math.U64); ok { + r0 = rf() } else { - r0 = ret.Get(0).(bool) + r0 = ret.Get(0).(math.U64) + } + + return r0 +} + +// Validator_GetActivationEpoch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetActivationEpoch' +type Validator_GetActivationEpoch_Call[WithdrawalCredentialsT backend.WithdrawalCredentials] struct { + *mock.Call +} + +// GetActivationEpoch is a helper method to define mock.On call +func (_e *Validator_Expecter[WithdrawalCredentialsT]) GetActivationEpoch() *Validator_GetActivationEpoch_Call[WithdrawalCredentialsT] { + return &Validator_GetActivationEpoch_Call[WithdrawalCredentialsT]{Call: _e.mock.On("GetActivationEpoch")} +} + +func (_c *Validator_GetActivationEpoch_Call[WithdrawalCredentialsT]) Run(run func()) *Validator_GetActivationEpoch_Call[WithdrawalCredentialsT] { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Validator_GetActivationEpoch_Call[WithdrawalCredentialsT]) Return(_a0 math.U64) *Validator_GetActivationEpoch_Call[WithdrawalCredentialsT] { + _c.Call.Return(_a0) + return _c +} + +func (_c *Validator_GetActivationEpoch_Call[WithdrawalCredentialsT]) RunAndReturn(run func() math.U64) *Validator_GetActivationEpoch_Call[WithdrawalCredentialsT] { + _c.Call.Return(run) + return _c +} + +// GetEffectiveBalance provides a mock function with given fields: +func (_m *Validator[WithdrawalCredentialsT]) GetEffectiveBalance() math.U64 { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetEffectiveBalance") + } + + var r0 math.U64 + if rf, ok := ret.Get(0).(func() math.U64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(math.U64) + } + + return r0 +} + +// Validator_GetEffectiveBalance_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetEffectiveBalance' +type Validator_GetEffectiveBalance_Call[WithdrawalCredentialsT backend.WithdrawalCredentials] struct { + *mock.Call +} + +// GetEffectiveBalance is a helper method to define mock.On call +func (_e *Validator_Expecter[WithdrawalCredentialsT]) GetEffectiveBalance() *Validator_GetEffectiveBalance_Call[WithdrawalCredentialsT] { + return &Validator_GetEffectiveBalance_Call[WithdrawalCredentialsT]{Call: _e.mock.On("GetEffectiveBalance")} +} + +func (_c *Validator_GetEffectiveBalance_Call[WithdrawalCredentialsT]) Run(run func()) *Validator_GetEffectiveBalance_Call[WithdrawalCredentialsT] { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Validator_GetEffectiveBalance_Call[WithdrawalCredentialsT]) Return(_a0 math.U64) *Validator_GetEffectiveBalance_Call[WithdrawalCredentialsT] { + _c.Call.Return(_a0) + return _c +} + +func (_c *Validator_GetEffectiveBalance_Call[WithdrawalCredentialsT]) RunAndReturn(run func() math.U64) *Validator_GetEffectiveBalance_Call[WithdrawalCredentialsT] { + _c.Call.Return(run) + return _c +} + +// GetExitEpoch provides a mock function with given fields: +func (_m *Validator[WithdrawalCredentialsT]) GetExitEpoch() math.U64 { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetExitEpoch") + } + + var r0 math.U64 + if rf, ok := ret.Get(0).(func() math.U64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(math.U64) + } + + return r0 +} + +// Validator_GetExitEpoch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetExitEpoch' +type Validator_GetExitEpoch_Call[WithdrawalCredentialsT backend.WithdrawalCredentials] struct { + *mock.Call +} + +// GetExitEpoch is a helper method to define mock.On call +func (_e *Validator_Expecter[WithdrawalCredentialsT]) GetExitEpoch() *Validator_GetExitEpoch_Call[WithdrawalCredentialsT] { + return &Validator_GetExitEpoch_Call[WithdrawalCredentialsT]{Call: _e.mock.On("GetExitEpoch")} +} + +func (_c *Validator_GetExitEpoch_Call[WithdrawalCredentialsT]) Run(run func()) *Validator_GetExitEpoch_Call[WithdrawalCredentialsT] { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Validator_GetExitEpoch_Call[WithdrawalCredentialsT]) Return(_a0 math.U64) *Validator_GetExitEpoch_Call[WithdrawalCredentialsT] { + _c.Call.Return(_a0) + return _c +} + +func (_c *Validator_GetExitEpoch_Call[WithdrawalCredentialsT]) RunAndReturn(run func() math.U64) *Validator_GetExitEpoch_Call[WithdrawalCredentialsT] { + _c.Call.Return(run) + return _c +} + +// GetPubkey provides a mock function with given fields: +func (_m *Validator[WithdrawalCredentialsT]) GetPubkey() crypto.BLSPubkey { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetPubkey") + } + + var r0 crypto.BLSPubkey + if rf, ok := ret.Get(0).(func() crypto.BLSPubkey); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(crypto.BLSPubkey) + } + } + + return r0 +} + +// Validator_GetPubkey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPubkey' +type Validator_GetPubkey_Call[WithdrawalCredentialsT backend.WithdrawalCredentials] struct { + *mock.Call +} + +// GetPubkey is a helper method to define mock.On call +func (_e *Validator_Expecter[WithdrawalCredentialsT]) GetPubkey() *Validator_GetPubkey_Call[WithdrawalCredentialsT] { + return &Validator_GetPubkey_Call[WithdrawalCredentialsT]{Call: _e.mock.On("GetPubkey")} +} + +func (_c *Validator_GetPubkey_Call[WithdrawalCredentialsT]) Run(run func()) *Validator_GetPubkey_Call[WithdrawalCredentialsT] { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Validator_GetPubkey_Call[WithdrawalCredentialsT]) Return(_a0 crypto.BLSPubkey) *Validator_GetPubkey_Call[WithdrawalCredentialsT] { + _c.Call.Return(_a0) + return _c +} + +func (_c *Validator_GetPubkey_Call[WithdrawalCredentialsT]) RunAndReturn(run func() crypto.BLSPubkey) *Validator_GetPubkey_Call[WithdrawalCredentialsT] { + _c.Call.Return(run) + return _c +} + +// GetWithdrawableEpoch provides a mock function with given fields: +func (_m *Validator[WithdrawalCredentialsT]) GetWithdrawableEpoch() math.U64 { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetWithdrawableEpoch") + } + + var r0 math.U64 + if rf, ok := ret.Get(0).(func() math.U64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(math.U64) + } + + return r0 +} + +// Validator_GetWithdrawableEpoch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWithdrawableEpoch' +type Validator_GetWithdrawableEpoch_Call[WithdrawalCredentialsT backend.WithdrawalCredentials] struct { + *mock.Call +} + +// GetWithdrawableEpoch is a helper method to define mock.On call +func (_e *Validator_Expecter[WithdrawalCredentialsT]) GetWithdrawableEpoch() *Validator_GetWithdrawableEpoch_Call[WithdrawalCredentialsT] { + return &Validator_GetWithdrawableEpoch_Call[WithdrawalCredentialsT]{Call: _e.mock.On("GetWithdrawableEpoch")} +} + +func (_c *Validator_GetWithdrawableEpoch_Call[WithdrawalCredentialsT]) Run(run func()) *Validator_GetWithdrawableEpoch_Call[WithdrawalCredentialsT] { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Validator_GetWithdrawableEpoch_Call[WithdrawalCredentialsT]) Return(_a0 math.U64) *Validator_GetWithdrawableEpoch_Call[WithdrawalCredentialsT] { + _c.Call.Return(_a0) + return _c +} + +func (_c *Validator_GetWithdrawableEpoch_Call[WithdrawalCredentialsT]) RunAndReturn(run func() math.U64) *Validator_GetWithdrawableEpoch_Call[WithdrawalCredentialsT] { + _c.Call.Return(run) + return _c +} + +// GetWithdrawalCredentials provides a mock function with given fields: +func (_m *Validator[WithdrawalCredentialsT]) GetWithdrawalCredentials() WithdrawalCredentialsT { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetWithdrawalCredentials") + } + + var r0 WithdrawalCredentialsT + if rf, ok := ret.Get(0).(func() WithdrawalCredentialsT); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(WithdrawalCredentialsT) } return r0 } -// Validator_IsFullyWithdrawable_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsFullyWithdrawable' -type Validator_IsFullyWithdrawable_Call[WithdrawalCredentialsT backend.WithdrawalCredentials] struct { +// Validator_GetWithdrawalCredentials_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWithdrawalCredentials' +type Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT backend.WithdrawalCredentials] struct { *mock.Call } -// IsFullyWithdrawable is a helper method to define mock.On call -// - amount math.U64 -// - epoch math.U64 -func (_e *Validator_Expecter[WithdrawalCredentialsT]) IsFullyWithdrawable(amount interface{}, epoch interface{}) *Validator_IsFullyWithdrawable_Call[WithdrawalCredentialsT] { - return &Validator_IsFullyWithdrawable_Call[WithdrawalCredentialsT]{Call: _e.mock.On("IsFullyWithdrawable", amount, epoch)} +// GetWithdrawalCredentials is a helper method to define mock.On call +func (_e *Validator_Expecter[WithdrawalCredentialsT]) GetWithdrawalCredentials() *Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT] { + return &Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT]{Call: _e.mock.On("GetWithdrawalCredentials")} } -func (_c *Validator_IsFullyWithdrawable_Call[WithdrawalCredentialsT]) Run(run func(amount math.U64, epoch math.U64)) *Validator_IsFullyWithdrawable_Call[WithdrawalCredentialsT] { +func (_c *Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT]) Run(run func()) *Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT] { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(math.U64), args[1].(math.U64)) + run() }) return _c } -func (_c *Validator_IsFullyWithdrawable_Call[WithdrawalCredentialsT]) Return(_a0 bool) *Validator_IsFullyWithdrawable_Call[WithdrawalCredentialsT] { +func (_c *Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT]) Return(_a0 WithdrawalCredentialsT) *Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT] { _c.Call.Return(_a0) return _c } -func (_c *Validator_IsFullyWithdrawable_Call[WithdrawalCredentialsT]) RunAndReturn(run func(math.U64, math.U64) bool) *Validator_IsFullyWithdrawable_Call[WithdrawalCredentialsT] { +func (_c *Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT]) RunAndReturn(run func() WithdrawalCredentialsT) *Validator_GetWithdrawalCredentials_Call[WithdrawalCredentialsT] { _c.Call.Return(run) return _c } -// IsPartiallyWithdrawable provides a mock function with given fields: amount1, amount2 -func (_m *Validator[WithdrawalCredentialsT]) IsPartiallyWithdrawable(amount1 math.U64, amount2 math.U64) bool { - ret := _m.Called(amount1, amount2) +// IsSlashed provides a mock function with given fields: +func (_m *Validator[WithdrawalCredentialsT]) IsSlashed() bool { + ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for IsPartiallyWithdrawable") + panic("no return value specified for IsSlashed") } var r0 bool - if rf, ok := ret.Get(0).(func(math.U64, math.U64) bool); ok { - r0 = rf(amount1, amount2) + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() } else { r0 = ret.Get(0).(bool) } @@ -132,31 +359,29 @@ func (_m *Validator[WithdrawalCredentialsT]) IsPartiallyWithdrawable(amount1 mat return r0 } -// Validator_IsPartiallyWithdrawable_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsPartiallyWithdrawable' -type Validator_IsPartiallyWithdrawable_Call[WithdrawalCredentialsT backend.WithdrawalCredentials] struct { +// Validator_IsSlashed_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsSlashed' +type Validator_IsSlashed_Call[WithdrawalCredentialsT backend.WithdrawalCredentials] struct { *mock.Call } -// IsPartiallyWithdrawable is a helper method to define mock.On call -// - amount1 math.U64 -// - amount2 math.U64 -func (_e *Validator_Expecter[WithdrawalCredentialsT]) IsPartiallyWithdrawable(amount1 interface{}, amount2 interface{}) *Validator_IsPartiallyWithdrawable_Call[WithdrawalCredentialsT] { - return &Validator_IsPartiallyWithdrawable_Call[WithdrawalCredentialsT]{Call: _e.mock.On("IsPartiallyWithdrawable", amount1, amount2)} +// IsSlashed is a helper method to define mock.On call +func (_e *Validator_Expecter[WithdrawalCredentialsT]) IsSlashed() *Validator_IsSlashed_Call[WithdrawalCredentialsT] { + return &Validator_IsSlashed_Call[WithdrawalCredentialsT]{Call: _e.mock.On("IsSlashed")} } -func (_c *Validator_IsPartiallyWithdrawable_Call[WithdrawalCredentialsT]) Run(run func(amount1 math.U64, amount2 math.U64)) *Validator_IsPartiallyWithdrawable_Call[WithdrawalCredentialsT] { +func (_c *Validator_IsSlashed_Call[WithdrawalCredentialsT]) Run(run func()) *Validator_IsSlashed_Call[WithdrawalCredentialsT] { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(math.U64), args[1].(math.U64)) + run() }) return _c } -func (_c *Validator_IsPartiallyWithdrawable_Call[WithdrawalCredentialsT]) Return(_a0 bool) *Validator_IsPartiallyWithdrawable_Call[WithdrawalCredentialsT] { +func (_c *Validator_IsSlashed_Call[WithdrawalCredentialsT]) Return(_a0 bool) *Validator_IsSlashed_Call[WithdrawalCredentialsT] { _c.Call.Return(_a0) return _c } -func (_c *Validator_IsPartiallyWithdrawable_Call[WithdrawalCredentialsT]) RunAndReturn(run func(math.U64, math.U64) bool) *Validator_IsPartiallyWithdrawable_Call[WithdrawalCredentialsT] { +func (_c *Validator_IsSlashed_Call[WithdrawalCredentialsT]) RunAndReturn(run func() bool) *Validator_IsSlashed_Call[WithdrawalCredentialsT] { _c.Call.Return(run) return _c } diff --git a/mod/node-api/backend/mocks/withdrawal_credentials.mock.go b/mod/node-api/backend/mocks/withdrawal_credentials.mock.go index 4a1be87f57..174e215add 100644 --- a/mod/node-api/backend/mocks/withdrawal_credentials.mock.go +++ b/mod/node-api/backend/mocks/withdrawal_credentials.mock.go @@ -20,6 +20,53 @@ func (_m *WithdrawalCredentials) EXPECT() *WithdrawalCredentials_Expecter { return &WithdrawalCredentials_Expecter{mock: &_m.Mock} } +// Bytes provides a mock function with given fields: +func (_m *WithdrawalCredentials) Bytes() []byte { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Bytes") + } + + var r0 []byte + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + return r0 +} + +// WithdrawalCredentials_Bytes_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Bytes' +type WithdrawalCredentials_Bytes_Call struct { + *mock.Call +} + +// Bytes is a helper method to define mock.On call +func (_e *WithdrawalCredentials_Expecter) Bytes() *WithdrawalCredentials_Bytes_Call { + return &WithdrawalCredentials_Bytes_Call{Call: _e.mock.On("Bytes")} +} + +func (_c *WithdrawalCredentials_Bytes_Call) Run(run func()) *WithdrawalCredentials_Bytes_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *WithdrawalCredentials_Bytes_Call) Return(_a0 []byte) *WithdrawalCredentials_Bytes_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *WithdrawalCredentials_Bytes_Call) RunAndReturn(run func() []byte) *WithdrawalCredentials_Bytes_Call { + _c.Call.Return(run) + return _c +} + // ToExecutionAddress provides a mock function with given fields: func (_m *WithdrawalCredentials) ToExecutionAddress() (common.ExecutionAddress, error) { ret := _m.Called() diff --git a/mod/node-api/backend/types.go b/mod/node-api/backend/types.go index 44e26ff4f7..db3aa3972b 100644 --- a/mod/node-api/backend/types.go +++ b/mod/node-api/backend/types.go @@ -25,6 +25,7 @@ import ( "github.com/berachain/beacon-kit/mod/primitives/pkg/common" "github.com/berachain/beacon-kit/mod/primitives/pkg/constraints" + "github.com/berachain/beacon-kit/mod/primitives/pkg/crypto" "github.com/berachain/beacon-kit/mod/primitives/pkg/math" "github.com/berachain/beacon-kit/mod/primitives/pkg/transition" "github.com/berachain/beacon-kit/mod/state-transition/pkg/core" @@ -124,12 +125,22 @@ type Validator[WithdrawalCredentialsT WithdrawalCredentials] interface { // GetWithdrawalCredentials returns the withdrawal credentials of the // validator. GetWithdrawalCredentials() WithdrawalCredentialsT - // IsFullyWithdrawable checks if the validator is fully withdrawable given a - // certain Gwei amount and epoch. - IsFullyWithdrawable(amount math.Gwei, epoch math.Epoch) bool - // IsPartiallyWithdrawable checks if the validator is partially withdrawable - // given two Gwei amounts. - IsPartiallyWithdrawable(amount1 math.Gwei, amount2 math.Gwei) bool + // GetPubkey returns the public key of the validator. + GetPubkey() crypto.BLSPubkey + // GetEffectiveBalance returns the effective balance of the validator. + GetEffectiveBalance() math.Gwei + // IsSlashed returns true if the validator is slashed. + IsSlashed() bool + // GetActivationEligibilityEpoch returns the epoch when the validator + // became eligible for activation. + GetActivationEligibilityEpoch() math.Epoch + // GetActivationEpoch returns the epoch when the validator was activated. + GetActivationEpoch() math.Epoch + // GetExitEpoch returns the epoch when the validator exited. + GetExitEpoch() math.Epoch + // GetWithdrawableEpoch returns the epoch when the validator + // can withdraw their balance. + GetWithdrawableEpoch() math.Epoch } // Withdrawal represents an interface for a withdrawal. @@ -147,4 +158,6 @@ type WithdrawalCredentials interface { // ToExecutionAddress converts the withdrawal credentials to an execution // address. ToExecutionAddress() (common.ExecutionAddress, error) + // Bytes returns the raw byte representation of the withdrawal credentials. + Bytes() []byte } diff --git a/mod/node-api/backend/validator.go b/mod/node-api/backend/validator.go index 9efcf61c56..1b211df421 100644 --- a/mod/node-api/backend/validator.go +++ b/mod/node-api/backend/validator.go @@ -27,10 +27,11 @@ import ( ) func (b Backend[ - _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, ValidatorT, _, _, _, + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, ValidatorT, _, _, + WithdrawalCredentialsT, ]) ValidatorByID( slot math.Slot, id string, -) (*beacontypes.ValidatorData[ValidatorT], error) { +) (*beacontypes.ValidatorData[ValidatorT, WithdrawalCredentialsT], error) { // TODO: to adhere to the spec, this shouldn't error if the error // is not found, but i can't think of a way to do that without coupling // db impl to the api impl. @@ -50,7 +51,7 @@ func (b Backend[ if err != nil { return nil, err } - return &beacontypes.ValidatorData[ValidatorT]{ + return &beacontypes.ValidatorData[ValidatorT, WithdrawalCredentialsT]{ ValidatorBalanceData: beacontypes.ValidatorBalanceData{ Index: index.Unwrap(), Balance: balance.Unwrap(), @@ -62,11 +63,14 @@ func (b Backend[ // TODO: filter by status func (b Backend[ - _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, ValidatorT, _, _, _, + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, ValidatorT, _, _, + WithdrawalCredentialsT, ]) ValidatorsByIDs( slot math.Slot, ids []string, _ []string, -) ([]*beacontypes.ValidatorData[ValidatorT], error) { - validatorsData := make([]*beacontypes.ValidatorData[ValidatorT], 0) +) ([]*beacontypes.ValidatorData[ValidatorT, WithdrawalCredentialsT], error) { + validatorsData := make( + []*beacontypes.ValidatorData[ValidatorT, WithdrawalCredentialsT], + 0) for _, id := range ids { // TODO: we can probably optimize this via a getAllValidators // query and then filtering but blocked by the fact that IDs diff --git a/mod/node-api/engines/echo/vaildator.go b/mod/node-api/engines/echo/vaildator.go index 0031ee4aad..3ddf58ae50 100644 --- a/mod/node-api/engines/echo/vaildator.go +++ b/mod/node-api/engines/echo/vaildator.go @@ -60,12 +60,13 @@ func (cv *CustomValidator) Validate(i interface{}) error { func ConstructValidator() *validator.Validate { validators := map[string](func(fl validator.FieldLevel) bool){ - "state_id": ValidateStateID, - "block_id": ValidateBlockID, - "timestamp_id": ValidateTimestampID, - "validator_id": ValidateValidatorID, - "epoch": ValidateUint64, - "slot": ValidateUint64, + "state_id": ValidateStateID, + "block_id": ValidateBlockID, + "timestamp_id": ValidateTimestampID, + "validator_id": ValidateValidatorID, + "epoch": ValidateUint64, + "slot": ValidateUint64, + "validator_status": ValidateValidatorStatus, } validate := validator.New() for tag, fn := range validators { diff --git a/mod/node-api/handlers/beacon/backend.go b/mod/node-api/handlers/beacon/backend.go index f16b956482..2024a1761a 100644 --- a/mod/node-api/handlers/beacon/backend.go +++ b/mod/node-api/handlers/beacon/backend.go @@ -27,12 +27,17 @@ import ( ) // Backend is the interface for backend of the beacon API. -type Backend[BlockHeaderT, ForkT, ValidatorT any] interface { +type Backend[ + BlockHeaderT any, + ForkT any, + ValidatorT types.Validator[WithdrawalCredentialsT], + WithdrawalCredentialsT types.WithdrawalCredentials, +] interface { GenesisBackend BlockBackend[BlockHeaderT] RandaoBackend StateBackend[ForkT] - ValidatorBackend[ValidatorT] + ValidatorBackend[ValidatorT, WithdrawalCredentialsT] HistoricalBackend[ForkT] // GetSlotByBlockRoot retrieves the slot by a given root from the store. GetSlotByBlockRoot(root common.Root) (math.Slot, error) @@ -64,15 +69,18 @@ type StateBackend[ForkT any] interface { StateForkAtSlot(slot math.Slot) (ForkT, error) } -type ValidatorBackend[ValidatorT any] interface { +type ValidatorBackend[ + ValidatorT types.Validator[WithdrawalCredentialsT], + WithdrawalCredentialsT types.WithdrawalCredentials, +] interface { ValidatorByID( slot math.Slot, id string, - ) (*types.ValidatorData[ValidatorT], error) + ) (*types.ValidatorData[ValidatorT, WithdrawalCredentialsT], error) ValidatorsByIDs( slot math.Slot, ids []string, statuses []string, - ) ([]*types.ValidatorData[ValidatorT], error) + ) ([]*types.ValidatorData[ValidatorT, WithdrawalCredentialsT], error) ValidatorBalancesByIDs( slot math.Slot, ids []string, diff --git a/mod/node-api/handlers/beacon/block.go b/mod/node-api/handlers/beacon/block.go index dee3ef7912..1d51decdb5 100644 --- a/mod/node-api/handlers/beacon/block.go +++ b/mod/node-api/handlers/beacon/block.go @@ -25,7 +25,9 @@ import ( "github.com/berachain/beacon-kit/mod/node-api/handlers/utils" ) -func (h *Handler[_, ContextT, _, _]) GetBlockRewards(c ContextT) (any, error) { +func (h *Handler[_, ContextT, _, _, _]) GetBlockRewards(c ContextT) ( + any, error, +) { req, err := utils.BindAndValidate[beacontypes.GetBlockRewardsRequest]( c, h.Logger(), ) diff --git a/mod/node-api/handlers/beacon/genesis.go b/mod/node-api/handlers/beacon/genesis.go index 1566eaaec9..9a51e587ed 100644 --- a/mod/node-api/handlers/beacon/genesis.go +++ b/mod/node-api/handlers/beacon/genesis.go @@ -26,7 +26,7 @@ import ( "github.com/berachain/beacon-kit/mod/node-api/handlers/utils" ) -func (h *Handler[_, ContextT, _, _]) GetGenesis(_ ContextT) (any, error) { +func (h *Handler[_, ContextT, _, _, _]) GetGenesis(_ ContextT) (any, error) { genesisRoot, err := h.backend.GenesisValidatorsRoot(utils.Genesis) if err != nil { return nil, err diff --git a/mod/node-api/handlers/beacon/handler.go b/mod/node-api/handlers/beacon/handler.go index 9677ec43e9..e66053284a 100644 --- a/mod/node-api/handlers/beacon/handler.go +++ b/mod/node-api/handlers/beacon/handler.go @@ -30,23 +30,42 @@ import ( type Handler[ BeaconBlockHeaderT types.BeaconBlockHeader, ContextT context.Context, - ForkT any, - ValidatorT any, + ForkT types.Fork, + ValidatorT types.Validator[WithdrawalCredentialsT], + WithdrawalCredentialsT types.WithdrawalCredentials, ] struct { *handlers.BaseHandler[ContextT] - backend Backend[BeaconBlockHeaderT, ForkT, ValidatorT] + backend Backend[ + BeaconBlockHeaderT, + ForkT, + ValidatorT, + WithdrawalCredentialsT, + ] } // NewHandler creates a new handler for the beacon API. func NewHandler[ BeaconBlockHeaderT types.BeaconBlockHeader, ContextT context.Context, - ForkT any, - ValidatorT any, + ForkT types.Fork, + ValidatorT types.Validator[WithdrawalCredentialsT], + WithdrawalCredentialsT types.WithdrawalCredentials, ]( - backend Backend[BeaconBlockHeaderT, ForkT, ValidatorT], -) *Handler[BeaconBlockHeaderT, ContextT, ForkT, ValidatorT] { - h := &Handler[BeaconBlockHeaderT, ContextT, ForkT, ValidatorT]{ + backend Backend[BeaconBlockHeaderT, ForkT, ValidatorT, WithdrawalCredentialsT], +) *Handler[ + BeaconBlockHeaderT, + ContextT, + ForkT, + ValidatorT, + WithdrawalCredentialsT, +] { + h := &Handler[ + BeaconBlockHeaderT, + ContextT, + ForkT, + ValidatorT, + WithdrawalCredentialsT, + ]{ BaseHandler: handlers.NewBaseHandler( handlers.NewRouteSet[ContextT](""), ), diff --git a/mod/node-api/handlers/beacon/header.go b/mod/node-api/handlers/beacon/header.go index 9cba5a83b5..487de56e7b 100644 --- a/mod/node-api/handlers/beacon/header.go +++ b/mod/node-api/handlers/beacon/header.go @@ -27,7 +27,7 @@ import ( ) func (h *Handler[ - BeaconBlockHeaderT, ContextT, _, _, + BeaconBlockHeaderT, ContextT, _, _, _, ]) GetBlockHeaders(c ContextT) (any, error) { req, err := utils.BindAndValidate[beacontypes.GetBlockHeadersRequest]( c, h.Logger(), @@ -58,7 +58,7 @@ func (h *Handler[ } func (h *Handler[ - BeaconBlockHeaderT, ContextT, _, _, + BeaconBlockHeaderT, ContextT, _, _, _, ]) GetBlockHeaderByID(c ContextT) (any, error) { req, err := utils.BindAndValidate[beacontypes.GetBlockHeaderRequest]( c, h.Logger(), diff --git a/mod/node-api/handlers/beacon/historical.go b/mod/node-api/handlers/beacon/historical.go index b97e26f55b..c0eb76caea 100644 --- a/mod/node-api/handlers/beacon/historical.go +++ b/mod/node-api/handlers/beacon/historical.go @@ -26,7 +26,7 @@ import ( "github.com/berachain/beacon-kit/mod/node-api/handlers/utils" ) -func (h *Handler[_, ContextT, _, _]) GetStateRoot(c ContextT) (any, error) { +func (h *Handler[_, ContextT, _, _, _]) GetStateRoot(c ContextT) (any, error) { req, err := utils.BindAndValidate[beacontypes.GetStateRootRequest]( c, h.Logger(), ) @@ -51,7 +51,7 @@ func (h *Handler[_, ContextT, _, _]) GetStateRoot(c ContextT) (any, error) { }, nil } -func (h *Handler[_, ContextT, _, _]) GetStateFork(c ContextT) (any, error) { +func (h *Handler[_, ContextT, _, _, _]) GetStateFork(c ContextT) (any, error) { req, err := utils.BindAndValidate[beacontypes.GetStateForkRequest]( c, h.Logger(), ) @@ -69,6 +69,6 @@ func (h *Handler[_, ContextT, _, _]) GetStateFork(c ContextT) (any, error) { return beacontypes.ValidatorResponse{ ExecutionOptimistic: false, // stubbed Finalized: false, // stubbed - Data: types.Wrap(fork), + Data: beacontypes.ForkData{Fork: fork}, }, nil } diff --git a/mod/node-api/handlers/beacon/randao.go b/mod/node-api/handlers/beacon/randao.go index 244453842d..81d20dd08d 100644 --- a/mod/node-api/handlers/beacon/randao.go +++ b/mod/node-api/handlers/beacon/randao.go @@ -26,7 +26,7 @@ import ( "github.com/berachain/beacon-kit/mod/primitives/pkg/math" ) -func (h *Handler[_, ContextT, _, _]) GetRandao(c ContextT) (any, error) { +func (h *Handler[_, ContextT, _, _, _]) GetRandao(c ContextT) (any, error) { req, err := utils.BindAndValidate[beacontypes.GetRandaoRequest]( c, h.Logger(), @@ -52,6 +52,8 @@ func (h *Handler[_, ContextT, _, _]) GetRandao(c ContextT) (any, error) { return beacontypes.ValidatorResponse{ ExecutionOptimistic: false, // stubbed Finalized: false, // stubbed - Data: randao, + Data: beacontypes.RandaoData{ + Randao: randao, + }, }, nil } diff --git a/mod/node-api/handlers/beacon/routes.go b/mod/node-api/handlers/beacon/routes.go index f83debb1e3..cbeb8b3840 100644 --- a/mod/node-api/handlers/beacon/routes.go +++ b/mod/node-api/handlers/beacon/routes.go @@ -28,7 +28,7 @@ import ( ) //nolint:funlen // routes are long -func (h *Handler[_, ContextT, _, _]) RegisterRoutes( +func (h *Handler[_, ContextT, _, _, _]) RegisterRoutes( logger log.Logger, ) { h.SetLogger(logger) @@ -153,6 +153,11 @@ func (h *Handler[_, ContextT, _, _]) RegisterRoutes( Path: "/eth/v1/beacon/deposit_snapshot", Handler: h.NotImplemented, }, + { + Method: http.MethodGet, + Path: "/eth/v1/beacon/rewards/blocks/:block_id", + Handler: h.GetBlockRewards, + }, { Method: http.MethodPost, Path: "/eth/v1/beacon/rewards/attestation/:epoch", diff --git a/mod/node-api/handlers/beacon/types/request.go b/mod/node-api/handlers/beacon/types/request.go index 56010908ce..fe64ef0a45 100644 --- a/mod/node-api/handlers/beacon/types/request.go +++ b/mod/node-api/handlers/beacon/types/request.go @@ -60,7 +60,9 @@ type GetValidatorBalancesRequest struct { type PostValidatorBalancesRequest struct { types.StateIDRequest - IDs []string `validate:"dive,validator_id"` + // IDs are meant to be sent as array in the body rather than a + // field in the JSON object in the request body. + IDs []string `json:"-" validate:"dive,validator_id"` } type GetStateCommitteesRequest struct { diff --git a/mod/node-api/handlers/beacon/types/response.go b/mod/node-api/handlers/beacon/types/response.go index 6bf3641d07..019edd44f1 100644 --- a/mod/node-api/handlers/beacon/types/response.go +++ b/mod/node-api/handlers/beacon/types/response.go @@ -21,6 +21,10 @@ package types import ( + "encoding/hex" + "encoding/json" + "strconv" + "github.com/berachain/beacon-kit/mod/primitives/pkg/bytes" "github.com/berachain/beacon-kit/mod/primitives/pkg/common" ) @@ -31,11 +35,6 @@ type ValidatorResponse struct { Data any `json:"data"` } -type BlockResponse struct { - Version string `json:"version"` - ValidatorResponse -} - type BlockHeaderResponse[BlockHeaderT any] struct { Root common.Root `json:"root"` Canonical bool `json:"canonical"` @@ -57,22 +56,81 @@ type RootData struct { Root common.Root `json:"root"` } -type ValidatorData[ValidatorT any] struct { +type ValidatorBalanceData struct { + Index uint64 `json:"index"` + Balance uint64 `json:"balance"` +} + +func (vbd ValidatorBalanceData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Index string `json:"index"` + Balance string `json:"balance"` + }{ + Index: strconv.FormatUint(vbd.Index, 10), + Balance: strconv.FormatUint(vbd.Balance, 10), + }) +} + +type ValidatorData[ + ValidatorT Validator[WithdrawalCredentialsT], + WithdrawalCredentialsT WithdrawalCredentials, +] struct { ValidatorBalanceData Status string `json:"status"` Validator ValidatorT `json:"validator"` } -type ValidatorBalanceData struct { - Index uint64 `json:"index,string"` - Balance uint64 `json:"balance,string"` +type validatorJSON struct { + PublicKey string `json:"pubkey"` + WithdrawalCredentials string `json:"withdrawal_credentials"` + EffectiveBalance string `json:"effective_balance"` + Slashed bool `json:"slashed"` + ActivationEligibilityEpoch string `json:"activation_eligibility_epoch"` + ActivationEpoch string `json:"activation_epoch"` + ExitEpoch string `json:"exit_epoch"` + WithdrawableEpoch string `json:"withdrawable_epoch"` +} + +type responseJSON struct { + Index string `json:"index"` + Balance string `json:"balance"` + Status string `json:"status"` + Validator validatorJSON `json:"validator"` } -//nolint:staticcheck // todo: figure this out. -type CommitteeData struct { - Index uint64 `json:"index,string"` - Slot uint64 `json:"slot,string"` - Validators []uint64 `json:"validators,string"` +func (vd ValidatorData[ + ValidatorT, WithdrawalCredentialsT, +]) MarshalJSON() ([]byte, error) { + withdrawalCredentials := vd.Validator.GetWithdrawalCredentials() + withdrawalCredentialsBytes := withdrawalCredentials.Bytes() + + return json.Marshal(responseJSON{ + Index: strconv.FormatUint(vd.Index, 10), + Balance: strconv.FormatUint(vd.Balance, 10), + Status: vd.Status, + Validator: validatorJSON{ + PublicKey: vd.Validator.GetPubkey().String(), + WithdrawalCredentials: "0x" + hex.EncodeToString( + withdrawalCredentialsBytes, + ), + EffectiveBalance: strconv.FormatUint( + vd.Validator.GetEffectiveBalance().Unwrap(), 10, + ), + Slashed: vd.Validator.IsSlashed(), + ActivationEligibilityEpoch: strconv.FormatUint( + vd.Validator.GetActivationEligibilityEpoch().Unwrap(), 10, + ), + ActivationEpoch: strconv.FormatUint( + vd.Validator.GetActivationEpoch().Unwrap(), 10, + ), + ExitEpoch: strconv.FormatUint( + vd.Validator.GetExitEpoch().Unwrap(), 10, + ), + WithdrawableEpoch: strconv.FormatUint( + vd.Validator.GetWithdrawableEpoch().Unwrap(), 10, + ), + }, + }) } type BlockRewardsData struct { @@ -83,3 +141,25 @@ type BlockRewardsData struct { ProposerSlashings uint64 `json:"proposer_slashings,string"` AttesterSlashings uint64 `json:"attester_slashings,string"` } + +type RandaoData struct { + Randao common.Bytes32 `json:"randao"` +} + +type ForkData struct { + Fork +} + +type forkJSON struct { + PreviousVersion string `json:"previous_version"` + CurrentVersion string `json:"current_version"` + Epoch string `json:"epoch"` +} + +func (fr ForkData) MarshalJSON() ([]byte, error) { + return json.Marshal(forkJSON{ + PreviousVersion: fr.GetPreviousVersion().String(), + CurrentVersion: fr.GetCurrentVersion().String(), + Epoch: strconv.FormatUint(fr.GetEpoch().Unwrap(), 10), + }) +} diff --git a/mod/node-api/handlers/beacon/types/types.go b/mod/node-api/handlers/beacon/types/types.go index 02c45a3d32..d445b022f1 100644 --- a/mod/node-api/handlers/beacon/types/types.go +++ b/mod/node-api/handlers/beacon/types/types.go @@ -20,9 +20,39 @@ package types -import "github.com/berachain/beacon-kit/mod/primitives/pkg/common" +import ( + "github.com/berachain/beacon-kit/mod/primitives/pkg/common" + "github.com/berachain/beacon-kit/mod/primitives/pkg/crypto" + "github.com/berachain/beacon-kit/mod/primitives/pkg/math" +) // BeaconBlockHeader is the interface for the beacon block header. type BeaconBlockHeader interface { GetBodyRoot() common.Root } + +// Fork is the interface for the fork type. +type Fork interface { + GetPreviousVersion() common.Version + GetCurrentVersion() common.Version + GetEpoch() math.Epoch +} + +// Validator represents an interface for a validator with generic withdrawal +// credentials. WithdrawalCredentialsT is a type parameter that must implement +// the WithdrawalCredentials interface. +type Validator[WithdrawalCredentialsT WithdrawalCredentials] interface { + GetPubkey() crypto.BLSPubkey + GetWithdrawalCredentials() WithdrawalCredentialsT + GetEffectiveBalance() math.Gwei + IsSlashed() bool + GetActivationEligibilityEpoch() math.Epoch + GetActivationEpoch() math.Epoch + GetExitEpoch() math.Epoch + GetWithdrawableEpoch() math.Epoch +} + +// WithdrawalCredentials represents an interface for withdrawal credentials. +type WithdrawalCredentials interface { + Bytes() []byte +} diff --git a/mod/node-api/handlers/beacon/validators.go b/mod/node-api/handlers/beacon/validators.go index f6379eeb8c..01cfb13d91 100644 --- a/mod/node-api/handlers/beacon/validators.go +++ b/mod/node-api/handlers/beacon/validators.go @@ -21,12 +21,13 @@ package beacon import ( + "github.com/berachain/beacon-kit/mod/errors" beacontypes "github.com/berachain/beacon-kit/mod/node-api/handlers/beacon/types" "github.com/berachain/beacon-kit/mod/node-api/handlers/types" "github.com/berachain/beacon-kit/mod/node-api/handlers/utils" ) -func (h *Handler[_, ContextT, _, _]) GetStateValidators( +func (h *Handler[_, ContextT, _, _, _]) GetStateValidators( c ContextT, ) (any, error) { req, err := utils.BindAndValidate[beacontypes.GetStateValidatorsRequest]( @@ -61,7 +62,7 @@ func (h *Handler[_, ContextT, _, _]) GetStateValidators( }, nil } -func (h *Handler[_, ContextT, _, _]) PostStateValidators( +func (h *Handler[_, ContextT, _, _, _]) PostStateValidators( c ContextT, ) (any, error) { req, err := utils.BindAndValidate[beacontypes.PostStateValidatorsRequest]( @@ -93,7 +94,7 @@ func (h *Handler[_, ContextT, _, _]) PostStateValidators( }, nil } -func (h *Handler[_, ContextT, _, _]) GetStateValidator( +func (h *Handler[_, ContextT, _, _, _]) GetStateValidator( c ContextT, ) (any, error) { req, err := utils.BindAndValidate[beacontypes.GetStateValidatorRequest]( @@ -116,7 +117,7 @@ func (h *Handler[_, ContextT, _, _]) GetStateValidator( return validator, nil } -func (h *Handler[_, ContextT, _, _]) GetStateValidatorBalances( +func (h *Handler[_, ContextT, _, _, _]) GetStateValidatorBalances( c ContextT, ) (any, error) { req, err := utils.BindAndValidate[beacontypes.GetValidatorBalancesRequest]( @@ -143,25 +144,35 @@ func (h *Handler[_, ContextT, _, _]) GetStateValidatorBalances( }, nil } -func (h *Handler[_, ContextT, _, _]) PostStateValidatorBalances( +func (h *Handler[_, ContextT, _, _, _]) PostStateValidatorBalances( c ContextT, ) (any, error) { - req, err := utils.BindAndValidate[beacontypes.PostValidatorBalancesRequest]( - c, h.Logger(), - ) - if err != nil { - return nil, err + var ids []string + if err := c.Bind(&ids); err != nil { + return nil, types.ErrInvalidRequest } + + // TODO: Find a way to pass the state_id from request. + // Currently only head is supported. + req := beacontypes.PostValidatorBalancesRequest{ + StateIDRequest: types.StateIDRequest{StateID: "head"}, + IDs: ids, + } + + if err := c.Validate(&req); err != nil { + return nil, types.ErrInvalidRequest + } + slot, err := utils.SlotFromStateID(req.StateID, h.backend) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "err getting slot for req %v ", req) } - balances, err := h.backend.ValidatorBalancesByIDs( - slot, - req.IDs, - ) + + h.Logger().Info("PostStateValidatorBalances", "slot", slot, "req", req) + + balances, err := h.backend.ValidatorBalancesByIDs(slot, req.IDs) if err != nil { - return nil, err + return nil, errors.Wrap(err, "err in backend") } return beacontypes.ValidatorResponse{ ExecutionOptimistic: false, // stubbed diff --git a/mod/node-core/pkg/components/api_handlers.go b/mod/node-core/pkg/components/api_handlers.go index 83448ae897..b0e7a83ee9 100644 --- a/mod/node-core/pkg/components/api_handlers.go +++ b/mod/node-core/pkg/components/api_handlers.go @@ -50,7 +50,7 @@ type NodeAPIHandlersInput[ ] struct { depinject.In BeaconAPIHandler *beaconapi.Handler[ - BeaconBlockHeaderT, NodeAPIContextT, *Fork, *Validator, + BeaconBlockHeaderT, NodeAPIContextT, *Fork, *Validator, WithdrawalCredentials, ] BuilderAPIHandler *builderapi.Handler[NodeAPIContextT] ConfigAPIHandler *configapi.Handler[NodeAPIContextT] @@ -107,14 +107,16 @@ func ProvideNodeAPIBeaconHandler[ *Fork, NodeT, *Validator, + WithdrawalCredentials, ]) *beaconapi.Handler[ - BeaconBlockHeaderT, NodeAPIContextT, *Fork, *Validator, + BeaconBlockHeaderT, NodeAPIContextT, *Fork, *Validator, WithdrawalCredentials, ] { return beaconapi.NewHandler[ BeaconBlockHeaderT, NodeAPIContextT, *Fork, *Validator, + WithdrawalCredentials, ](b) } @@ -170,6 +172,7 @@ func ProvideNodeAPIProofHandler[ *Fork, NodeT, *Validator, + WithdrawalCredentials, ]) *proofapi.Handler[ BeaconBlockHeaderT, BeaconStateT, BeaconStateMarshallableT, NodeAPIContextT, ExecutionPayloadHeaderT, *Validator, diff --git a/mod/node-core/pkg/components/interfaces.go b/mod/node-core/pkg/components/interfaces.go index 370ae20f27..73c30acbe4 100644 --- a/mod/node-core/pkg/components/interfaces.go +++ b/mod/node-core/pkg/components/interfaces.go @@ -1085,7 +1085,8 @@ type ( BeaconStateT any, ForkT any, NodeT any, - ValidatorT any, + ValidatorT types.Validator[WithdrawalCredentialsT], + WithdrawalCredentialsT types.WithdrawalCredentials, ] interface { AttachQueryBackend(node NodeT) ChainSpec() common.ChainSpec @@ -1094,7 +1095,7 @@ type ( GetParentSlotByTimestamp(timestamp math.U64) (math.Slot, error) NodeAPIBeaconBackend[ - BeaconStateT, BeaconBlockHeaderT, ForkT, ValidatorT, + BeaconStateT, BeaconBlockHeaderT, ForkT, ValidatorT, WithdrawalCredentialsT, ] NodeAPIProofBackend[ BeaconBlockHeaderT, BeaconStateT, ForkT, ValidatorT, @@ -1103,13 +1104,15 @@ type ( // NodeAPIBackend is the interface for backend of the beacon API. NodeAPIBeaconBackend[ - BeaconStateT, BeaconBlockHeaderT, ForkT, ValidatorT any, + BeaconStateT, BeaconBlockHeaderT, ForkT any, + ValidatorT types.Validator[WithdrawalCredentialsT], + WithdrawalCredentialsT types.WithdrawalCredentials, ] interface { GenesisBackend BlockBackend[BeaconBlockHeaderT] RandaoBackend StateBackend[BeaconStateT, ForkT] - ValidatorBackend[ValidatorT] + ValidatorBackend[ValidatorT, WithdrawalCredentialsT] HistoricalBackend[ForkT] // GetSlotByBlockRoot retrieves the slot by a given root from the store. GetSlotByBlockRoot(root common.Root) (math.Slot, error) @@ -1151,15 +1154,18 @@ type ( StateFromSlotForProof(slot math.Slot) (BeaconStateT, math.Slot, error) } - ValidatorBackend[ValidatorT any] interface { + ValidatorBackend[ + ValidatorT types.Validator[WithdrawalCredentialsT], + WithdrawalCredentialsT types.WithdrawalCredentials, + ] interface { ValidatorByID( slot math.Slot, id string, - ) (*types.ValidatorData[ValidatorT], error) + ) (*types.ValidatorData[ValidatorT, WithdrawalCredentialsT], error) ValidatorsByIDs( slot math.Slot, ids []string, statuses []string, - ) ([]*types.ValidatorData[ValidatorT], error) + ) ([]*types.ValidatorData[ValidatorT, WithdrawalCredentialsT], error) ValidatorBalancesByIDs( slot math.Slot, ids []string, diff --git a/testing/e2e/e2e_api_test.go b/testing/e2e/e2e_api_test.go index b9e14ab04a..6811d6ed8f 100644 --- a/testing/e2e/e2e_api_test.go +++ b/testing/e2e/e2e_api_test.go @@ -22,13 +22,14 @@ package e2e_test import ( beaconapi "github.com/attestantio/go-eth2-client/api" + "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/berachain/beacon-kit/mod/node-api/handlers/utils" "github.com/berachain/beacon-kit/testing/e2e/config" + "github.com/berachain/beacon-kit/testing/e2e/suite/types" ) -// TestBeaconAPISuite tests that the api test suite is setup correctly with a -// working beacon node-api client. -func (s *BeaconKitE2ESuite) TestBeaconAPIStartup() { +// initBeaconTest initializes the any tests for the beacon node api. +func (s *BeaconKitE2ESuite) initBeaconTest() *types.ConsensusClient { // Wait for execution block 5. err := s.WaitForFinalizedBlockNumber(5) s.Require().NoError(err) @@ -37,6 +38,13 @@ func (s *BeaconKitE2ESuite) TestBeaconAPIStartup() { client := s.ConsensusClients()[config.DefaultClient] s.Require().NotNil(client) + return client +} + +// TestBeaconStateRoot tests the beacon node api for beacon state root. +func (s *BeaconKitE2ESuite) TestBeaconStateRoot() { + client := s.initBeaconTest() + // Ensure the state root is not nil. stateRootResp, err := client.BeaconStateRoot( s.Ctx(), @@ -48,3 +56,144 @@ func (s *BeaconKitE2ESuite) TestBeaconAPIStartup() { s.Require().NotEmpty(stateRootResp) s.Require().False(stateRootResp.Data.IsZero()) } + +// TestBeaconFork tests the beacon node api for beacon fork. +func (s *BeaconKitE2ESuite) TestBeaconFork() { + client := s.initBeaconTest() + + stateForkResp, err := client.Fork(s.Ctx(), &beaconapi.ForkOpts{ + State: utils.StateIDHead, + }) + s.Require().NoError(err) + s.Require().NotEmpty(stateForkResp) + + fork := stateForkResp.Data + s.Require().NotEmpty(fork.PreviousVersion) + s.Require().NotEmpty(fork.CurrentVersion) + expectedVersion := phase0.Version{0x04, 0x00, 0x00, 0x00} + s.Require().Equal( + expectedVersion, + fork.PreviousVersion, + "PreviousVersion does not match expected value", + ) + s.Require().Equal( + expectedVersion, + fork.CurrentVersion, + "CurrentVersion does not match expected value", + ) + s.Require().Equal( + phase0.Epoch(0), + fork.Epoch, + "Epoch does not match expected value", + ) +} + +// TestBeaconValidators tests the beacon node api for beacon validators. +// +//nolint:lll +func (s *BeaconKitE2ESuite) TestBeaconValidators() { + client := s.initBeaconTest() + + indices := []phase0.ValidatorIndex{0} + // Ensure the validators are not nil. + validatorsResp, err := client.Validators( + s.Ctx(), + &beaconapi.ValidatorsOpts{ + State: utils.StateIDHead, + Indices: indices, + }, + ) + s.Require().NoError(err) + s.Require().NotNil(validatorsResp) + + validatorData := validatorsResp.Data + s.Require().NotNil(validatorData, "Validator data should not be nil") + s.Require(). + Len(validatorData, len(indices), "Number of validator responses should match number of requested indices") + + for _, validator := range validatorData { + s.Require().NotNil(validator, "Validator should not be nil") + + s.Require(). + Contains(indices, validator.Index, "Validator index should be one of the requested indices") + + s.Require(). + NotEmpty(validator.Validator.PublicKey, "Validator public key should not be empty") + s.Require(). + Len(validator.Validator.PublicKey, 48, "Validator public key should be 48 bytes long") + + s.Require(). + NotEmpty(validator.Validator.WithdrawalCredentials, "Withdrawal credentials should not be empty") + s.Require(). + Len(validator.Validator.WithdrawalCredentials, 32, "Withdrawal credentials should be 32 bytes long") + + s.Require(). + True(validator.Validator.EffectiveBalance > 0, "Effective balance should be positive") + s.Require(). + True(validator.Validator.EffectiveBalance <= 32e9, "Effective balance should not exceed 32 ETH") + + s.Require(). + False(validator.Validator.Slashed, "Slashed status should not be true") + + s.Require(). + True(validator.Validator.ActivationEpoch >= validator.Validator.ActivationEligibilityEpoch, + "Activation epoch should be greater than or equal to activation eligibility epoch") + + s.Require(). + True(validator.Validator.WithdrawableEpoch >= validator.Validator.ExitEpoch, + "Withdrawable epoch should be greater than or equal to exit epoch") + + s.Require(). + NotEmpty(validator.Status, "Validator status should not be empty") + + s.Require(). + True(validator.Balance > 0, "Validator balance should be positive") + s.Require(). + True(validator.Balance <= 32e9, "Validator balance should not exceed 32 ETH") + } +} + +func (s *BeaconKitE2ESuite) TestBeaconValidatorBalances() { + client := s.initBeaconTest() + + indices := []phase0.ValidatorIndex{0} + // Ensure the validator balances are not nil. + validatorBalancesResp, err := client.ValidatorBalances( + s.Ctx(), + &beaconapi.ValidatorBalancesOpts{ + State: utils.StateIDHead, + Indices: indices, + }, + ) + s.Require().NoError(err) + s.Require().NotNil(validatorBalancesResp) + balanceMap := validatorBalancesResp.Data + for _, index := range indices { + balance, exists := balanceMap[index] + s.Require(). + True(exists, "Balance should exist for validator index %d", index) + s.Require().True(balance > 0, "Validator balance should be positive") + } +} + +func (s *BeaconKitE2ESuite) TestBeaconRandao() { + client := s.initBeaconTest() + stateRandaoResp, err := client.BeaconStateRandao(s.Ctx(), + &beaconapi.BeaconStateRandaoOpts{ + State: utils.StateIDHead, + }) + s.Require().NoError(err) + s.Require().NotNil(stateRandaoResp) + s.Require().NotEmpty(stateRandaoResp.Data) + randao := stateRandaoResp.Data + s.Require().Len( + randao, + 32, + "Randao should be 32 bytes long", + ) + s.Require().NotEqual( + make([]byte, 32), + randao, + "Randao should not be all zeros", + ) +} diff --git a/testing/e2e/suite/types/consensus_client.go b/testing/e2e/suite/types/consensus_client.go index 8f7edfc351..84e4a2571a 100644 --- a/testing/e2e/suite/types/consensus_client.go +++ b/testing/e2e/suite/types/consensus_client.go @@ -24,10 +24,14 @@ import ( "context" "fmt" + beaconapi "github.com/attestantio/go-eth2-client/api" + apiv1 "github.com/attestantio/go-eth2-client/api/v1" beaconhttp "github.com/attestantio/go-eth2-client/http" + "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/berachain/beacon-kit/mod/errors" rpcclient "github.com/cometbft/cometbft/rpc/client" httpclient "github.com/cometbft/cometbft/rpc/client/http" + ctypes "github.com/cometbft/cometbft/rpc/core/types" "github.com/kurtosis-tech/kurtosis/api/golang/core/lib/enclaves" "github.com/rs/zerolog" ) @@ -37,10 +41,10 @@ type ConsensusClient struct { *WrappedServiceContext // Comet JSON-RPC client - rpcclient.Client + cometClient rpcclient.Client // Beacon node-api client - BeaconKitNodeClient + beaconClient BeaconKitNodeClient // Cancel function for the context cancelFunc context.CancelFunc @@ -71,7 +75,7 @@ func (cc *ConsensusClient) Connect(ctx context.Context) error { if err != nil { return err } - cc.Client = client + cc.cometClient = client // Then try to get the public port for the node API. nodePort, ok := cc.WrappedServiceContext.GetPublicPorts()["node-api"] @@ -79,7 +83,7 @@ func (cc *ConsensusClient) Connect(ctx context.Context) error { panic("Couldn't find the public port for the node API") } cancelCtx, cancel := context.WithCancel(ctx) - cc.BeaconKitNodeClient, err = NewBeaconKitNodeClient( + cc.beaconClient, err = NewBeaconKitNodeClient( cancelCtx, beaconhttp.WithAddress( fmt.Sprintf("http://0.0.0.0:%d", nodePort.GetNumber()), @@ -118,7 +122,7 @@ func (cc ConsensusClient) Stop( // GetPubKey returns the public key of the validator running on this node. func (cc ConsensusClient) GetPubKey(ctx context.Context) ([]byte, error) { - res, err := cc.Client.Status(ctx) + res, err := cc.cometClient.Status(ctx) if err != nil { return nil, err } else if res.ValidatorInfo.PubKey == nil { @@ -132,7 +136,7 @@ func (cc ConsensusClient) GetPubKey(ctx context.Context) ([]byte, error) { func (cc ConsensusClient) GetConsensusPower( ctx context.Context, ) (uint64, error) { - res, err := cc.Client.Status(ctx) + res, err := cc.cometClient.Status(ctx) if err != nil { return 0, err } @@ -143,7 +147,7 @@ func (cc ConsensusClient) GetConsensusPower( // IsActive returns true if the node is an active validator. func (cc ConsensusClient) IsActive(ctx context.Context) (bool, error) { - res, err := cc.Client.Status(ctx) + res, err := cc.cometClient.Status(ctx) if err != nil { return false, err } @@ -151,5 +155,67 @@ func (cc ConsensusClient) IsActive(ctx context.Context) (bool, error) { return res.ValidatorInfo.VotingPower > 0, nil } +// ABCIInfo returns the ABCI info of the node. +func (cc ConsensusClient) ABCIInfo( + ctx context.Context, +) (*ctypes.ResultABCIInfo, error) { + return cc.cometClient.ABCIInfo(ctx) +} + +// BeaconStateRoot returns the beacon state root of the node. +func (cc ConsensusClient) BeaconStateRoot( + ctx context.Context, + opts *beaconapi.BeaconStateRootOpts, +) (*beaconapi.Response[*phase0.Root], error) { + if cc.beaconClient == nil { + return nil, errors.New("beacon client is not initialized") + } + return cc.beaconClient.BeaconStateRoot(ctx, opts) +} + +// Fork returns the fork of the node. +func (cc ConsensusClient) Fork( + ctx context.Context, + opts *beaconapi.ForkOpts, +) (*beaconapi.Response[*phase0.Fork], error) { + if cc.beaconClient == nil { + return nil, errors.New("beacon client is not initialized") + } + return cc.beaconClient.Fork(ctx, opts) +} + +// ValidatorBalances returns the validator balances of the node. +func (cc ConsensusClient) ValidatorBalances( + ctx context.Context, + opts *beaconapi.ValidatorBalancesOpts, +) (*beaconapi.Response[map[phase0.ValidatorIndex]phase0.Gwei], error) { + if cc.beaconClient == nil { + return nil, errors.New("beacon client is not initialized") + } + return cc.beaconClient.ValidatorBalances(ctx, opts) +} + +// Validators returns the validator. +func (cc ConsensusClient) Validators( + ctx context.Context, + opts *beaconapi.ValidatorsOpts, +) (*beaconapi.Response[map[phase0.ValidatorIndex]*apiv1.Validator], error) { + if cc.beaconClient == nil { + return nil, errors.New("beacon client is not initialized") + } + return cc.beaconClient.Validators(ctx, opts) +} + +// BeaconStateRandao returns the beacon state randao of the node. +func (cc ConsensusClient) BeaconStateRandao( + ctx context.Context, + opts *beaconapi.BeaconStateRandaoOpts, +) (*beaconapi.Response[*phase0.Root], error) { + if cc.beaconClient == nil { + return nil, errors.New("beacon client is not initialized") + } + return cc.beaconClient.BeaconStateRandao(ctx, opts) +} + // TODO: Add helpers for the beacon node-api client (converting from // go-eth2-client types to beacon-kit consensus types).