mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-06 20:13:59 -05:00
Implement process pending payments
This commit is contained in:
16
beacon-chain/core/gloas/BUILD.bazel
Normal file
16
beacon-chain/core/gloas/BUILD.bazel
Normal file
@@ -0,0 +1,16 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["pending_payment.go"],
|
||||
importpath = "github.com/OffchainLabs/prysm/v7/beacon-chain/core/gloas",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
],
|
||||
)
|
||||
90
beacon-chain/core/gloas/pending_payment.go
Normal file
90
beacon-chain/core/gloas/pending_payment.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package gloas
|
||||
|
||||
import (
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/helpers"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ProcessBuilderPendingPayments processes the builder pending payments from the previous epoch.
|
||||
// Spec v1.6.1 (pseudocode):
|
||||
// process_builder_pending_payments(state: BeaconState) -> None:
|
||||
//
|
||||
// quorum = get_builder_payment_quorum_threshold(state)
|
||||
// for payment in state.builder_pending_payments[:SLOTS_PER_EPOCH]:
|
||||
// if payment.weight >= quorum:
|
||||
// exit_epoch = compute_exit_epoch_and_update_churn(state, payment.withdrawal.amount)
|
||||
// payment.withdrawal.withdrawable_epoch =
|
||||
// Epoch(exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY)
|
||||
// state.builder_pending_withdrawals.append(payment.withdrawal)
|
||||
// state.builder_pending_payments =
|
||||
// state.builder_pending_payments[SLOTS_PER_EPOCH:]
|
||||
// + [BuilderPendingPayment() for _ in range(SLOTS_PER_EPOCH)]
|
||||
func ProcessBuilderPendingPayments(state state.BeaconState) error {
|
||||
quorum, err := builderQuorumThreshold(state)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not compute builder payment quorum threshold")
|
||||
}
|
||||
|
||||
payments, err := state.BuilderPendingPayments()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get builder pending payments")
|
||||
}
|
||||
|
||||
cfg := params.BeaconConfig()
|
||||
slotsPerEpoch := uint64(cfg.SlotsPerEpoch)
|
||||
minWithdrawDelay := uint64(cfg.MinValidatorWithdrawabilityDelay)
|
||||
for i := uint64(0); i < slotsPerEpoch; i++ {
|
||||
payment := payments[i]
|
||||
if quorum > payment.Weight {
|
||||
continue
|
||||
}
|
||||
|
||||
amount := payment.Withdrawal.Amount
|
||||
exitEpoch, err := state.ExitEpochAndUpdateChurn(amount)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not compute exit epoch for payment %d", i)
|
||||
}
|
||||
|
||||
withdrawableEpoch, err := exitEpoch.SafeAdd(minWithdrawDelay)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not compute withdrawable epoch for payment %d", i)
|
||||
}
|
||||
payment.Withdrawal.WithdrawableEpoch = withdrawableEpoch
|
||||
|
||||
if err := state.AppendBuilderPendingWithdrawal(payment.Withdrawal); err != nil {
|
||||
return errors.Wrapf(err, "could not append builder pending withdrawal %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
if err := state.RotateBuilderPendingPayments(); err != nil {
|
||||
return errors.Wrap(err, "could not rotate builder pending payments")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// builderQuorumThreshold calculates the quorum threshold for builder payments.
|
||||
// Spec v1.6.1 (pseudocode):
|
||||
// def get_builder_payment_quorum_threshold(state: BeaconState) -> uint64:
|
||||
//
|
||||
// per_slot_balance = get_total_active_balance(state) // SLOTS_PER_EPOCH
|
||||
// quorum = per_slot_balance * BUILDER_PAYMENT_THRESHOLD_NUMERATOR
|
||||
// return uint64(quorum // BUILDER_PAYMENT_THRESHOLD_DENOMINATOR)
|
||||
func builderQuorumThreshold(state state.BeaconState) (primitives.Gwei, error) {
|
||||
activeBalance, err := helpers.TotalActiveBalance(state)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not get total active balance")
|
||||
}
|
||||
|
||||
cfg := params.BeaconConfig()
|
||||
slotsPerEpoch := uint64(cfg.SlotsPerEpoch)
|
||||
numerator := cfg.BuilderPaymentThresholdNumerator
|
||||
denominator := cfg.BuilderPaymentThresholdDenominator
|
||||
|
||||
activeBalancePerSlot := activeBalance / slotsPerEpoch
|
||||
quorum := (activeBalancePerSlot * numerator) / denominator
|
||||
return primitives.Gwei(quorum), nil
|
||||
}
|
||||
110
beacon-chain/core/gloas/pending_payment_test.go
Normal file
110
beacon-chain/core/gloas/pending_payment_test.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package gloas
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/helpers"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
state_native "github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/require"
|
||||
)
|
||||
|
||||
func TestBuilderQuorumThreshold(t *testing.T) {
|
||||
helpers.ClearCache()
|
||||
cfg := params.BeaconConfig()
|
||||
|
||||
validators := []*ethpb.Validator{
|
||||
{EffectiveBalance: cfg.MaxEffectiveBalance, ActivationEpoch: 0, ExitEpoch: 1},
|
||||
{EffectiveBalance: cfg.MaxEffectiveBalance, ActivationEpoch: 0, ExitEpoch: 1},
|
||||
}
|
||||
st, err := state_native.InitializeFromProtoUnsafeGloas(ðpb.BeaconStateGloas{Validators: validators})
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := builderQuorumThreshold(st)
|
||||
require.NoError(t, err)
|
||||
|
||||
total := uint64(len(validators)) * cfg.MaxEffectiveBalance
|
||||
perSlot := total / uint64(cfg.SlotsPerEpoch)
|
||||
want := (perSlot * cfg.BuilderPaymentThresholdNumerator) / cfg.BuilderPaymentThresholdDenominator
|
||||
require.Equal(t, primitives.Gwei(want), got)
|
||||
}
|
||||
|
||||
func TestProcessBuilderPendingPayments(t *testing.T) {
|
||||
helpers.ClearCache()
|
||||
cfg := params.BeaconConfig()
|
||||
|
||||
validators := []*ethpb.Validator{
|
||||
{EffectiveBalance: cfg.MaxEffectiveBalance, ActivationEpoch: 0, ExitEpoch: 1},
|
||||
{EffectiveBalance: cfg.MaxEffectiveBalance, ActivationEpoch: 0, ExitEpoch: 1},
|
||||
}
|
||||
pbSt, err := state_native.InitializeFromProtoPhase0(ðpb.BeaconState{Validators: validators})
|
||||
require.NoError(t, err)
|
||||
|
||||
total := uint64(len(validators)) * cfg.MaxEffectiveBalance
|
||||
perSlot := total / uint64(cfg.SlotsPerEpoch)
|
||||
quorum := (perSlot * cfg.BuilderPaymentThresholdNumerator) / cfg.BuilderPaymentThresholdDenominator
|
||||
|
||||
slotsPerEpoch := int(cfg.SlotsPerEpoch)
|
||||
payments := make([]*ethpb.BuilderPendingPayment, 2*slotsPerEpoch)
|
||||
for i := range payments {
|
||||
payments[i] = ðpb.BuilderPendingPayment{
|
||||
Withdrawal: ðpb.BuilderPendingWithdrawal{
|
||||
FeeRecipient: make([]byte, 20),
|
||||
},
|
||||
}
|
||||
}
|
||||
payments[0].Weight = primitives.Gwei(quorum + 1)
|
||||
payments[0].Withdrawal.Amount = 1
|
||||
|
||||
st := &testProcessState{
|
||||
BeaconState: pbSt,
|
||||
payments: payments,
|
||||
exitEpoch: 7,
|
||||
}
|
||||
rotatedHead := st.payments[slotsPerEpoch]
|
||||
|
||||
require.NoError(t, ProcessBuilderPendingPayments(st))
|
||||
require.Equal(t, 1, len(st.withdrawals))
|
||||
|
||||
wantEpoch, err := st.exitEpoch.SafeAdd(uint64(cfg.MinValidatorWithdrawabilityDelay))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, wantEpoch, st.withdrawals[0].WithdrawableEpoch)
|
||||
require.Equal(t, rotatedHead, st.payments[0])
|
||||
}
|
||||
|
||||
type testProcessState struct {
|
||||
state.BeaconState
|
||||
payments []*ethpb.BuilderPendingPayment
|
||||
withdrawals []*ethpb.BuilderPendingWithdrawal
|
||||
exitEpoch primitives.Epoch
|
||||
}
|
||||
|
||||
func (t *testProcessState) BuilderPendingPayments() ([]*ethpb.BuilderPendingPayment, error) {
|
||||
return t.payments, nil
|
||||
}
|
||||
|
||||
func (t *testProcessState) AppendBuilderPendingWithdrawal(withdrawal *ethpb.BuilderPendingWithdrawal) error {
|
||||
t.withdrawals = append(t.withdrawals, withdrawal)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *testProcessState) RotateBuilderPendingPayments() error {
|
||||
slotsPerEpoch := int(params.BeaconConfig().SlotsPerEpoch)
|
||||
rotated := append([]*ethpb.BuilderPendingPayment{}, t.payments[slotsPerEpoch:]...)
|
||||
for i := 0; i < slotsPerEpoch; i++ {
|
||||
rotated = append(rotated, ðpb.BuilderPendingPayment{
|
||||
Withdrawal: ðpb.BuilderPendingWithdrawal{
|
||||
FeeRecipient: make([]byte, 20),
|
||||
},
|
||||
})
|
||||
}
|
||||
t.payments = rotated
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *testProcessState) ExitEpochAndUpdateChurn(_ primitives.Gwei) (primitives.Epoch, error) {
|
||||
return t.exitEpoch, nil
|
||||
}
|
||||
@@ -5,6 +5,7 @@ go_library(
|
||||
srcs = [
|
||||
"error.go",
|
||||
"interfaces.go",
|
||||
"interfaces_gloas.go",
|
||||
"prometheus.go",
|
||||
],
|
||||
importpath = "github.com/OffchainLabs/prysm/v7/beacon-chain/state",
|
||||
|
||||
@@ -63,6 +63,7 @@ type ReadOnlyBeaconState interface {
|
||||
ReadOnlyDeposits
|
||||
ReadOnlyConsolidations
|
||||
ReadOnlyProposerLookahead
|
||||
ReadOnlyBuilderPendingPayments
|
||||
ToProtoUnsafe() any
|
||||
ToProto() any
|
||||
GenesisTime() time.Time
|
||||
@@ -98,6 +99,8 @@ type WriteOnlyBeaconState interface {
|
||||
WriteOnlyWithdrawals
|
||||
WriteOnlyDeposits
|
||||
WriteOnlyProposerLookahead
|
||||
WriteOnlyBuilderPendingPayments
|
||||
WriteOnlyBuilderPendingWithdrawals
|
||||
SetGenesisTime(val time.Time) error
|
||||
SetGenesisValidatorsRoot(val []byte) error
|
||||
SetSlot(val primitives.Slot) error
|
||||
|
||||
20
beacon-chain/state/interfaces_gloas.go
Normal file
20
beacon-chain/state/interfaces_gloas.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// ReadOnlyBuilderPendingPayments defines methods for reading builder pending payments from the beacon state.
|
||||
type ReadOnlyBuilderPendingPayments interface {
|
||||
BuilderPendingPayments() ([]*ethpb.BuilderPendingPayment, error)
|
||||
}
|
||||
|
||||
// WriteOnlyBuilderPendingPayments defines methods for writing builder pending payments to the beacon state.
|
||||
type WriteOnlyBuilderPendingPayments interface {
|
||||
RotateBuilderPendingPayments() error
|
||||
}
|
||||
|
||||
// WriteOnlyBuilderPendingWithdrawals defines methods for writing builder pending withdrawals to the beacon state.
|
||||
type WriteOnlyBuilderPendingWithdrawals interface {
|
||||
AppendBuilderPendingWithdrawal(*ethpb.BuilderPendingWithdrawal) error
|
||||
}
|
||||
@@ -14,6 +14,7 @@ go_library(
|
||||
"getters_deposits.go",
|
||||
"getters_eth1.go",
|
||||
"getters_exit.go",
|
||||
"getters_gloas.go",
|
||||
"getters_misc.go",
|
||||
"getters_participation.go",
|
||||
"getters_payload_header.go",
|
||||
@@ -23,7 +24,6 @@ go_library(
|
||||
"getters_sync_committee.go",
|
||||
"getters_validator.go",
|
||||
"getters_withdrawal.go",
|
||||
"gloas.go",
|
||||
"hasher.go",
|
||||
"multi_value_slices.go",
|
||||
"proofs.go",
|
||||
@@ -36,6 +36,7 @@ go_library(
|
||||
"setters_deposit_requests.go",
|
||||
"setters_deposits.go",
|
||||
"setters_eth1.go",
|
||||
"setters_gloas.go",
|
||||
"setters_misc.go",
|
||||
"setters_participation.go",
|
||||
"setters_payload_header.go",
|
||||
|
||||
@@ -17,6 +17,14 @@ func (b *BeaconState) executionPayloadAvailabilityVal() []byte {
|
||||
return availability
|
||||
}
|
||||
|
||||
// BuilderPendingPayments returns the builder pending payments in the beacon state.
|
||||
func (b *BeaconState) BuilderPendingPayments() ([]*ethpb.BuilderPendingPayment, error) {
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
|
||||
return b.builderPendingPaymentsVal(), nil
|
||||
}
|
||||
|
||||
// builderPendingPaymentsVal returns a copy of the builder pending payments.
|
||||
// This assumes that a lock is already held on BeaconState.
|
||||
func (b *BeaconState) builderPendingPaymentsVal() []*ethpb.BuilderPendingPayment {
|
||||
@@ -47,6 +55,15 @@ func (b *BeaconState) builderPendingWithdrawalsVal() []*ethpb.BuilderPendingWith
|
||||
return withdrawals
|
||||
}
|
||||
|
||||
// executionPayloadHeaderGloas returns a copy of the beacon state execution payload header for Gloas.
|
||||
// This assumes that a lock is already held on BeaconState.
|
||||
func (b *BeaconState) executionPayloadHeaderGloas() *ethpb.ExecutionPayloadBid {
|
||||
if b.latestExecutionPayloadBid == nil {
|
||||
return nil
|
||||
}
|
||||
return b.latestExecutionPayloadBid.Copy()
|
||||
}
|
||||
|
||||
// latestBlockHashVal returns a copy of the latest block hash.
|
||||
// This assumes that a lock is already held on BeaconState.
|
||||
func (b *BeaconState) latestBlockHashVal() []byte {
|
||||
@@ -721,3 +721,13 @@ func ProtobufBeaconStateFulu(s any) (*ethpb.BeaconStateFulu, error) {
|
||||
}
|
||||
return pbState, nil
|
||||
}
|
||||
|
||||
// ProtobufBeaconStateGloas transforms an input into beacon state Gloas in the form of protobuf.
|
||||
// Error is returned if the input is not type protobuf beacon state.
|
||||
func ProtobufBeaconStateGloas(s any) (*ethpb.BeaconStateGloas, error) {
|
||||
pbState, ok := s.(*ethpb.BeaconStateGloas)
|
||||
if !ok {
|
||||
return nil, errors.New("input is not type pb.BeaconStateGloas")
|
||||
}
|
||||
return pbState, nil
|
||||
}
|
||||
|
||||
73
beacon-chain/state/state-native/setters_gloas.go
Normal file
73
beacon-chain/state/state-native/setters_gloas.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package state_native
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native/types"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state/stateutil"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// RotateBuilderPendingPayments rotates the queue by dropping slots per epoch payments from the
|
||||
// front and appending slots per epoch empty payments to the end.
|
||||
// This implements: state.builder_pending_payments = state.builder_pending_payments[SLOTS_PER_EPOCH:] + [BuilderPendingPayment() for _ in range(SLOTS_PER_EPOCH)]
|
||||
func (b *BeaconState) RotateBuilderPendingPayments() error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
oldPayments := b.builderPendingPayments
|
||||
slotsPerEpoch := uint64(params.BeaconConfig().SlotsPerEpoch)
|
||||
newPayments := make([]*ethpb.BuilderPendingPayment, 2*slotsPerEpoch)
|
||||
|
||||
copy(newPayments, oldPayments[slotsPerEpoch:])
|
||||
|
||||
for i := uint64(len(oldPayments)) - slotsPerEpoch; i < uint64(len(newPayments)); i++ {
|
||||
newPayments[i] = emptyPayment()
|
||||
}
|
||||
|
||||
if b.sharedFieldReferences[types.BuilderPendingPayments].Refs() > 1 {
|
||||
b.sharedFieldReferences[types.BuilderPendingPayments].MinusRef()
|
||||
b.sharedFieldReferences[types.BuilderPendingPayments] = stateutil.NewRef(1)
|
||||
}
|
||||
|
||||
b.builderPendingPayments = newPayments
|
||||
b.markFieldAsDirty(types.BuilderPendingPayments)
|
||||
b.rebuildTrie[types.BuilderPendingPayments] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// AppendBuilderPendingWithdrawal appends a builder pending withdrawal to the beacon state.
|
||||
// If the withdrawals slice is shared, it copies the slice first to preserve references.
|
||||
func (b *BeaconState) AppendBuilderPendingWithdrawal(withdrawal *ethpb.BuilderPendingWithdrawal) error {
|
||||
if withdrawal == nil {
|
||||
return errors.New("cannot append nil builder pending withdrawal")
|
||||
}
|
||||
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
withdrawals := b.builderPendingWithdrawals
|
||||
if b.sharedFieldReferences[types.BuilderPendingWithdrawals].Refs() > 1 {
|
||||
withdrawals = make([]*ethpb.BuilderPendingWithdrawal, len(b.builderPendingWithdrawals), len(b.builderPendingWithdrawals)+1)
|
||||
copy(withdrawals, b.builderPendingWithdrawals)
|
||||
b.sharedFieldReferences[types.BuilderPendingWithdrawals].MinusRef()
|
||||
b.sharedFieldReferences[types.BuilderPendingWithdrawals] = stateutil.NewRef(1)
|
||||
}
|
||||
|
||||
b.builderPendingWithdrawals = append(withdrawals, withdrawal)
|
||||
b.markFieldAsDirty(types.BuilderPendingWithdrawals)
|
||||
return nil
|
||||
}
|
||||
|
||||
func emptyPayment() *ethpb.BuilderPendingPayment {
|
||||
return ðpb.BuilderPendingPayment{
|
||||
Weight: 0,
|
||||
Withdrawal: ðpb.BuilderPendingWithdrawal{
|
||||
FeeRecipient: make([]byte, 20),
|
||||
Amount: 0,
|
||||
BuilderIndex: 0,
|
||||
WithdrawableEpoch: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
92
beacon-chain/state/state-native/setters_gloas_test.go
Normal file
92
beacon-chain/state/state-native/setters_gloas_test.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package state_native
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native/types"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state/stateutil"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/require"
|
||||
)
|
||||
|
||||
func TestRotateBuilderPendingPayments(t *testing.T) {
|
||||
totalPayments := 2 * params.BeaconConfig().SlotsPerEpoch
|
||||
payments := make([]*ethpb.BuilderPendingPayment, totalPayments)
|
||||
for i := range payments {
|
||||
idx := uint64(i)
|
||||
payments[i] = ðpb.BuilderPendingPayment{
|
||||
Weight: primitives.Gwei(idx * 100e9),
|
||||
Withdrawal: ðpb.BuilderPendingWithdrawal{
|
||||
FeeRecipient: make([]byte, 20),
|
||||
Amount: primitives.Gwei(idx * 1e9),
|
||||
BuilderIndex: primitives.ValidatorIndex(idx + 100),
|
||||
WithdrawableEpoch: primitives.Epoch(idx + 1000),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
statePb, err := InitializeFromProtoUnsafeGloas(ðpb.BeaconStateGloas{
|
||||
BuilderPendingPayments: payments,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
st, ok := statePb.(*BeaconState)
|
||||
require.Equal(t, true, ok)
|
||||
st.sharedFieldReferences[types.BuilderPendingPayments] = stateutil.NewRef(1)
|
||||
|
||||
oldPayments, err := st.BuilderPendingPayments()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, st.RotateBuilderPendingPayments())
|
||||
|
||||
newPayments, err := st.BuilderPendingPayments()
|
||||
require.NoError(t, err)
|
||||
slotsPerEpoch := int(params.BeaconConfig().SlotsPerEpoch)
|
||||
for i := 0; i < slotsPerEpoch; i++ {
|
||||
require.DeepEqual(t, oldPayments[slotsPerEpoch+i], newPayments[i])
|
||||
}
|
||||
|
||||
for i := slotsPerEpoch; i < 2*slotsPerEpoch; i++ {
|
||||
payment := newPayments[i]
|
||||
require.Equal(t, primitives.Gwei(0), payment.Weight)
|
||||
require.Equal(t, 20, len(payment.Withdrawal.FeeRecipient))
|
||||
require.Equal(t, primitives.Gwei(0), payment.Withdrawal.Amount)
|
||||
require.Equal(t, primitives.ValidatorIndex(0), payment.Withdrawal.BuilderIndex)
|
||||
require.Equal(t, primitives.Epoch(0), payment.Withdrawal.WithdrawableEpoch)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendBuilderPendingWithdrawal_CopyOnWrite(t *testing.T) {
|
||||
wd := ðpb.BuilderPendingWithdrawal{
|
||||
FeeRecipient: make([]byte, 20),
|
||||
Amount: 1,
|
||||
BuilderIndex: 2,
|
||||
WithdrawableEpoch: 3,
|
||||
}
|
||||
statePb, err := InitializeFromProtoUnsafeGloas(ðpb.BeaconStateGloas{
|
||||
BuilderPendingWithdrawals: []*ethpb.BuilderPendingWithdrawal{wd},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
st, ok := statePb.(*BeaconState)
|
||||
require.Equal(t, true, ok)
|
||||
|
||||
copied := st.Copy().(*BeaconState)
|
||||
require.Equal(t, uint(2), st.sharedFieldReferences[types.BuilderPendingWithdrawals].Refs())
|
||||
|
||||
appended := ðpb.BuilderPendingWithdrawal{
|
||||
FeeRecipient: make([]byte, 20),
|
||||
Amount: 4,
|
||||
BuilderIndex: 5,
|
||||
WithdrawableEpoch: 6,
|
||||
}
|
||||
require.NoError(t, copied.AppendBuilderPendingWithdrawal(appended))
|
||||
|
||||
require.Equal(t, 1, len(st.builderPendingWithdrawals))
|
||||
require.Equal(t, 2, len(copied.builderPendingWithdrawals))
|
||||
require.DeepEqual(t, wd, copied.builderPendingWithdrawals[0])
|
||||
require.DeepEqual(t, appended, copied.builderPendingWithdrawals[1])
|
||||
require.DeepEqual(t, wd, st.builderPendingWithdrawals[0])
|
||||
require.Equal(t, uint(1), st.sharedFieldReferences[types.BuilderPendingWithdrawals].Refs())
|
||||
require.Equal(t, uint(1), copied.sharedFieldReferences[types.BuilderPendingWithdrawals].Refs())
|
||||
}
|
||||
@@ -290,6 +290,10 @@ type BeaconChainConfig struct {
|
||||
ValidatorCustodyRequirement uint64 `yaml:"VALIDATOR_CUSTODY_REQUIREMENT" spec:"true"` // ValidatorCustodyRequirement is the minimum number of custody groups an honest node with validators attached custodies and serves samples from
|
||||
BalancePerAdditionalCustodyGroup uint64 `yaml:"BALANCE_PER_ADDITIONAL_CUSTODY_GROUP" spec:"true"` // BalancePerAdditionalCustodyGroup is the balance increment corresponding to one additional group to custody.
|
||||
|
||||
// Values introduced in Gloas upgrade
|
||||
BuilderPaymentThresholdNumerator uint64 `yaml:"BUILDER_PAYMENT_THRESHOLD_NUMERATOR" spec:"true"` // BuilderPaymentThresholdNumerator is the numerator for builder payment quorum threshold calculation.
|
||||
BuilderPaymentThresholdDenominator uint64 `yaml:"BUILDER_PAYMENT_THRESHOLD_DENOMINATOR" spec:"true"` // BuilderPaymentThresholdDenominator is the denominator for builder payment quorum threshold calculation.
|
||||
|
||||
// Networking Specific Parameters
|
||||
MaxPayloadSize uint64 `yaml:"MAX_PAYLOAD_SIZE" spec:"true"` // MAX_PAYLOAD_SIZE is the maximum allowed size of uncompressed payload in gossip messages and rpc chunks.
|
||||
AttestationSubnetCount uint64 `yaml:"ATTESTATION_SUBNET_COUNT" spec:"true"` // AttestationSubnetCount is the number of attestation subnets used in the gossipsub protocol.
|
||||
|
||||
@@ -328,6 +328,11 @@ var mainnetBeaconConfig = &BeaconChainConfig{
|
||||
MinEpochsForDataColumnSidecarsRequest: 4096,
|
||||
ValidatorCustodyRequirement: 8,
|
||||
BalancePerAdditionalCustodyGroup: 32_000_000_000,
|
||||
|
||||
// Values related to gloas
|
||||
BuilderPaymentThresholdNumerator: 6,
|
||||
BuilderPaymentThresholdDenominator: 10,
|
||||
|
||||
// Values related to networking parameters.
|
||||
MaxPayloadSize: 10 * 1 << 20, // 10 MiB
|
||||
AttestationSubnetCount: 64,
|
||||
|
||||
@@ -200,6 +200,7 @@ go_test(
|
||||
"fulu__sanity__blocks_test.go",
|
||||
"fulu__sanity__slots_test.go",
|
||||
"fulu__ssz_static__ssz_static_test.go",
|
||||
"gloas__epoch_processing__process_builder_pending_payments_test.go",
|
||||
"gloas__ssz_static__ssz_static_test.go",
|
||||
"phase0__epoch_processing__effective_balance_updates_test.go",
|
||||
"phase0__epoch_processing__epoch_processing_test.go",
|
||||
@@ -278,6 +279,7 @@ go_test(
|
||||
"//testing/spectest/shared/fulu/rewards:go_default_library",
|
||||
"//testing/spectest/shared/fulu/sanity:go_default_library",
|
||||
"//testing/spectest/shared/fulu/ssz_static:go_default_library",
|
||||
"//testing/spectest/shared/gloas/epoch_processing:go_default_library",
|
||||
"//testing/spectest/shared/gloas/ssz_static:go_default_library",
|
||||
"//testing/spectest/shared/phase0/epoch_processing:go_default_library",
|
||||
"//testing/spectest/shared/phase0/finality:go_default_library",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package mainnet
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/testing/spectest/shared/gloas/epoch_processing"
|
||||
)
|
||||
|
||||
func TestMainnet_Gloas_EpochProcessing_ProcessBuilderPendingPayments(t *testing.T) {
|
||||
epoch_processing.RunBuilderPendingPaymentsTests(t, "mainnet")
|
||||
}
|
||||
@@ -206,6 +206,7 @@ go_test(
|
||||
"fulu__sanity__blocks_test.go",
|
||||
"fulu__sanity__slots_test.go",
|
||||
"fulu__ssz_static__ssz_static_test.go",
|
||||
"gloas__epoch_processing__process_builder_pending_payments_test.go",
|
||||
"gloas__ssz_static__ssz_static_test.go",
|
||||
"phase0__epoch_processing__effective_balance_updates_test.go",
|
||||
"phase0__epoch_processing__epoch_processing_test.go",
|
||||
@@ -288,6 +289,7 @@ go_test(
|
||||
"//testing/spectest/shared/fulu/rewards:go_default_library",
|
||||
"//testing/spectest/shared/fulu/sanity:go_default_library",
|
||||
"//testing/spectest/shared/fulu/ssz_static:go_default_library",
|
||||
"//testing/spectest/shared/gloas/epoch_processing:go_default_library",
|
||||
"//testing/spectest/shared/gloas/ssz_static:go_default_library",
|
||||
"//testing/spectest/shared/phase0/epoch_processing:go_default_library",
|
||||
"//testing/spectest/shared/phase0/finality:go_default_library",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package minimal
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/testing/spectest/shared/gloas/epoch_processing"
|
||||
)
|
||||
|
||||
func TestMinimal_Gloas_EpochProcessing_ProcessBuilderPendingPayments(t *testing.T) {
|
||||
epoch_processing.RunBuilderPendingPaymentsTests(t, "minimal")
|
||||
}
|
||||
26
testing/spectest/shared/gloas/epoch_processing/BUILD.bazel
Normal file
26
testing/spectest/shared/gloas/epoch_processing/BUILD.bazel
Normal file
@@ -0,0 +1,26 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"builder_pending_payments.go",
|
||||
"helpers.go",
|
||||
],
|
||||
importpath = "github.com/OffchainLabs/prysm/v7/testing/spectest/shared/gloas/epoch_processing",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/core/gloas:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/state-native:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"//testing/spectest/utils:go_default_library",
|
||||
"//testing/util:go_default_library",
|
||||
"@com_github_golang_snappy//:go_default_library",
|
||||
"@com_github_google_go_cmp//cmp:go_default_library",
|
||||
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
"@org_golang_google_protobuf//testing/protocmp:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,28 @@
|
||||
package epoch_processing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/gloas"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/spectest/utils"
|
||||
)
|
||||
|
||||
func RunBuilderPendingPaymentsTests(t *testing.T, config string) {
|
||||
require.NoError(t, utils.SetConfig(t, config))
|
||||
testFolders, testsFolderPath := utils.TestFolders(t, config, "gloas", "epoch_processing/builder_pending_payments/pyspec_tests")
|
||||
fmt.Println(testsFolderPath, testFolders)
|
||||
for _, folder := range testFolders {
|
||||
t.Run(folder.Name(), func(t *testing.T) {
|
||||
folderPath := path.Join(testsFolderPath, folder.Name())
|
||||
RunEpochOperationTest(t, folderPath, processBuilderPendingPayments)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func processBuilderPendingPayments(t *testing.T, st state.BeaconState) (state.BeaconState, error) {
|
||||
return st, gloas.ProcessBuilderPendingPayments(st)
|
||||
}
|
||||
78
testing/spectest/shared/gloas/epoch_processing/helpers.go
Normal file
78
testing/spectest/shared/gloas/epoch_processing/helpers.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package epoch_processing
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
state_native "github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/util"
|
||||
"github.com/bazelbuild/rules_go/go/tools/bazel"
|
||||
"github.com/golang/snappy"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
)
|
||||
|
||||
type epochOperation func(*testing.T, state.BeaconState) (state.BeaconState, error)
|
||||
|
||||
// RunEpochOperationTest takes in the prestate and processes it through the
|
||||
// passed in epoch operation function and checks the post state with the expected post state.
|
||||
func RunEpochOperationTest(
|
||||
t *testing.T,
|
||||
testFolderPath string,
|
||||
operationFn epochOperation,
|
||||
) {
|
||||
preBeaconStateFile, err := util.BazelFileBytes(path.Join(testFolderPath, "pre.ssz_snappy"))
|
||||
require.NoError(t, err)
|
||||
preBeaconStateSSZ, err := snappy.Decode(nil /* dst */, preBeaconStateFile)
|
||||
require.NoError(t, err, "Failed to decompress")
|
||||
preBeaconStateBase := ðpb.BeaconStateGloas{}
|
||||
if err := preBeaconStateBase.UnmarshalSSZ(preBeaconStateSSZ); err != nil {
|
||||
t.Fatalf("Failed to unmarshal: %v", err)
|
||||
}
|
||||
preBeaconState, err := state_native.InitializeFromProtoGloas(preBeaconStateBase)
|
||||
require.NoError(t, err)
|
||||
|
||||
// If the post.ssz is not present, it means the test should fail on our end.
|
||||
postSSZFilepath, err := bazel.Runfile(path.Join(testFolderPath, "post.ssz_snappy"))
|
||||
postSSZExists := true
|
||||
if err != nil && strings.Contains(err.Error(), "could not locate file") {
|
||||
postSSZExists = false
|
||||
} else if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
beaconState, err := operationFn(t, preBeaconState)
|
||||
if postSSZExists {
|
||||
require.NoError(t, err)
|
||||
|
||||
postBeaconStateFile, err := os.ReadFile(postSSZFilepath) // #nosec G304
|
||||
require.NoError(t, err)
|
||||
postBeaconStateSSZ, err := snappy.Decode(nil /* dst */, postBeaconStateFile)
|
||||
require.NoError(t, err, "Failed to decompress")
|
||||
postBeaconState := ðpb.BeaconStateGloas{}
|
||||
if err := postBeaconState.UnmarshalSSZ(postBeaconStateSSZ); err != nil {
|
||||
t.Fatalf("Failed to unmarshal: %v", err)
|
||||
}
|
||||
|
||||
pbState, err := state_native.ProtobufBeaconStateGloas(beaconState.ToProtoUnsafe())
|
||||
require.NoError(t, err)
|
||||
if !proto.Equal(pbState, postBeaconState) {
|
||||
t.Log(cmp.Diff(postBeaconState, pbState, protocmp.Transform()))
|
||||
t.Fatal("Post state does not match expected")
|
||||
}
|
||||
} else {
|
||||
// Note: This doesn't test anything worthwhile. It essentially tests
|
||||
// that *any* error has occurred, not any specific error.
|
||||
if err == nil {
|
||||
t.Fatal("Did not fail when expected")
|
||||
}
|
||||
t.Logf("Expected failure; failure reason = %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user