Skip to content

Commit

Permalink
thursday progress
Browse files Browse the repository at this point in the history
  • Loading branch information
prestonvanloon committed Apr 22, 2024
1 parent ea9a3f5 commit 661cc58
Show file tree
Hide file tree
Showing 19 changed files with 1,672 additions and 109 deletions.
4 changes: 2 additions & 2 deletions beacon-chain/blockchain/execution_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState,
var attr payloadattribute.Attributer
switch st.Version() {
case version.Deneb:
withdrawals, err := st.ExpectedWithdrawals()
withdrawals, _, err := st.ExpectedWithdrawals()
if err != nil {
log.WithError(err).Error("Could not get expected withdrawals to get payload attribute")
return emptyAttri
Expand All @@ -342,7 +342,7 @@ func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState,
return emptyAttri
}
case version.Capella:
withdrawals, err := st.ExpectedWithdrawals()
withdrawals, _, err := st.ExpectedWithdrawals()
if err != nil {
log.WithError(err).Error("Could not get expected withdrawals to get payload attribute")
return emptyAttri
Expand Down
61 changes: 37 additions & 24 deletions beacon-chain/core/blocks/withdrawals.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,35 +120,41 @@ func ValidateBLSToExecutionChange(st state.ReadOnlyBeaconState, signed *ethpb.Si
//
// Spec pseudocode definitions:
//
// def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
// expected_withdrawals = get_expected_withdrawals(state)
// assert len(payload.withdrawals) == len(expected_withdrawals)
//
// for expected_withdrawal, withdrawal in zip(expected_withdrawals, payload.withdrawals):
// assert withdrawal == expected_withdrawal
// decrease_balance(state, withdrawal.validator_index, withdrawal.amount)
//
// # Update the next withdrawal index if this block contained withdrawals
// if len(expected_withdrawals) != 0:
// latest_withdrawal = expected_withdrawals[-1]
// state.next_withdrawal_index = WithdrawalIndex(latest_withdrawal.index + 1)
//
// # Update the next validator index to start the next withdrawal sweep
// if len(expected_withdrawals) == MAX_WITHDRAWALS_PER_PAYLOAD:
// # Next sweep starts after the latest withdrawal's validator index
// next_validator_index = ValidatorIndex((expected_withdrawals[-1].validator_index + 1) % len(state.validators))
// state.next_withdrawal_validator_index = next_validator_index
// else:
// # Advance sweep by the max length of the sweep if there was not a full set of withdrawals
// next_index = state.next_withdrawal_validator_index + MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP
// next_validator_index = ValidatorIndex(next_index % len(state.validators))
// state.next_withdrawal_validator_index = next_validator_index
// def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
// expected_withdrawals, partial_withdrawals_count = get_expected_withdrawals(state) # [Modified in EIP7251]
//
// assert len(payload.withdrawals) == len(expected_withdrawals)
//
// for expected_withdrawal, withdrawal in zip(expected_withdrawals, payload.withdrawals):
// assert withdrawal == expected_withdrawal
// decrease_balance(state, withdrawal.validator_index, withdrawal.amount)
//
// # [New in EIP7251] update pending partial withdrawals
// state.pending_partial_withdrawals = state.pending_partial_withdrawals[partial_withdrawals_count:]
//
// # Update the next withdrawal index if this block contained withdrawals
// if len(expected_withdrawals) != 0:
// latest_withdrawal = expected_withdrawals[-1]
// state.next_withdrawal_index = WithdrawalIndex(latest_withdrawal.index + 1)
//
// # Update the next validator index to start the next withdrawal sweep
// if len(expected_withdrawals) == MAX_WITHDRAWALS_PER_PAYLOAD:
// # Next sweep starts after the latest withdrawal's validator index
// next_validator_index = ValidatorIndex((expected_withdrawals[-1].validator_index + 1) % len(state.validators))
// state.next_withdrawal_validator_index = next_validator_index
// else:
// # Advance sweep by the max length of the sweep if there was not a full set of withdrawals
// next_index = state.next_withdrawal_validator_index + MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP
// next_validator_index = ValidatorIndex(next_index % len(state.validators))
// state.next_withdrawal_validator_index = next_validator_index
func ProcessWithdrawals(st state.BeaconState, executionData interfaces.ExecutionData) (state.BeaconState, error) {
expectedWithdrawals, err := st.ExpectedWithdrawals()
expectedWithdrawals, partials, err := st.ExpectedWithdrawals()
if err != nil {
return nil, errors.Wrap(err, "could not get expected withdrawals")
}

_ = partials // TODO: Deal with this

var wdRoot [32]byte
if executionData.IsBlinded() {
r, err := executionData.WithdrawalsRoot()
Expand Down Expand Up @@ -181,6 +187,13 @@ func ProcessWithdrawals(st state.BeaconState, executionData interfaces.Execution
return nil, errors.Wrap(err, "could not decrease balance")
}
}

if partials > 0 {
if err := st.DequeuePartialWithdrawals(partials); err != nil {
return nil, fmt.Errorf("could not dequeue partial withdrawals: %w", err)
}
}

if len(expectedWithdrawals) > 0 {
if err := st.SetNextWithdrawalIndex(expectedWithdrawals[len(expectedWithdrawals)-1].Index + 1); err != nil {
return nil, errors.Wrap(err, "could not set next withdrawal index")
Expand Down
111 changes: 111 additions & 0 deletions beacon-chain/core/eip7251/transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epoch/precompute"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/params"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/time/slots"
"go.opencensus.io/trace"
)
Expand Down Expand Up @@ -255,3 +257,112 @@ func ProcessPendingConsolidations(ctx context.Context, st state.BeaconState, act

return st, nil
}

// ProcessConsolidations --
//
// Spec definition:
//
// def process_consolidation(state: BeaconState, signed_consolidation: SignedConsolidation) -> None:
// # If the pending consolidations queue is full, no consolidations are allowed in the block
// assert len(state.pending_consolidations) < PENDING_CONSOLIDATIONS_LIMIT
// # If there is too little available consolidation churn limit, no consolidations are allowed in the block
// assert get_consolidation_churn_limit(state) > MIN_ACTIVATION_BALANCE
// consolidation = signed_consolidation.message
// # Verify that source != target, so a consolidation cannot be used as an exit.
// assert consolidation.source_index != consolidation.target_index
//
// source_validator = state.validators[consolidation.source_index]
// target_validator = state.validators[consolidation.target_index]
// # Verify the source and the target are active
// current_epoch = get_current_epoch(state)
// assert is_active_validator(source_validator, current_epoch)
// assert is_active_validator(target_validator, current_epoch)
// # Verify exits for source and target have not been initiated
// assert source_validator.exit_epoch == FAR_FUTURE_EPOCH
// assert target_validator.exit_epoch == FAR_FUTURE_EPOCH
// # Consolidations must specify an epoch when they become valid; they are not valid before then
// assert current_epoch >= consolidation.epoch
//
// # Verify the source and the target have Execution layer withdrawal credentials
// assert has_execution_withdrawal_credential(source_validator)
// assert has_execution_withdrawal_credential(target_validator)
// # Verify the same withdrawal address
// assert source_validator.withdrawal_credentials[12:] == target_validator.withdrawal_credentials[12:]
//
// # Verify consolidation is signed by the source and the target
// domain = compute_domain(DOMAIN_CONSOLIDATION, genesis_validators_root=state.genesis_validators_root)
// signing_root = compute_signing_root(consolidation, domain)
// pubkeys = [source_validator.pubkey, target_validator.pubkey]
// assert bls.FastAggregateVerify(pubkeys, signing_root, signed_consolidation.signature)
//
// # Initiate source validator exit and append pending consolidation
// source_validator.exit_epoch = compute_consolidation_epoch_and_update_churn(
// state, source_validator.effective_balance)
// source_validator.withdrawable_epoch = Epoch(
// source_validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
// )
// state.pending_consolidations.append(PendingConsolidation(
// source_index=consolidation.source_index,
// target_index=consolidation.target_index
// ))
func ProcessConsolidations(ctx context.Context, st state.BeaconState, cs []*ethpb.SignedConsolidation) (state.BeaconState, error) {
_, span := trace.StartSpan(ctx, "eip7251.ProcessConsolidations")
defer span.End()

if st == nil || st.IsNil() {
return nil, errors.New("nil state")
}
if cs == nil {
return nil, errors.New("nil consolidations")
}

for _, c := range cs {
if c == nil || c.Message == nil {
return nil, errors.New("nil consolidation")
}

// TODO: can these be moved outside of the loop?
if st.NumPendingConsolidations() >= params.BeaconConfig().PendingConsolidationsLimit {
return nil, errors.New("pending consolidations queue is full")
}

totalBalance, err := helpers.TotalActiveBalance(st)
if err != nil {
return nil, err
}
if helpers.ConsolidationChurnLimit(totalBalance) <= params.BeaconConfig().MinActivationBalance {
return nil, errors.New("too little available consolidation churn limit")
}
currentEpoch := slots.ToEpoch(st.Slot())
// END TODO

if c.Message.SourceIndex == c.Message.TargetIndex {
return nil, errors.New("source and target index are the same")
}
source, err := st.ValidatorAtIndex(c.Message.SourceIndex)
if err != nil {
return nil, err
}
target, err := st.ValidatorAtIndex(c.Message.TargetIndex)
if err != nil {
return nil, err
}
if !helpers.IsActiveValidator(source, currentEpoch) {
return nil, errors.New("source is not active")
}
if !helpers.IsActiveValidator(target, currentEpoch) {
return nil, errors.New("target is not active")
}
if source.ExitEpoch != params.BeaconConfig().FarFutureEpoch {
return nil, errors.New("source exit epoch has been initiated")
}
if target.ExitEpoch != params.BeaconConfig().FarFutureEpoch {
return nil, errors.New("target exit epoch has been initiated")
}
if currentEpoch < c.Message.Epoch {
return nil, errors.New("consolidation is not valid yet")
}
}

return nil, errors.New("not implemented")
}
1 change: 1 addition & 0 deletions beacon-chain/core/transition/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ go_library(
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/capella:go_default_library",
"//beacon-chain/core/deneb:go_default_library",
"//beacon-chain/core/eip7251:go_default_library",
"//beacon-chain/core/epoch:go_default_library",
"//beacon-chain/core/epoch/precompute:go_default_library",
"//beacon-chain/core/execution:go_default_library",
Expand Down
42 changes: 40 additions & 2 deletions beacon-chain/core/transition/transition_no_verify_sig.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
b "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/eip7251"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition/interop"
v "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
Expand Down Expand Up @@ -234,8 +235,11 @@ func ProcessBlockNoVerifyAnySig(
// for_ops(body.proposer_slashings, process_proposer_slashing)
// for_ops(body.attester_slashings, process_attester_slashing)
// for_ops(body.attestations, process_attestation)
// for_ops(body.deposits, process_deposit)
// for_ops(body.voluntary_exits, process_voluntary_exit)
// for_ops(body.deposits, process_deposit) # [Modified in EIP7251]
// for_ops(body.voluntary_exits, process_voluntary_exit) # [Modified in EIP7251]
// for_ops(body.bls_to_execution_changes, process_bls_to_execution_change)
// for_ops(body.execution_payload.withdraw_requests, process_execution_layer_withdraw_request) # [New in EIP7251]
// for_ops(body.consolidations, process_consolidation) # [New in EIP7251]
func ProcessOperationsNoVerifyAttsSigs(
ctx context.Context,
state state.BeaconState,
Expand All @@ -262,6 +266,11 @@ func ProcessOperationsNoVerifyAttsSigs(
if err != nil {
return nil, err
}
case version.EIP7251:
state, err = eip7251Operations(ctx, state, beaconBlock)
if err != nil {
return nil, err
}
default:
return nil, errors.New("block does not have correct version")
}
Expand Down Expand Up @@ -378,16 +387,45 @@ func VerifyBlobCommitmentCount(blk interfaces.ReadOnlyBeaconBlock) error {
return nil
}

// eip7251Operations --
//
// Spec definition:
//
// def process_operations(state: BeaconState, body: ReadOnlyBeaconBlockBody) -> None:
// # Verify that outstanding deposits are processed up to the maximum number of deposits
// assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index)
//
// def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None:
// for operation in operations:
// fn(state, operation)
//
// for_ops(body.proposer_slashings, process_proposer_slashing)
// for_ops(body.attester_slashings, process_attester_slashing)
// for_ops(body.attestations, process_attestation)
// for_ops(body.deposits, process_deposit) # [Modified in EIP7251]
// for_ops(body.voluntary_exits, process_voluntary_exit) # [Modified in EIP7251]
// for_ops(body.bls_to_execution_changes, process_bls_to_execution_change)
// for_ops(body.execution_payload.withdraw_requests, process_execution_layer_withdraw_request) # [New in EIP7251]
// for_ops(body.consolidations, process_consolidation) # [New in EIP7251]
func eip7251Operations(
ctx context.Context,
st state.BeaconState,
block interfaces.ReadOnlyBeaconBlock) (state.BeaconState, error) {

// EIP-7251 extends the altair operations.
st, err := altairOperations(ctx, st, block)
if err != nil {
return nil, err
}

cs, err := block.Body().Consolidations()
if err != nil {
return nil, errors.Wrap(err, "could not get consolidations")
}
st, err = eip7251.ProcessConsolidations(ctx, st, cs)
if err != nil {
return nil, errors.Wrap(err, "could not process consolidations")
}

// TODO
return st, nil
Expand Down
2 changes: 1 addition & 1 deletion beacon-chain/rpc/eth/builder/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func (s *Server) ExpectedWithdrawals(w http.ResponseWriter, r *http.Request) {
})
return
}
withdrawals, err := st.ExpectedWithdrawals()
withdrawals, _, err := st.ExpectedWithdrawals()
if err != nil {
httputil.WriteError(w, &httputil.DefaultJsonError{
Message: "could not get expected withdrawals",
Expand Down
4 changes: 2 additions & 2 deletions beacon-chain/rpc/eth/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ func (s *Server) sendPayloadAttributes(ctx context.Context, w http.ResponseWrite
SuggestedFeeRecipient: hexutil.Encode(headPayload.FeeRecipient()),
}
case version.Capella:
withdrawals, err := headState.ExpectedWithdrawals()
withdrawals, _, err := headState.ExpectedWithdrawals()
if err != nil {
return write(w, flusher, "Could not get head state expected withdrawals: "+err.Error())
}
Expand All @@ -451,7 +451,7 @@ func (s *Server) sendPayloadAttributes(ctx context.Context, w http.ResponseWrite
Withdrawals: structs.WithdrawalsFromConsensus(withdrawals),
}
case version.Deneb:
withdrawals, err := headState.ExpectedWithdrawals()
withdrawals, _, err := headState.ExpectedWithdrawals()
if err != nil {
return write(w, flusher, "Could not get head state expected withdrawals: "+err.Error())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBe
var attr payloadattribute.Attributer
switch st.Version() {
case version.Deneb:
withdrawals, err := st.ExpectedWithdrawals()
withdrawals, _, err := st.ExpectedWithdrawals()
if err != nil {
return nil, false, err
}
Expand All @@ -143,7 +143,7 @@ func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBe
return nil, false, err
}
case version.Capella:
withdrawals, err := st.ExpectedWithdrawals()
withdrawals, _, err := st.ExpectedWithdrawals()
if err != nil {
return nil, false, err
}
Expand Down
4 changes: 3 additions & 1 deletion beacon-chain/state/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ type ReadOnlyAttestations interface {

// ReadOnlyWithdrawals defines a struct which only has read access to withdrawal methods.
type ReadOnlyWithdrawals interface {
ExpectedWithdrawals() ([]*enginev1.Withdrawal, error)
ExpectedWithdrawals() ([]*enginev1.Withdrawal, uint64, error)
NextWithdrawalValidatorIndex() (primitives.ValidatorIndex, error)
NextWithdrawalIndex() (uint64, error)
}
Expand Down Expand Up @@ -212,6 +212,7 @@ type ReadOnlyEIP7251 interface {
EarliestConsolidationEpoch() (primitives.Epoch, error)
PendingBalanceDeposits() ([]*ethpb.PendingBalanceDeposit, error)
PendingConsolidations() ([]*ethpb.PendingConsolidation, error)
NumPendingConsolidations() uint64
}

// WriteOnlyBlockRoots defines a struct which only has write access to block roots methods.
Expand Down Expand Up @@ -304,4 +305,5 @@ type WriteOnlyEIP7241 interface {
SetPendingBalanceDeposits(val []*ethpb.PendingBalanceDeposit) error
SetDepositBalanceToConsume(gwei uint64) error
SetPendingConsolidations(val []*ethpb.PendingConsolidation) error
DequeuePartialWithdrawals(idx uint64) error
}
6 changes: 6 additions & 0 deletions beacon-chain/state/state-native/getters_eip7251.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ func (b *BeaconState) PendingConsolidations() ([]*ethpb.PendingConsolidation, er
return b.pendingConsolidations, nil
}

func (b *BeaconState) NumPendingConsolidations() uint64 {
b.lock.RLock() // TODO: Is this necessary?
defer b.lock.RUnlock()
return uint64(len(b.pendingConsolidations))
}

func (b *BeaconState) SetDepositBalanceToConsume(gwei uint64) error {
if b.version < version.EIP7251 {
return errNotSupported("SetDepositBalanceToConsume", b.version)
Expand Down
Loading

0 comments on commit 661cc58

Please sign in to comment.