diff --git a/.circleci/config.yml b/.circleci/config.yml index 7e064fa..12efccf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,7 +21,7 @@ jobs: - restore_cache: keys: - - spec-tests-v1_5_0_alpha10 + - spec-tests-v1_5_0_beta2 - run: name: Update spec tests cache @@ -32,7 +32,7 @@ jobs: command: TEST_OUT_DIR=$TEST_RESULTS make test - save_cache: - key: spec-tests-v1_5_0_alpha10 + key: spec-tests-v1_5_0_beta2 paths: - tests/spec/eth2.0-spec-tests diff --git a/Makefile b/Makefile index 563bbfd..18005dc 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ clean: create-test-dir: mkdir -p $(TEST_OUT_DIR) -SPEC_VERSION ?= v1.5.0-alpha.10 +SPEC_VERSION ?= v1.5.0-beta.2 clear-tests: rm -rf tests/spec/eth2.0-spec-tests diff --git a/eth2/beacon/common/indices.go b/eth2/beacon/common/indices.go index cf811b3..f5c7832 100644 --- a/eth2/beacon/common/indices.go +++ b/eth2/beacon/common/indices.go @@ -39,3 +39,37 @@ func (p CommitteeIndices) HashTreeRoot(spec *Spec, hFn tree.HashFn) Root { func (c *Phase0Preset) CommitteeIndices() ListTypeDef { return ListType(ValidatorIndexType, uint64(c.MAX_VALIDATORS_PER_COMMITTEE)) } + +type SlotCommitteeIndices []ValidatorIndex + +func (p *SlotCommitteeIndices) Deserialize(spec *Spec, dr *codec.DecodingReader) error { + return dr.List(func() codec.Deserializable { + i := len(*p) + *p = append(*p, ValidatorIndex(0)) + return &((*p)[i]) + }, ValidatorIndexType.TypeByteLength(), uint64(spec.MAX_VALIDATORS_PER_COMMITTEE*spec.MAX_COMMITTEES_PER_SLOT)) +} + +func (a SlotCommitteeIndices) Serialize(_ *Spec, w *codec.EncodingWriter) error { + return w.List(func(i uint64) codec.Serializable { + return a[i] + }, ValidatorIndexType.TypeByteLength(), uint64(len(a))) +} + +func (a SlotCommitteeIndices) ByteLength(*Spec) uint64 { + return ValidatorIndexType.TypeByteLength() * uint64(len(a)) +} + +func (*SlotCommitteeIndices) FixedLength(*Spec) uint64 { + return 0 +} + +func (p SlotCommitteeIndices) HashTreeRoot(spec *Spec, hFn tree.HashFn) Root { + return hFn.Uint64ListHTR(func(i uint64) uint64 { + return uint64(p[i]) + }, uint64(len(p)), uint64(spec.MAX_VALIDATORS_PER_COMMITTEE*spec.MAX_COMMITTEES_PER_SLOT)) +} + +func SlotCommitteeIndicesType(c *Spec) ListTypeDef { + return ListType(ValidatorIndexType, uint64(c.MAX_VALIDATORS_PER_COMMITTEE*c.MAX_COMMITTEES_PER_SLOT)) +} diff --git a/eth2/beacon/common/requests.go b/eth2/beacon/common/requests.go new file mode 100644 index 0000000..80fc0e6 --- /dev/null +++ b/eth2/beacon/common/requests.go @@ -0,0 +1,221 @@ +package common + +import ( + "github.com/protolambda/ztyp/codec" + "github.com/protolambda/ztyp/tree" + . "github.com/protolambda/ztyp/view" +) + +type DepositRequest struct { + Pubkey BLSPubkey `json:"pubkey" yaml:"pubkey"` + WithdrawalCredentials Bytes32 `json:"withdrawal_credentials" yaml:"withdrawal_credentials"` + Amount Gwei `json:"amount" yaml:"amount"` + Signature BLSSignature `json:"signature" yaml:"signature"` + Index Uint64View `json:"index" yaml:"index"` +} + +var DepositRequestType = ContainerType("DepositRequest", []FieldDef{ + {"pubkey", BLSPubkeyType}, + {"withdrawal_credentials", Bytes32Type}, + {"amount", GweiType}, + {"signature", BLSSignatureType}, + {"index", Uint64Type}, +}) + +func (a *DepositRequest) Deserialize(dr *codec.DecodingReader) error { + return dr.FixedLenContainer(&a.Pubkey, &a.WithdrawalCredentials, &a.Amount, &a.Signature, &a.Index) +} + +func (a *DepositRequest) Serialize(w *codec.EncodingWriter) error { + return w.FixedLenContainer(&a.Pubkey, &a.WithdrawalCredentials, &a.Amount, &a.Signature, &a.Index) +} + +func (a *DepositRequest) ByteLength() uint64 { + return codec.ContainerLength(&a.Pubkey, &a.WithdrawalCredentials, &a.Amount, &a.Signature, &a.Index) +} + +func (a *DepositRequest) FixedLength() uint64 { + return codec.ContainerLength(&a.Pubkey, &a.WithdrawalCredentials, &a.Amount, &a.Signature, &a.Index) +} + +func (a *DepositRequest) HashTreeRoot(hFn tree.HashFn) Root { + return hFn.HashTreeRoot(&a.Pubkey, &a.WithdrawalCredentials, &a.Amount, &a.Signature, &a.Index) +} + +type WithdrawalRequest struct { + SourceAddress Eth1Address `json:"source_address" yaml:"source_address"` + ValidatorPubkey BLSPubkey `json:"validator_pubkey" yaml:"validator_pubkey"` + Amount Gwei `json:"amount" yaml:"amount"` +} + +var WithdrawalRequestType = ContainerType("WithdrawalRequest", []FieldDef{ + {"source_address", Eth1AddressType}, + {"validator_pubkey", BLSPubkeyType}, + {"amount", GweiType}, +}) + +func (a *WithdrawalRequest) Deserialize(dr *codec.DecodingReader) error { + return dr.FixedLenContainer(&a.SourceAddress, &a.ValidatorPubkey, &a.Amount) +} + +func (a *WithdrawalRequest) Serialize(w *codec.EncodingWriter) error { + return w.FixedLenContainer(&a.SourceAddress, &a.ValidatorPubkey, &a.Amount) +} + +func (a *WithdrawalRequest) ByteLength() uint64 { + return codec.ContainerLength(&a.SourceAddress, &a.ValidatorPubkey, &a.Amount) +} + +func (a *WithdrawalRequest) FixedLength() uint64 { + return codec.ContainerLength(&a.SourceAddress, &a.ValidatorPubkey, &a.Amount) +} + +func (a *WithdrawalRequest) HashTreeRoot(hFn tree.HashFn) Root { + return hFn.HashTreeRoot(&a.SourceAddress, &a.ValidatorPubkey, &a.Amount) +} + +type ConsolidationRequest struct { + SourceAddress Eth1Address `json:"source_address" yaml:"source_address"` + SourcePubkey BLSPubkey `json:"source_pubkey" yaml:"source_pubkey"` + TargetPubkey BLSPubkey `json:"target_pubkey" yaml:"target_pubkey"` +} + +var ConsolidationRequestType = ContainerType("ConsolidationRequest", []FieldDef{ + {"source_address", Eth1AddressType}, + {"source_pubkey", BLSPubkeyType}, + {"target_pubkey", BLSPubkeyType}, +}) + +func (a *ConsolidationRequest) Deserialize(dr *codec.DecodingReader) error { + return dr.FixedLenContainer(&a.SourceAddress, &a.SourcePubkey, &a.TargetPubkey) +} + +func (a *ConsolidationRequest) Serialize(w *codec.EncodingWriter) error { + return w.FixedLenContainer(&a.SourceAddress, &a.SourcePubkey, &a.TargetPubkey) +} + +func (a *ConsolidationRequest) ByteLength() uint64 { + return codec.ContainerLength(&a.SourceAddress, &a.SourcePubkey, &a.TargetPubkey) +} + +func (a *ConsolidationRequest) FixedLength() uint64 { + return codec.ContainerLength(&a.SourceAddress, &a.SourcePubkey, &a.TargetPubkey) +} + +func (a *ConsolidationRequest) HashTreeRoot(hFn tree.HashFn) Root { + return hFn.HashTreeRoot(&a.SourceAddress, &a.SourcePubkey, &a.TargetPubkey) +} + +type DepositRequests []DepositRequest + +func DepositRequestsType(spec *Spec) ListTypeDef { + return ListType(DepositRequestType, uint64(spec.MAX_DEPOSIT_REQUESTS_PER_PAYLOAD)) +} + +func (li *DepositRequests) Deserialize(spec *Spec, dr *codec.DecodingReader) error { + return dr.List(func() codec.Deserializable { + i := len(*li) + *li = append(*li, DepositRequest{}) + return &((*li)[i]) + }, DepositRequestType.TypeByteLength(), uint64(spec.MAX_DEPOSIT_REQUESTS_PER_PAYLOAD)) +} + +func (li DepositRequests) Serialize(_ *Spec, w *codec.EncodingWriter) error { + return w.List(func(i uint64) codec.Serializable { + return &li[i] + }, DepositRequestType.TypeByteLength(), uint64(len(li))) +} + +func (li DepositRequests) ByteLength(_ *Spec) (out uint64) { + return DepositRequestType.TypeByteLength() * uint64(len(li)) +} + +func (*DepositRequests) FixedLength(*Spec) uint64 { + return 0 +} + +func (li DepositRequests) HashTreeRoot(spec *Spec, hFn tree.HashFn) Root { + length := uint64(len(li)) + return hFn.ComplexListHTR(func(i uint64) tree.HTR { + if i < length { + return &li[i] + } + return nil + }, length, uint64(spec.MAX_DEPOSIT_REQUESTS_PER_PAYLOAD)) +} + +type WithdrawalRequests []WithdrawalRequest + +func WithdrawalRequestsType(spec *Spec) ListTypeDef { + return ListType(WithdrawalRequestType, uint64(spec.MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD)) +} + +func (li *WithdrawalRequests) Deserialize(spec *Spec, dr *codec.DecodingReader) error { + return dr.List(func() codec.Deserializable { + i := len(*li) + *li = append(*li, WithdrawalRequest{}) + return &((*li)[i]) + }, WithdrawalRequestType.TypeByteLength(), uint64(spec.MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD)) +} + +func (li WithdrawalRequests) Serialize(_ *Spec, w *codec.EncodingWriter) error { + return w.List(func(i uint64) codec.Serializable { + return &li[i] + }, WithdrawalRequestType.TypeByteLength(), uint64(len(li))) +} + +func (li WithdrawalRequests) ByteLength(_ *Spec) (out uint64) { + return WithdrawalRequestType.TypeByteLength() * uint64(len(li)) +} + +func (*WithdrawalRequests) FixedLength(*Spec) uint64 { + return 0 +} + +func (li WithdrawalRequests) HashTreeRoot(spec *Spec, hFn tree.HashFn) Root { + length := uint64(len(li)) + return hFn.ComplexListHTR(func(i uint64) tree.HTR { + if i < length { + return &li[i] + } + return nil + }, length, uint64(spec.MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD)) +} + +type ConsolidationRequests []ConsolidationRequest + +func ConsolidationRequestsType(spec *Spec) ListTypeDef { + return ListType(ConsolidationRequestType, uint64(spec.MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD)) +} + +func (li *ConsolidationRequests) Deserialize(spec *Spec, dr *codec.DecodingReader) error { + return dr.List(func() codec.Deserializable { + i := len(*li) + *li = append(*li, ConsolidationRequest{}) + return &((*li)[i]) + }, ConsolidationRequestType.TypeByteLength(), uint64(spec.MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD)) +} + +func (li ConsolidationRequests) Serialize(_ *Spec, w *codec.EncodingWriter) error { + return w.List(func(i uint64) codec.Serializable { + return &li[i] + }, ConsolidationRequestType.TypeByteLength(), uint64(len(li))) +} + +func (li ConsolidationRequests) ByteLength(_ *Spec) (out uint64) { + return ConsolidationRequestType.TypeByteLength() * uint64(len(li)) +} + +func (*ConsolidationRequests) FixedLength(*Spec) uint64 { + return 0 +} + +func (li ConsolidationRequests) HashTreeRoot(spec *Spec, hFn tree.HashFn) Root { + length := uint64(len(li)) + return hFn.ComplexListHTR(func(i uint64) tree.HTR { + if i < length { + return &li[i] + } + return nil + }, length, uint64(spec.MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD)) +} diff --git a/eth2/beacon/common/requests_pending.go b/eth2/beacon/common/requests_pending.go new file mode 100644 index 0000000..1e46f0a --- /dev/null +++ b/eth2/beacon/common/requests_pending.go @@ -0,0 +1,219 @@ +package common + +import ( + "github.com/protolambda/ztyp/codec" + "github.com/protolambda/ztyp/tree" + . "github.com/protolambda/ztyp/view" +) + +type PendingDeposit struct { + Pubkey BLSPubkey `json:"pubkey" yaml:"pubkey"` + WithdrawalCredentials Bytes32 `json:"withdrawal_credentials" yaml:"withdrawal_credentials"` + Amount Gwei `json:"amount" yaml:"amount"` + Signature BLSSignature `json:"signature" yaml:"signature"` + Slot Slot `json:"slot" yaml:"slot"` +} + +var PendingDepositType = ContainerType("PendingDeposit", []FieldDef{ + {"pubkey", BLSPubkeyType}, + {"withdrawal_credentials", Bytes32Type}, + {"amount", GweiType}, + {"signature", BLSSignatureType}, + {"slot", SlotType}, +}) + +func (a *PendingDeposit) Deserialize(dr *codec.DecodingReader) error { + return dr.FixedLenContainer(&a.Pubkey, &a.WithdrawalCredentials, &a.Amount, &a.Signature, &a.Slot) +} + +func (a *PendingDeposit) Serialize(w *codec.EncodingWriter) error { + return w.FixedLenContainer(&a.Pubkey, &a.WithdrawalCredentials, &a.Amount, &a.Signature, &a.Slot) +} + +func (a *PendingDeposit) ByteLength() uint64 { + return codec.ContainerLength(&a.Pubkey, &a.WithdrawalCredentials, &a.Amount, &a.Signature, &a.Slot) +} + +func (a *PendingDeposit) FixedLength() uint64 { + return codec.ContainerLength(&a.Pubkey, &a.WithdrawalCredentials, &a.Amount, &a.Signature, &a.Slot) +} + +func (a *PendingDeposit) HashTreeRoot(hFn tree.HashFn) Root { + return hFn.HashTreeRoot(&a.Pubkey, &a.WithdrawalCredentials, &a.Amount, &a.Signature, &a.Slot) +} + +type PendingPartialWithdrawal struct { + ValidatorIndex ValidatorIndex `json:"validator_index" yaml:"validator_index"` + Amount Gwei `json:"amount" yaml:"amount"` + WithdrawableEpoch Epoch `json:"withdrawable_epoch" yaml:"withdrawable_epoch"` +} + +var PendingPartialWithdrawalType = ContainerType("PendingPartialWithdrawal", []FieldDef{ + {"validator_index", ValidatorIndexType}, + {"amount", GweiType}, + {"withdrawable_epoch", EpochType}, +}) + +func (a *PendingPartialWithdrawal) Deserialize(dr *codec.DecodingReader) error { + return dr.FixedLenContainer(&a.ValidatorIndex, &a.Amount, &a.WithdrawableEpoch) +} + +func (a *PendingPartialWithdrawal) Serialize(w *codec.EncodingWriter) error { + return w.FixedLenContainer(&a.ValidatorIndex, &a.Amount, &a.WithdrawableEpoch) +} + +func (a *PendingPartialWithdrawal) ByteLength() uint64 { + return codec.ContainerLength(&a.ValidatorIndex, &a.Amount, &a.WithdrawableEpoch) +} + +func (a *PendingPartialWithdrawal) FixedLength() uint64 { + return codec.ContainerLength(&a.ValidatorIndex, &a.Amount, &a.WithdrawableEpoch) +} + +func (a *PendingPartialWithdrawal) HashTreeRoot(hFn tree.HashFn) Root { + return hFn.HashTreeRoot(&a.ValidatorIndex, &a.Amount, &a.WithdrawableEpoch) +} + +type PendingConsolidation struct { + SourceIndex ValidatorIndex `json:"source_index" yaml:"source_index"` + TargetIndex ValidatorIndex `json:"target_index" yaml:"target_index"` +} + +var PendingConsolidationType = ContainerType("PendingConsolidation", []FieldDef{ + {"source_index", ValidatorIndexType}, + {"target_index", ValidatorIndexType}, +}) + +func (a *PendingConsolidation) Deserialize(dr *codec.DecodingReader) error { + return dr.FixedLenContainer(&a.SourceIndex, &a.TargetIndex) +} + +func (a *PendingConsolidation) Serialize(w *codec.EncodingWriter) error { + return w.FixedLenContainer(&a.SourceIndex, &a.TargetIndex) +} + +func (a *PendingConsolidation) ByteLength() uint64 { + return codec.ContainerLength(&a.SourceIndex, &a.TargetIndex) +} + +func (a *PendingConsolidation) FixedLength() uint64 { + return codec.ContainerLength(&a.SourceIndex, &a.TargetIndex) +} + +func (a *PendingConsolidation) HashTreeRoot(hFn tree.HashFn) Root { + return hFn.HashTreeRoot(&a.SourceIndex, &a.TargetIndex) +} + +type PendingDeposits []PendingDeposit + +func PendingDepositsType(spec *Spec) ListTypeDef { + return ListType(PendingDepositType, uint64(spec.PENDING_DEPOSITS_LIMIT)) +} + +func (li *PendingDeposits) Deserialize(spec *Spec, dr *codec.DecodingReader) error { + return dr.List(func() codec.Deserializable { + i := len(*li) + *li = append(*li, PendingDeposit{}) + return &((*li)[i]) + }, PendingDepositType.TypeByteLength(), uint64(spec.PENDING_DEPOSITS_LIMIT)) +} + +func (li PendingDeposits) Serialize(_ *Spec, w *codec.EncodingWriter) error { + return w.List(func(i uint64) codec.Serializable { + return &li[i] + }, PendingDepositType.TypeByteLength(), uint64(len(li))) +} + +func (li PendingDeposits) ByteLength(_ *Spec) (out uint64) { + return PendingDepositType.TypeByteLength() * uint64(len(li)) +} + +func (*PendingDeposits) FixedLength(*Spec) uint64 { + return 0 +} + +func (li PendingDeposits) HashTreeRoot(spec *Spec, hFn tree.HashFn) Root { + length := uint64(len(li)) + return hFn.ComplexListHTR(func(i uint64) tree.HTR { + if i < length { + return &li[i] + } + return nil + }, length, uint64(spec.PENDING_DEPOSITS_LIMIT)) +} + +type PendingPartialWithdrawals []PendingPartialWithdrawal + +func PendingPartialWithdrawalsType(spec *Spec) ListTypeDef { + return ListType(PendingPartialWithdrawalType, uint64(spec.PENDING_PARTIAL_WITHDRAWALS_LIMIT)) +} + +func (li *PendingPartialWithdrawals) Deserialize(spec *Spec, dr *codec.DecodingReader) error { + return dr.List(func() codec.Deserializable { + i := len(*li) + *li = append(*li, PendingPartialWithdrawal{}) + return &((*li)[i]) + }, PendingPartialWithdrawalType.TypeByteLength(), uint64(spec.PENDING_PARTIAL_WITHDRAWALS_LIMIT)) +} + +func (li PendingPartialWithdrawals) Serialize(_ *Spec, w *codec.EncodingWriter) error { + return w.List(func(i uint64) codec.Serializable { + return &li[i] + }, PendingPartialWithdrawalType.TypeByteLength(), uint64(len(li))) +} + +func (li PendingPartialWithdrawals) ByteLength(_ *Spec) (out uint64) { + return PendingPartialWithdrawalType.TypeByteLength() * uint64(len(li)) +} + +func (*PendingPartialWithdrawals) FixedLength(*Spec) uint64 { + return 0 +} + +func (li PendingPartialWithdrawals) HashTreeRoot(spec *Spec, hFn tree.HashFn) Root { + length := uint64(len(li)) + return hFn.ComplexListHTR(func(i uint64) tree.HTR { + if i < length { + return &li[i] + } + return nil + }, length, uint64(spec.PENDING_PARTIAL_WITHDRAWALS_LIMIT)) +} + +type PendingConsolidations []PendingConsolidation + +func PendingConsolidationsType(spec *Spec) ListTypeDef { + return ListType(PendingConsolidationType, uint64(spec.PENDING_CONSOLIDATIONS_LIMIT)) +} + +func (li *PendingConsolidations) Deserialize(spec *Spec, dr *codec.DecodingReader) error { + return dr.List(func() codec.Deserializable { + i := len(*li) + *li = append(*li, PendingConsolidation{}) + return &((*li)[i]) + }, PendingConsolidationType.TypeByteLength(), uint64(spec.PENDING_CONSOLIDATIONS_LIMIT)) +} + +func (li PendingConsolidations) Serialize(_ *Spec, w *codec.EncodingWriter) error { + return w.List(func(i uint64) codec.Serializable { + return &li[i] + }, PendingConsolidationType.TypeByteLength(), uint64(len(li))) +} + +func (li PendingConsolidations) ByteLength(_ *Spec) (out uint64) { + return PendingConsolidationType.TypeByteLength() * uint64(len(li)) +} + +func (*PendingConsolidations) FixedLength(*Spec) uint64 { + return 0 +} + +func (li PendingConsolidations) HashTreeRoot(spec *Spec, hFn tree.HashFn) Root { + length := uint64(len(li)) + return hFn.ComplexListHTR(func(i uint64) tree.HTR { + if i < length { + return &li[i] + } + return nil + }, length, uint64(spec.PENDING_CONSOLIDATIONS_LIMIT)) +} diff --git a/eth2/beacon/common/spec.go b/eth2/beacon/common/spec.go index db0f297..dba6510 100644 --- a/eth2/beacon/common/spec.go +++ b/eth2/beacon/common/spec.go @@ -122,6 +122,36 @@ type DenebPreset struct { KZG_COMMITMENT_INCLUSION_PROOF_DEPTH Uint64View `yaml:"KZG_COMMITMENT_INCLUSION_PROOF_DEPTH" json:"KZG_COMMITMENT_INCLUSION_PROOF_DEPTH"` } +type ElectraPreset struct { + // Gwei values + MIN_ACTIVATION_BALANCE Gwei `yaml:"MIN_ACTIVATION_BALANCE" json:"MIN_ACTIVATION_BALANCE"` + MAX_EFFECTIVE_BALANCE_ELECTRA Gwei `yaml:"MAX_EFFECTIVE_BALANCE_ELECTRA" json:"MAX_EFFECTIVE_BALANCE_ELECTRA"` + + // State list lengths + PENDING_DEPOSITS_LIMIT Uint64View `yaml:"PENDING_DEPOSITS_LIMIT" json:"PENDING_DEPOSITS_LIMIT"` + PENDING_PARTIAL_WITHDRAWALS_LIMIT Uint64View `yaml:"PENDING_PARTIAL_WITHDRAWALS_LIMIT" json:"PENDING_PARTIAL_WITHDRAWALS_LIMIT"` + PENDING_CONSOLIDATIONS_LIMIT Uint64View `yaml:"PENDING_CONSOLIDATIONS_LIMIT" json:"PENDING_CONSOLIDATIONS_LIMIT"` + + // Reward and penalty quotients + MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA Uint64View `yaml:"MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA" json:"MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA"` + WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA Uint64View `yaml:"WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA" json:"WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA"` + + // Max operations per block + MAX_ATTESTER_SLASHINGS_ELECTRA Uint64View `yaml:"MAX_ATTESTER_SLASHINGS_ELECTRA" json:"MAX_ATTESTER_SLASHINGS_ELECTRA"` + MAX_ATTESTATIONS_ELECTRA Uint64View `yaml:"MAX_ATTESTATIONS_ELECTRA" json:"MAX_ATTESTATIONS_ELECTRA"` + MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD Uint64View `yaml:"MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD" json:"MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD"` + + // Execution + MAX_DEPOSIT_REQUESTS_PER_PAYLOAD Uint64View `yaml:"MAX_DEPOSIT_REQUESTS_PER_PAYLOAD" json:"MAX_DEPOSIT_REQUESTS_PER_PAYLOAD"` + MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD Uint64View `yaml:"MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD" json:"MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD"` + + // Withdrawals processing + MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP Uint64View `yaml:"MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP" json:"MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP"` + + // Pending deposits processing + MAX_PENDING_DEPOSITS_PER_EPOCH Uint64View `yaml:"MAX_PENDING_DEPOSITS_PER_EPOCH" json:"MAX_PENDING_DEPOSITS_PER_EPOCH"` +} + type Config struct { PRESET_BASE string `yaml:"PRESET_BASE" json:"PRESET_BASE"` @@ -154,8 +184,6 @@ type Config struct { DENEB_FORK_VERSION Version `yaml:"DENEB_FORK_VERSION" json:"DENEB_FORK_VERSION"` DENEB_FORK_EPOCH Epoch `yaml:"DENEB_FORK_EPOCH" json:"DENEB_FORK_EPOCH"` - MAX_BLOBS_PER_BLOCK Uint64View `yaml:"MAX_BLOBS_PER_BLOCK" json:"MAX_BLOBS_PER_BLOCK"` - // Electra ELECTRA_FORK_VERSION Version `yaml:"ELECTRA_FORK_VERSION" json:"ELECTRA_FORK_VERSION"` ELECTRA_FORK_EPOCH Epoch `yaml:"ELECTRA_FORK_EPOCH" json:"ELECTRA_FORK_EPOCH"` @@ -164,9 +192,12 @@ type Config struct { FULU_FORK_VERSION Version `yaml:"FULU_FORK_VERSION" json:"FULU_FORK_VERSION"` FULU_FORK_EPOCH Epoch `yaml:"FULU_FORK_EPOCH" json:"FULU_FORK_EPOCH"` - // WHISK - WHISK_FORK_VERSION Version `yaml:"WHISK_FORK_VERSION" json:"WHISK_FORK_VERSION"` - WHISK_FORK_EPOCH Epoch `yaml:"WHISK_FORK_EPOCH" json:"WHISK_FORK_EPOCH"` + // EIP7441 + EIP7441_FORK_VERSION Version `yaml:"EIP7441_FORK_VERSION" json:"EIP7441_FORK_VERSION"` + EIP7441_FORK_EPOCH Epoch `yaml:"EIP7441_FORK_EPOCH" json:"EIP7441_FORK_EPOCH"` + // EIP7732 + EIP7732_FORK_VERSION Version `yaml:"EIP7732_FORK_VERSION" json:"EIP7732_FORK_VERSION"` + EIP7732_FORK_EPOCH Epoch `yaml:"EIP7732_FORK_EPOCH" json:"EIP7732_FORK_EPOCH"` // Time parameters SECONDS_PER_SLOT Timestamp `yaml:"SECONDS_PER_SLOT" json:"SECONDS_PER_SLOT"` @@ -176,12 +207,11 @@ type Config struct { ETH1_FOLLOW_DISTANCE Uint64View `yaml:"ETH1_FOLLOW_DISTANCE" json:"ETH1_FOLLOW_DISTANCE"` // Validator cycle - INACTIVITY_SCORE_BIAS Uint64View `yaml:"INACTIVITY_SCORE_BIAS" json:"INACTIVITY_SCORE_BIAS"` - INACTIVITY_SCORE_RECOVERY_RATE Uint64View `yaml:"INACTIVITY_SCORE_RECOVERY_RATE" json:"INACTIVITY_SCORE_RECOVERY_RATE"` - EJECTION_BALANCE Gwei `yaml:"EJECTION_BALANCE" json:"EJECTION_BALANCE"` - MIN_PER_EPOCH_CHURN_LIMIT Uint64View `yaml:"MIN_PER_EPOCH_CHURN_LIMIT" json:"MIN_PER_EPOCH_CHURN_LIMIT"` - CHURN_LIMIT_QUOTIENT Uint64View `yaml:"CHURN_LIMIT_QUOTIENT" json:"CHURN_LIMIT_QUOTIENT"` - // New in Deneb:EIP7514 + INACTIVITY_SCORE_BIAS Uint64View `yaml:"INACTIVITY_SCORE_BIAS" json:"INACTIVITY_SCORE_BIAS"` + INACTIVITY_SCORE_RECOVERY_RATE Uint64View `yaml:"INACTIVITY_SCORE_RECOVERY_RATE" json:"INACTIVITY_SCORE_RECOVERY_RATE"` + EJECTION_BALANCE Gwei `yaml:"EJECTION_BALANCE" json:"EJECTION_BALANCE"` + MIN_PER_EPOCH_CHURN_LIMIT Uint64View `yaml:"MIN_PER_EPOCH_CHURN_LIMIT" json:"MIN_PER_EPOCH_CHURN_LIMIT"` + CHURN_LIMIT_QUOTIENT Uint64View `yaml:"CHURN_LIMIT_QUOTIENT" json:"CHURN_LIMIT_QUOTIENT"` MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT Uint64View `yaml:"MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT" json:"MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT"` // Fork choice @@ -196,11 +226,10 @@ type Config struct { DEPOSIT_CONTRACT_ADDRESS Eth1Address `yaml:"DEPOSIT_CONTRACT_ADDRESS" json:"DEPOSIT_CONTRACT_ADDRESS"` // Networking - GOSSIP_MAX_SIZE Uint64View `yaml:"GOSSIP_MAX_SIZE" json:"GOSSIP_MAX_SIZE"` + MAX_PAYLOAD_SIZE Uint64View `yaml:"MAX_PAYLOAD_SIZE" json:"MAX_PAYLOAD_SIZE"` MAX_REQUEST_BLOCKS Uint64View `yaml:"MAX_REQUEST_BLOCKS" json:"MAX_REQUEST_BLOCKS"` EPOCHS_PER_SUBNET_SUBSCRIPTION Uint64View `yaml:"EPOCHS_PER_SUBNET_SUBSCRIPTION" json:"EPOCHS_PER_SUBNET_SUBSCRIPTION"` MIN_EPOCHS_FOR_BLOCK_REQUESTS Uint64View `yaml:"MIN_EPOCHS_FOR_BLOCK_REQUESTS" json:"MIN_EPOCHS_FOR_BLOCK_REQUESTS"` - MAX_CHUNK_SIZE Uint64View `yaml:"MAX_CHUNK_SIZE" json:"MAX_CHUNK_SIZE"` TTFB_TIMEOUT Uint64View `yaml:"TTFB_TIMEOUT" json:"TTFB_TIMEOUT"` RESP_TIMEOUT Uint64View `yaml:"RESP_TIMEOUT" json:"RESP_TIMEOUT"` ATTESTATION_PROPAGATION_SLOT_RANGE Uint64View `yaml:"ATTESTATION_PROPAGATION_SLOT_RANGE" json:"ATTESTATION_PROPAGATION_SLOT_RANGE"` @@ -214,13 +243,36 @@ type Config struct { // Deneb MAX_REQUEST_BLOCKS_DENEB Uint64View `yaml:"MAX_REQUEST_BLOCKS_DENEB" json:"MAX_REQUEST_BLOCKS_DENEB"` - MAX_REQUEST_BLOB_SIDECARS Uint64View `yaml:"MAX_REQUEST_BLOB_SIDECARS" json:"MAX_REQUEST_BLOB_SIDECARS"` MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS Uint64View `yaml:"MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS" json:"MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS"` BLOB_SIDECAR_SUBNET_COUNT Uint64View `yaml:"BLOB_SIDECAR_SUBNET_COUNT" json:"BLOB_SIDECAR_SUBNET_COUNT"` + MAX_BLOBS_PER_BLOCK Uint64View `yaml:"MAX_BLOBS_PER_BLOCK" json:"MAX_BLOBS_PER_BLOCK"` + MAX_REQUEST_BLOB_SIDECARS Uint64View `yaml:"MAX_REQUEST_BLOB_SIDECARS" json:"MAX_REQUEST_BLOB_SIDECARS"` - // Whish - WHISK_EPOCHS_PER_SHUFFLING_PHASE Uint64View `yaml:"WHISK_EPOCHS_PER_SHUFFLING_PHASE" json:"WHISK_EPOCHS_PER_SHUFFLING_PHASE"` - WHISK_PROPOSER_SELECTION_GAP Uint64View `yaml:"WHISK_PROPOSER_SELECTION_GAP" json:"WHISK_PROPOSER_SELECTION_GAP"` + // Electra + MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA Uint64View `yaml:"MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA" json:"MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA"` + MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT Uint64View `yaml:"MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT" json:"MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT"` + BLOB_SIDECAR_SUBNET_COUNT_ELECTRA Uint64View `yaml:"BLOB_SIDECAR_SUBNET_COUNT_ELECTRA" json:"BLOB_SIDECAR_SUBNET_COUNT_ELECTRA"` + MAX_BLOBS_PER_BLOCK_ELECTRA Uint64View `yaml:"MAX_BLOBS_PER_BLOCK_ELECTRA" json:"MAX_BLOBS_PER_BLOCK_ELECTRA"` + MAX_REQUEST_BLOB_SIDECARS_ELECTRA Uint64View `yaml:"MAX_REQUEST_BLOB_SIDECARS_ELECTRA" json:"MAX_REQUEST_BLOB_SIDECARS_ELECTRA"` + + // Fulu + NUMBER_OF_COLUMNS Uint64View `yaml:"NUMBER_OF_COLUMNS" json:"NUMBER_OF_COLUMNS"` + NUMBER_OF_CUSTODY_GROUPS Uint64View `yaml:"NUMBER_OF_CUSTODY_GROUPS" json:"NUMBER_OF_CUSTODY_GROUPS"` + DATA_COLUMN_SIDECAR_SUBNET_COUNT Uint64View `yaml:"DATA_COLUMN_SIDECAR_SUBNET_COUNT" json:"DATA_COLUMN_SIDECAR_SUBNET_COUNT"` + MAX_REQUEST_DATA_COLUMN_SIDECARS Uint64View `yaml:"MAX_REQUEST_DATA_COLUMN_SIDECARS" json:"MAX_REQUEST_DATA_COLUMN_SIDECARS"` + SAMPLES_PER_SLOT Uint64View `yaml:"SAMPLES_PER_SLOT" json:"SAMPLES_PER_SLOT"` + CUSTODY_REQUIREMENT Uint64View `yaml:"CUSTODY_REQUIREMENT" json:"CUSTODY_REQUIREMENT"` + VALIDATOR_CUSTODY_REQUIREMENT Uint64View `yaml:"VALIDATOR_CUSTODY_REQUIREMENT" json:"VALIDATOR_CUSTODY_REQUIREMENT"` + BALANCE_PER_ADDITIONAL_CUSTODY_GROUP Uint64View `yaml:"BALANCE_PER_ADDITIONAL_CUSTODY_GROUP" json:"BALANCE_PER_ADDITIONAL_CUSTODY_GROUP"` + MAX_BLOBS_PER_BLOCK_FULU Uint64View `yaml:"MAX_BLOBS_PER_BLOCK_FULU" json:"MAX_BLOBS_PER_BLOCK_FULU"` + MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS Uint64View `yaml:"MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS" json:"MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS"` + + // EIP7441 + EPOCHS_PER_SHUFFLING_PHASE Uint64View `yaml:"EPOCHS_PER_SHUFFLING_PHASE" json:"EPOCHS_PER_SHUFFLING_PHASE"` + PROPOSER_SELECTION_GAP Uint64View `yaml:"PROPOSER_SELECTION_GAP" json:"PROPOSER_SELECTION_GAP"` + + // EIP7732 + MAX_REQUEST_PAYLOADS Uint64View `yaml:"MAX_REQUEST_PAYLOADS" json:"MAX_REQUEST_PAYLOADS"` } type SpecObj interface { @@ -294,6 +346,7 @@ type Spec struct { BellatrixPreset `json:",inline" yaml:",inline"` CapellaPreset `json:",inline" yaml:",inline"` DenebPreset `json:",inline" yaml:",inline"` + ElectraPreset `json:",inline" yaml:",inline"` Config `json:",inline" yaml:",inline"` ExecutionEngine `json:"-" yaml:"-"` @@ -312,7 +365,11 @@ func (spec *Spec) ForkVersion(slot Slot) Version { return spec.ALTAIR_FORK_VERSION } else if epoch < spec.CAPELLA_FORK_EPOCH { return spec.BELLATRIX_FORK_VERSION + } else if epoch < spec.DENEB_FORK_EPOCH { + return spec.DENEB_FORK_VERSION + } else if epoch < spec.ELECTRA_FORK_EPOCH { + return spec.ELECTRA_FORK_VERSION } else { - return spec.CAPELLA_FORK_VERSION + return spec.FULU_FORK_VERSION } } diff --git a/eth2/beacon/electra/aggregate_and_proof.go b/eth2/beacon/electra/aggregate_and_proof.go new file mode 100644 index 0000000..a8e6e18 --- /dev/null +++ b/eth2/beacon/electra/aggregate_and_proof.go @@ -0,0 +1,58 @@ +package electra + +import ( + "github.com/protolambda/zrnt/eth2/beacon/common" + "github.com/protolambda/ztyp/codec" + "github.com/protolambda/ztyp/tree" +) + +type SignedAggregateAndProof struct { + Message AggregateAndProof `json:"message"` + Signature common.BLSSignature `json:"signature"` +} + +func (a *SignedAggregateAndProof) Deserialize(spec *common.Spec, dr *codec.DecodingReader) error { + return dr.Container(spec.Wrap(&a.Message), &a.Signature) +} + +func (a *SignedAggregateAndProof) Serialize(spec *common.Spec, w *codec.EncodingWriter) error { + return w.Container(spec.Wrap(&a.Message), &a.Signature) +} + +func (a *SignedAggregateAndProof) ByteLength(spec *common.Spec) uint64 { + return codec.ContainerLength(spec.Wrap(&a.Message), &a.Signature) +} + +func (a *SignedAggregateAndProof) FixedLength(*common.Spec) uint64 { + return 0 +} + +func (a *SignedAggregateAndProof) HashTreeRoot(spec *common.Spec, hFn tree.HashFn) common.Root { + return hFn.HashTreeRoot(spec.Wrap(&a.Message), &a.Signature) +} + +type AggregateAndProof struct { + AggregatorIndex common.ValidatorIndex `json:"aggregator_index"` + Aggregate Attestation `json:"aggregate"` + SelectionProof common.BLSSignature `json:"selection_proof"` +} + +func (a *AggregateAndProof) Deserialize(spec *common.Spec, dr *codec.DecodingReader) error { + return dr.Container(&a.AggregatorIndex, spec.Wrap(&a.Aggregate), &a.SelectionProof) +} + +func (a *AggregateAndProof) Serialize(spec *common.Spec, w *codec.EncodingWriter) error { + return w.Container(&a.AggregatorIndex, spec.Wrap(&a.Aggregate), &a.SelectionProof) +} + +func (a *AggregateAndProof) ByteLength(spec *common.Spec) uint64 { + return codec.ContainerLength(&a.AggregatorIndex, spec.Wrap(&a.Aggregate), &a.SelectionProof) +} + +func (a *AggregateAndProof) FixedLength(*common.Spec) uint64 { + return 0 +} + +func (a *AggregateAndProof) HashTreeRoot(spec *common.Spec, hFn tree.HashFn) common.Root { + return hFn.HashTreeRoot(&a.AggregatorIndex, spec.Wrap(&a.Aggregate), &a.SelectionProof) +} diff --git a/eth2/beacon/electra/attestation.go b/eth2/beacon/electra/attestation.go new file mode 100644 index 0000000..77ed054 --- /dev/null +++ b/eth2/beacon/electra/attestation.go @@ -0,0 +1,166 @@ +package electra + +import ( + "encoding/json" + "github.com/protolambda/zrnt/eth2/beacon/common" + "github.com/protolambda/zrnt/eth2/beacon/phase0" + "github.com/protolambda/ztyp/codec" + "github.com/protolambda/ztyp/tree" + . "github.com/protolambda/ztyp/view" +) + +type SingleAttestation struct { + CommitteeIndex common.CommitteeIndex `json:"committee_index" yaml:"committee_index"` + AttesterIndex common.ValidatorIndex `json:"attester_index" yaml:"attester_index"` + Data phase0.AttestationData `json:"data" yaml:"data"` + Signature common.BLSSignature `json:"signature" yaml:"signature"` +} + +func (a *SingleAttestation) Deserialize(dr *codec.DecodingReader) error { + return dr.FixedLenContainer(&a.CommitteeIndex, &a.AttesterIndex, &a.Data, &a.Signature) +} + +func (a *SingleAttestation) Serialize(w *codec.EncodingWriter) error { + return w.FixedLenContainer(&a.CommitteeIndex, &a.AttesterIndex, &a.Data, &a.Signature) +} + +func (a *SingleAttestation) ByteLength() uint64 { + return SingleAttestationType.TypeByteLength() +} + +func (*SingleAttestation) FixedLength() uint64 { + return SingleAttestationType.TypeByteLength() +} + +func (a *SingleAttestation) HashTreeRoot(hFn tree.HashFn) common.Root { + return hFn.HashTreeRoot(&a.CommitteeIndex, &a.AttesterIndex, &a.Data, &a.Signature) +} + +var SingleAttestationType = ContainerType("SingleAttestation", []FieldDef{ + {"committee_index", common.CommitteeIndexType}, + {"attester_index", common.ValidatorIndexType}, + {"data", phase0.AttestationDataType}, + {"signature", common.BLSSignatureType}, +}) + +type Attestation struct { + // [Modified in Electra:EIP7549] + AggregationBits AttestationBits `json:"aggregation_bits" yaml:"aggregation_bits"` + Data phase0.AttestationData `json:"data" yaml:"data"` + Signature common.BLSSignature `json:"signature" yaml:"signature"` + // [New in Electra:EIP7549] + CommitteeBits CommitteeBits `json:"committee_bits" yaml:"committee_bits"` +} + +func (a *Attestation) Deserialize(spec *common.Spec, dr *codec.DecodingReader) error { + return dr.Container(spec.Wrap(&a.AggregationBits), &a.Data, &a.Signature, spec.Wrap(&a.CommitteeBits)) +} + +func (a *Attestation) Serialize(spec *common.Spec, w *codec.EncodingWriter) error { + return w.Container(spec.Wrap(&a.AggregationBits), &a.Data, &a.Signature, spec.Wrap(&a.CommitteeBits)) +} + +func (a *Attestation) ByteLength(spec *common.Spec) uint64 { + return codec.ContainerLength(spec.Wrap(&a.AggregationBits), &a.Data, &a.Signature, spec.Wrap(&a.CommitteeBits)) +} + +func (a *Attestation) FixedLength(*common.Spec) uint64 { + return 0 +} + +func (a *Attestation) HashTreeRoot(spec *common.Spec, hFn tree.HashFn) common.Root { + return hFn.HashTreeRoot(spec.Wrap(&a.AggregationBits), &a.Data, a.Signature, spec.Wrap(&a.CommitteeBits)) +} + +func BlockAttestationsType(spec *common.Spec) ListTypeDef { + return ListType(AttestationType(spec), uint64(spec.MAX_ATTESTATIONS_ELECTRA)) +} + +func AttestationType(spec *common.Spec) *ContainerTypeDef { + return ContainerType("Attestation", []FieldDef{ + {"aggregation_bits", AttestationBitsType(spec)}, + {"data", phase0.AttestationDataType}, + {"signature", common.BLSSignatureType}, + {"committee_bits", CommitteeBitsType(spec)}, + }) +} + +type IndexedAttestation struct { + // [Modified in Electra:EIP7549] + // List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT] + AttestingIndices common.SlotCommitteeIndices `json:"attesting_indices" yaml:"attesting_indices"` + Data phase0.AttestationData `json:"data" yaml:"data"` + Signature common.BLSSignature `json:"signature" yaml:"signature"` +} + +func (p *IndexedAttestation) Deserialize(spec *common.Spec, dr *codec.DecodingReader) error { + return dr.Container(spec.Wrap(&p.AttestingIndices), &p.Data, &p.Signature) +} + +func (a *IndexedAttestation) Serialize(spec *common.Spec, w *codec.EncodingWriter) error { + return w.Container(spec.Wrap(&a.AttestingIndices), &a.Data, &a.Signature) +} + +func (a *IndexedAttestation) ByteLength(spec *common.Spec) uint64 { + return codec.ContainerLength(spec.Wrap(&a.AttestingIndices), &a.Data, &a.Signature) +} + +func (*IndexedAttestation) FixedLength(*common.Spec) uint64 { + return 0 +} + +func (p *IndexedAttestation) HashTreeRoot(spec *common.Spec, hFn tree.HashFn) common.Root { + return hFn.HashTreeRoot(spec.Wrap(&p.AttestingIndices), &p.Data, p.Signature) +} + +func IndexedAttestationType(spec *common.Spec) *ContainerTypeDef { + return ContainerType("IndexedAttestation", []FieldDef{ + {"attesting_indices", common.SlotCommitteeIndicesType(spec)}, + {"data", phase0.AttestationDataType}, + {"signature", common.BLSSignatureType}, + }) +} + +type Attestations []Attestation + +func (a *Attestations) Deserialize(spec *common.Spec, dr *codec.DecodingReader) error { + return dr.List(func() codec.Deserializable { + i := len(*a) + *a = append(*a, Attestation{}) + return spec.Wrap(&((*a)[i])) + }, 0, uint64(spec.MAX_ATTESTATIONS_ELECTRA)) +} + +func (a Attestations) Serialize(spec *common.Spec, w *codec.EncodingWriter) error { + return w.List(func(i uint64) codec.Serializable { + return spec.Wrap(&a[i]) + }, 0, uint64(len(a))) +} + +func (a Attestations) ByteLength(spec *common.Spec) (out uint64) { + for _, v := range a { + out += v.ByteLength(spec) + codec.OFFSET_SIZE + } + return +} + +func (a *Attestations) FixedLength(*common.Spec) uint64 { + return 0 +} + +func (li Attestations) HashTreeRoot(spec *common.Spec, hFn tree.HashFn) common.Root { + length := uint64(len(li)) + return hFn.ComplexListHTR(func(i uint64) tree.HTR { + if i < length { + return spec.Wrap(&li[i]) + } + return nil + }, length, uint64(spec.MAX_ATTESTATIONS_ELECTRA)) +} + +func (li Attestations) MarshalJSON() ([]byte, error) { + if li == nil { + return json.Marshal([]Attestation{}) // encode as empty list, not null + } + return json.Marshal([]Attestation(li)) +} diff --git a/eth2/beacon/electra/attestation_bits.go b/eth2/beacon/electra/attestation_bits.go new file mode 100644 index 0000000..10e8025 --- /dev/null +++ b/eth2/beacon/electra/attestation_bits.go @@ -0,0 +1,177 @@ +package electra + +import ( + "bytes" + "fmt" + + "github.com/protolambda/zrnt/eth2/beacon/common" + "github.com/protolambda/ztyp/bitfields" + "github.com/protolambda/ztyp/codec" + "github.com/protolambda/ztyp/conv" + "github.com/protolambda/ztyp/tree" + . "github.com/protolambda/ztyp/view" +) + +// AttestationBits is formatted as a serialized SSZ bitlist, including the delimit bit +type AttestationBits []byte + +func (li AttestationBits) View(spec *common.Spec) *AttestationBitsView { + v, _ := AttestationBitsType(spec).Deserialize(codec.NewDecodingReader(bytes.NewReader(li), uint64(len(li)))) + return &AttestationBitsView{v.(*BitListView)} +} + +func (li *AttestationBits) Deserialize(spec *common.Spec, dr *codec.DecodingReader) error { + return dr.BitList((*[]byte)(li), uint64(spec.MAX_VALIDATORS_PER_COMMITTEE*spec.MAX_COMMITTEES_PER_SLOT)) +} + +func (a AttestationBits) Serialize(spec *common.Spec, w *codec.EncodingWriter) error { + return w.BitList(a[:]) +} + +func (a AttestationBits) ByteLength(spec *common.Spec) uint64 { + return uint64(len(a)) +} + +func (a *AttestationBits) FixedLength(*common.Spec) uint64 { + return 0 +} + +func (li AttestationBits) HashTreeRoot(spec *common.Spec, hFn tree.HashFn) common.Root { + return hFn.BitListHTR(li, uint64(spec.MAX_VALIDATORS_PER_COMMITTEE*spec.MAX_COMMITTEES_PER_SLOT)) +} + +func (cb AttestationBits) MarshalText() ([]byte, error) { + return conv.BytesMarshalText(cb[:]) +} + +func (cb *AttestationBits) UnmarshalText(text []byte) error { + return conv.DynamicBytesUnmarshalText((*[]byte)(cb), text) +} + +func (cb AttestationBits) String() string { + return conv.BytesString(cb[:]) +} + +func (cb AttestationBits) BitLen() uint64 { + return bitfields.BitlistLen(cb) +} + +func (cb AttestationBits) GetBit(i uint64) bool { + return bitfields.GetBit(cb, i) +} + +func (cb AttestationBits) SetBit(i uint64, v bool) { + bitfields.SetBit(cb, i, v) +} + +// Sets the bits to true that are true in other. (in place) +func (cb AttestationBits) Or(other AttestationBits) { + for i := 0; i < len(cb); i++ { + cb[i] |= other[i] + } +} + +// In-place filters a list of committees indices to only keep the bitfield participants. +// The result is not sorted. Returns the re-sliced filtered participants list. +// +// WARNING: unsafe to use, panics if committee size does not match. +func (cb AttestationBits) FilterParticipants(committee []common.ValidatorIndex) []common.ValidatorIndex { + out := committee[:0] + bitLen := cb.BitLen() + if bitLen != uint64(len(committee)) { + panic("committee mismatch, bitfield length does not match") + } + for i := uint64(0); i < bitLen; i++ { + if bitfields.GetBit(cb, i) { + out = append(out, committee[i]) + } + } + return out +} + +// In-place filters a list of committees indices to only keep the bitfield NON-participants. +// The result is not sorted. Returns the re-sliced filtered non-participants list. +// +// WARNING: unsafe to use, panics if committee size does not match. +func (cb AttestationBits) FilterNonParticipants(committee []common.ValidatorIndex) []common.ValidatorIndex { + out := committee[:0] + bitLen := cb.BitLen() + if bitLen != uint64(len(committee)) { + panic("committee mismatch, bitfield length does not match") + } + for i := uint64(0); i < bitLen; i++ { + if !bitfields.GetBit(cb, i) { + out = append(out, committee[i]) + } + } + return out +} + +// Returns true if other only has bits set to 1 that this bitfield also has set to 1 +func (cb AttestationBits) Covers(other AttestationBits) (bool, error) { + if a, b := cb.BitLen(), other.BitLen(); a != b { + return false, fmt.Errorf("bitfield length mismatch: %d <> %d", a, b) + } + return bitfields.Covers(cb, other) +} + +func (cb AttestationBits) OnesCount() uint64 { + return bitfields.BitlistOnesCount(cb) +} + +func (cb AttestationBits) SingleParticipant(committee []common.ValidatorIndex) (common.ValidatorIndex, error) { + bitLen := cb.BitLen() + if bitLen != uint64(len(committee)) { + return 0, fmt.Errorf("committee mismatch, bitfield length %d does not match committee size %d", bitLen, len(committee)) + } + var found *common.ValidatorIndex + for i := uint64(0); i < bitLen; i++ { + if cb.GetBit(i) { + if found == nil { + found = &committee[i] + } else { + return 0, fmt.Errorf("found at least two participants: %d and %d", *found, committee[i]) + } + } + } + if found == nil { + return 0, fmt.Errorf("found no participants") + } + return *found, nil +} + +func (cb AttestationBits) Copy() AttestationBits { + // append won't find capacity, and thus put contents into new array, and then returns typed slice of it. + return append(AttestationBits(nil), cb...) +} + +func AttestationBitsType(spec *common.Spec) *BitListTypeDef { + return BitListType(uint64(spec.MAX_VALIDATORS_PER_COMMITTEE * spec.MAX_COMMITTEES_PER_SLOT)) +} + +type AttestationBitsView struct { + *BitListView +} + +func AsAttestationBits(v View, err error) (*AttestationBitsView, error) { + c, err := AsBitList(v, err) + return &AttestationBitsView{c}, err +} + +func (v *AttestationBitsView) Raw() (AttestationBits, error) { + bitLength, err := v.Length() + if err != nil { + return nil, err + } + // rounded up, and then an extra bit for delimiting. ((bitLength + 7 + 1)/ 8) + byteLength := (bitLength / 8) + 1 + var buf bytes.Buffer + if err := v.Serialize(codec.NewEncodingWriter(&buf)); err != nil { + return nil, err + } + out := AttestationBits(buf.Bytes()) + if uint64(len(out)) != byteLength { + return nil, fmt.Errorf("failed to convert attestation tree bits view to raw bits") + } + return out, nil +} diff --git a/eth2/beacon/electra/attester_slashing.go b/eth2/beacon/electra/attester_slashing.go new file mode 100644 index 0000000..84d19db --- /dev/null +++ b/eth2/beacon/electra/attester_slashing.go @@ -0,0 +1,92 @@ +package electra + +import ( + "encoding/json" + + "github.com/protolambda/zrnt/eth2/beacon/common" + "github.com/protolambda/ztyp/codec" + "github.com/protolambda/ztyp/tree" + . "github.com/protolambda/ztyp/view" +) + +type AttesterSlashing struct { + // [Modified in Electra:EIP7549] + Attestation1 IndexedAttestation `json:"attestation_1" yaml:"attestation_1"` + // [Modified in Electra:EIP7549] + Attestation2 IndexedAttestation `json:"attestation_2" yaml:"attestation_2"` +} + +func (a *AttesterSlashing) Deserialize(spec *common.Spec, dr *codec.DecodingReader) error { + return dr.Container(spec.Wrap(&a.Attestation1), spec.Wrap(&a.Attestation2)) +} + +func (a *AttesterSlashing) Serialize(spec *common.Spec, w *codec.EncodingWriter) error { + return w.Container(spec.Wrap(&a.Attestation1), spec.Wrap(&a.Attestation2)) +} + +func (a *AttesterSlashing) ByteLength(spec *common.Spec) uint64 { + return codec.ContainerLength(spec.Wrap(&a.Attestation1), spec.Wrap(&a.Attestation2)) +} + +func (a *AttesterSlashing) FixedLength(*common.Spec) uint64 { + return 0 +} + +func (a *AttesterSlashing) HashTreeRoot(spec *common.Spec, hFn tree.HashFn) common.Root { + return hFn.HashTreeRoot(spec.Wrap(&a.Attestation1), spec.Wrap(&a.Attestation2)) +} + +func BlockAttesterSlashingsType(spec *common.Spec) ListTypeDef { + return ListType(AttesterSlashingType(spec), uint64(spec.MAX_ATTESTER_SLASHINGS)) +} + +func AttesterSlashingType(spec *common.Spec) *ContainerTypeDef { + return ContainerType("AttesterSlashing", []FieldDef{ + {"attestation_1", IndexedAttestationType(spec)}, + {"attestation_2", IndexedAttestationType(spec)}, + }) +} + +type AttesterSlashings []AttesterSlashing + +func (a *AttesterSlashings) Deserialize(spec *common.Spec, dr *codec.DecodingReader) error { + return dr.List(func() codec.Deserializable { + i := len(*a) + *a = append(*a, AttesterSlashing{}) + return spec.Wrap(&((*a)[i])) + }, 0, uint64(spec.MAX_ATTESTER_SLASHINGS_ELECTRA)) +} + +func (a AttesterSlashings) Serialize(spec *common.Spec, w *codec.EncodingWriter) error { + return w.List(func(i uint64) codec.Serializable { + return spec.Wrap(&a[i]) + }, 0, uint64(len(a))) +} + +func (a AttesterSlashings) ByteLength(spec *common.Spec) (out uint64) { + for _, v := range a { + out += v.ByteLength(spec) + codec.OFFSET_SIZE + } + return +} + +func (a *AttesterSlashings) FixedLength(*common.Spec) uint64 { + return 0 +} + +func (li AttesterSlashings) HashTreeRoot(spec *common.Spec, hFn tree.HashFn) common.Root { + length := uint64(len(li)) + return hFn.ComplexListHTR(func(i uint64) tree.HTR { + if i < length { + return spec.Wrap(&li[i]) + } + return nil + }, length, uint64(spec.MAX_ATTESTER_SLASHINGS_ELECTRA)) +} + +func (li AttesterSlashings) MarshalJSON() ([]byte, error) { + if li == nil { + return json.Marshal([]AttesterSlashing{}) // encode as empty list, not null + } + return json.Marshal([]AttesterSlashing(li)) +} diff --git a/eth2/beacon/electra/block.go b/eth2/beacon/electra/block.go new file mode 100644 index 0000000..01f684f --- /dev/null +++ b/eth2/beacon/electra/block.go @@ -0,0 +1,377 @@ +package electra + +import ( + "fmt" + + "github.com/protolambda/zrnt/eth2/beacon/altair" + "github.com/protolambda/zrnt/eth2/beacon/common" + "github.com/protolambda/zrnt/eth2/beacon/deneb" + "github.com/protolambda/zrnt/eth2/beacon/phase0" + "github.com/protolambda/ztyp/codec" + "github.com/protolambda/ztyp/tree" + . "github.com/protolambda/ztyp/view" +) + +type SignedBeaconBlock struct { + Message BeaconBlock `json:"message" yaml:"message"` + Signature common.BLSSignature `json:"signature" yaml:"signature"` +} + +var _ common.EnvelopeBuilder = (*SignedBeaconBlock)(nil) + +func (b *SignedBeaconBlock) Envelope(spec *common.Spec, digest common.ForkDigest) *common.BeaconBlockEnvelope { + header := b.Message.Header(spec) + return &common.BeaconBlockEnvelope{ + ForkDigest: digest, + BeaconBlockHeader: *header, + Body: &b.Message.Body, + BlockRoot: header.HashTreeRoot(tree.GetHashFn()), + Signature: b.Signature, + } +} + +func (b *SignedBeaconBlock) Deserialize(spec *common.Spec, dr *codec.DecodingReader) error { + return dr.Container(spec.Wrap(&b.Message), &b.Signature) +} + +func (b *SignedBeaconBlock) Serialize(spec *common.Spec, w *codec.EncodingWriter) error { + return w.Container(spec.Wrap(&b.Message), &b.Signature) +} + +func (b *SignedBeaconBlock) ByteLength(spec *common.Spec) uint64 { + return codec.ContainerLength(spec.Wrap(&b.Message), &b.Signature) +} + +func (a *SignedBeaconBlock) FixedLength(*common.Spec) uint64 { + return 0 +} + +func (b *SignedBeaconBlock) HashTreeRoot(spec *common.Spec, hFn tree.HashFn) common.Root { + return hFn.HashTreeRoot(spec.Wrap(&b.Message), b.Signature) +} + +func (block *SignedBeaconBlock) SignedHeader(spec *common.Spec) *common.SignedBeaconBlockHeader { + return &common.SignedBeaconBlockHeader{ + Message: *block.Message.Header(spec), + Signature: block.Signature, + } +} + +type BeaconBlock struct { + Slot common.Slot `json:"slot" yaml:"slot"` + ProposerIndex common.ValidatorIndex `json:"proposer_index" yaml:"proposer_index"` + ParentRoot common.Root `json:"parent_root" yaml:"parent_root"` + StateRoot common.Root `json:"state_root" yaml:"state_root"` + Body BeaconBlockBody `json:"body" yaml:"body"` +} + +func (b *BeaconBlock) Deserialize(spec *common.Spec, dr *codec.DecodingReader) error { + return dr.Container(&b.Slot, &b.ProposerIndex, &b.ParentRoot, &b.StateRoot, spec.Wrap(&b.Body)) +} + +func (b *BeaconBlock) Serialize(spec *common.Spec, w *codec.EncodingWriter) error { + return w.Container(&b.Slot, &b.ProposerIndex, &b.ParentRoot, &b.StateRoot, spec.Wrap(&b.Body)) +} + +func (b *BeaconBlock) ByteLength(spec *common.Spec) uint64 { + return codec.ContainerLength(&b.Slot, &b.ProposerIndex, &b.ParentRoot, &b.StateRoot, spec.Wrap(&b.Body)) +} + +func (a *BeaconBlock) FixedLength(*common.Spec) uint64 { + return 0 +} + +func (b *BeaconBlock) HashTreeRoot(spec *common.Spec, hFn tree.HashFn) common.Root { + return hFn.HashTreeRoot(b.Slot, b.ProposerIndex, b.ParentRoot, b.StateRoot, spec.Wrap(&b.Body)) +} + +func BeaconBlockType(spec *common.Spec) *ContainerTypeDef { + return ContainerType("BeaconBlock", []FieldDef{ + {"slot", common.SlotType}, + {"proposer_index", common.ValidatorIndexType}, + {"parent_root", RootType}, + {"state_root", RootType}, + {"body", BeaconBlockBodyType(spec)}, + }) +} + +func SignedBeaconBlockType(spec *common.Spec) *ContainerTypeDef { + return ContainerType("SignedBeaconBlock", []FieldDef{ + {"message", BeaconBlockType(spec)}, + {"signature", common.BLSSignatureType}, + }) +} + +func (block *BeaconBlock) Header(spec *common.Spec) *common.BeaconBlockHeader { + return &common.BeaconBlockHeader{ + Slot: block.Slot, + ProposerIndex: block.ProposerIndex, + ParentRoot: block.ParentRoot, + StateRoot: block.StateRoot, + BodyRoot: block.Body.HashTreeRoot(spec, tree.GetHashFn()), + } +} + +type BeaconBlockBody struct { + RandaoReveal common.BLSSignature `json:"randao_reveal" yaml:"randao_reveal"` + Eth1Data common.Eth1Data `json:"eth1_data" yaml:"eth1_data"` + Graffiti common.Root `json:"graffiti" yaml:"graffiti"` + + // Operations + ProposerSlashings phase0.ProposerSlashings `json:"proposer_slashings" yaml:"proposer_slashings"` + // [Modified in Electra:EIP7549], MAX_ATTESTER_SLASHINGS_ELECTRA + AttesterSlashings AttesterSlashings `json:"attester_slashings" yaml:"attester_slashings"` + // [Modified in Electra:EIP7549], MAX_ATTESTATIONS_ELECTRA + Attestations Attestations `json:"attestations" yaml:"attestations"` + Deposits phase0.Deposits `json:"deposits" yaml:"deposits"` + VoluntaryExits phase0.VoluntaryExits `json:"voluntary_exits" yaml:"voluntary_exits"` + SyncAggregate altair.SyncAggregate `json:"sync_aggregate" yaml:"sync_aggregate"` + // Execution + ExecutionPayload deneb.ExecutionPayload `json:"execution_payload" yaml:"execution_payload"` + BLSToExecutionChanges common.SignedBLSToExecutionChanges `json:"bls_to_execution_changes" yaml:"bls_to_execution_changes"` + BlobKZGCommitments deneb.KZGCommitments `json:"blob_kzg_commitments" yaml:"blob_kzg_commitments"` + // [New in Electra] + ExecutionRequests ExecutionRequests `json:"execution_requests" yaml:"execution_requests"` +} + +func (b *BeaconBlockBody) Deserialize(spec *common.Spec, dr *codec.DecodingReader) error { + return dr.Container( + &b.RandaoReveal, &b.Eth1Data, + &b.Graffiti, spec.Wrap(&b.ProposerSlashings), + spec.Wrap(&b.AttesterSlashings), spec.Wrap(&b.Attestations), + spec.Wrap(&b.Deposits), spec.Wrap(&b.VoluntaryExits), + spec.Wrap(&b.SyncAggregate), spec.Wrap(&b.ExecutionPayload), + spec.Wrap(&b.BLSToExecutionChanges), + spec.Wrap(&b.BlobKZGCommitments), + spec.Wrap(&b.ExecutionRequests), + ) +} + +func (b *BeaconBlockBody) Serialize(spec *common.Spec, w *codec.EncodingWriter) error { + return w.Container( + &b.RandaoReveal, &b.Eth1Data, + &b.Graffiti, spec.Wrap(&b.ProposerSlashings), + spec.Wrap(&b.AttesterSlashings), spec.Wrap(&b.Attestations), + spec.Wrap(&b.Deposits), spec.Wrap(&b.VoluntaryExits), + spec.Wrap(&b.SyncAggregate), spec.Wrap(&b.ExecutionPayload), + spec.Wrap(&b.BLSToExecutionChanges), + spec.Wrap(&b.BlobKZGCommitments), + spec.Wrap(&b.ExecutionRequests), + ) +} + +func (b *BeaconBlockBody) ByteLength(spec *common.Spec) uint64 { + return codec.ContainerLength( + &b.RandaoReveal, &b.Eth1Data, + &b.Graffiti, spec.Wrap(&b.ProposerSlashings), + spec.Wrap(&b.AttesterSlashings), spec.Wrap(&b.Attestations), + spec.Wrap(&b.Deposits), spec.Wrap(&b.VoluntaryExits), + spec.Wrap(&b.SyncAggregate), spec.Wrap(&b.ExecutionPayload), + spec.Wrap(&b.BLSToExecutionChanges), + spec.Wrap(&b.BlobKZGCommitments), + spec.Wrap(&b.ExecutionRequests), + ) +} + +func (a *BeaconBlockBody) FixedLength(*common.Spec) uint64 { + return 0 +} + +func (b *BeaconBlockBody) HashTreeRoot(spec *common.Spec, hFn tree.HashFn) common.Root { + return hFn.HashTreeRoot( + b.RandaoReveal, &b.Eth1Data, + b.Graffiti, spec.Wrap(&b.ProposerSlashings), + spec.Wrap(&b.AttesterSlashings), spec.Wrap(&b.Attestations), + spec.Wrap(&b.Deposits), spec.Wrap(&b.VoluntaryExits), + spec.Wrap(&b.SyncAggregate), spec.Wrap(&b.ExecutionPayload), + spec.Wrap(&b.BLSToExecutionChanges), + spec.Wrap(&b.BlobKZGCommitments), + spec.Wrap(&b.ExecutionRequests), + ) +} + +func (b *BeaconBlockBody) CheckLimits(spec *common.Spec) error { + if x := uint64(len(b.ProposerSlashings)); x > uint64(spec.MAX_PROPOSER_SLASHINGS) { + return fmt.Errorf("too many proposer slashings: %d", x) + } + if x := uint64(len(b.AttesterSlashings)); x > uint64(spec.MAX_ATTESTER_SLASHINGS_ELECTRA) { + return fmt.Errorf("too many attester slashings: %d", x) + } + if x := uint64(len(b.Attestations)); x > uint64(spec.MAX_ATTESTATIONS_ELECTRA) { + return fmt.Errorf("too many attestations: %d", x) + } + if x := uint64(len(b.Deposits)); x > uint64(spec.MAX_DEPOSITS) { + return fmt.Errorf("too many deposits: %d", x) + } + if x := uint64(len(b.VoluntaryExits)); x > uint64(spec.MAX_VOLUNTARY_EXITS) { + return fmt.Errorf("too many voluntary exits: %d", x) + } + // TODO: also check sum of byte size, sanity check block size. + if x := uint64(len(b.ExecutionPayload.Transactions)); x > uint64(spec.MAX_TRANSACTIONS_PER_PAYLOAD) { + return fmt.Errorf("too many transactions: %d", x) + } + if x := uint64(len(b.BLSToExecutionChanges)); x > uint64(spec.MAX_BLS_TO_EXECUTION_CHANGES) { + return fmt.Errorf("too many bls-to-execution changes: %d", x) + } + if x := uint64(len(b.BlobKZGCommitments)); x > uint64(spec.MAX_BLOBS_PER_BLOCK) { + return fmt.Errorf("too many blob kzg commitments: %d", x) + } + if x := uint64(len(b.ExecutionRequests.Deposits)); x > uint64(spec.MAX_DEPOSIT_REQUESTS_PER_PAYLOAD) { + return fmt.Errorf("too many execution request deposits: %d", x) + } + if x := uint64(len(b.ExecutionRequests.Withdrawals)); x > uint64(spec.MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD) { + return fmt.Errorf("too many execution request withdrawals: %d", x) + } + if x := uint64(len(b.ExecutionRequests.Consolidations)); x > uint64(spec.MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD) { + return fmt.Errorf("too many execution request consolidations: %d", x) + } + return nil +} + +func (b *BeaconBlockBody) Shallow(spec *common.Spec) *BeaconBlockBodyShallow { + return &BeaconBlockBodyShallow{ + RandaoReveal: b.RandaoReveal, + Eth1Data: b.Eth1Data, + Graffiti: b.Graffiti, + ProposerSlashings: b.ProposerSlashings, + AttesterSlashings: b.AttesterSlashings, + Attestations: b.Attestations, + Deposits: b.Deposits, + VoluntaryExits: b.VoluntaryExits, + SyncAggregate: b.SyncAggregate, + ExecutionPayloadRoot: b.ExecutionPayload.HashTreeRoot(spec, tree.GetHashFn()), + BLSToExecutionChanges: b.BLSToExecutionChanges, + BlobKZGCommitments: b.BlobKZGCommitments, + ExecutionRequests: b.ExecutionRequests, + } +} + +func (b *BeaconBlockBody) GetTransactions() []common.Transaction { + return b.ExecutionPayload.Transactions +} + +func (b *BeaconBlockBody) GetBlobKZGCommitments() []common.KZGCommitment { + return b.BlobKZGCommitments +} + +func BeaconBlockBodyType(spec *common.Spec) *ContainerTypeDef { + return ContainerType("BeaconBlockBody", []FieldDef{ + {"randao_reveal", common.BLSSignatureType}, + {"eth1_data", common.Eth1DataType}, // Eth1 data vote + {"graffiti", common.Bytes32Type}, // Arbitrary data + // Operations + {"proposer_slashings", phase0.BlockProposerSlashingsType(spec)}, + {"attester_slashings", BlockAttesterSlashingsType(spec)}, + {"attestations", BlockAttestationsType(spec)}, + {"deposits", phase0.BlockDepositsType(spec)}, + {"voluntary_exits", phase0.BlockVoluntaryExitsType(spec)}, + {"sync_aggregate", altair.SyncAggregateType(spec)}, + // Capella + {"execution_payload", deneb.ExecutionPayloadType(spec)}, + {"bls_to_execution_changes", common.BlockSignedBLSToExecutionChangesType(spec)}, + // Deneb + {"blob_kzg_commitments", deneb.KZGCommitmentsType(spec)}, + // Electra + {"execution_requests", ExecutionRequestsType(spec)}, + }) +} + +type BeaconBlockBodyShallow struct { + RandaoReveal common.BLSSignature `json:"randao_reveal" yaml:"randao_reveal"` + Eth1Data common.Eth1Data `json:"eth1_data" yaml:"eth1_data"` + Graffiti common.Root `json:"graffiti" yaml:"graffiti"` + + ProposerSlashings phase0.ProposerSlashings `json:"proposer_slashings" yaml:"proposer_slashings"` + AttesterSlashings AttesterSlashings `json:"attester_slashings" yaml:"attester_slashings"` + Attestations Attestations `json:"attestations" yaml:"attestations"` + Deposits phase0.Deposits `json:"deposits" yaml:"deposits"` + VoluntaryExits phase0.VoluntaryExits `json:"voluntary_exits" yaml:"voluntary_exits"` + + SyncAggregate altair.SyncAggregate `json:"sync_aggregate" yaml:"sync_aggregate"` + + ExecutionPayloadRoot common.Root `json:"execution_payload_root" yaml:"execution_payload_root"` + + BLSToExecutionChanges common.SignedBLSToExecutionChanges `json:"bls_to_execution_changes" yaml:"bls_to_execution_changes"` + + BlobKZGCommitments deneb.KZGCommitments `json:"blob_kzg_commitments" yaml:"blob_kzg_commitments"` // new in EIP-4844 + // [New in Electra] + ExecutionRequests ExecutionRequests `json:"execution_requests" yaml:"execution_requests"` +} + +func (b *BeaconBlockBodyShallow) Deserialize(spec *common.Spec, dr *codec.DecodingReader) error { + return dr.Container( + &b.RandaoReveal, &b.Eth1Data, + &b.Graffiti, spec.Wrap(&b.ProposerSlashings), + spec.Wrap(&b.AttesterSlashings), spec.Wrap(&b.Attestations), + spec.Wrap(&b.Deposits), spec.Wrap(&b.VoluntaryExits), + spec.Wrap(&b.SyncAggregate), &b.ExecutionPayloadRoot, + spec.Wrap(&b.BLSToExecutionChanges), + spec.Wrap(&b.BlobKZGCommitments), + spec.Wrap(&b.ExecutionRequests), + ) +} + +func (b *BeaconBlockBodyShallow) Serialize(spec *common.Spec, w *codec.EncodingWriter) error { + return w.Container( + &b.RandaoReveal, &b.Eth1Data, + &b.Graffiti, spec.Wrap(&b.ProposerSlashings), + spec.Wrap(&b.AttesterSlashings), spec.Wrap(&b.Attestations), + spec.Wrap(&b.Deposits), spec.Wrap(&b.VoluntaryExits), + spec.Wrap(&b.SyncAggregate), &b.ExecutionPayloadRoot, + spec.Wrap(&b.BLSToExecutionChanges), + spec.Wrap(&b.BlobKZGCommitments), + spec.Wrap(&b.ExecutionRequests), + ) +} + +func (b *BeaconBlockBodyShallow) ByteLength(spec *common.Spec) uint64 { + return codec.ContainerLength( + &b.RandaoReveal, &b.Eth1Data, + &b.Graffiti, spec.Wrap(&b.ProposerSlashings), + spec.Wrap(&b.AttesterSlashings), spec.Wrap(&b.Attestations), + spec.Wrap(&b.Deposits), spec.Wrap(&b.VoluntaryExits), + spec.Wrap(&b.SyncAggregate), &b.ExecutionPayloadRoot, + spec.Wrap(&b.BLSToExecutionChanges), + spec.Wrap(&b.BlobKZGCommitments), + spec.Wrap(&b.ExecutionRequests), + ) +} + +func (a *BeaconBlockBodyShallow) FixedLength(*common.Spec) uint64 { + return 0 +} + +func (b *BeaconBlockBodyShallow) HashTreeRoot(spec *common.Spec, hFn tree.HashFn) common.Root { + return hFn.HashTreeRoot( + b.RandaoReveal, &b.Eth1Data, + b.Graffiti, spec.Wrap(&b.ProposerSlashings), + spec.Wrap(&b.AttesterSlashings), spec.Wrap(&b.Attestations), + spec.Wrap(&b.Deposits), spec.Wrap(&b.VoluntaryExits), + spec.Wrap(&b.SyncAggregate), &b.ExecutionPayloadRoot, + spec.Wrap(&b.BLSToExecutionChanges), + spec.Wrap(&b.BlobKZGCommitments), + spec.Wrap(&b.ExecutionRequests), + ) +} + +func (b *BeaconBlockBodyShallow) WithExecutionPayload(spec *common.Spec, payload deneb.ExecutionPayload) (*BeaconBlockBody, error) { + payloadRoot := payload.HashTreeRoot(spec, tree.GetHashFn()) + if b.ExecutionPayloadRoot != payloadRoot { + return nil, fmt.Errorf("payload does not match expected root: %s <> %s", b.ExecutionPayloadRoot, payloadRoot) + } + return &BeaconBlockBody{ + RandaoReveal: b.RandaoReveal, + Eth1Data: b.Eth1Data, + Graffiti: b.Graffiti, + ProposerSlashings: b.ProposerSlashings, + AttesterSlashings: b.AttesterSlashings, + Attestations: b.Attestations, + Deposits: b.Deposits, + VoluntaryExits: b.VoluntaryExits, + SyncAggregate: b.SyncAggregate, + ExecutionPayload: payload, + BLSToExecutionChanges: b.BLSToExecutionChanges, + BlobKZGCommitments: b.BlobKZGCommitments, + ExecutionRequests: b.ExecutionRequests, + }, nil +} diff --git a/eth2/beacon/electra/committee_bits.go b/eth2/beacon/electra/committee_bits.go new file mode 100644 index 0000000..7f0c1ef --- /dev/null +++ b/eth2/beacon/electra/committee_bits.go @@ -0,0 +1,94 @@ +package electra + +import ( + "bytes" + "fmt" + + "github.com/protolambda/zrnt/eth2/beacon/common" + "github.com/protolambda/ztyp/bitfields" + "github.com/protolambda/ztyp/codec" + "github.com/protolambda/ztyp/conv" + "github.com/protolambda/ztyp/tree" + . "github.com/protolambda/ztyp/view" +) + +// CommitteeBits is formatted as a serialized SSZ bitvector, +// with trailing zero bits if length does not align with byte length. +type CommitteeBits []byte + +func (li CommitteeBits) View(spec *common.Spec) *CommitteeBitsView { + v, _ := CommitteeBitsType(spec).Deserialize(codec.NewDecodingReader(bytes.NewReader(li), uint64(len(li)))) + return &CommitteeBitsView{v.(*BitVectorView)} +} + +func (li *CommitteeBits) Deserialize(spec *common.Spec, dr *codec.DecodingReader) error { + return dr.BitVector((*[]byte)(li), uint64(spec.MAX_COMMITTEES_PER_SLOT)) +} + +func (li CommitteeBits) Serialize(spec *common.Spec, w *codec.EncodingWriter) error { + return w.BitVector(li[:]) +} + +func (li CommitteeBits) ByteLength(spec *common.Spec) uint64 { + return (uint64(spec.MAX_COMMITTEES_PER_SLOT) + 7) / 8 +} + +func (li *CommitteeBits) FixedLength(spec *common.Spec) uint64 { + return (uint64(spec.MAX_COMMITTEES_PER_SLOT) + 7) / 8 +} + +func (li CommitteeBits) HashTreeRoot(spec *common.Spec, hFn tree.HashFn) common.Root { + if li == nil { + // By default, we should initialize the full bits. + // We can't do that in the struct case dynamically based on preset, but we can at least output the correct HTR. + return CommitteeBitsType(spec).New().HashTreeRoot(hFn) + } + return hFn.BitVectorHTR(li) +} + +func (li CommitteeBits) MarshalText() ([]byte, error) { + return conv.BytesMarshalText(li[:]) +} + +func (li *CommitteeBits) UnmarshalText(text []byte) error { + return conv.DynamicBytesUnmarshalText((*[]byte)(li), text) +} + +func (li CommitteeBits) String() string { + return conv.BytesString(li[:]) +} + +func (li CommitteeBits) GetBit(i uint64) bool { + return bitfields.GetBit(li, i) +} + +func (li CommitteeBits) SetBit(i uint64, v bool) { + bitfields.SetBit(li, i, v) +} + +type CommitteeBitsView struct { + *BitVectorView +} + +func AsCommitteeBits(v View, err error) (*CommitteeBitsView, error) { + c, err := AsBitVector(v, err) + return &CommitteeBitsView{c}, err +} + +func (v *CommitteeBitsView) Raw(spec *common.Spec) (CommitteeBits, error) { + byteLen := int((spec.MAX_COMMITTEES_PER_SLOT + 7) / 8) + var buf bytes.Buffer + buf.Grow(byteLen) + if err := v.Serialize(codec.NewEncodingWriter(&buf)); err != nil { + return nil, err + } + out := CommitteeBits(buf.Bytes()) + if len(out) != byteLen { + return nil, fmt.Errorf("failed to convert sync committee tree bits view to raw bits") + } + return out, nil +} + +func CommitteeBitsType(spec *common.Spec) *BitVectorTypeDef { + return BitVectorType(uint64(spec.MAX_COMMITTEES_PER_SLOT)) +} diff --git a/eth2/beacon/electra/fork.go b/eth2/beacon/electra/fork.go new file mode 100644 index 0000000..d1295c9 --- /dev/null +++ b/eth2/beacon/electra/fork.go @@ -0,0 +1,12 @@ +package electra + +import ( + "errors" + + "github.com/protolambda/zrnt/eth2/beacon/common" + "github.com/protolambda/zrnt/eth2/beacon/deneb" +) + +func UpgradeToElectra(spec *common.Spec, epc *common.EpochsContext, pre *deneb.BeaconStateView) (*BeaconStateView, error) { + return nil, errors.New("upgrade of deneb state to electra state is not supported") +} diff --git a/eth2/beacon/electra/requests.go b/eth2/beacon/electra/requests.go new file mode 100644 index 0000000..2e75fb7 --- /dev/null +++ b/eth2/beacon/electra/requests.go @@ -0,0 +1,45 @@ +package electra + +import ( + "github.com/protolambda/zrnt/eth2/beacon/common" + "github.com/protolambda/ztyp/codec" + "github.com/protolambda/ztyp/tree" + . "github.com/protolambda/ztyp/view" +) + +type ExecutionRequests struct { + // [New in Electra:EIP6110] + Deposits common.DepositRequests `json:"deposits" yaml:"deposits"` + // [New in Electra:EIP7002:EIP7251] + Withdrawals common.WithdrawalRequests `json:"withdrawals" yaml:"withdrawals"` + // [New in Electra:EIP7251] + Consolidations common.ConsolidationRequests `json:"consolidations" yaml:"consolidations"` +} + +func ExecutionRequestsType(spec *common.Spec) *ContainerTypeDef { + return ContainerType("ExecutionRequests", []FieldDef{ + {"deposits", common.DepositRequestsType(spec)}, + {"withdrawals", common.WithdrawalRequestsType(spec)}, + {"consolidations", common.ConsolidationRequestsType(spec)}, + }) +} + +func (p *ExecutionRequests) Deserialize(spec *common.Spec, dr *codec.DecodingReader) error { + return dr.Container(spec.Wrap(&p.Deposits), spec.Wrap(&p.Withdrawals), spec.Wrap(&p.Consolidations)) +} + +func (p *ExecutionRequests) Serialize(spec *common.Spec, w *codec.EncodingWriter) error { + return w.Container(spec.Wrap(&p.Deposits), spec.Wrap(&p.Withdrawals), spec.Wrap(&p.Consolidations)) +} + +func (p *ExecutionRequests) ByteLength(spec *common.Spec) uint64 { + return codec.ContainerLength(spec.Wrap(&p.Deposits), spec.Wrap(&p.Withdrawals), spec.Wrap(&p.Consolidations)) +} + +func (*ExecutionRequests) FixedLength(*common.Spec) uint64 { + return 0 +} + +func (p *ExecutionRequests) HashTreeRoot(spec *common.Spec, hFn tree.HashFn) common.Root { + return hFn.HashTreeRoot(spec.Wrap(&p.Deposits), spec.Wrap(&p.Withdrawals), spec.Wrap(&p.Consolidations)) +} diff --git a/eth2/beacon/electra/state.go b/eth2/beacon/electra/state.go new file mode 100644 index 0000000..e9601fe --- /dev/null +++ b/eth2/beacon/electra/state.go @@ -0,0 +1,707 @@ +package electra + +import ( + "bytes" + + "github.com/protolambda/ztyp/codec" + "github.com/protolambda/ztyp/tree" + . "github.com/protolambda/ztyp/view" + + "github.com/protolambda/zrnt/eth2/beacon/altair" + "github.com/protolambda/zrnt/eth2/beacon/capella" + "github.com/protolambda/zrnt/eth2/beacon/common" + "github.com/protolambda/zrnt/eth2/beacon/deneb" + "github.com/protolambda/zrnt/eth2/beacon/phase0" +) + +type BeaconState struct { + // Versioning + GenesisTime common.Timestamp `json:"genesis_time" yaml:"genesis_time"` + GenesisValidatorsRoot common.Root `json:"genesis_validators_root" yaml:"genesis_validators_root"` + Slot common.Slot `json:"slot" yaml:"slot"` + Fork common.Fork `json:"fork" yaml:"fork"` + // History + LatestBlockHeader common.BeaconBlockHeader `json:"latest_block_header" yaml:"latest_block_header"` + BlockRoots phase0.HistoricalBatchRoots `json:"block_roots" yaml:"block_roots"` + StateRoots phase0.HistoricalBatchRoots `json:"state_roots" yaml:"state_roots"` + HistoricalRoots phase0.HistoricalRoots `json:"historical_roots" yaml:"historical_roots"` // Frozen in Capella, replaced by historical_summaries + // Eth1 + Eth1Data common.Eth1Data `json:"eth1_data" yaml:"eth1_data"` + Eth1DataVotes phase0.Eth1DataVotes `json:"eth1_data_votes" yaml:"eth1_data_votes"` + Eth1DepositIndex common.DepositIndex `json:"eth1_deposit_index" yaml:"eth1_deposit_index"` + // Registry + Validators phase0.ValidatorRegistry `json:"validators" yaml:"validators"` + Balances phase0.Balances `json:"balances" yaml:"balances"` + RandaoMixes phase0.RandaoMixes `json:"randao_mixes" yaml:"randao_mixes"` + Slashings phase0.SlashingsHistory `json:"slashings" yaml:"slashings"` + // Participation + PreviousEpochParticipation altair.ParticipationRegistry `json:"previous_epoch_participation" yaml:"previous_epoch_participation"` + CurrentEpochParticipation altair.ParticipationRegistry `json:"current_epoch_participation" yaml:"current_epoch_participation"` + // Finality + JustificationBits common.JustificationBits `json:"justification_bits" yaml:"justification_bits"` + PreviousJustifiedCheckpoint common.Checkpoint `json:"previous_justified_checkpoint" yaml:"previous_justified_checkpoint"` + CurrentJustifiedCheckpoint common.Checkpoint `json:"current_justified_checkpoint" yaml:"current_justified_checkpoint"` + FinalizedCheckpoint common.Checkpoint `json:"finalized_checkpoint" yaml:"finalized_checkpoint"` + // Inactivity + InactivityScores altair.InactivityScores `json:"inactivity_scores" yaml:"inactivity_scores"` + // Light client sync committees + CurrentSyncCommittee common.SyncCommittee `json:"current_sync_committee" yaml:"current_sync_committee"` + NextSyncCommittee common.SyncCommittee `json:"next_sync_committee" yaml:"next_sync_committee"` + // Execution-layer (modified in EIP-4844) + LatestExecutionPayloadHeader deneb.ExecutionPayloadHeader `json:"latest_execution_payload_header" yaml:"latest_execution_payload_header"` + // Withdrawals + NextWithdrawalIndex common.WithdrawalIndex `json:"next_withdrawal_index" yaml:"next_withdrawal_index"` + NextWithdrawalValidatorIndex common.ValidatorIndex `json:"next_withdrawal_validator_index" yaml:"next_withdrawal_validator_index"` + // Deep history valid from Capella onwards + HistoricalSummaries capella.HistoricalSummaries `json:"historical_summaries"` + // [New in Electra:EIP6110] + DepositRequestsStartIndex Uint64View `json:"deposit_requests_start_index" yaml:"deposit_requests_start_index"` + // [New in Electra:EIP7251] + DepositBalanceToConsume common.Gwei `json:"deposit_balance_to_consume" yaml:"deposit_balance_to_consume"` + // [New in Electra:EIP7251] + ExitBalanceToConsume common.Gwei `json:"exit_balance_to_consume" yaml:"exit_balance_to_consume"` + // [New in Electra:EIP7251] + EarliestExitEpoch common.Epoch `json:"earliest_exit_epoch" yaml:"earliest_exit_epoch"` + // [New in Electra:EIP7251] + ConsolidationBalanceToConsume common.Gwei `json:"consolidation_balance_to_consume" yaml:"consolidation_balance_to_consume"` + // [New in Electra:EIP7251] + EarliestConsolidationEpoch common.Epoch `json:"earliest_consolidation_epoch" yaml:"earliest_consolidation_epoch"` + // [New in Electra:EIP7251] + PendingDeposits common.PendingDeposits `json:"pending_deposits" yaml:"pending_deposits"` + // [New in Electra:EIP7251] + PendingPartialWithdrawals common.PendingPartialWithdrawals `json:"pending_partial_withdrawals" yaml:"pending_partial_withdrawals"` + // [New in Electra:EIP7251] + PendingConsolidations common.PendingConsolidations `json:"pending_consolidations" yaml:"pending_consolidations"` +} + +func (v *BeaconState) Deserialize(spec *common.Spec, dr *codec.DecodingReader) error { + return dr.Container(&v.GenesisTime, &v.GenesisValidatorsRoot, + &v.Slot, &v.Fork, &v.LatestBlockHeader, + spec.Wrap(&v.BlockRoots), spec.Wrap(&v.StateRoots), spec.Wrap(&v.HistoricalRoots), + &v.Eth1Data, spec.Wrap(&v.Eth1DataVotes), &v.Eth1DepositIndex, + spec.Wrap(&v.Validators), spec.Wrap(&v.Balances), + spec.Wrap(&v.RandaoMixes), spec.Wrap(&v.Slashings), + spec.Wrap(&v.PreviousEpochParticipation), spec.Wrap(&v.CurrentEpochParticipation), + &v.JustificationBits, + &v.PreviousJustifiedCheckpoint, &v.CurrentJustifiedCheckpoint, + &v.FinalizedCheckpoint, + spec.Wrap(&v.InactivityScores), + spec.Wrap(&v.CurrentSyncCommittee), spec.Wrap(&v.NextSyncCommittee), + &v.LatestExecutionPayloadHeader, + &v.NextWithdrawalIndex, &v.NextWithdrawalValidatorIndex, + spec.Wrap(&v.HistoricalSummaries), + &v.DepositRequestsStartIndex, &v.DepositBalanceToConsume, + &v.ExitBalanceToConsume, &v.EarliestExitEpoch, + &v.ConsolidationBalanceToConsume, &v.EarliestConsolidationEpoch, + spec.Wrap(&v.PendingDeposits), + spec.Wrap(&v.PendingPartialWithdrawals), + spec.Wrap(&v.PendingConsolidations), + ) +} + +func (v *BeaconState) Serialize(spec *common.Spec, w *codec.EncodingWriter) error { + return w.Container(&v.GenesisTime, &v.GenesisValidatorsRoot, + &v.Slot, &v.Fork, &v.LatestBlockHeader, + spec.Wrap(&v.BlockRoots), spec.Wrap(&v.StateRoots), spec.Wrap(&v.HistoricalRoots), + &v.Eth1Data, spec.Wrap(&v.Eth1DataVotes), &v.Eth1DepositIndex, + spec.Wrap(&v.Validators), spec.Wrap(&v.Balances), + spec.Wrap(&v.RandaoMixes), spec.Wrap(&v.Slashings), + spec.Wrap(&v.PreviousEpochParticipation), spec.Wrap(&v.CurrentEpochParticipation), + &v.JustificationBits, + &v.PreviousJustifiedCheckpoint, &v.CurrentJustifiedCheckpoint, + &v.FinalizedCheckpoint, + spec.Wrap(&v.InactivityScores), + spec.Wrap(&v.CurrentSyncCommittee), spec.Wrap(&v.NextSyncCommittee), + &v.LatestExecutionPayloadHeader, + &v.NextWithdrawalIndex, &v.NextWithdrawalValidatorIndex, + spec.Wrap(&v.HistoricalSummaries), + &v.DepositRequestsStartIndex, &v.DepositBalanceToConsume, + &v.ExitBalanceToConsume, &v.EarliestExitEpoch, + &v.ConsolidationBalanceToConsume, &v.EarliestConsolidationEpoch, + spec.Wrap(&v.PendingDeposits), + spec.Wrap(&v.PendingPartialWithdrawals), + spec.Wrap(&v.PendingConsolidations), + ) +} + +func (v *BeaconState) ByteLength(spec *common.Spec) uint64 { + return codec.ContainerLength(&v.GenesisTime, &v.GenesisValidatorsRoot, + &v.Slot, &v.Fork, &v.LatestBlockHeader, + spec.Wrap(&v.BlockRoots), spec.Wrap(&v.StateRoots), spec.Wrap(&v.HistoricalRoots), + &v.Eth1Data, spec.Wrap(&v.Eth1DataVotes), &v.Eth1DepositIndex, + spec.Wrap(&v.Validators), spec.Wrap(&v.Balances), + spec.Wrap(&v.RandaoMixes), spec.Wrap(&v.Slashings), + spec.Wrap(&v.PreviousEpochParticipation), spec.Wrap(&v.CurrentEpochParticipation), + &v.JustificationBits, + &v.PreviousJustifiedCheckpoint, &v.CurrentJustifiedCheckpoint, + &v.FinalizedCheckpoint, + spec.Wrap(&v.InactivityScores), + spec.Wrap(&v.CurrentSyncCommittee), spec.Wrap(&v.NextSyncCommittee), + &v.LatestExecutionPayloadHeader, + &v.NextWithdrawalIndex, &v.NextWithdrawalValidatorIndex, + spec.Wrap(&v.HistoricalSummaries), + &v.DepositRequestsStartIndex, &v.DepositBalanceToConsume, + &v.ExitBalanceToConsume, &v.EarliestExitEpoch, + &v.ConsolidationBalanceToConsume, &v.EarliestConsolidationEpoch, + spec.Wrap(&v.PendingDeposits), + spec.Wrap(&v.PendingPartialWithdrawals), + spec.Wrap(&v.PendingConsolidations), + ) +} + +func (*BeaconState) FixedLength(*common.Spec) uint64 { + return 0 // dynamic size +} + +func (v *BeaconState) HashTreeRoot(spec *common.Spec, hFn tree.HashFn) common.Root { + return hFn.HashTreeRoot(&v.GenesisTime, &v.GenesisValidatorsRoot, + &v.Slot, &v.Fork, &v.LatestBlockHeader, + spec.Wrap(&v.BlockRoots), spec.Wrap(&v.StateRoots), spec.Wrap(&v.HistoricalRoots), + &v.Eth1Data, spec.Wrap(&v.Eth1DataVotes), &v.Eth1DepositIndex, + spec.Wrap(&v.Validators), spec.Wrap(&v.Balances), + spec.Wrap(&v.RandaoMixes), spec.Wrap(&v.Slashings), + spec.Wrap(&v.PreviousEpochParticipation), spec.Wrap(&v.CurrentEpochParticipation), + &v.JustificationBits, + &v.PreviousJustifiedCheckpoint, &v.CurrentJustifiedCheckpoint, + &v.FinalizedCheckpoint, + spec.Wrap(&v.InactivityScores), + spec.Wrap(&v.CurrentSyncCommittee), spec.Wrap(&v.NextSyncCommittee), + &v.LatestExecutionPayloadHeader, + &v.NextWithdrawalIndex, &v.NextWithdrawalValidatorIndex, + spec.Wrap(&v.HistoricalSummaries), + &v.DepositRequestsStartIndex, &v.DepositBalanceToConsume, + &v.ExitBalanceToConsume, &v.EarliestExitEpoch, + &v.ConsolidationBalanceToConsume, &v.EarliestConsolidationEpoch, + spec.Wrap(&v.PendingDeposits), + spec.Wrap(&v.PendingPartialWithdrawals), + spec.Wrap(&v.PendingConsolidations), + ) +} + +// Hack to make state fields consistent and verifiable without using many hardcoded indices +// A trade-off to interpret the state as tree, without generics, and access fields by index very fast. +const ( + _stateGenesisTime = iota + _stateGenesisValidatorsRoot + _stateSlot + _stateFork + _stateLatestBlockHeader + _stateBlockRoots + _stateStateRoots + _stateHistoricalRoots + _stateEth1Data + _stateEth1DataVotes + _stateEth1DepositIndex + _stateValidators + _stateBalances + _stateRandaoMixes + _stateSlashings + _statePreviousEpochParticipation + _stateCurrentEpochParticipation + _stateJustificationBits + _statePreviousJustifiedCheckpoint + _stateCurrentJustifiedCheckpoint + _stateFinalizedCheckpoint + _inactivityScores + _currentSyncCommittee + _nextSyncCommittee + _latestExecutionPayloadHeader + _nextWithdrawalIndex + _nextWithdrawalValidatorIndex + _historicalSummaries + _depositRequestsStartIndex + _depositBalanceToConsume + _exitBalanceToConsume + _earliestExitEpoch + _consolidationBalanceToConsume + _earliestConsolidationEpoch + _pendingDeposits + _pendingPartialWithdrawals + _pendingConsolidations +) + +func BeaconStateType(spec *common.Spec) *ContainerTypeDef { + return ContainerType("BeaconState", []FieldDef{ + // Versioning + {"genesis_time", Uint64Type}, + {"genesis_validators_root", RootType}, + {"slot", common.SlotType}, + {"fork", common.ForkType}, + // History + {"latest_block_header", common.BeaconBlockHeaderType}, + {"block_roots", phase0.BatchRootsType(spec)}, + {"state_roots", phase0.BatchRootsType(spec)}, + {"historical_roots", phase0.HistoricalRootsType(spec)}, + // Eth1 + {"eth1_data", common.Eth1DataType}, + {"eth1_data_votes", phase0.Eth1DataVotesType(spec)}, + {"eth1_deposit_index", Uint64Type}, + // Registry + {"validators", phase0.ValidatorsRegistryType(spec)}, + {"balances", phase0.RegistryBalancesType(spec)}, + // Randomness + {"randao_mixes", phase0.RandaoMixesType(spec)}, + // Slashings + {"slashings", phase0.SlashingsType(spec)}, + // Participation + {"previous_epoch_participation", altair.ParticipationRegistryType(spec)}, + {"current_epoch_participation", altair.ParticipationRegistryType(spec)}, + // Finality + {"justification_bits", common.JustificationBitsType}, + {"previous_justified_checkpoint", common.CheckpointType}, + {"current_justified_checkpoint", common.CheckpointType}, + {"finalized_checkpoint", common.CheckpointType}, + // Inactivity + {"inactivity_scores", altair.InactivityScoresType(spec)}, + // Sync + {"current_sync_committee", common.SyncCommitteeType(spec)}, + {"next_sync_committee", common.SyncCommitteeType(spec)}, + // Execution-layer + {"latest_execution_payload_header", deneb.ExecutionPayloadHeaderType}, + // Withdrawals + {"next_withdrawal_index", common.WithdrawalIndexType}, + {"next_withdrawal_validator_index", common.ValidatorIndexType}, + // Deep history valid from Capella onwards + {"historical_summaries", capella.HistoricalSummariesType(spec)}, + {"deposit_requests_start_index", Uint64Type}, + {"deposit_balance_to_consume", common.GweiType}, + {"exit_balance_to_consume", common.GweiType}, + {"earliest_exit_epoch", common.EpochType}, + {"consolidation_balance_to_consume", common.GweiType}, + {"earliest_consolidation_epoch", common.EpochType}, + {"pending_deposits", common.PendingDepositsType(spec)}, + {"pending_partial_withdrawals", common.PendingPartialWithdrawalsType(spec)}, + {"pending_consolidations", common.PendingConsolidationsType(spec)}, + }) +} + +// To load a state: +// +// state, err := beacon.AsBeaconStateView(beacon.BeaconStateType.Deserialize(codec.NewDecodingReader(reader, size))) +func AsBeaconStateView(v View, err error) (*BeaconStateView, error) { + c, err := AsContainer(v, err) + return &BeaconStateView{c}, err +} + +type BeaconStateView struct { + *ContainerView +} + +var _ common.BeaconState = (*phase0.BeaconStateView)(nil) + +func NewBeaconStateView(spec *common.Spec) *BeaconStateView { + return &BeaconStateView{ContainerView: BeaconStateType(spec).New()} +} + +func (state *BeaconStateView) GenesisTime() (common.Timestamp, error) { + return common.AsTimestamp(state.Get(_stateGenesisTime)) +} + +func (state *BeaconStateView) SetGenesisTime(t common.Timestamp) error { + return state.Set(_stateGenesisTime, Uint64View(t)) +} + +func (state *BeaconStateView) GenesisValidatorsRoot() (common.Root, error) { + return AsRoot(state.Get(_stateGenesisValidatorsRoot)) +} + +func (state *BeaconStateView) SetGenesisValidatorsRoot(r common.Root) error { + rv := RootView(r) + return state.Set(_stateGenesisValidatorsRoot, &rv) +} + +func (state *BeaconStateView) Slot() (common.Slot, error) { + return common.AsSlot(state.Get(_stateSlot)) +} + +func (state *BeaconStateView) SetSlot(slot common.Slot) error { + return state.Set(_stateSlot, Uint64View(slot)) +} + +func (state *BeaconStateView) Fork() (common.Fork, error) { + fv, err := common.AsFork(state.Get(_stateFork)) + if err != nil { + return common.Fork{}, err + } + return fv.Raw() +} + +func (state *BeaconStateView) SetFork(f common.Fork) error { + return state.Set(_stateFork, f.View()) +} + +func (state *BeaconStateView) LatestBlockHeader() (*common.BeaconBlockHeader, error) { + h, err := common.AsBeaconBlockHeader(state.Get(_stateLatestBlockHeader)) + if err != nil { + return nil, err + } + return h.Raw() +} + +func (state *BeaconStateView) SetLatestBlockHeader(v *common.BeaconBlockHeader) error { + return state.Set(_stateLatestBlockHeader, v.View()) +} + +func (state *BeaconStateView) BlockRoots() (common.BatchRoots, error) { + return phase0.AsBatchRoots(state.Get(_stateBlockRoots)) +} + +func (state *BeaconStateView) StateRoots() (common.BatchRoots, error) { + return phase0.AsBatchRoots(state.Get(_stateStateRoots)) +} + +func (state *BeaconStateView) HistoricalRoots() (common.HistoricalRoots, error) { + return phase0.AsHistoricalRoots(state.Get(_stateHistoricalRoots)) +} + +func (state *BeaconStateView) Eth1Data() (common.Eth1Data, error) { + dat, err := common.AsEth1Data(state.Get(_stateEth1Data)) + if err != nil { + return common.Eth1Data{}, err + } + return dat.Raw() +} + +func (state *BeaconStateView) SetEth1Data(v common.Eth1Data) error { + return state.Set(_stateEth1Data, v.View()) +} + +func (state *BeaconStateView) Eth1DataVotes() (common.Eth1DataVotes, error) { + return phase0.AsEth1DataVotes(state.Get(_stateEth1DataVotes)) +} + +func (state *BeaconStateView) Eth1DepositIndex() (common.DepositIndex, error) { + return common.AsDepositIndex(state.Get(_stateEth1DepositIndex)) +} + +func (state *BeaconStateView) IncrementDepositIndex() error { + depIndex, err := state.Eth1DepositIndex() + if err != nil { + return err + } + return state.Set(_stateEth1DepositIndex, Uint64View(depIndex+1)) +} + +func (state *BeaconStateView) Validators() (common.ValidatorRegistry, error) { + return phase0.AsValidatorsRegistry(state.Get(_stateValidators)) +} + +func (state *BeaconStateView) Balances() (common.BalancesRegistry, error) { + return phase0.AsRegistryBalances(state.Get(_stateBalances)) +} + +func (state *BeaconStateView) SetBalances(balances []common.Gwei) error { + typ := state.Fields[_stateBalances].Type.(*BasicListTypeDef) + balancesView, err := phase0.Balances(balances).View(typ.ListLimit) + if err != nil { + return err + } + return state.Set(_stateBalances, balancesView) +} + +func (state *BeaconStateView) AddValidator(spec *common.Spec, pub common.BLSPubkey, withdrawalCreds common.Root, balance common.Gwei) error { + effBalance := balance - (balance % spec.EFFECTIVE_BALANCE_INCREMENT) + if effBalance > spec.MAX_EFFECTIVE_BALANCE { + effBalance = spec.MAX_EFFECTIVE_BALANCE + } + validatorRaw := phase0.Validator{ + Pubkey: pub, + WithdrawalCredentials: withdrawalCreds, + ActivationEligibilityEpoch: common.FAR_FUTURE_EPOCH, + ActivationEpoch: common.FAR_FUTURE_EPOCH, + ExitEpoch: common.FAR_FUTURE_EPOCH, + WithdrawableEpoch: common.FAR_FUTURE_EPOCH, + EffectiveBalance: effBalance, + } + validators, err := phase0.AsValidatorsRegistry(state.Get(_stateValidators)) + if err != nil { + return err + } + if err := validators.Append(validatorRaw.View()); err != nil { + return err + } + bals, err := state.Balances() + if err != nil { + return err + } + if err := bals.AppendBalance(balance); err != nil { + return err + } + // New in Altair: init participation + prevPart, err := state.PreviousEpochParticipation() + if err != nil { + return err + } + if err := prevPart.Append(Uint8View(altair.ParticipationFlags(0))); err != nil { + return err + } + currPart, err := state.CurrentEpochParticipation() + if err != nil { + return err + } + if err := currPart.Append(Uint8View(altair.ParticipationFlags(0))); err != nil { + return err + } + inActivityScores, err := state.InactivityScores() + if err != nil { + return err + } + if err := inActivityScores.Append(Uint8View(0)); err != nil { + return err + } + // New in Altair: init inactivity score + return nil +} + +func (state *BeaconStateView) RandaoMixes() (common.RandaoMixes, error) { + return phase0.AsRandaoMixes(state.Get(_stateRandaoMixes)) +} + +func (state *BeaconStateView) SeedRandao(spec *common.Spec, seed common.Root) error { + v, err := phase0.SeedRandao(spec, seed) + if err != nil { + return err + } + return state.Set(_stateRandaoMixes, v) +} + +func (state *BeaconStateView) Slashings() (common.Slashings, error) { + return phase0.AsSlashings(state.Get(_stateSlashings)) +} + +func (state *BeaconStateView) PreviousEpochParticipation() (*altair.ParticipationRegistryView, error) { + return altair.AsParticipationRegistry(state.Get(_statePreviousEpochParticipation)) +} + +func (state *BeaconStateView) CurrentEpochParticipation() (*altair.ParticipationRegistryView, error) { + return altair.AsParticipationRegistry(state.Get(_stateCurrentEpochParticipation)) +} + +func (state *BeaconStateView) JustificationBits() (common.JustificationBits, error) { + b, err := common.AsJustificationBits(state.Get(_stateJustificationBits)) + if err != nil { + return common.JustificationBits{}, err + } + return b.Raw() +} + +func (state *BeaconStateView) SetJustificationBits(bits common.JustificationBits) error { + b, err := common.AsJustificationBits(state.Get(_stateJustificationBits)) + if err != nil { + return err + } + return b.Set(bits) +} + +func (state *BeaconStateView) PreviousJustifiedCheckpoint() (common.Checkpoint, error) { + c, err := common.AsCheckPoint(state.Get(_statePreviousJustifiedCheckpoint)) + if err != nil { + return common.Checkpoint{}, err + } + return c.Raw() +} + +func (state *BeaconStateView) SetPreviousJustifiedCheckpoint(c common.Checkpoint) error { + v, err := common.AsCheckPoint(state.Get(_statePreviousJustifiedCheckpoint)) + if err != nil { + return err + } + return v.Set(&c) +} + +func (state *BeaconStateView) CurrentJustifiedCheckpoint() (common.Checkpoint, error) { + c, err := common.AsCheckPoint(state.Get(_stateCurrentJustifiedCheckpoint)) + if err != nil { + return common.Checkpoint{}, err + } + return c.Raw() +} + +func (state *BeaconStateView) SetCurrentJustifiedCheckpoint(c common.Checkpoint) error { + v, err := common.AsCheckPoint(state.Get(_stateCurrentJustifiedCheckpoint)) + if err != nil { + return err + } + return v.Set(&c) +} + +func (state *BeaconStateView) FinalizedCheckpoint() (common.Checkpoint, error) { + c, err := common.AsCheckPoint(state.Get(_stateFinalizedCheckpoint)) + if err != nil { + return common.Checkpoint{}, err + } + return c.Raw() +} + +func (state *BeaconStateView) SetFinalizedCheckpoint(c common.Checkpoint) error { + v, err := common.AsCheckPoint(state.Get(_stateFinalizedCheckpoint)) + if err != nil { + return err + } + return v.Set(&c) +} + +func (state *BeaconStateView) InactivityScores() (*altair.InactivityScoresView, error) { + return altair.AsInactivityScores(state.Get(_inactivityScores)) +} + +func (state *BeaconStateView) CurrentSyncCommittee() (*common.SyncCommitteeView, error) { + return common.AsSyncCommittee(state.Get(_currentSyncCommittee)) +} + +func (state *BeaconStateView) SetCurrentSyncCommittee(v *common.SyncCommitteeView) error { + return state.Set(_currentSyncCommittee, v) +} + +func (state *BeaconStateView) NextSyncCommittee() (*common.SyncCommitteeView, error) { + return common.AsSyncCommittee(state.Get(_nextSyncCommittee)) +} + +func (state *BeaconStateView) SetNextSyncCommittee(v *common.SyncCommitteeView) error { + return state.Set(_nextSyncCommittee, v) +} + +func (state *BeaconStateView) RotateSyncCommittee(next *common.SyncCommitteeView) error { + v, err := state.Get(_nextSyncCommittee) + if err != nil { + return err + } + if err := state.Set(_currentSyncCommittee, v); err != nil { + return err + } + return state.Set(_nextSyncCommittee, next) +} + +func (state *BeaconStateView) LatestExecutionPayloadHeader() (*deneb.ExecutionPayloadHeaderView, error) { + return deneb.AsExecutionPayloadHeader(state.Get(_latestExecutionPayloadHeader)) +} + +func (state *BeaconStateView) SetLatestExecutionPayloadHeader(h *deneb.ExecutionPayloadHeader) error { + return state.Set(_latestExecutionPayloadHeader, h.View()) +} + +func (state *BeaconStateView) NextWithdrawalIndex() (common.WithdrawalIndex, error) { + v, err := state.Get(_nextWithdrawalIndex) + return common.AsWithdrawalIndex(v, err) +} + +func (state *BeaconStateView) IncrementNextWithdrawalIndex() error { + nextIndex, err := state.NextWithdrawalIndex() + if err != nil { + return err + } + return state.Set(_nextWithdrawalIndex, Uint64View(nextIndex+1)) +} + +func (state *BeaconStateView) SetNextWithdrawalIndex(nextIndex common.WithdrawalIndex) error { + return state.Set(_nextWithdrawalIndex, Uint64View(nextIndex)) +} + +func (state *BeaconStateView) NextWithdrawalValidatorIndex() (common.ValidatorIndex, error) { + v, err := state.Get(_nextWithdrawalValidatorIndex) + return common.AsValidatorIndex(v, err) +} + +func (state *BeaconStateView) SetNextWithdrawalValidatorIndex(nextValidator common.ValidatorIndex) error { + return state.Set(_nextWithdrawalValidatorIndex, Uint64View(nextValidator)) +} + +func (state *BeaconStateView) HistoricalSummaries() (capella.HistoricalSummariesList, error) { + v, err := state.Get(_historicalSummaries) + return capella.AsHistoricalSummaries(v, err) +} + +func (state *BeaconStateView) DepositRequestsStartIndex() (Uint64View, error) { + v, err := state.Get(_depositRequestsStartIndex) + return AsUint64(v, err) +} + +func (state *BeaconStateView) SetDepositRequestsStartIndex(v Uint64View) error { + return state.Set(_depositRequestsStartIndex, &v) +} + +func (state *BeaconStateView) DepositBalanceToConsume() (common.Gwei, error) { + v, err := state.Get(_depositBalanceToConsume) + return common.AsGwei(v, err) +} + +func (state *BeaconStateView) SetDepositBalanceToConsume(v common.Gwei) error { + return state.Set(_depositBalanceToConsume, (*Uint64View)(&v)) +} + +func (state *BeaconStateView) ExitBalanceToConsume() (common.Gwei, error) { + v, err := state.Get(_exitBalanceToConsume) + return common.AsGwei(v, err) +} + +func (state *BeaconStateView) SetExitBalanceToConsume(v common.Gwei) error { + return state.Set(_exitBalanceToConsume, Uint64View(v)) +} + +func (state *BeaconStateView) EarliestExitEpoch() (common.Epoch, error) { + v, err := state.Get(_earliestExitEpoch) + return common.AsEpoch(v, err) +} + +func (state *BeaconStateView) SetEarliestExitEpoch(v common.Epoch) error { + return state.Set(_earliestExitEpoch, Uint64View(v)) +} + +func (state *BeaconStateView) ConsolidationBalanceToConsume() (common.Gwei, error) { + v, err := state.Get(_consolidationBalanceToConsume) + return common.AsGwei(v, err) +} + +func (state *BeaconStateView) SetConsolidationBalanceToConsume(v common.Gwei) error { + return state.Set(_consolidationBalanceToConsume, Uint64View(v)) +} + +func (state *BeaconStateView) EarliestConsolidationEpoch() (common.Epoch, error) { + v, err := state.Get(_earliestConsolidationEpoch) + return common.AsEpoch(v, err) +} + +func (state *BeaconStateView) SetEarliestConsolidationEpoch(v common.Epoch) error { + return state.Set(_earliestConsolidationEpoch, Uint64View(v)) +} + +// not exposed via getters yet (yet): +//_pendingDeposits +//_pendingPartialWithdrawals +//_pendingConsolidations + +func (state *BeaconStateView) ForkSettings(spec *common.Spec) *common.ForkSettings { + return &common.ForkSettings{ + MinSlashingPenaltyQuotient: uint64(spec.MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX), + ProportionalSlashingMultiplier: uint64(spec.PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX), + InactivityPenaltyQuotient: uint64(spec.INACTIVITY_PENALTY_QUOTIENT_BELLATRIX), + CalcProposerShare: func(whistleblowerReward common.Gwei) common.Gwei { + return whistleblowerReward * altair.PROPOSER_WEIGHT / altair.WEIGHT_DENOMINATOR + }, + } +} + +// Raw converts the tree-structured state into a flattened native Go structure. +func (state *BeaconStateView) Raw(spec *common.Spec) (*BeaconState, error) { + var buf bytes.Buffer + if err := state.Serialize(codec.NewEncodingWriter(&buf)); err != nil { + return nil, err + } + var raw BeaconState + err := raw.Deserialize(spec, codec.NewDecodingReader(bytes.NewReader(buf.Bytes()), uint64(len(buf.Bytes())))) + if err != nil { + return nil, err + } + return &raw, nil +} + +func (state *BeaconStateView) CopyState() (common.BeaconState, error) { + return AsBeaconStateView(state.ContainerView.Copy()) +} + +type ExecutionTrackingBeaconState interface { + common.BeaconState + + LatestExecutionPayloadHeader() (*deneb.ExecutionPayloadHeaderView, error) + SetLatestExecutionPayloadHeader(h *deneb.ExecutionPayloadHeader) error +} diff --git a/eth2/beacon/electra/transition.go b/eth2/beacon/electra/transition.go new file mode 100644 index 0000000..7948a30 --- /dev/null +++ b/eth2/beacon/electra/transition.go @@ -0,0 +1,16 @@ +package electra + +import ( + "context" + "errors" + + "github.com/protolambda/zrnt/eth2/beacon/common" +) + +func (state *BeaconStateView) ProcessEpoch(ctx context.Context, spec *common.Spec, epc *common.EpochsContext) error { + return errors.New("electra epoch processing is not supported") +} + +func (state *BeaconStateView) ProcessBlock(ctx context.Context, spec *common.Spec, epc *common.EpochsContext, benv *common.BeaconBlockEnvelope) error { + return errors.New("electra block processing is not supported") +} diff --git a/eth2/beacon/fork.go b/eth2/beacon/fork.go index c112051..a64aede 100644 --- a/eth2/beacon/fork.go +++ b/eth2/beacon/fork.go @@ -3,6 +3,7 @@ package beacon import ( "context" "fmt" + "github.com/protolambda/zrnt/eth2/beacon/electra" "github.com/protolambda/zrnt/eth2/beacon/altair" "github.com/protolambda/zrnt/eth2/beacon/bellatrix" @@ -19,6 +20,8 @@ type ForkDecoder struct { Bellatrix common.ForkDigest Capella common.ForkDigest Deneb common.ForkDigest + Electra common.ForkDigest + Fulu common.ForkDigest // TODO more forks } @@ -30,6 +33,8 @@ func NewForkDecoder(spec *common.Spec, genesisValRoot common.Root) *ForkDecoder Bellatrix: common.ComputeForkDigest(spec.BELLATRIX_FORK_VERSION, genesisValRoot), Capella: common.ComputeForkDigest(spec.CAPELLA_FORK_VERSION, genesisValRoot), Deneb: common.ComputeForkDigest(spec.DENEB_FORK_VERSION, genesisValRoot), + Electra: common.ComputeForkDigest(spec.ELECTRA_FORK_VERSION, genesisValRoot), + Fulu: common.ComputeForkDigest(spec.FULU_FORK_VERSION, genesisValRoot), } } @@ -50,6 +55,8 @@ func (d *ForkDecoder) BlockAllocator(digest common.ForkDigest) (func() OpaqueBlo return func() OpaqueBlock { return new(capella.SignedBeaconBlock) }, nil case d.Deneb: return func() OpaqueBlock { return new(deneb.SignedBeaconBlock) }, nil + case d.Electra: + return func() OpaqueBlock { return new(electra.SignedBeaconBlock) }, nil default: return nil, fmt.Errorf("unrecognized fork digest: %s", digest) } @@ -62,12 +69,14 @@ func (d *ForkDecoder) ForkDigest(epoch common.Epoch) common.ForkDigest { return d.Altair } else if epoch < d.Spec.CAPELLA_FORK_EPOCH { return d.Bellatrix - } else { - // only consider deneb if it's actually set equal or higher than capella, to ignore it if it's missing in a config. - if d.Spec.DENEB_FORK_EPOCH >= d.Spec.CAPELLA_FORK_EPOCH && epoch >= d.Spec.DENEB_FORK_EPOCH { - return d.Deneb - } + } else if epoch < d.Spec.DENEB_FORK_EPOCH { return d.Capella + } else if epoch < d.Spec.ELECTRA_FORK_EPOCH { + return d.Deneb + } else if epoch < d.Spec.FULU_FORK_EPOCH { + return d.Electra + } else { + return d.Fulu } } @@ -107,7 +116,14 @@ func (s *StandardUpgradeableBeaconState) UpgradeMaybe(ctx context.Context, spec if tpre, ok := s.BeaconState.(*capella.BeaconStateView); ok && slot == common.Slot(spec.DENEB_FORK_EPOCH)*spec.SLOTS_PER_EPOCH { post, err := deneb.UpgradeToDeneb(spec, epc, tpre) if err != nil { - return fmt.Errorf("failed to upgrade bellatrix to capella state: %v", err) + return fmt.Errorf("failed to upgrade capella to deneb state: %v", err) + } + s.BeaconState = post + } + if tpre, ok := s.BeaconState.(*deneb.BeaconStateView); ok && slot == common.Slot(spec.ELECTRA_FORK_EPOCH)*spec.SLOTS_PER_EPOCH { + post, err := electra.UpgradeToElectra(spec, epc, tpre) + if err != nil { + return fmt.Errorf("failed to upgrade deneb to electra state: %v", err) } s.BeaconState = post } @@ -173,6 +189,17 @@ func EnvelopeToSignedBeaconBlock(benv *common.BeaconBlockEnvelope) (common.SpecO }, Signature: benv.Signature, }, nil + case *electra.BeaconBlockBody: + return &electra.SignedBeaconBlock{ + Message: electra.BeaconBlock{ + Slot: benv.Slot, + ProposerIndex: benv.ProposerIndex, + ParentRoot: benv.ParentRoot, + StateRoot: benv.StateRoot, + Body: *x, + }, + Signature: benv.Signature, + }, nil default: return nil, fmt.Errorf("cannot convert beacon block envelope to full signed block, unrecognized body type: %T", x) } diff --git a/eth2/configs/cli.go b/eth2/configs/cli.go index b40b77a..1ce6132 100644 --- a/eth2/configs/cli.go +++ b/eth2/configs/cli.go @@ -19,6 +19,7 @@ type SpecOptions struct { BellatrixPreset string `ask:"--preset-bellatrix" help:"Eth2 bellatrix spec preset, name or path to YAML"` CapellaPreset string `ask:"--preset-capella" help:"Eth2 capella spec preset, name or path to YAML"` DenebPreset string `ask:"--preset-deneb" help:"Eth2 deneb spec preset, name or path to YAML"` + ElectraPreset string `ask:"--preset-electra" help:"Eth2 electra spec preset, name or path to YAML"` // TODO: execution engine config for Bellatrix // TODO: trusted setup config for Sharding @@ -31,6 +32,7 @@ type LegacyConfig struct { common.BellatrixPreset `yaml:",inline"` common.CapellaPreset `yaml:",inline"` common.DenebPreset `yaml:",inline"` + common.ElectraPreset `yaml:",inline"` common.Config `yaml:",inline"` } @@ -59,6 +61,7 @@ func (c *SpecOptions) Spec() (*common.Spec, error) { spec.BellatrixPreset = legacy.BellatrixPreset spec.CapellaPreset = legacy.CapellaPreset spec.DenebPreset = legacy.DenebPreset + spec.ElectraPreset = legacy.ElectraPreset spec.Config = legacy.Config } } @@ -158,6 +161,23 @@ func (c *SpecOptions) Spec() (*common.Spec, error) { return nil, fmt.Errorf("failed to decode deneb preset: %v", err) } } + + switch c.ElectraPreset { + case "mainnet": + spec.ElectraPreset = Mainnet.ElectraPreset + case "minimal": + spec.ElectraPreset = Minimal.ElectraPreset + default: + f, err := os.Open(c.ElectraPreset) + if err != nil { + return nil, fmt.Errorf("failed to open electra preset file: %v", err) + } + dec := yaml.NewDecoder(f) + if err := dec.Decode(&spec.ElectraPreset); err != nil { + return nil, fmt.Errorf("failed to decode electra preset: %v", err) + } + } + spec.ExecutionEngine = nil return &spec, nil } @@ -170,4 +190,5 @@ func (c *SpecOptions) Default() { c.BellatrixPreset = "mainnet" c.CapellaPreset = "mainnet" c.DenebPreset = "mainnet" + c.ElectraPreset = "mainnet" } diff --git a/eth2/configs/mainnet.go b/eth2/configs/mainnet.go index 87426ab..62fd5a9 100644 --- a/eth2/configs/mainnet.go +++ b/eth2/configs/mainnet.go @@ -1,137 +1,38 @@ package configs import ( - "github.com/protolambda/ztyp/view" - + _ "embed" "github.com/protolambda/zrnt/eth2/beacon/common" ) +//go:embed yamls/presets/mainnet/phase0.yaml +var mainnetPhase0Preset []byte + +//go:embed yamls/presets/mainnet/altair.yaml +var mainnetAltairPreset []byte + +//go:embed yamls/presets/mainnet/bellatrix.yaml +var mainnetBellatrixPreset []byte + +//go:embed yamls/presets/mainnet/capella.yaml +var mainnetCapellaPreset []byte + +//go:embed yamls/presets/mainnet/deneb.yaml +var mainnetDenebPreset []byte + +//go:embed yamls/presets/mainnet/electra.yaml +var mainnetElectraPreset []byte + +//go:embed yamls/configs/mainnet.yaml +var mainnetConfig []byte + var Mainnet = &common.Spec{ - Phase0Preset: common.Phase0Preset{ - MAX_COMMITTEES_PER_SLOT: 64, - TARGET_COMMITTEE_SIZE: 128, - MAX_VALIDATORS_PER_COMMITTEE: 2048, - SHUFFLE_ROUND_COUNT: 90, - HYSTERESIS_QUOTIENT: 4, - HYSTERESIS_DOWNWARD_MULTIPLIER: 1, - HYSTERESIS_UPWARD_MULTIPLIER: 5, - MIN_DEPOSIT_AMOUNT: 1000_000_000, - MAX_EFFECTIVE_BALANCE: 32_000_000_000, - EFFECTIVE_BALANCE_INCREMENT: 1_000_000_000, - MIN_ATTESTATION_INCLUSION_DELAY: 1, - SLOTS_PER_EPOCH: 32, - MIN_SEED_LOOKAHEAD: 1, - MAX_SEED_LOOKAHEAD: 4, - EPOCHS_PER_ETH1_VOTING_PERIOD: 64, - SLOTS_PER_HISTORICAL_ROOT: 8192, - MIN_EPOCHS_TO_INACTIVITY_PENALTY: 4, - EPOCHS_PER_HISTORICAL_VECTOR: 1 << 16, - EPOCHS_PER_SLASHINGS_VECTOR: 1 << 13, - HISTORICAL_ROOTS_LIMIT: 1 << 24, - VALIDATOR_REGISTRY_LIMIT: 1 << 40, - BASE_REWARD_FACTOR: 64, - PROPORTIONAL_SLASHING_MULTIPLIER: 1, - WHISTLEBLOWER_REWARD_QUOTIENT: 512, - PROPOSER_REWARD_QUOTIENT: 8, - INACTIVITY_PENALTY_QUOTIENT: 1 << 26, - MIN_SLASHING_PENALTY_QUOTIENT: 128, - MAX_PROPOSER_SLASHINGS: 16, - MAX_ATTESTER_SLASHINGS: 2, - MAX_ATTESTATIONS: 128, - MAX_DEPOSITS: 16, - MAX_VOLUNTARY_EXITS: 16, - }, - AltairPreset: common.AltairPreset{ - INACTIVITY_PENALTY_QUOTIENT_ALTAIR: 3 * (1 << 24), - MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR: 64, - PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR: 2, - SYNC_COMMITTEE_SIZE: 512, - EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 256, - MIN_SYNC_COMMITTEE_PARTICIPANTS: 1, - }, - BellatrixPreset: common.BellatrixPreset{ - INACTIVITY_PENALTY_QUOTIENT_BELLATRIX: 16777216, - MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX: 32, - PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX: 3, - MAX_BYTES_PER_TRANSACTION: 1073741824, - MAX_TRANSACTIONS_PER_PAYLOAD: 1048576, - BYTES_PER_LOGS_BLOOM: 256, - MAX_EXTRA_DATA_BYTES: 32, - }, - CapellaPreset: common.CapellaPreset{ - MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP: 16384, - MAX_BLS_TO_EXECUTION_CHANGES: 16, - MAX_WITHDRAWALS_PER_PAYLOAD: 16, - }, - DenebPreset: common.DenebPreset{ - FIELD_ELEMENTS_PER_BLOB: 4096, - MAX_BLOB_COMMITMENTS_PER_BLOCK: 4096, - KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: 17, - }, - Config: common.Config{ - PRESET_BASE: "mainnet", - CONFIG_NAME: "mainnet", - TERMINAL_TOTAL_DIFFICULTY: view.MustUint256("58750000000000000000000"), - TERMINAL_BLOCK_HASH: common.Bytes32{}, - TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: ^common.Epoch(0), - MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 1 << 14, - MIN_GENESIS_TIME: 1606824000, - GENESIS_FORK_VERSION: common.Version{0x00, 0x00, 0x00, 0x00}, - GENESIS_DELAY: 604800, - ALTAIR_FORK_VERSION: common.Version{0x01, 0x00, 0x00, 0x00}, - ALTAIR_FORK_EPOCH: common.Epoch(74240), - BELLATRIX_FORK_VERSION: common.Version{0x02, 0x00, 0x00, 0x00}, - BELLATRIX_FORK_EPOCH: common.Epoch(144896), - CAPELLA_FORK_VERSION: common.Version{0x03, 0x00, 0x00, 0x00}, - CAPELLA_FORK_EPOCH: common.Epoch(194048), - DENEB_FORK_VERSION: common.Version{0x04, 0x00, 0x00, 0x00}, - DENEB_FORK_EPOCH: common.Epoch(269568), - ELECTRA_FORK_VERSION: common.Version{0x05, 0x00, 0x00, 0x00}, - ELECTRA_FORK_EPOCH: ^common.Epoch(0), - FULU_FORK_VERSION: common.Version{0x06, 0x00, 0x00, 0x00}, - FULU_FORK_EPOCH: ^common.Epoch(0), - WHISK_FORK_VERSION: common.Version{0x08, 0x00, 0x00, 0x00}, - WHISK_FORK_EPOCH: ^common.Epoch(0), - SECONDS_PER_SLOT: 12, - SECONDS_PER_ETH1_BLOCK: 14, - MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256, - SHARD_COMMITTEE_PERIOD: 256, - ETH1_FOLLOW_DISTANCE: 2048, - INACTIVITY_SCORE_BIAS: 4, - INACTIVITY_SCORE_RECOVERY_RATE: 16, - EJECTION_BALANCE: 16_000_000_000, - MIN_PER_EPOCH_CHURN_LIMIT: 4, - CHURN_LIMIT_QUOTIENT: 1 << 16, - MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 8, - PROPOSER_SCORE_BOOST: 40, - REORG_HEAD_WEIGHT_THRESHOLD: 20, - REORG_PARENT_WEIGHT_THRESHOLD: 160, - REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2, - DEPOSIT_CHAIN_ID: 1, - DEPOSIT_NETWORK_ID: 1, - DEPOSIT_CONTRACT_ADDRESS: [20]byte{0x00, 0x00, 0x00, 0x00, 0x21, 0x9a, 0xb5, 0x40, 0x35, 0x6c, 0xBB, 0x83, 0x9C, 0xbe, 0x05, 0x30, 0x3d, 0x77, 0x05, 0xFa}, - GOSSIP_MAX_SIZE: 10 * (1 << 20), - MAX_REQUEST_BLOCKS: 1024, - EPOCHS_PER_SUBNET_SUBSCRIPTION: 256, - MIN_EPOCHS_FOR_BLOCK_REQUESTS: 33024, - MAX_CHUNK_SIZE: 10485760, - TTFB_TIMEOUT: 5, - RESP_TIMEOUT: 10, - ATTESTATION_PROPAGATION_SLOT_RANGE: 32, - MAXIMUM_GOSSIP_CLOCK_DISPARITY: 500, - MESSAGE_DOMAIN_INVALID_SNAPPY: common.NetworkMessageDomain{0, 0, 0, 0}, - MESSAGE_DOMAIN_VALID_SNAPPY: common.NetworkMessageDomain{1, 0, 0, 0}, - SUBNETS_PER_NODE: 2, - ATTESTATION_SUBNET_COUNT: 64, - ATTESTATION_SUBNET_EXTRA_BITS: 0, - ATTESTATION_SUBNET_PREFIX_BITS: 6, - MAX_REQUEST_BLOCKS_DENEB: 128, - MAX_REQUEST_BLOB_SIDECARS: 768, - MAX_BLOBS_PER_BLOCK: 6, - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: 4096, - BLOB_SIDECAR_SUBNET_COUNT: 6, - WHISK_EPOCHS_PER_SHUFFLING_PHASE: 256, - WHISK_PROPOSER_SELECTION_GAP: 2, - }, + Phase0Preset: mustYAML[common.Phase0Preset](mainnetPhase0Preset), + AltairPreset: mustYAML[common.AltairPreset](mainnetAltairPreset), + BellatrixPreset: mustYAML[common.BellatrixPreset](mainnetBellatrixPreset), + CapellaPreset: mustYAML[common.CapellaPreset](mainnetCapellaPreset), + DenebPreset: mustYAML[common.DenebPreset](mainnetDenebPreset), + ElectraPreset: mustYAML[common.ElectraPreset](mainnetElectraPreset), + Config: mustYAML[common.Config](mainnetConfig), ExecutionEngine: nil, } diff --git a/eth2/configs/minimal.go b/eth2/configs/minimal.go index 166b383..968b443 100644 --- a/eth2/configs/minimal.go +++ b/eth2/configs/minimal.go @@ -1,137 +1,38 @@ package configs import ( - "github.com/protolambda/ztyp/view" - + _ "embed" "github.com/protolambda/zrnt/eth2/beacon/common" ) +//go:embed yamls/presets/minimal/phase0.yaml +var minimalPhase0Preset []byte + +//go:embed yamls/presets/minimal/altair.yaml +var minimalAltairPreset []byte + +//go:embed yamls/presets/minimal/bellatrix.yaml +var minimalBellatrixPreset []byte + +//go:embed yamls/presets/minimal/capella.yaml +var minimalCapellaPreset []byte + +//go:embed yamls/presets/minimal/deneb.yaml +var minimalDenebPreset []byte + +//go:embed yamls/presets/minimal/electra.yaml +var minimalElectraPreset []byte + +//go:embed yamls/configs/minimal.yaml +var minimalConfig []byte + var Minimal = &common.Spec{ - Phase0Preset: common.Phase0Preset{ - MAX_COMMITTEES_PER_SLOT: 4, - TARGET_COMMITTEE_SIZE: 4, - MAX_VALIDATORS_PER_COMMITTEE: 2048, - SHUFFLE_ROUND_COUNT: 10, - HYSTERESIS_QUOTIENT: 4, - HYSTERESIS_DOWNWARD_MULTIPLIER: 1, - HYSTERESIS_UPWARD_MULTIPLIER: 5, - MIN_DEPOSIT_AMOUNT: 1_000_000_000, - MAX_EFFECTIVE_BALANCE: 32_000_000_000, - EFFECTIVE_BALANCE_INCREMENT: 1_000_000_000, - MIN_ATTESTATION_INCLUSION_DELAY: 1, - SLOTS_PER_EPOCH: 8, - MIN_SEED_LOOKAHEAD: 1, - MAX_SEED_LOOKAHEAD: 4, - EPOCHS_PER_ETH1_VOTING_PERIOD: 4, - SLOTS_PER_HISTORICAL_ROOT: 64, - MIN_EPOCHS_TO_INACTIVITY_PENALTY: 4, - EPOCHS_PER_HISTORICAL_VECTOR: 64, - EPOCHS_PER_SLASHINGS_VECTOR: 64, - HISTORICAL_ROOTS_LIMIT: 1 << 24, - VALIDATOR_REGISTRY_LIMIT: 1 << 40, - BASE_REWARD_FACTOR: 64, - WHISTLEBLOWER_REWARD_QUOTIENT: 512, - PROPOSER_REWARD_QUOTIENT: 8, - INACTIVITY_PENALTY_QUOTIENT: 1 << 25, - MIN_SLASHING_PENALTY_QUOTIENT: 64, - PROPORTIONAL_SLASHING_MULTIPLIER: 2, - MAX_PROPOSER_SLASHINGS: 16, - MAX_ATTESTER_SLASHINGS: 2, - MAX_ATTESTATIONS: 128, - MAX_DEPOSITS: 16, - MAX_VOLUNTARY_EXITS: 16, - }, - AltairPreset: common.AltairPreset{ - INACTIVITY_PENALTY_QUOTIENT_ALTAIR: 3 * (1 << 24), - MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR: 64, - PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR: 2, - SYNC_COMMITTEE_SIZE: 32, - EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 8, - MIN_SYNC_COMMITTEE_PARTICIPANTS: 1, - }, - BellatrixPreset: common.BellatrixPreset{ - INACTIVITY_PENALTY_QUOTIENT_BELLATRIX: 16777216, - MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX: 32, - PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX: 3, - MAX_BYTES_PER_TRANSACTION: 1073741824, - MAX_TRANSACTIONS_PER_PAYLOAD: 1048576, - BYTES_PER_LOGS_BLOOM: 256, - MAX_EXTRA_DATA_BYTES: 32, - }, - CapellaPreset: common.CapellaPreset{ - MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP: 16, - MAX_BLS_TO_EXECUTION_CHANGES: 16, - MAX_WITHDRAWALS_PER_PAYLOAD: 4, - }, - DenebPreset: common.DenebPreset{ - FIELD_ELEMENTS_PER_BLOB: 4096, - MAX_BLOB_COMMITMENTS_PER_BLOCK: 32, - KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: 10, - }, - Config: common.Config{ - PRESET_BASE: "minimal", - CONFIG_NAME: "minimal", - TERMINAL_TOTAL_DIFFICULTY: view.MustUint256("115792089237316195423570985008687907853269984665640564039457584007913129638912"), - TERMINAL_BLOCK_HASH: common.Bytes32{}, - TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: ^common.Epoch(0), - MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 64, - MIN_GENESIS_TIME: 1578009600, - GENESIS_FORK_VERSION: common.Version{0x00, 0x00, 0x00, 0x01}, - GENESIS_DELAY: 300, - ALTAIR_FORK_VERSION: common.Version{0x01, 0x00, 0x00, 0x01}, - ALTAIR_FORK_EPOCH: ^common.Epoch(0), - BELLATRIX_FORK_VERSION: common.Version{0x02, 0x00, 0x00, 0x01}, - BELLATRIX_FORK_EPOCH: ^common.Epoch(0), - CAPELLA_FORK_VERSION: common.Version{0x03, 0x00, 0x00, 0x01}, - CAPELLA_FORK_EPOCH: ^common.Epoch(0), - DENEB_FORK_VERSION: common.Version{0x04, 0x00, 0x00, 0x01}, - DENEB_FORK_EPOCH: ^common.Epoch(0), - ELECTRA_FORK_VERSION: common.Version{0x05, 0x00, 0x00, 0x01}, - ELECTRA_FORK_EPOCH: ^common.Epoch(0), - FULU_FORK_VERSION: common.Version{0x06, 0x00, 0x00, 0x01}, - FULU_FORK_EPOCH: ^common.Epoch(0), - WHISK_FORK_VERSION: common.Version{0x08, 0x00, 0x00, 0x01}, - WHISK_FORK_EPOCH: ^common.Epoch(0), - SECONDS_PER_SLOT: 6, - SECONDS_PER_ETH1_BLOCK: 14, - MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256, - SHARD_COMMITTEE_PERIOD: 64, - ETH1_FOLLOW_DISTANCE: 16, - INACTIVITY_SCORE_BIAS: 4, - INACTIVITY_SCORE_RECOVERY_RATE: 16, - EJECTION_BALANCE: 16_000_000_000, - MIN_PER_EPOCH_CHURN_LIMIT: 2, - CHURN_LIMIT_QUOTIENT: 32, - MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 4, - PROPOSER_SCORE_BOOST: 40, - REORG_HEAD_WEIGHT_THRESHOLD: 20, - REORG_PARENT_WEIGHT_THRESHOLD: 160, - REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2, - DEPOSIT_CHAIN_ID: 5, - DEPOSIT_NETWORK_ID: 5, - DEPOSIT_CONTRACT_ADDRESS: [20]byte{0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90}, - GOSSIP_MAX_SIZE: 10 * (1 << 20), - MAX_REQUEST_BLOCKS: 1024, - EPOCHS_PER_SUBNET_SUBSCRIPTION: 256, - MIN_EPOCHS_FOR_BLOCK_REQUESTS: 272, - MAX_CHUNK_SIZE: 10485760, - TTFB_TIMEOUT: 5, - RESP_TIMEOUT: 10, - ATTESTATION_PROPAGATION_SLOT_RANGE: 32, - MAXIMUM_GOSSIP_CLOCK_DISPARITY: 500, - MESSAGE_DOMAIN_INVALID_SNAPPY: common.NetworkMessageDomain{0, 0, 0, 0}, - MESSAGE_DOMAIN_VALID_SNAPPY: common.NetworkMessageDomain{1, 0, 0, 0}, - SUBNETS_PER_NODE: 2, - ATTESTATION_SUBNET_COUNT: 64, - ATTESTATION_SUBNET_EXTRA_BITS: 0, - ATTESTATION_SUBNET_PREFIX_BITS: 6, - MAX_REQUEST_BLOCKS_DENEB: 128, - MAX_REQUEST_BLOB_SIDECARS: 768, - MAX_BLOBS_PER_BLOCK: 6, - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: 4096, - BLOB_SIDECAR_SUBNET_COUNT: 6, - WHISK_EPOCHS_PER_SHUFFLING_PHASE: 4, - WHISK_PROPOSER_SELECTION_GAP: 1, - }, + Phase0Preset: mustYAML[common.Phase0Preset](minimalPhase0Preset), + AltairPreset: mustYAML[common.AltairPreset](minimalAltairPreset), + BellatrixPreset: mustYAML[common.BellatrixPreset](minimalBellatrixPreset), + CapellaPreset: mustYAML[common.CapellaPreset](minimalCapellaPreset), + DenebPreset: mustYAML[common.DenebPreset](minimalDenebPreset), + ElectraPreset: mustYAML[common.ElectraPreset](minimalElectraPreset), + Config: mustYAML[common.Config](minimalConfig), ExecutionEngine: nil, } diff --git a/eth2/configs/util.go b/eth2/configs/util.go index 831f00c..ff40fb0 100644 --- a/eth2/configs/util.go +++ b/eth2/configs/util.go @@ -1,13 +1,13 @@ package configs -import "math/big" +import ( + "gopkg.in/yaml.v3" +) -// mustBigInt is a helper function for config initialization. -// DO NOT USE for untrusted config data. Panics if invalid int. -func mustBigInt(v string) *big.Int { - var x big.Int - if err := x.UnmarshalText([]byte(v)); err != nil { +func mustYAML[T any](data []byte) T { + var elem T + if err := yaml.Unmarshal(data, &elem); err != nil { panic(err) } - return &x + return elem } diff --git a/eth2/configs/yaml_test.go b/eth2/configs/yaml_test.go index 9ea82d9..d6ed22a 100644 --- a/eth2/configs/yaml_test.go +++ b/eth2/configs/yaml_test.go @@ -4,6 +4,7 @@ import ( "io/ioutil" "path/filepath" "reflect" + "strings" "testing" "gopkg.in/yaml.v3" @@ -19,122 +20,42 @@ func mustLoad(path ...string) []byte { return b } -func TestYamlDecodingMainnetConfig(t *testing.T) { - var conf common.Config - if err := yaml.Unmarshal(mustLoad("configs", "mainnet"), &conf); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(conf, Mainnet.Config) { - t.Fatal("Failed to load mainnet config") - } +func yamlTest[E any](t *testing.T, path ...string) { + t.Run("yaml-"+strings.Join(path, "-"), func(t *testing.T) { + var conf E + if err := yaml.Unmarshal(mustLoad(path...), &conf); err != nil { + t.Fatalf("failed to decode: %v", err) + } + roundTripTest(t, conf) + }) } -func TestYamlDecodingMinimalConfig(t *testing.T) { - var conf common.Config - if err := yaml.Unmarshal(mustLoad("configs", "minimal"), &conf); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(conf, Minimal.Config) { - t.Fatal("Failed to load minimal config") - } +func roundTripTest[E any](t *testing.T, conf E) { + t.Run("roundtrip", func(t *testing.T) { + data, err := yaml.Marshal(conf) + if err != nil { + t.Fatalf("failed to encode: %v", err) + } + var conf2 E + if err := yaml.Unmarshal(data, &conf2); err != nil { + t.Fatalf("failed to decode again: %v", err) + } + if !reflect.DeepEqual(conf, conf2) { + t.Fatal("Failed to roundtrip") + } + }) } -func TestYamlDecodingMainnetPhase0(t *testing.T) { - var conf common.Phase0Preset - if err := yaml.Unmarshal(mustLoad("presets", "mainnet", "phase0"), &conf); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(conf, Mainnet.Phase0Preset) { - t.Fatal("Failed to load mainnet phase0 preset") - } -} - -func TestYamlDecodingMainnetAltair(t *testing.T) { - var conf common.AltairPreset - if err := yaml.Unmarshal(mustLoad("presets", "mainnet", "altair"), &conf); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(conf, Mainnet.AltairPreset) { - t.Fatal("Failed to load mainnet altair preset") - } -} - -func TestYamlDecodingMainnetBellatrix(t *testing.T) { - var conf common.BellatrixPreset - if err := yaml.Unmarshal(mustLoad("presets", "mainnet", "bellatrix"), &conf); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(conf, Mainnet.BellatrixPreset) { - t.Fatal("Failed to load mainnet bellatrix preset") - } -} - -func TestYamlDecodingMainnetCapella(t *testing.T) { - var conf common.CapellaPreset - if err := yaml.Unmarshal(mustLoad("presets", "mainnet", "capella"), &conf); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(conf, Mainnet.CapellaPreset) { - t.Fatal("Failed to load mainnet capella preset") - } -} - -func TestYamlDecodingMainnetDeneb(t *testing.T) { - var conf common.DenebPreset - if err := yaml.Unmarshal(mustLoad("presets", "mainnet", "deneb"), &conf); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(conf, Mainnet.DenebPreset) { - t.Fatal("Failed to load mainnet deneb preset") - } -} - -func TestYamlDecodingMinimalPhase0(t *testing.T) { - var conf common.Phase0Preset - if err := yaml.Unmarshal(mustLoad("presets", "minimal", "phase0"), &conf); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(conf, Minimal.Phase0Preset) { - t.Fatal("Failed to load minimal phase0 preset") - } -} - -func TestYamlDecodingMinimalAltair(t *testing.T) { - var conf common.AltairPreset - if err := yaml.Unmarshal(mustLoad("presets", "minimal", "altair"), &conf); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(conf, Minimal.AltairPreset) { - t.Fatal("Failed to load minimal altair preset") - } -} - -func TestYamlDecodingMinimalBellatrix(t *testing.T) { - var conf common.BellatrixPreset - if err := yaml.Unmarshal(mustLoad("presets", "minimal", "bellatrix"), &conf); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(conf, Minimal.BellatrixPreset) { - t.Fatal("Failed to load minimal bellatrix preset") - } -} - -func TestYamlDecodingMinimalCapella(t *testing.T) { - var conf common.CapellaPreset - if err := yaml.Unmarshal(mustLoad("presets", "minimal", "capella"), &conf); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(conf, Minimal.CapellaPreset) { - t.Fatal("Failed to load minimal capella preset") - } -} - -func TestYamlDecodingMinimalDeneb(t *testing.T) { - var conf common.DenebPreset - if err := yaml.Unmarshal(mustLoad("presets", "minimal", "deneb"), &conf); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(conf, Minimal.DenebPreset) { - t.Fatal("Failed to load minimal deneb preset") +func TestConfigs(t *testing.T) { + for _, presetName := range []string{"minimal", "mainnet"} { + yamlTest[common.Config](t, "configs", presetName) + yamlTest[common.Phase0Preset](t, "presets", presetName, "phase0") + yamlTest[common.AltairPreset](t, "presets", presetName, "altair") + yamlTest[common.BellatrixPreset](t, "presets", presetName, "bellatrix") + yamlTest[common.CapellaPreset](t, "presets", presetName, "capella") + yamlTest[common.DenebPreset](t, "presets", presetName, "deneb") + yamlTest[common.ElectraPreset](t, "presets", presetName, "electra") } + roundTripTest[common.Spec](t, *Minimal) + roundTripTest[common.Spec](t, *Mainnet) } diff --git a/eth2/configs/yamls/configs/mainnet.yaml b/eth2/configs/yamls/configs/mainnet.yaml index deb3dcf..e42ae9c 100644 --- a/eth2/configs/yamls/configs/mainnet.yaml +++ b/eth2/configs/yamls/configs/mainnet.yaml @@ -56,9 +56,9 @@ ELECTRA_FORK_EPOCH: 18446744073709551615 # temporary stub # Fulu FULU_FORK_VERSION: 0x06000000 FULU_FORK_EPOCH: 18446744073709551615 # temporary stub -# WHISK -WHISK_FORK_VERSION: 0x08000000 # temporary stub -WHISK_FORK_EPOCH: 18446744073709551615 +# EIP7441 +EIP7441_FORK_VERSION: 0x08000000 # temporary stub +EIP7441_FORK_EPOCH: 18446744073709551615 # EIP7732 EIP7732_FORK_VERSION: 0x09000000 # temporary stub EIP7732_FORK_EPOCH: 18446744073709551615 @@ -115,15 +115,13 @@ DEPOSIT_CONTRACT_ADDRESS: 0x00000000219ab540356cBB839Cbe05303d7705Fa # Networking # --------------------------------------------------------------- # `10 * 2**20` (= 10485760, 10 MiB) -GOSSIP_MAX_SIZE: 10485760 +MAX_PAYLOAD_SIZE: 10485760 # `2**10` (= 1024) MAX_REQUEST_BLOCKS: 1024 # `2**8` (= 256) EPOCHS_PER_SUBNET_SUBSCRIPTION: 256 # `MIN_VALIDATOR_WITHDRAWABILITY_DELAY + CHURN_LIMIT_QUOTIENT // 2` (= 33024, ~5 months) MIN_EPOCHS_FOR_BLOCK_REQUESTS: 33024 -# `10 * 2**20` (=10485760, 10 MiB) -MAX_CHUNK_SIZE: 10485760 # 5s TTFB_TIMEOUT: 5 # 10s @@ -172,15 +170,14 @@ DATA_COLUMN_SIDECAR_SUBNET_COUNT: 128 MAX_REQUEST_DATA_COLUMN_SIDECARS: 16384 SAMPLES_PER_SLOT: 8 CUSTODY_REQUIREMENT: 4 +VALIDATOR_CUSTODY_REQUIREMENT: 8 +BALANCE_PER_ADDITIONAL_CUSTODY_GROUP: 32000000000 MAX_BLOBS_PER_BLOCK_FULU: 12 -# `MAX_REQUEST_BLOCKS_DENEB * MAX_BLOBS_PER_BLOCK_FULU` -MAX_REQUEST_BLOB_SIDECARS_FULU: 1536 +MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: 4096 -# Whisk -# `Epoch(2**8)` -WHISK_EPOCHS_PER_SHUFFLING_PHASE: 256 -# `Epoch(2)` -WHISK_PROPOSER_SELECTION_GAP: 2 +# EIP7441 +EPOCHS_PER_SHUFFLING_PHASE: 256 +PROPOSER_SELECTION_GAP: 2 # EIP7732 MAX_REQUEST_PAYLOADS: 128 diff --git a/eth2/configs/yamls/configs/minimal.yaml b/eth2/configs/yamls/configs/minimal.yaml index 460474e..530bf1e 100644 --- a/eth2/configs/yamls/configs/minimal.yaml +++ b/eth2/configs/yamls/configs/minimal.yaml @@ -55,9 +55,9 @@ ELECTRA_FORK_EPOCH: 18446744073709551615 # Fulu FULU_FORK_VERSION: 0x06000001 FULU_FORK_EPOCH: 18446744073709551615 -# WHISK -WHISK_FORK_VERSION: 0x08000001 -WHISK_FORK_EPOCH: 18446744073709551615 +# EIP7441 +EIP7441_FORK_VERSION: 0x08000001 +EIP7441_FORK_EPOCH: 18446744073709551615 # EIP7732 EIP7732_FORK_VERSION: 0x09000001 EIP7732_FORK_EPOCH: 18446744073709551615 @@ -116,15 +116,13 @@ DEPOSIT_CONTRACT_ADDRESS: 0x1234567890123456789012345678901234567890 # Networking # --------------------------------------------------------------- # `10 * 2**20` (= 10485760, 10 MiB) -GOSSIP_MAX_SIZE: 10485760 +MAX_PAYLOAD_SIZE: 10485760 # `2**10` (= 1024) MAX_REQUEST_BLOCKS: 1024 # `2**8` (= 256) EPOCHS_PER_SUBNET_SUBSCRIPTION: 256 # [customized] `MIN_VALIDATOR_WITHDRAWABILITY_DELAY + CHURN_LIMIT_QUOTIENT // 2` (= 272) MIN_EPOCHS_FOR_BLOCK_REQUESTS: 272 -# `10 * 2**20` (=10485760, 10 MiB) -MAX_CHUNK_SIZE: 10485760 # 5s TTFB_TIMEOUT: 5 # 10s @@ -173,13 +171,14 @@ DATA_COLUMN_SIDECAR_SUBNET_COUNT: 128 MAX_REQUEST_DATA_COLUMN_SIDECARS: 16384 SAMPLES_PER_SLOT: 8 CUSTODY_REQUIREMENT: 4 +VALIDATOR_CUSTODY_REQUIREMENT: 8 +BALANCE_PER_ADDITIONAL_CUSTODY_GROUP: 32000000000 MAX_BLOBS_PER_BLOCK_FULU: 12 -# `MAX_REQUEST_BLOCKS_DENEB * MAX_BLOBS_PER_BLOCK_FULU` -MAX_REQUEST_BLOB_SIDECARS_FULU: 1536 +MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: 4096 -# Whisk -WHISK_EPOCHS_PER_SHUFFLING_PHASE: 4 -WHISK_PROPOSER_SELECTION_GAP: 1 +# EIP7441 +EPOCHS_PER_SHUFFLING_PHASE: 4 +PROPOSER_SELECTION_GAP: 1 # EIP7732 MAX_REQUEST_PAYLOADS: 128 diff --git a/eth2/configs/yamls/presets/mainnet/eip7441.yaml b/eth2/configs/yamls/presets/mainnet/eip7441.yaml new file mode 100644 index 0000000..097f595 --- /dev/null +++ b/eth2/configs/yamls/presets/mainnet/eip7441.yaml @@ -0,0 +1,16 @@ +# Mainnet preset - EIP7441 + +# Misc +# --------------------------------------------------------------- +# `uint64(4)` +CURDLEPROOFS_N_BLINDERS: 4 +# `uint64(2**14)` +CANDIDATE_TRACKERS_COUNT: 16384 +# `uint64(2**13)` must be < CANDIDATE_TRACKERS_COUNT +PROPOSER_TRACKERS_COUNT: 8192 +# `uint64(2**7 - CURDLEPROOFS_N_BLINDERS)` +VALIDATORS_PER_SHUFFLE: 124 +# `uint64(2**15)` TODO: will be replaced by a fix format once there's a serialized format +MAX_SHUFFLE_PROOF_SIZE: 32768 +# `uint64(2**10)` TODO: will be replaced by a fix format once there's a serialized format +MAX_OPENING_PROOF_SIZE: 1024 diff --git a/eth2/configs/yamls/presets/minimal/eip7441.yaml b/eth2/configs/yamls/presets/minimal/eip7441.yaml new file mode 100644 index 0000000..5588beb --- /dev/null +++ b/eth2/configs/yamls/presets/minimal/eip7441.yaml @@ -0,0 +1,16 @@ +# Minimal preset - EIP7441 + +# Misc +# --------------------------------------------------------------- +# [customized] +CURDLEPROOFS_N_BLINDERS: 4 +# [customized] +CANDIDATE_TRACKERS_COUNT: 32 +# [customized] +PROPOSER_TRACKERS_COUNT: 16 +# [customized] +VALIDATORS_PER_SHUFFLE: 4 +# `uint64(2**15)` TODO: will be replaced by a fix format once there's a serialized format +MAX_SHUFFLE_PROOF_SIZE: 32768 +# `uint64(2**10)` TODO: will be replaced by a fix format once there's a serialized format +MAX_OPENING_PROOF_SIZE: 1024 diff --git a/eth2/version.go b/eth2/version.go index fc72397..558bde8 100644 --- a/eth2/version.go +++ b/eth2/version.go @@ -1,3 +1,3 @@ package eth2 -const VERSION = "v0.33.1" +const VERSION = "v0.34.1" diff --git a/tests/spec/test_runners/ssz_static/ssz_static_test.go b/tests/spec/test_runners/ssz_static/ssz_static_test.go index 6a9819e..4ea27ac 100644 --- a/tests/spec/test_runners/ssz_static/ssz_static_test.go +++ b/tests/spec/test_runners/ssz_static/ssz_static_test.go @@ -3,6 +3,8 @@ package ssz_static import ( "bytes" "encoding/hex" + "github.com/protolambda/zrnt/eth2/beacon/deneb" + "github.com/protolambda/zrnt/eth2/beacon/electra" "io/ioutil" "testing" @@ -102,6 +104,8 @@ var objs = map[test_util.ForkName]map[string]ObjAllocator{ "altair": {}, "bellatrix": {}, "capella": {}, + "deneb": {}, + "electra": {}, } func init() { @@ -134,6 +138,8 @@ func init() { objs["altair"][k] = v objs["bellatrix"][k] = v objs["capella"][k] = v + objs["deneb"][k] = v + objs["electra"][k] = v } objs["phase0"]["BeaconBlockBody"] = func() interface{} { return new(phase0.BeaconBlockBody) } objs["phase0"]["BeaconBlock"] = func() interface{} { return new(phase0.BeaconBlock) } @@ -172,6 +178,31 @@ func init() { objs["capella"]["Withdrawal"] = func() interface{} { return new(common.Withdrawal) } objs["capella"]["BLSToExecutionChange"] = func() interface{} { return new(common.BLSToExecutionChange) } objs["capella"]["SignedBLSToExecutionChange"] = func() interface{} { return new(common.SignedBLSToExecutionChange) } + + objs["deneb"]["BeaconBlockBody"] = func() interface{} { return new(deneb.BeaconBlockBody) } + objs["deneb"]["BeaconBlock"] = func() interface{} { return new(deneb.BeaconBlock) } + objs["deneb"]["BeaconState"] = func() interface{} { return new(deneb.BeaconState) } + objs["deneb"]["SignedBeaconBlock"] = func() interface{} { return new(deneb.SignedBeaconBlock) } + objs["deneb"]["ExecutionPayload"] = func() interface{} { return new(deneb.ExecutionPayload) } + objs["deneb"]["ExecutionPayloadHeader"] = func() interface{} { return new(deneb.ExecutionPayloadHeader) } + + objs["electra"]["BeaconBlockBody"] = func() interface{} { return new(electra.BeaconBlockBody) } + objs["electra"]["BeaconBlock"] = func() interface{} { return new(electra.BeaconBlock) } + objs["electra"]["BeaconState"] = func() interface{} { return new(electra.BeaconState) } + objs["electra"]["SignedBeaconBlock"] = func() interface{} { return new(electra.SignedBeaconBlock) } + objs["electra"]["AggregateAndProof"] = func() interface{} { return new(electra.AggregateAndProof) } + objs["electra"]["SignedAggregateAndProof"] = func() interface{} { return new(electra.SignedAggregateAndProof) } + objs["electra"]["Attestation"] = func() interface{} { return new(electra.Attestation) } + objs["electra"]["SingleAttestation"] = func() interface{} { return new(electra.SingleAttestation) } + objs["electra"]["IndexedAttestation"] = func() interface{} { return new(electra.IndexedAttestation) } + objs["electra"]["AttesterSlashing"] = func() interface{} { return new(electra.AttesterSlashing) } + objs["electra"]["DepositRequest"] = func() interface{} { return new(common.DepositRequest) } + objs["electra"]["WithdrawalRequest"] = func() interface{} { return new(common.WithdrawalRequest) } + objs["electra"]["ConsolidationRequest"] = func() interface{} { return new(common.ConsolidationRequest) } + objs["electra"]["PendingDeposit"] = func() interface{} { return new(common.PendingDeposit) } + objs["electra"]["PendingPartialWithdrawal"] = func() interface{} { return new(common.PendingPartialWithdrawal) } + objs["electra"]["PendingConsolidation"] = func() interface{} { return new(common.PendingConsolidation) } + objs["electra"]["ExecutionRequests"] = func() interface{} { return new(electra.ExecutionRequests) } } type RootsYAML struct {