mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 23:48:06 -05:00
Implement process exec payload
This commit is contained in:
276
beacon-chain/core/gloas/payload_test.go
Normal file
276
beacon-chain/core/gloas/payload_test.go
Normal file
@@ -0,0 +1,276 @@
|
||||
package gloas
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
|
||||
"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/blocks"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
"github.com/OffchainLabs/prysm/v7/crypto/bls"
|
||||
"github.com/OffchainLabs/prysm/v7/encoding/ssz"
|
||||
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v7/time/slots"
|
||||
)
|
||||
|
||||
type payloadFixture struct {
|
||||
state state.BeaconState
|
||||
signed interfaces.ROSignedExecutionPayloadEnvelope
|
||||
signedProto *ethpb.SignedExecutionPayloadEnvelope
|
||||
envelope *ethpb.ExecutionPayloadEnvelope
|
||||
payload *enginev1.ExecutionPayloadDeneb
|
||||
slot primitives.Slot
|
||||
}
|
||||
|
||||
func buildPayloadFixture(t *testing.T, mutate func(payload *enginev1.ExecutionPayloadDeneb, bid *ethpb.ExecutionPayloadBid, envelope *ethpb.ExecutionPayloadEnvelope)) payloadFixture {
|
||||
t.Helper()
|
||||
|
||||
cfg := params.BeaconConfig()
|
||||
slot := primitives.Slot(5)
|
||||
builderIdx := primitives.ValidatorIndex(0)
|
||||
|
||||
sk, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
pk := sk.PublicKey().Marshal()
|
||||
|
||||
randao := bytes.Repeat([]byte{0xAA}, 32)
|
||||
parentHash := bytes.Repeat([]byte{0xBB}, 32)
|
||||
blockHash := bytes.Repeat([]byte{0xCC}, 32)
|
||||
|
||||
withdrawals := []*enginev1.Withdrawal{
|
||||
{Index: 0, ValidatorIndex: 1, Address: bytes.Repeat([]byte{0x01}, 20), Amount: 0},
|
||||
}
|
||||
withdrawalsRoot, err := ssz.WithdrawalSliceRoot(withdrawals, cfg.MaxWithdrawalsPerPayload)
|
||||
require.NoError(t, err)
|
||||
|
||||
blobCommitments := [][]byte{}
|
||||
blobRoot, err := ssz.KzgCommitmentsRoot(blobCommitments)
|
||||
require.NoError(t, err)
|
||||
|
||||
payload := &enginev1.ExecutionPayloadDeneb{
|
||||
ParentHash: parentHash,
|
||||
FeeRecipient: bytes.Repeat([]byte{0x01}, 20),
|
||||
StateRoot: bytes.Repeat([]byte{0x02}, 32),
|
||||
ReceiptsRoot: bytes.Repeat([]byte{0x03}, 32),
|
||||
LogsBloom: bytes.Repeat([]byte{0x04}, 256),
|
||||
PrevRandao: randao,
|
||||
BlockNumber: 1,
|
||||
GasLimit: 1,
|
||||
GasUsed: 0,
|
||||
Timestamp: 100,
|
||||
ExtraData: []byte{},
|
||||
BaseFeePerGas: bytes.Repeat([]byte{0x05}, 32),
|
||||
BlockHash: blockHash,
|
||||
Transactions: [][]byte{},
|
||||
Withdrawals: withdrawals,
|
||||
BlobGasUsed: 0,
|
||||
ExcessBlobGas: 0,
|
||||
}
|
||||
|
||||
bid := ðpb.ExecutionPayloadBid{
|
||||
ParentBlockHash: parentHash,
|
||||
ParentBlockRoot: bytes.Repeat([]byte{0xDD}, 32),
|
||||
BlockHash: blockHash,
|
||||
PrevRandao: randao,
|
||||
GasLimit: 1,
|
||||
BuilderIndex: builderIdx,
|
||||
Slot: slot,
|
||||
Value: 0,
|
||||
ExecutionPayment: 0,
|
||||
BlobKzgCommitmentsRoot: blobRoot[:],
|
||||
FeeRecipient: bytes.Repeat([]byte{0xEE}, 20),
|
||||
}
|
||||
|
||||
header := ðpb.BeaconBlockHeader{
|
||||
Slot: slot,
|
||||
ParentRoot: bytes.Repeat([]byte{0x11}, 32),
|
||||
StateRoot: bytes.Repeat([]byte{0x22}, 32),
|
||||
BodyRoot: bytes.Repeat([]byte{0x33}, 32),
|
||||
}
|
||||
headerRoot, err := header.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
envelope := ðpb.ExecutionPayloadEnvelope{
|
||||
Slot: slot,
|
||||
BuilderIndex: builderIdx,
|
||||
BeaconBlockRoot: headerRoot[:],
|
||||
Payload: payload,
|
||||
BlobKzgCommitments: blobCommitments,
|
||||
ExecutionRequests: &enginev1.ExecutionRequests{},
|
||||
}
|
||||
|
||||
if mutate != nil {
|
||||
mutate(payload, bid, envelope)
|
||||
}
|
||||
|
||||
withdrawalsRoot, err = ssz.WithdrawalSliceRoot(payload.Withdrawals, cfg.MaxWithdrawalsPerPayload)
|
||||
require.NoError(t, err)
|
||||
blobRoot, err = ssz.KzgCommitmentsRoot(envelope.BlobKzgCommitments)
|
||||
require.NoError(t, err)
|
||||
|
||||
genesisRoot := bytes.Repeat([]byte{0xAB}, 32)
|
||||
blockRoots := make([][]byte, cfg.SlotsPerHistoricalRoot)
|
||||
stateRoots := make([][]byte, cfg.SlotsPerHistoricalRoot)
|
||||
for i := range blockRoots {
|
||||
blockRoots[i] = bytes.Repeat([]byte{0x44}, 32)
|
||||
stateRoots[i] = bytes.Repeat([]byte{0x55}, 32)
|
||||
}
|
||||
randaoMixes := make([][]byte, cfg.EpochsPerHistoricalVector)
|
||||
for i := range randaoMixes {
|
||||
randaoMixes[i] = randao
|
||||
}
|
||||
|
||||
withdrawalCreds := make([]byte, 32)
|
||||
withdrawalCreds[0] = cfg.ETH1AddressWithdrawalPrefixByte
|
||||
|
||||
eth1Data := ðpb.Eth1Data{
|
||||
DepositRoot: bytes.Repeat([]byte{0x66}, 32),
|
||||
DepositCount: 0,
|
||||
BlockHash: bytes.Repeat([]byte{0x77}, 32),
|
||||
}
|
||||
|
||||
vals := []*ethpb.Validator{
|
||||
{
|
||||
PublicKey: pk,
|
||||
WithdrawalCredentials: withdrawalCreds,
|
||||
EffectiveBalance: cfg.MinActivationBalance + 1_000,
|
||||
},
|
||||
}
|
||||
balances := []uint64{cfg.MinActivationBalance + 1_000}
|
||||
|
||||
payments := make([]*ethpb.BuilderPendingPayment, cfg.SlotsPerEpoch*2)
|
||||
for i := range payments {
|
||||
payments[i] = ðpb.BuilderPendingPayment{
|
||||
Withdrawal: ðpb.BuilderPendingWithdrawal{
|
||||
FeeRecipient: make([]byte, 20),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
executionPayloadAvailability := make([]byte, cfg.SlotsPerHistoricalRoot/8)
|
||||
|
||||
genesisTime := uint64(0)
|
||||
slotSeconds := cfg.SecondsPerSlot * uint64(slot)
|
||||
if payload.Timestamp > slotSeconds {
|
||||
genesisTime = payload.Timestamp - slotSeconds
|
||||
}
|
||||
|
||||
stProto := ðpb.BeaconStateGloas{
|
||||
Slot: slot,
|
||||
GenesisTime: genesisTime,
|
||||
GenesisValidatorsRoot: genesisRoot,
|
||||
Fork: ðpb.Fork{
|
||||
CurrentVersion: bytes.Repeat([]byte{0x01}, 4),
|
||||
PreviousVersion: bytes.Repeat([]byte{0x01}, 4),
|
||||
Epoch: 0,
|
||||
},
|
||||
LatestBlockHeader: header,
|
||||
BlockRoots: blockRoots,
|
||||
StateRoots: stateRoots,
|
||||
RandaoMixes: randaoMixes,
|
||||
Eth1Data: eth1Data,
|
||||
Validators: vals,
|
||||
Balances: balances,
|
||||
LatestBlockHash: payload.ParentHash,
|
||||
LatestWithdrawalsRoot: withdrawalsRoot[:],
|
||||
LatestExecutionPayloadBid: bid,
|
||||
BuilderPendingPayments: payments,
|
||||
ExecutionPayloadAvailability: executionPayloadAvailability,
|
||||
BuilderPendingWithdrawals: []*ethpb.BuilderPendingWithdrawal{},
|
||||
}
|
||||
|
||||
st, err := state_native.InitializeFromProtoGloas(stProto)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := st.Copy()
|
||||
ctx := context.Background()
|
||||
require.NoError(t, processExecutionRequests(ctx, expected, envelope.ExecutionRequests))
|
||||
require.NoError(t, queueBuilderPayment(ctx, expected))
|
||||
require.NoError(t, expected.SetExecutionPayloadAvailability(slot, true))
|
||||
var blockHashArr [32]byte
|
||||
copy(blockHashArr[:], payload.BlockHash)
|
||||
require.NoError(t, expected.SetLatestBlockHash(blockHashArr))
|
||||
expectedRoot, err := expected.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
envelope.StateRoot = expectedRoot[:]
|
||||
|
||||
epoch := slots.ToEpoch(slot)
|
||||
domain, err := signing.Domain(st.Fork(), epoch, cfg.DomainBeaconBuilder, st.GenesisValidatorsRoot())
|
||||
require.NoError(t, err)
|
||||
signingRoot, err := signing.ComputeSigningRoot(envelope, domain)
|
||||
require.NoError(t, err)
|
||||
signature := sk.Sign(signingRoot[:]).Marshal()
|
||||
|
||||
signedProto := ðpb.SignedExecutionPayloadEnvelope{
|
||||
Message: envelope,
|
||||
Signature: signature,
|
||||
}
|
||||
signed, err := blocks.WrappedROSignedExecutionPayloadEnvelope(signedProto)
|
||||
require.NoError(t, err)
|
||||
|
||||
return payloadFixture{
|
||||
state: st,
|
||||
signed: signed,
|
||||
signedProto: signedProto,
|
||||
envelope: envelope,
|
||||
payload: payload,
|
||||
slot: slot,
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessExecutionPayload_Success(t *testing.T) {
|
||||
fixture := buildPayloadFixture(t, nil)
|
||||
require.NoError(t, ProcessExecutionPayload(t.Context(), fixture.state, fixture.signed))
|
||||
|
||||
latestHash, err := fixture.state.LatestBlockHash()
|
||||
require.NoError(t, err)
|
||||
var expectedHash [32]byte
|
||||
copy(expectedHash[:], fixture.payload.BlockHash)
|
||||
require.Equal(t, expectedHash, latestHash)
|
||||
|
||||
payment, err := fixture.state.BuilderPendingPayment(fixture.slot)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, payment)
|
||||
require.Equal(t, primitives.Gwei(0), payment.Withdrawal.Amount)
|
||||
}
|
||||
|
||||
func TestProcessExecutionPayload_PrevRandaoMismatch(t *testing.T) {
|
||||
fixture := buildPayloadFixture(t, func(_ *enginev1.ExecutionPayloadDeneb, bid *ethpb.ExecutionPayloadBid, _ *ethpb.ExecutionPayloadEnvelope) {
|
||||
bid.PrevRandao = bytes.Repeat([]byte{0xFF}, 32)
|
||||
})
|
||||
|
||||
err := ProcessExecutionPayload(t.Context(), fixture.state, fixture.signed)
|
||||
require.ErrorContains(t, "prev randao", err)
|
||||
}
|
||||
|
||||
func TestQueueBuilderPayment_ZeroAmountClearsSlot(t *testing.T) {
|
||||
fixture := buildPayloadFixture(t, nil)
|
||||
|
||||
require.NoError(t, queueBuilderPayment(t.Context(), fixture.state))
|
||||
|
||||
payment, err := fixture.state.BuilderPendingPayment(fixture.slot)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, payment)
|
||||
require.Equal(t, primitives.Gwei(0), payment.Withdrawal.Amount)
|
||||
}
|
||||
|
||||
func TestVerifyExecutionPayloadEnvelopeSignature_InvalidSig(t *testing.T) {
|
||||
fixture := buildPayloadFixture(t, nil)
|
||||
|
||||
signedProto := ðpb.SignedExecutionPayloadEnvelope{
|
||||
Message: fixture.signedProto.Message,
|
||||
Signature: bytes.Repeat([]byte{0xFF}, 96),
|
||||
}
|
||||
badSigned, err := blocks.WrappedROSignedExecutionPayloadEnvelope(signedProto)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = verifyExecutionPayloadEnvelopeSignature(fixture.state, badSigned)
|
||||
require.ErrorContains(t, "invalid signature format", err)
|
||||
}
|
||||
Reference in New Issue
Block a user