mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 13:58:09 -05:00
Compare commits
80 Commits
v3.1.2
...
post-merge
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d3e627d2d | ||
|
|
016a027c48 | ||
|
|
3040f3ae4d | ||
|
|
c8f1d51280 | ||
|
|
5cb5ce7e14 | ||
|
|
910822400f | ||
|
|
c0297ca0c2 | ||
|
|
d51fb3a648 | ||
|
|
3ab486e140 | ||
|
|
f52dbe3c8f | ||
|
|
166c284ca4 | ||
|
|
2654e3f080 | ||
|
|
ed65688252 | ||
|
|
4bae07fdbb | ||
|
|
42808a0c29 | ||
|
|
11cbe8fcb9 | ||
|
|
938fca1933 | ||
|
|
604688a00e | ||
|
|
f2399e21e1 | ||
|
|
395e49972e | ||
|
|
ee099d3f03 | ||
|
|
e2ffaf983a | ||
|
|
0d4b98cd0a | ||
|
|
fe1281dc1a | ||
|
|
9761bd0753 | ||
|
|
0bcddb3009 | ||
|
|
ee9da3aded | ||
|
|
d58d3f2c57 | ||
|
|
4b033f4cc7 | ||
|
|
07d0a00f88 | ||
|
|
08d63a0cd0 | ||
|
|
f02ad8a68b | ||
|
|
466fd359a2 | ||
|
|
b5f8e69b6b | ||
|
|
cf466702df | ||
|
|
de73baa4a7 | ||
|
|
d4a3b74bc6 | ||
|
|
216cdd9361 | ||
|
|
68c3e939b8 | ||
|
|
bf7e17db8f | ||
|
|
ead9a83d8d | ||
|
|
2fef03414d | ||
|
|
7b63d5c08c | ||
|
|
af6d5e9149 | ||
|
|
d0d7021c1d | ||
|
|
4db1a02763 | ||
|
|
69d7f7f6ca | ||
|
|
4e342b8802 | ||
|
|
02566de74c | ||
|
|
90ecd23d41 | ||
|
|
4d68211ad4 | ||
|
|
19f6d3bef6 | ||
|
|
37108e6ed8 | ||
|
|
d33af46c90 | ||
|
|
53d4659654 | ||
|
|
09e117370a | ||
|
|
8ade8afb73 | ||
|
|
872021f10d | ||
|
|
bb09295072 | ||
|
|
e4b2b1ea7d | ||
|
|
ead329e610 | ||
|
|
a0c5669511 | ||
|
|
0fd5253915 | ||
|
|
86e855d499 | ||
|
|
001ae30a59 | ||
|
|
d1f0e5dd55 | ||
|
|
2142b13a41 | ||
|
|
ffac232d89 | ||
|
|
26b46301d2 | ||
|
|
fdf913aed9 | ||
|
|
9435d10652 | ||
|
|
8fa481cb93 | ||
|
|
26087d7b2d | ||
|
|
5a3a01090a | ||
|
|
55690de685 | ||
|
|
007c776d8a | ||
|
|
a15e0797e4 | ||
|
|
1572c530b5 | ||
|
|
652303522f | ||
|
|
39a7988e9e |
8
.bazelrc
8
.bazelrc
@@ -21,7 +21,6 @@ build --sandbox_default_allow_network=false
|
||||
|
||||
# Stamp binaries with git information
|
||||
build --workspace_status_command=./hack/workspace_status.sh
|
||||
build --stamp
|
||||
|
||||
# Prevent PATH changes from rebuilding when switching from IDE to command line.
|
||||
build --incompatible_strict_action_env
|
||||
@@ -38,7 +37,6 @@ build:minimal --@io_bazel_rules_go//go/config:tags=minimal
|
||||
|
||||
# Release flags
|
||||
build:release --compilation_mode=opt
|
||||
build:release --config=llvm
|
||||
|
||||
# LLVM compiler for building C/C++ dependencies.
|
||||
build:llvm --define compiler=llvm
|
||||
@@ -232,3 +230,9 @@ build --modify_execution_info='GoStdlib.*=+no-remote-cache'
|
||||
|
||||
# Set bazel gotag
|
||||
build --define gotags=bazel
|
||||
|
||||
# Build the binary with Beacon API calls for the validator
|
||||
build --flag_alias=use_beacon_api=//validator/client/validator-client-factory:use_beacon_api
|
||||
|
||||
build:beacon_api --use_beacon_api
|
||||
build:beacon_api --define=gotags=use_beacon_api
|
||||
|
||||
12
.github/workflows/go.yml
vendored
12
.github/workflows/go.yml
vendored
@@ -26,10 +26,10 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Go 1.18
|
||||
- name: Set up Go 1.19
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: 1.19
|
||||
- name: Run Gosec Security Scanner
|
||||
run: | # https://github.com/securego/gosec/issues/469
|
||||
export PATH=$PATH:$(go env GOPATH)/bin
|
||||
@@ -43,16 +43,16 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Go 1.18
|
||||
- name: Set up Go 1.19
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: 1.19
|
||||
id: go
|
||||
|
||||
- name: Golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: v1.47.2
|
||||
version: v1.50.1
|
||||
args: --config=.golangci.yml --out-${NO_FUTURE}format colored-line-number
|
||||
|
||||
build:
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: 1.19
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
|
||||
@@ -6,7 +6,7 @@ run:
|
||||
- proto
|
||||
- tools/analyzers
|
||||
timeout: 10m
|
||||
go: '1.18'
|
||||
go: '1.19'
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
|
||||
@@ -176,7 +176,7 @@ load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_depe
|
||||
go_rules_dependencies()
|
||||
|
||||
go_register_toolchains(
|
||||
go_version = "1.18.5",
|
||||
go_version = "1.19.3",
|
||||
nogo = "@//:nogo",
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
Package beacon provides a client for interacting with the standard Eth Beacon Node API.
|
||||
Interactive swagger documentation for the API is available here: https://ethereum.github.io/beacon-APIs/
|
||||
|
||||
*/
|
||||
package beacon
|
||||
|
||||
@@ -3,7 +3,9 @@ Copyright 2017 Albert Tedja
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
@@ -3,7 +3,9 @@ Copyright 2017 Albert Tedja
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
@@ -304,31 +304,6 @@ func TestService_HeadGenesisValidatorsRoot(t *testing.T) {
|
||||
root = c.HeadGenesisValidatorsRoot()
|
||||
require.DeepEqual(t, root[:], s.GenesisValidatorsRoot())
|
||||
}
|
||||
func TestService_ChainHeads_ProtoArray(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}}
|
||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||
st, blkRoot, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot))
|
||||
st, blkRoot, err = prepareForkchoiceState(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot))
|
||||
st, blkRoot, err = prepareForkchoiceState(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot))
|
||||
st, blkRoot, err = prepareForkchoiceState(ctx, 103, [32]byte{'d'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot))
|
||||
st, blkRoot, err = prepareForkchoiceState(ctx, 104, [32]byte{'e'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot))
|
||||
|
||||
roots, slots := c.ChainHeads()
|
||||
require.DeepEqual(t, [][32]byte{{'c'}, {'d'}, {'e'}}, roots)
|
||||
require.DeepEqual(t, []types.Slot{102, 103, 104}, slots)
|
||||
}
|
||||
|
||||
//
|
||||
// A <- B <- C
|
||||
|
||||
@@ -14,7 +14,7 @@ var (
|
||||
// errNilFinalizedCheckpoint is returned when a nil finalized checkpt is returned from a state.
|
||||
errNilFinalizedCheckpoint = errors.New("nil finalized checkpoint returned from state")
|
||||
// errNilJustifiedCheckpoint is returned when a nil justified checkpt is returned from a state.
|
||||
errNilJustifiedCheckpoint = errors.New("nil finalized checkpoint returned from state")
|
||||
errNilJustifiedCheckpoint = errors.New("nil justified checkpoint returned from state")
|
||||
// errInvalidNilSummary is returned when a nil summary is returned from the DB.
|
||||
errInvalidNilSummary = errors.New("nil summary returned from the DB")
|
||||
// errWrongBlockCount is returned when the wrong number of blocks or block roots is used
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
@@ -38,6 +39,7 @@ type notifyForkchoiceUpdateArg struct {
|
||||
func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkchoiceUpdateArg) (*enginev1.PayloadIDBytes, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.notifyForkchoiceUpdate")
|
||||
defer span.End()
|
||||
log.Infof("notifyForkchoiceUpdate, fork version=%#x, headRoot=%#x, headState.latest_block_header=%#x", arg.headState.Fork().CurrentVersion, arg.headRoot, arg.headState.LatestBlockHeader().BodyRoot)
|
||||
|
||||
headBlk := arg.headBlock
|
||||
if headBlk == nil || headBlk.IsNil() || headBlk.Body().IsNil() {
|
||||
@@ -50,7 +52,25 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
|
||||
log.WithError(err).Error("Could not determine if head block is execution block")
|
||||
return nil, nil
|
||||
}
|
||||
var eth1BlockHash []byte
|
||||
if !isExecutionBlk {
|
||||
br, err := headBlk.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: this is a hack
|
||||
// try to grab the payload header from the state in case the block doesn't have it but the state does
|
||||
sp, err := arg.headState.LatestExecutionPayloadHeader()
|
||||
if err != nil {
|
||||
log.WithError(err).Infof("notifyForkchoiceUpdate, skipping non-execution block with root=%#x", br)
|
||||
return nil, nil
|
||||
}
|
||||
eth1BlockHash = sp.BlockHash()
|
||||
if len(sp.BlockHash()) != 32 || bytes.Equal(eth1BlockHash, params.BeaconConfig().ZeroHash[:]) {
|
||||
log.Infof("notifyForkchoiceUpdate, skipping non-execution block with root=%#x and empty payload header block hash", br)
|
||||
return nil, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
headPayload, err := headBlk.Body().Execution()
|
||||
@@ -58,10 +78,11 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
|
||||
log.WithError(err).Error("Could not get execution payload for head block")
|
||||
return nil, nil
|
||||
}
|
||||
eth1BlockHash = headPayload.BlockHash()
|
||||
finalizedHash := s.ForkChoicer().FinalizedPayloadBlockHash()
|
||||
justifiedHash := s.ForkChoicer().JustifiedPayloadBlockHash()
|
||||
fcs := &enginev1.ForkchoiceState{
|
||||
HeadBlockHash: headPayload.BlockHash(),
|
||||
HeadBlockHash: eth1BlockHash,
|
||||
SafeBlockHash: justifiedHash[:],
|
||||
FinalizedBlockHash: finalizedHash[:],
|
||||
}
|
||||
@@ -80,7 +101,7 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
|
||||
forkchoiceUpdatedOptimisticNodeCount.Inc()
|
||||
log.WithFields(logrus.Fields{
|
||||
"headSlot": headBlk.Slot(),
|
||||
"headPayloadBlockHash": fmt.Sprintf("%#x", bytesutil.Trunc(headPayload.BlockHash())),
|
||||
"headPayloadBlockHash": fmt.Sprintf("%#x", bytesutil.Trunc(eth1BlockHash)),
|
||||
"finalizedPayloadBlockHash": fmt.Sprintf("%#x", bytesutil.Trunc(finalizedHash[:])),
|
||||
}).Info("Called fork choice updated with optimistic block")
|
||||
return payloadID, nil
|
||||
@@ -145,18 +166,21 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
log.WithField("payloadID", fmt.Sprintf("%#x", payloadID)).WithField("lastValidHash", fmt.Sprintf("%#x", lastValidHash)).Infof("notifyForkchoiceUpdate, no error from ee call")
|
||||
forkchoiceUpdatedValidNodeCount.Inc()
|
||||
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, arg.headRoot); err != nil {
|
||||
log.WithError(err).Error("Could not set head root to valid")
|
||||
log.WithField("headRoot", fmt.Sprintf("%#x", arg.headRoot)).WithField("payloadID", fmt.Sprintf("%#x", payloadID)).WithField("lastValidHash", fmt.Sprintf("%#x", lastValidHash)).WithError(err).Error("Could not set head root to valid")
|
||||
return nil, nil
|
||||
}
|
||||
log.Infof("notifyForkchoiceUpdate, set optimistic to valid for root=%#x", arg.headRoot)
|
||||
if hasAttr && payloadID != nil { // If the forkchoice update call has an attribute, update the proposer payload ID cache.
|
||||
var pId [8]byte
|
||||
copy(pId[:], payloadID[:])
|
||||
s.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(nextSlot, proposerId, pId, arg.headRoot)
|
||||
log.Infof("notifyForkchoiceUpdate, SetProposerAndPayloadIDs(nextSlot=%d, proposerId=%d, pId=%#x, arg.headRoot=%#x)", arg.headRoot, proposerId, pId, arg.headRoot)
|
||||
} else if hasAttr && payloadID == nil {
|
||||
log.WithFields(logrus.Fields{
|
||||
"blockHash": fmt.Sprintf("%#x", headPayload.BlockHash()),
|
||||
"blockHash": fmt.Sprintf("%#x", eth1BlockHash),
|
||||
"slot": headBlk.Slot(),
|
||||
}).Error("Received nil payload ID on VALID engine response")
|
||||
}
|
||||
@@ -180,7 +204,7 @@ func (s *Service) getPayloadHash(ctx context.Context, root []byte) ([32]byte, er
|
||||
return bytesutil.ToBytes32(payload.BlockHash()), nil
|
||||
}
|
||||
|
||||
// notifyForkchoiceUpdate signals execution engine on a new payload.
|
||||
// notifyNewPayload signals execution engine on a new payload.
|
||||
// It returns true if the EL has returned VALID for the block
|
||||
func (s *Service) notifyNewPayload(ctx context.Context, postStateVersion int,
|
||||
postStateHeader interfaces.ExecutionData, blk interfaces.SignedBeaconBlock) (bool, error) {
|
||||
|
||||
@@ -207,7 +207,7 @@ func (s *Service) setHead(root [32]byte, block interfaces.SignedBeaconBlock, sta
|
||||
block: bCp,
|
||||
state: state.Copy(),
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// This sets head view object which is used to track the head slot, root, block and state. The method
|
||||
|
||||
@@ -87,6 +87,7 @@ func logBlockSyncStatus(block interfaces.BeaconBlock, blockRoot [32]byte, justif
|
||||
"version": version.String(block.Version()),
|
||||
"sinceSlotStartTime": prysmTime.Now().Sub(startTime),
|
||||
"chainServiceProcessedTime": prysmTime.Now().Sub(receivedTime),
|
||||
"deposits": fmt.Sprintf("%d/%d", len(block.Body().Deposits()), block.Body().Eth1Data().DepositCount),
|
||||
}).Debug("Synced new block")
|
||||
} else {
|
||||
log.WithFields(logrus.Fields{
|
||||
|
||||
@@ -22,20 +22,21 @@ import (
|
||||
// validateMergeBlock validates terminal block hash in the event of manual overrides before checking for total difficulty.
|
||||
//
|
||||
// def validate_merge_block(block: BeaconBlock) -> None:
|
||||
// if TERMINAL_BLOCK_HASH != Hash32():
|
||||
// # If `TERMINAL_BLOCK_HASH` is used as an override, the activation epoch must be reached.
|
||||
// assert compute_epoch_at_slot(block.slot) >= TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH
|
||||
// assert block.body.execution_payload.parent_hash == TERMINAL_BLOCK_HASH
|
||||
// return
|
||||
//
|
||||
// pow_block = get_pow_block(block.body.execution_payload.parent_hash)
|
||||
// # Check if `pow_block` is available
|
||||
// assert pow_block is not None
|
||||
// pow_parent = get_pow_block(pow_block.parent_hash)
|
||||
// # Check if `pow_parent` is available
|
||||
// assert pow_parent is not None
|
||||
// # Check if `pow_block` is a valid terminal PoW block
|
||||
// assert is_valid_terminal_pow_block(pow_block, pow_parent)
|
||||
// if TERMINAL_BLOCK_HASH != Hash32():
|
||||
// # If `TERMINAL_BLOCK_HASH` is used as an override, the activation epoch must be reached.
|
||||
// assert compute_epoch_at_slot(block.slot) >= TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH
|
||||
// assert block.body.execution_payload.parent_hash == TERMINAL_BLOCK_HASH
|
||||
// return
|
||||
//
|
||||
// pow_block = get_pow_block(block.body.execution_payload.parent_hash)
|
||||
// # Check if `pow_block` is available
|
||||
// assert pow_block is not None
|
||||
// pow_parent = get_pow_block(pow_block.parent_hash)
|
||||
// # Check if `pow_parent` is available
|
||||
// assert pow_parent is not None
|
||||
// # Check if `pow_block` is a valid terminal PoW block
|
||||
// assert is_valid_terminal_pow_block(pow_block, pow_parent)
|
||||
func (s *Service) validateMergeBlock(ctx context.Context, b interfaces.SignedBeaconBlock) error {
|
||||
if err := blocks.BeaconBlockIsNil(b); err != nil {
|
||||
return err
|
||||
@@ -105,10 +106,11 @@ func (s *Service) getBlkParentHashAndTD(ctx context.Context, blkHash []byte) ([]
|
||||
// validateTerminalBlockHash validates if the merge block is a valid terminal PoW block.
|
||||
// spec code:
|
||||
// if TERMINAL_BLOCK_HASH != Hash32():
|
||||
// # If `TERMINAL_BLOCK_HASH` is used as an override, the activation epoch must be reached.
|
||||
// assert compute_epoch_at_slot(block.slot) >= TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH
|
||||
// assert block.body.execution_payload.parent_hash == TERMINAL_BLOCK_HASH
|
||||
// return
|
||||
//
|
||||
// # If `TERMINAL_BLOCK_HASH` is used as an override, the activation epoch must be reached.
|
||||
// assert compute_epoch_at_slot(block.slot) >= TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH
|
||||
// assert block.body.execution_payload.parent_hash == TERMINAL_BLOCK_HASH
|
||||
// return
|
||||
func validateTerminalBlockHash(blkSlot types.Slot, payload interfaces.ExecutionData) error {
|
||||
if bytesutil.ToBytes32(params.BeaconConfig().TerminalBlockHash.Bytes()) == [32]byte{} {
|
||||
return nil
|
||||
@@ -125,9 +127,10 @@ func validateTerminalBlockHash(blkSlot types.Slot, payload interfaces.ExecutionD
|
||||
// validateTerminalBlockDifficulties validates terminal pow block by comparing own total difficulty with parent's total difficulty.
|
||||
//
|
||||
// def is_valid_terminal_pow_block(block: PowBlock, parent: PowBlock) -> bool:
|
||||
// is_total_difficulty_reached = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
||||
// is_parent_total_difficulty_valid = parent.total_difficulty < TERMINAL_TOTAL_DIFFICULTY
|
||||
// return is_total_difficulty_reached and is_parent_total_difficulty_valid
|
||||
//
|
||||
// is_total_difficulty_reached = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
||||
// is_parent_total_difficulty_valid = parent.total_difficulty < TERMINAL_TOTAL_DIFFICULTY
|
||||
// return is_total_difficulty_reached and is_parent_total_difficulty_valid
|
||||
func validateTerminalBlockDifficulties(currentDifficulty *uint256.Int, parentDifficulty *uint256.Int) (bool, error) {
|
||||
b, ok := new(big.Int).SetString(params.BeaconConfig().TerminalTotalDifficulty, 10)
|
||||
if !ok {
|
||||
|
||||
@@ -19,23 +19,24 @@ import (
|
||||
// The delay is handled by the caller in `processAttestations`.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def on_attestation(store: Store, attestation: Attestation) -> None:
|
||||
// """
|
||||
// Run ``on_attestation`` upon receiving a new ``attestation`` from either within a block or directly on the wire.
|
||||
//
|
||||
// An ``attestation`` that is asserted as invalid may be valid at a later time,
|
||||
// consider scheduling it for later processing in such case.
|
||||
// """
|
||||
// validate_on_attestation(store, attestation)
|
||||
// store_target_checkpoint_state(store, attestation.data.target)
|
||||
// def on_attestation(store: Store, attestation: Attestation) -> None:
|
||||
// """
|
||||
// Run ``on_attestation`` upon receiving a new ``attestation`` from either within a block or directly on the wire.
|
||||
//
|
||||
// # Get state at the `target` to fully validate attestation
|
||||
// target_state = store.checkpoint_states[attestation.data.target]
|
||||
// indexed_attestation = get_indexed_attestation(target_state, attestation)
|
||||
// assert is_valid_indexed_attestation(target_state, indexed_attestation)
|
||||
// An ``attestation`` that is asserted as invalid may be valid at a later time,
|
||||
// consider scheduling it for later processing in such case.
|
||||
// """
|
||||
// validate_on_attestation(store, attestation)
|
||||
// store_target_checkpoint_state(store, attestation.data.target)
|
||||
//
|
||||
// # Update latest messages for attesting indices
|
||||
// update_latest_messages(store, indexed_attestation.attesting_indices, attestation)
|
||||
// # Get state at the `target` to fully validate attestation
|
||||
// target_state = store.checkpoint_states[attestation.data.target]
|
||||
// indexed_attestation = get_indexed_attestation(target_state, attestation)
|
||||
// assert is_valid_indexed_attestation(target_state, indexed_attestation)
|
||||
//
|
||||
// # Update latest messages for attesting indices
|
||||
// update_latest_messages(store, indexed_attestation.attesting_indices, attestation)
|
||||
func (s *Service) OnAttestation(ctx context.Context, a *ethpb.Attestation) error {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.onAttestation")
|
||||
defer span.End()
|
||||
|
||||
@@ -45,52 +45,53 @@ var initialSyncBlockCacheSize = uint64(2 * params.BeaconConfig().SlotsPerEpoch)
|
||||
// computation in this method and methods it calls into.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
||||
// block = signed_block.message
|
||||
// # Parent block must be known
|
||||
// assert block.parent_root in store.block_states
|
||||
// # Make a copy of the state to avoid mutability issues
|
||||
// pre_state = copy(store.block_states[block.parent_root])
|
||||
// # Blocks cannot be in the future. If they are, their consideration must be delayed until the are in the past.
|
||||
// assert get_current_slot(store) >= block.slot
|
||||
//
|
||||
// # Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor)
|
||||
// finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
|
||||
// assert block.slot > finalized_slot
|
||||
// # Check block is a descendant of the finalized block at the checkpoint finalized slot
|
||||
// assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root
|
||||
// def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
||||
// block = signed_block.message
|
||||
// # Parent block must be known
|
||||
// assert block.parent_root in store.block_states
|
||||
// # Make a copy of the state to avoid mutability issues
|
||||
// pre_state = copy(store.block_states[block.parent_root])
|
||||
// # Blocks cannot be in the future. If they are, their consideration must be delayed until the are in the past.
|
||||
// assert get_current_slot(store) >= block.slot
|
||||
//
|
||||
// # Check the block is valid and compute the post-state
|
||||
// state = pre_state.copy()
|
||||
// state_transition(state, signed_block, True)
|
||||
// # Add new block to the store
|
||||
// store.blocks[hash_tree_root(block)] = block
|
||||
// # Add new state for this block to the store
|
||||
// store.block_states[hash_tree_root(block)] = state
|
||||
// # Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor)
|
||||
// finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
|
||||
// assert block.slot > finalized_slot
|
||||
// # Check block is a descendant of the finalized block at the checkpoint finalized slot
|
||||
// assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root
|
||||
//
|
||||
// # Update justified checkpoint
|
||||
// if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch:
|
||||
// if state.current_justified_checkpoint.epoch > store.best_justified_checkpoint.epoch:
|
||||
// store.best_justified_checkpoint = state.current_justified_checkpoint
|
||||
// if should_update_justified_checkpoint(store, state.current_justified_checkpoint):
|
||||
// store.justified_checkpoint = state.current_justified_checkpoint
|
||||
// # Check the block is valid and compute the post-state
|
||||
// state = pre_state.copy()
|
||||
// state_transition(state, signed_block, True)
|
||||
// # Add new block to the store
|
||||
// store.blocks[hash_tree_root(block)] = block
|
||||
// # Add new state for this block to the store
|
||||
// store.block_states[hash_tree_root(block)] = state
|
||||
//
|
||||
// # Update finalized checkpoint
|
||||
// if state.finalized_checkpoint.epoch > store.finalized_checkpoint.epoch:
|
||||
// store.finalized_checkpoint = state.finalized_checkpoint
|
||||
// # Update justified checkpoint
|
||||
// if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch:
|
||||
// if state.current_justified_checkpoint.epoch > store.best_justified_checkpoint.epoch:
|
||||
// store.best_justified_checkpoint = state.current_justified_checkpoint
|
||||
// if should_update_justified_checkpoint(store, state.current_justified_checkpoint):
|
||||
// store.justified_checkpoint = state.current_justified_checkpoint
|
||||
//
|
||||
// # Potentially update justified if different from store
|
||||
// if store.justified_checkpoint != state.current_justified_checkpoint:
|
||||
// # Update justified if new justified is later than store justified
|
||||
// if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch:
|
||||
// store.justified_checkpoint = state.current_justified_checkpoint
|
||||
// return
|
||||
// # Update finalized checkpoint
|
||||
// if state.finalized_checkpoint.epoch > store.finalized_checkpoint.epoch:
|
||||
// store.finalized_checkpoint = state.finalized_checkpoint
|
||||
//
|
||||
// # Update justified if store justified is not in chain with finalized checkpoint
|
||||
// finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
|
||||
// ancestor_at_finalized_slot = get_ancestor(store, store.justified_checkpoint.root, finalized_slot)
|
||||
// if ancestor_at_finalized_slot != store.finalized_checkpoint.root:
|
||||
// store.justified_checkpoint = state.current_justified_checkpoint
|
||||
// # Potentially update justified if different from store
|
||||
// if store.justified_checkpoint != state.current_justified_checkpoint:
|
||||
// # Update justified if new justified is later than store justified
|
||||
// if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch:
|
||||
// store.justified_checkpoint = state.current_justified_checkpoint
|
||||
// return
|
||||
//
|
||||
// # Update justified if store justified is not in chain with finalized checkpoint
|
||||
// finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
|
||||
// ancestor_at_finalized_slot = get_ancestor(store, store.justified_checkpoint.root, finalized_slot)
|
||||
// if ancestor_at_finalized_slot != store.finalized_checkpoint.root:
|
||||
// store.justified_checkpoint = state.current_justified_checkpoint
|
||||
func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlock, blockRoot [32]byte) error {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.onBlock")
|
||||
defer span.End()
|
||||
@@ -664,26 +665,8 @@ func (s *Service) fillMissingPayloadIDRoutine(ctx context.Context, stateFeed *ev
|
||||
for {
|
||||
select {
|
||||
case ti := <-ticker.C:
|
||||
if !atHalfSlot(ti) {
|
||||
continue
|
||||
}
|
||||
// Head root should be empty when retrieving proposer index for the next slot.
|
||||
_, id, has := s.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(s.CurrentSlot()+1, [32]byte{} /* head root */)
|
||||
// There exists proposer for next slot, but we haven't called fcu w/ payload attribute yet.
|
||||
if has && id == [8]byte{} {
|
||||
headBlock, err := s.headBlock()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get head block")
|
||||
} else {
|
||||
if _, err := s.notifyForkchoiceUpdate(ctx, ¬ifyForkchoiceUpdateArg{
|
||||
headState: s.headState(ctx),
|
||||
headRoot: s.headRoot(),
|
||||
headBlock: headBlock.Block(),
|
||||
}); err != nil {
|
||||
log.WithError(err).Error("Could not prepare payload on empty ID")
|
||||
}
|
||||
}
|
||||
missedPayloadIDFilledCount.Inc()
|
||||
if err := s.fillMissingBlockPayloadId(ctx, ti); err != nil {
|
||||
log.WithError(err).Error("Could not fill missing payload ID")
|
||||
}
|
||||
case <-s.ctx.Done():
|
||||
log.Debug("Context closed, exiting routine")
|
||||
@@ -698,3 +681,31 @@ func atHalfSlot(t time.Time) bool {
|
||||
s := params.BeaconConfig().SecondsPerSlot
|
||||
return uint64(t.Second())%s == s/2
|
||||
}
|
||||
|
||||
func (s *Service) fillMissingBlockPayloadId(ctx context.Context, ti time.Time) error {
|
||||
if !atHalfSlot(ti) {
|
||||
return nil
|
||||
}
|
||||
if s.CurrentSlot() == s.cfg.ForkChoiceStore.HighestReceivedBlockSlot() {
|
||||
return nil
|
||||
}
|
||||
// Head root should be empty when retrieving proposer index for the next slot.
|
||||
_, id, has := s.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(s.CurrentSlot()+1, [32]byte{} /* head root */)
|
||||
// There exists proposer for next slot, but we haven't called fcu w/ payload attribute yet.
|
||||
if has && id == [8]byte{} {
|
||||
missedPayloadIDFilledCount.Inc()
|
||||
headBlock, err := s.headBlock()
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
if _, err := s.notifyForkchoiceUpdate(ctx, ¬ifyForkchoiceUpdateArg{
|
||||
headState: s.headState(ctx),
|
||||
headRoot: s.headRoot(),
|
||||
headBlock: headBlock.Block(),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -185,15 +185,16 @@ func (s *Service) updateFinalized(ctx context.Context, cp *ethpb.Checkpoint) err
|
||||
// ancestor returns the block root of an ancestry block from the input block root.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_ancestor(store: Store, root: Root, slot: Slot) -> Root:
|
||||
// block = store.blocks[root]
|
||||
// if block.slot > slot:
|
||||
// return get_ancestor(store, block.parent_root, slot)
|
||||
// elif block.slot == slot:
|
||||
// return root
|
||||
// else:
|
||||
// # root is older than queried slot, thus a skip slot. Return most recent root prior to slot
|
||||
// return root
|
||||
//
|
||||
// def get_ancestor(store: Store, root: Root, slot: Slot) -> Root:
|
||||
// block = store.blocks[root]
|
||||
// if block.slot > slot:
|
||||
// return get_ancestor(store, block.parent_root, slot)
|
||||
// elif block.slot == slot:
|
||||
// return root
|
||||
// else:
|
||||
// # root is older than queried slot, thus a skip slot. Return most recent root prior to slot
|
||||
// return root
|
||||
func (s *Service) ancestor(ctx context.Context, root []byte, slot types.Slot) ([]byte, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.ancestor")
|
||||
defer span.End()
|
||||
|
||||
@@ -446,9 +446,12 @@ func TestFillForkChoiceMissingBlocks_FinalizedSibling(t *testing.T) {
|
||||
}
|
||||
|
||||
// blockTree1 constructs the following tree:
|
||||
// /- B1
|
||||
//
|
||||
// /- B1
|
||||
//
|
||||
// B0 /- B5 - B7
|
||||
// \- B3 - B4 - B6 - B8
|
||||
//
|
||||
// \- B3 - B4 - B6 - B8
|
||||
func blockTree1(t *testing.T, beaconDB db.Database, genesisRoot []byte) ([][]byte, error) {
|
||||
genesisRoot = bytesutil.PadTo(genesisRoot, 32)
|
||||
b0 := util.NewBeaconBlock()
|
||||
@@ -1989,216 +1992,6 @@ func TestStore_NoViableHead_Liveness(t *testing.T) {
|
||||
require.Equal(t, false, optimistic)
|
||||
}
|
||||
|
||||
// See the description in #10777 and #10782 for the full setup
|
||||
// We sync optimistically a chain of blocks. Block 12 is the first block in Epoch
|
||||
// 2 (and the merge block in this sequence). Block 18 justifies it and Block 19 returns
|
||||
// INVALID from NewPayload, with LVH block 12. No head is viable. We check
|
||||
// that the node is optimistic and that we can actually import a chain of blocks on top of
|
||||
// 12 and recover. Notice that it takes two epochs to fully recover, and we stay
|
||||
// optimistic for the whole time.
|
||||
func TestStore_NoViableHead_Liveness_Protoarray(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
config := params.BeaconConfig()
|
||||
config.SlotsPerEpoch = 6
|
||||
config.AltairForkEpoch = 1
|
||||
config.BellatrixForkEpoch = 2
|
||||
params.OverrideBeaconConfig(config)
|
||||
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
mockEngine := &mockExecution.EngineClient{ErrNewPayload: execution.ErrAcceptedSyncingPayloadStatus, ErrForkchoiceUpdated: execution.ErrAcceptedSyncingPayloadStatus}
|
||||
fc := doublylinkedtree.New()
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithAttestationPool(attestations.NewPool()),
|
||||
WithStateGen(stategen.New(beaconDB, fc)),
|
||||
WithForkChoiceStore(fc),
|
||||
WithStateNotifier(&mock.MockStateNotifier{}),
|
||||
WithExecutionEngineCaller(mockEngine),
|
||||
WithProposerIdsCache(cache.NewProposerPayloadIDsCache()),
|
||||
}
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
|
||||
st, keys := util.DeterministicGenesisState(t, 64)
|
||||
stateRoot, err := st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err, "Could not hash genesis state")
|
||||
|
||||
require.NoError(t, service.saveGenesisData(ctx, st))
|
||||
|
||||
genesis := blocks.NewGenesisBlock(stateRoot[:])
|
||||
wsb, err := consensusblocks.NewSignedBeaconBlock(genesis)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wsb), "Could not save genesis block")
|
||||
|
||||
parentRoot, err := genesis.Block.HashTreeRoot()
|
||||
require.NoError(t, err, "Could not get signing root")
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, st, parentRoot), "Could not save genesis state")
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveHeadBlockRoot(ctx, parentRoot), "Could not save genesis state")
|
||||
|
||||
for i := 1; i < 6; i++ {
|
||||
driftGenesisTime(service, int64(i), 0)
|
||||
st, err := service.HeadState(ctx)
|
||||
require.NoError(t, err)
|
||||
b, err := util.GenerateFullBlock(st, keys, util.DefaultBlockGenConfig(), types.Slot(i))
|
||||
require.NoError(t, err)
|
||||
wsb, err := consensusblocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
root, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.onBlock(ctx, wsb, root))
|
||||
}
|
||||
|
||||
for i := 6; i < 12; i++ {
|
||||
driftGenesisTime(service, int64(i), 0)
|
||||
st, err := service.HeadState(ctx)
|
||||
require.NoError(t, err)
|
||||
b, err := util.GenerateFullBlockAltair(st, keys, util.DefaultBlockGenConfig(), types.Slot(i))
|
||||
require.NoError(t, err)
|
||||
wsb, err := consensusblocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
root, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
err = service.onBlock(ctx, wsb, root)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// import the merge block
|
||||
driftGenesisTime(service, 12, 0)
|
||||
st, err = service.HeadState(ctx)
|
||||
require.NoError(t, err)
|
||||
b, err := util.GenerateFullBlockBellatrix(st, keys, util.DefaultBlockGenConfig(), 12)
|
||||
require.NoError(t, err)
|
||||
wsb, err = consensusblocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
lastValidRoot, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
err = service.onBlock(ctx, wsb, lastValidRoot)
|
||||
require.NoError(t, err)
|
||||
// save the post state and the payload Hash of this block since it will
|
||||
// be the LVH
|
||||
validHeadState, err := service.HeadState(ctx)
|
||||
require.NoError(t, err)
|
||||
lvh := b.Block.Body.ExecutionPayload.BlockHash
|
||||
validjc := validHeadState.CurrentJustifiedCheckpoint()
|
||||
require.Equal(t, types.Epoch(0), validjc.Epoch)
|
||||
|
||||
// import blocks 13 through 18 to justify 12, these are invalid blocks
|
||||
invalidRoots := make([][32]byte, 19-13)
|
||||
for i := 13; i < 19; i++ {
|
||||
driftGenesisTime(service, int64(i), 0)
|
||||
st, err := service.HeadState(ctx)
|
||||
require.NoError(t, err)
|
||||
b, err := util.GenerateFullBlockBellatrix(st, keys, util.DefaultBlockGenConfig(), types.Slot(i))
|
||||
require.NoError(t, err)
|
||||
wsb, err := consensusblocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
invalidRoots[i-13], err = b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
err = service.onBlock(ctx, wsb, invalidRoots[i-13])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
// Check that we have justified the second epoch
|
||||
jc := service.ForkChoicer().JustifiedCheckpoint()
|
||||
require.Equal(t, types.Epoch(2), jc.Epoch)
|
||||
invalidHeadRoot := service.ForkChoicer().CachedHeadRoot()
|
||||
|
||||
// import block 19 to find out that the whole chain 13--18 was in fact
|
||||
// invalid
|
||||
mockEngine = &mockExecution.EngineClient{ErrNewPayload: execution.ErrInvalidPayloadStatus, NewPayloadResp: lvh}
|
||||
service.cfg.ExecutionEngineCaller = mockEngine
|
||||
driftGenesisTime(service, 19, 0)
|
||||
st, err = service.HeadState(ctx)
|
||||
require.NoError(t, err)
|
||||
b, err = util.GenerateFullBlockBellatrix(st, keys, util.DefaultBlockGenConfig(), 19)
|
||||
require.NoError(t, err)
|
||||
wsb, err = consensusblocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
root, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
err = service.onBlock(ctx, wsb, root)
|
||||
require.ErrorContains(t, "received an INVALID payload from execution engine", err)
|
||||
|
||||
// Check that forkchoice's head and store's headroot are the previous head (since the invalid block did
|
||||
// not finish importing and it was never imported to forkchoice). Check
|
||||
// also that the node is optimistic
|
||||
require.Equal(t, invalidHeadRoot, service.ForkChoicer().CachedHeadRoot())
|
||||
headRoot, err := service.HeadRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, invalidHeadRoot, bytesutil.ToBytes32(headRoot))
|
||||
optimistic, err := service.IsOptimistic(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, optimistic)
|
||||
|
||||
// Check that the invalid blocks are not in database
|
||||
for i := 0; i < 19-13; i++ {
|
||||
require.Equal(t, false, service.cfg.BeaconDB.HasBlock(ctx, invalidRoots[i]))
|
||||
}
|
||||
|
||||
// Check that the node's justified checkpoint does not agree with the
|
||||
// last valid state's justified checkpoint
|
||||
sjc := service.CurrentJustifiedCheckpt()
|
||||
require.Equal(t, types.Epoch(2), sjc.Epoch)
|
||||
|
||||
// import another block based on the last valid head state
|
||||
mockEngine = &mockExecution.EngineClient{}
|
||||
service.cfg.ExecutionEngineCaller = mockEngine
|
||||
driftGenesisTime(service, 20, 0)
|
||||
b, err = util.GenerateFullBlockBellatrix(validHeadState, keys, &util.BlockGenConfig{}, 20)
|
||||
require.NoError(t, err)
|
||||
wsb, err = consensusblocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
root, err = b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.onBlock(ctx, wsb, root))
|
||||
// Check that the head is still INVALID and the node is still optimistic
|
||||
require.Equal(t, invalidHeadRoot, service.ForkChoicer().CachedHeadRoot())
|
||||
optimistic, err = service.IsOptimistic(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, optimistic)
|
||||
st, err = service.cfg.StateGen.StateByRoot(ctx, root)
|
||||
require.NoError(t, err)
|
||||
// Import blocks 21--30 (Epoch 3 was not enough to justify 2)
|
||||
for i := 21; i < 30; i++ {
|
||||
driftGenesisTime(service, int64(i), 0)
|
||||
require.NoError(t, err)
|
||||
b, err := util.GenerateFullBlockBellatrix(st, keys, util.DefaultBlockGenConfig(), types.Slot(i))
|
||||
require.NoError(t, err)
|
||||
wsb, err := consensusblocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
root, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
err = service.onBlock(ctx, wsb, root)
|
||||
require.NoError(t, err)
|
||||
st, err = service.cfg.StateGen.StateByRoot(ctx, root)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
// Head should still be INVALID and the node optimistic
|
||||
require.Equal(t, invalidHeadRoot, service.ForkChoicer().CachedHeadRoot())
|
||||
optimistic, err = service.IsOptimistic(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, optimistic)
|
||||
|
||||
// Import block 30, it should justify Epoch 4 and become HEAD, the node
|
||||
// recovers
|
||||
driftGenesisTime(service, 30, 0)
|
||||
b, err = util.GenerateFullBlockBellatrix(st, keys, util.DefaultBlockGenConfig(), 30)
|
||||
require.NoError(t, err)
|
||||
wsb, err = consensusblocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
root, err = b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
err = service.onBlock(ctx, wsb, root)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, root, service.ForkChoicer().CachedHeadRoot())
|
||||
sjc = service.CurrentJustifiedCheckpt()
|
||||
require.Equal(t, types.Epoch(4), sjc.Epoch)
|
||||
optimistic, err = service.IsOptimistic(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, optimistic)
|
||||
}
|
||||
|
||||
type newForkChoicer func() forkchoice.ForkChoicer
|
||||
|
||||
// See the description in #10777 and #10782 for the full setup
|
||||
@@ -2496,6 +2289,21 @@ func TestOnBlock_HandleBlockAttestations(t *testing.T) {
|
||||
require.Equal(t, 1, len(service.cfg.AttPool.BlockAttestations()))
|
||||
}
|
||||
|
||||
func TestFillMissingBlockPayloadId_DiffSlotExitEarly(t *testing.T) {
|
||||
fc := doublylinkedtree.New()
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
opts := []Option{
|
||||
WithForkChoiceStore(fc),
|
||||
WithStateGen(stategen.New(beaconDB, fc)),
|
||||
}
|
||||
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.fillMissingBlockPayloadId(ctx, time.Unix(int64(params.BeaconConfig().SecondsPerSlot/2), 0)))
|
||||
}
|
||||
|
||||
// Helper function to simulate the block being on time or delayed for proposer
|
||||
// boost. It alters the genesisTime tracked by the store.
|
||||
func driftGenesisTime(s *Service, slot int64, delay int64) {
|
||||
|
||||
@@ -226,11 +226,11 @@ func TestService_ProcessAttestationsAndUpdateHead(t *testing.T) {
|
||||
require.Equal(t, 2, fcs.NodeCount())
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wsb))
|
||||
|
||||
// Generate attestatios for this block in Slot 1
|
||||
// Generate attestations for this block in Slot 1
|
||||
atts, err := util.GenerateAttestations(copied, pks, 1, 1, false)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.AttPool.SaveForkchoiceAttestations(atts))
|
||||
// Verify the target is in forchoice
|
||||
// Verify the target is in forkchoice
|
||||
require.Equal(t, true, fcs.HasNode(bytesutil.ToBytes32(atts[0].Data.BeaconBlockRoot)))
|
||||
|
||||
// Insert a new block to forkchoice
|
||||
|
||||
@@ -32,9 +32,9 @@ type SlashingReceiver interface {
|
||||
|
||||
// ReceiveBlock is a function that defines the operations (minus pubsub)
|
||||
// that are performed on a received block. The operations consist of:
|
||||
// 1. Validate block, apply state transition and update checkpoints
|
||||
// 2. Apply fork choice to the processed block
|
||||
// 3. Save latest head info
|
||||
// 1. Validate block, apply state transition and update checkpoints
|
||||
// 2. Apply fork choice to the processed block
|
||||
// 3. Save latest head info
|
||||
func (s *Service) ReceiveBlock(ctx context.Context, block interfaces.SignedBeaconBlock, blockRoot [32]byte) error {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.ReceiveBlock")
|
||||
defer span.End()
|
||||
|
||||
@@ -309,7 +309,10 @@ func (s *Service) initializeHeadFromDB(ctx context.Context) error {
|
||||
if err := s.setHead(finalizedRoot, finalizedBlock, finalizedState); err != nil {
|
||||
return errors.Wrap(err, "could not set head")
|
||||
}
|
||||
|
||||
_, err = s.notifyForkchoiceUpdate(s.ctx, ¬ifyForkchoiceUpdateArg{headState: finalizedState, headRoot: finalizedRoot, headBlock: finalizedBlock.Block()})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error calling FCU with finalized state at startup")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -444,6 +447,10 @@ func (s *Service) saveGenesisData(ctx context.Context, genesisState state.Beacon
|
||||
if err := s.setHead(genesisBlkRoot, genesisBlk, genesisState); err != nil {
|
||||
log.WithError(err).Fatal("Could not set head")
|
||||
}
|
||||
_, err = s.notifyForkchoiceUpdate(s.ctx, ¬ifyForkchoiceUpdateArg{headState: genesisState, headRoot: genesisBlkRoot, headBlock: genesisBlk.Block()})
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Could not call FCU with genesis")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
8
beacon-chain/cache/depositsnapshot/BUILD.bazel
vendored
Normal file
8
beacon-chain/cache/depositsnapshot/BUILD.bazel
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["merkle_tree.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/beacon-chain/cache/depositsnapshot",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
19
beacon-chain/cache/depositsnapshot/merkle_tree.go
vendored
Normal file
19
beacon-chain/cache/depositsnapshot/merkle_tree.go
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
package depositsnapshot
|
||||
|
||||
const (
|
||||
DepositContractDepth = 32 // Maximum tree depth as defined by EIP-4881.
|
||||
)
|
||||
|
||||
// MerkleTreeNode is the interface for a Merkle tree.
|
||||
type MerkleTreeNode interface {
|
||||
// GetRoot returns the root of the Merkle tree.
|
||||
GetRoot() [32]byte
|
||||
// IsFull returns whether there is space left for deposits.
|
||||
IsFull() bool
|
||||
// Finalize marks deposits of the Merkle tree as finalized.
|
||||
Finalize(deposits uint, depth uint) MerkleTreeNode
|
||||
// GetFinalized returns a list of hashes of all the finalized nodes and the number of deposits.
|
||||
GetFinalized(result [][32]byte) ([][32]byte, uint)
|
||||
// PushLeaf adds a new leaf node at the next available Zero node.
|
||||
PushLeaf(leaf [32]byte, deposits uint, depth uint) MerkleTreeNode
|
||||
}
|
||||
@@ -16,6 +16,7 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/altair",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//cmd/prysmctl/testnet:__pkg__",
|
||||
"//testing/endtoend/evaluators:__subpackages__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//testing/util:__pkg__",
|
||||
|
||||
@@ -82,23 +82,24 @@ func ProcessAttestationNoVerifySignature(
|
||||
// the proposer in state.
|
||||
//
|
||||
// Spec code:
|
||||
// # Update epoch participation flags
|
||||
// if data.target.epoch == get_current_epoch(state):
|
||||
// epoch_participation = state.current_epoch_participation
|
||||
// else:
|
||||
// epoch_participation = state.previous_epoch_participation
|
||||
//
|
||||
// proposer_reward_numerator = 0
|
||||
// for index in get_attesting_indices(state, data, attestation.aggregation_bits):
|
||||
// for flag_index, weight in enumerate(PARTICIPATION_FLAG_WEIGHTS):
|
||||
// if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index):
|
||||
// epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
|
||||
// proposer_reward_numerator += get_base_reward(state, index) * weight
|
||||
// # Update epoch participation flags
|
||||
// if data.target.epoch == get_current_epoch(state):
|
||||
// epoch_participation = state.current_epoch_participation
|
||||
// else:
|
||||
// epoch_participation = state.previous_epoch_participation
|
||||
//
|
||||
// # Reward proposer
|
||||
// proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
|
||||
// proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator)
|
||||
// increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
|
||||
// proposer_reward_numerator = 0
|
||||
// for index in get_attesting_indices(state, data, attestation.aggregation_bits):
|
||||
// for flag_index, weight in enumerate(PARTICIPATION_FLAG_WEIGHTS):
|
||||
// if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index):
|
||||
// epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
|
||||
// proposer_reward_numerator += get_base_reward(state, index) * weight
|
||||
//
|
||||
// # Reward proposer
|
||||
// proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
|
||||
// proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator)
|
||||
// increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
|
||||
func SetParticipationAndRewardProposer(
|
||||
ctx context.Context,
|
||||
beaconState state.BeaconState,
|
||||
@@ -157,12 +158,13 @@ func AddValidatorFlag(flag, flagPosition uint8) (uint8, error) {
|
||||
// EpochParticipation sets and returns the proposer reward numerator and epoch participation.
|
||||
//
|
||||
// Spec code:
|
||||
// proposer_reward_numerator = 0
|
||||
// for index in get_attesting_indices(state, data, attestation.aggregation_bits):
|
||||
// for flag_index, weight in enumerate(PARTICIPATION_FLAG_WEIGHTS):
|
||||
// if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index):
|
||||
// epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
|
||||
// proposer_reward_numerator += get_base_reward(state, index) * weight
|
||||
//
|
||||
// proposer_reward_numerator = 0
|
||||
// for index in get_attesting_indices(state, data, attestation.aggregation_bits):
|
||||
// for flag_index, weight in enumerate(PARTICIPATION_FLAG_WEIGHTS):
|
||||
// if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index):
|
||||
// epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
|
||||
// proposer_reward_numerator += get_base_reward(state, index) * weight
|
||||
func EpochParticipation(beaconState state.BeaconState, indices []uint64, epochParticipation []byte, participatedFlags map[uint8]bool, totalBalance uint64) (uint64, []byte, error) {
|
||||
cfg := params.BeaconConfig()
|
||||
sourceFlagIndex := cfg.TimelySourceFlagIndex
|
||||
@@ -218,9 +220,10 @@ func EpochParticipation(beaconState state.BeaconState, indices []uint64, epochPa
|
||||
// RewardProposer rewards proposer by increasing proposer's balance with input reward numerator and calculated reward denominator.
|
||||
//
|
||||
// Spec code:
|
||||
// proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
|
||||
// proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator)
|
||||
// increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
|
||||
//
|
||||
// proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
|
||||
// proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator)
|
||||
// increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
|
||||
func RewardProposer(ctx context.Context, beaconState state.BeaconState, proposerRewardNumerator uint64) error {
|
||||
cfg := params.BeaconConfig()
|
||||
d := (cfg.WeightDenominator - cfg.ProposerWeight) * cfg.WeightDenominator / cfg.ProposerWeight
|
||||
@@ -238,31 +241,32 @@ func RewardProposer(ctx context.Context, beaconState state.BeaconState, proposer
|
||||
//
|
||||
// Spec code:
|
||||
// def get_attestation_participation_flag_indices(state: BeaconState,
|
||||
// data: AttestationData,
|
||||
// inclusion_delay: uint64) -> Sequence[int]:
|
||||
// """
|
||||
// Return the flag indices that are satisfied by an attestation.
|
||||
// """
|
||||
// if data.target.epoch == get_current_epoch(state):
|
||||
// justified_checkpoint = state.current_justified_checkpoint
|
||||
// else:
|
||||
// justified_checkpoint = state.previous_justified_checkpoint
|
||||
//
|
||||
// # Matching roots
|
||||
// is_matching_source = data.source == justified_checkpoint
|
||||
// is_matching_target = is_matching_source and data.target.root == get_block_root(state, data.target.epoch)
|
||||
// is_matching_head = is_matching_target and data.beacon_block_root == get_block_root_at_slot(state, data.slot)
|
||||
// assert is_matching_source
|
||||
// data: AttestationData,
|
||||
// inclusion_delay: uint64) -> Sequence[int]:
|
||||
// """
|
||||
// Return the flag indices that are satisfied by an attestation.
|
||||
// """
|
||||
// if data.target.epoch == get_current_epoch(state):
|
||||
// justified_checkpoint = state.current_justified_checkpoint
|
||||
// else:
|
||||
// justified_checkpoint = state.previous_justified_checkpoint
|
||||
//
|
||||
// participation_flag_indices = []
|
||||
// if is_matching_source and inclusion_delay <= integer_squareroot(SLOTS_PER_EPOCH):
|
||||
// participation_flag_indices.append(TIMELY_SOURCE_FLAG_INDEX)
|
||||
// if is_matching_target and inclusion_delay <= SLOTS_PER_EPOCH:
|
||||
// participation_flag_indices.append(TIMELY_TARGET_FLAG_INDEX)
|
||||
// if is_matching_head and inclusion_delay == MIN_ATTESTATION_INCLUSION_DELAY:
|
||||
// participation_flag_indices.append(TIMELY_HEAD_FLAG_INDEX)
|
||||
// # Matching roots
|
||||
// is_matching_source = data.source == justified_checkpoint
|
||||
// is_matching_target = is_matching_source and data.target.root == get_block_root(state, data.target.epoch)
|
||||
// is_matching_head = is_matching_target and data.beacon_block_root == get_block_root_at_slot(state, data.slot)
|
||||
// assert is_matching_source
|
||||
//
|
||||
// return participation_flag_indices
|
||||
// participation_flag_indices = []
|
||||
// if is_matching_source and inclusion_delay <= integer_squareroot(SLOTS_PER_EPOCH):
|
||||
// participation_flag_indices.append(TIMELY_SOURCE_FLAG_INDEX)
|
||||
// if is_matching_target and inclusion_delay <= SLOTS_PER_EPOCH:
|
||||
// participation_flag_indices.append(TIMELY_TARGET_FLAG_INDEX)
|
||||
// if is_matching_head and inclusion_delay == MIN_ATTESTATION_INCLUSION_DELAY:
|
||||
// participation_flag_indices.append(TIMELY_HEAD_FLAG_INDEX)
|
||||
//
|
||||
// return participation_flag_indices
|
||||
func AttestationParticipationFlagIndices(beaconState state.BeaconState, data *ethpb.AttestationData, delay types.Slot) (map[uint8]bool, error) {
|
||||
currEpoch := time.CurrentEpoch(beaconState)
|
||||
var justifiedCheckpt *ethpb.Checkpoint
|
||||
@@ -304,9 +308,10 @@ func AttestationParticipationFlagIndices(beaconState state.BeaconState, data *et
|
||||
// MatchingStatus returns the matching statues for attestation data's source target and head.
|
||||
//
|
||||
// Spec code:
|
||||
// is_matching_source = data.source == justified_checkpoint
|
||||
// is_matching_target = is_matching_source and data.target.root == get_block_root(state, data.target.epoch)
|
||||
// is_matching_head = is_matching_target and data.beacon_block_root == get_block_root_at_slot(state, data.slot)
|
||||
//
|
||||
// is_matching_source = data.source == justified_checkpoint
|
||||
// is_matching_target = is_matching_source and data.target.root == get_block_root(state, data.target.epoch)
|
||||
// is_matching_head = is_matching_target and data.beacon_block_root == get_block_root_at_slot(state, data.slot)
|
||||
func MatchingStatus(beaconState state.BeaconState, data *ethpb.AttestationData, cp *ethpb.Checkpoint) (matchedSrc, matchedTgt, matchedHead bool, err error) {
|
||||
matchedSrc = attestation.CheckPointIsEqual(data.Source, cp)
|
||||
|
||||
|
||||
@@ -20,30 +20,31 @@ import (
|
||||
//
|
||||
// Spec code:
|
||||
// def process_sync_aggregate(state: BeaconState, sync_aggregate: SyncAggregate) -> None:
|
||||
// # Verify sync committee aggregate signature signing over the previous slot block root
|
||||
// committee_pubkeys = state.current_sync_committee.pubkeys
|
||||
// participant_pubkeys = [pubkey for pubkey, bit in zip(committee_pubkeys, sync_aggregate.sync_committee_bits) if bit]
|
||||
// previous_slot = max(state.slot, Slot(1)) - Slot(1)
|
||||
// domain = get_domain(state, DOMAIN_SYNC_COMMITTEE, compute_epoch_at_slot(previous_slot))
|
||||
// signing_root = compute_signing_root(get_block_root_at_slot(state, previous_slot), domain)
|
||||
// assert eth2_fast_aggregate_verify(participant_pubkeys, signing_root, sync_aggregate.sync_committee_signature)
|
||||
//
|
||||
// # Compute participant and proposer rewards
|
||||
// total_active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT
|
||||
// total_base_rewards = Gwei(get_base_reward_per_increment(state) * total_active_increments)
|
||||
// max_participant_rewards = Gwei(total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR // SLOTS_PER_EPOCH)
|
||||
// participant_reward = Gwei(max_participant_rewards // SYNC_COMMITTEE_SIZE)
|
||||
// proposer_reward = Gwei(participant_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT))
|
||||
// # Verify sync committee aggregate signature signing over the previous slot block root
|
||||
// committee_pubkeys = state.current_sync_committee.pubkeys
|
||||
// participant_pubkeys = [pubkey for pubkey, bit in zip(committee_pubkeys, sync_aggregate.sync_committee_bits) if bit]
|
||||
// previous_slot = max(state.slot, Slot(1)) - Slot(1)
|
||||
// domain = get_domain(state, DOMAIN_SYNC_COMMITTEE, compute_epoch_at_slot(previous_slot))
|
||||
// signing_root = compute_signing_root(get_block_root_at_slot(state, previous_slot), domain)
|
||||
// assert eth2_fast_aggregate_verify(participant_pubkeys, signing_root, sync_aggregate.sync_committee_signature)
|
||||
//
|
||||
// # Apply participant and proposer rewards
|
||||
// all_pubkeys = [v.pubkey for v in state.validators]
|
||||
// committee_indices = [ValidatorIndex(all_pubkeys.index(pubkey)) for pubkey in state.current_sync_committee.pubkeys]
|
||||
// for participant_index, participation_bit in zip(committee_indices, sync_aggregate.sync_committee_bits):
|
||||
// if participation_bit:
|
||||
// increase_balance(state, participant_index, participant_reward)
|
||||
// increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
|
||||
// else:
|
||||
// decrease_balance(state, participant_index, participant_reward)
|
||||
// # Compute participant and proposer rewards
|
||||
// total_active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT
|
||||
// total_base_rewards = Gwei(get_base_reward_per_increment(state) * total_active_increments)
|
||||
// max_participant_rewards = Gwei(total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR // SLOTS_PER_EPOCH)
|
||||
// participant_reward = Gwei(max_participant_rewards // SYNC_COMMITTEE_SIZE)
|
||||
// proposer_reward = Gwei(participant_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT))
|
||||
//
|
||||
// # Apply participant and proposer rewards
|
||||
// all_pubkeys = [v.pubkey for v in state.validators]
|
||||
// committee_indices = [ValidatorIndex(all_pubkeys.index(pubkey)) for pubkey in state.current_sync_committee.pubkeys]
|
||||
// for participant_index, participation_bit in zip(committee_indices, sync_aggregate.sync_committee_bits):
|
||||
// if participation_bit:
|
||||
// increase_balance(state, participant_index, participant_reward)
|
||||
// increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
|
||||
// else:
|
||||
// decrease_balance(state, participant_index, participant_reward)
|
||||
func ProcessSyncAggregate(ctx context.Context, s state.BeaconState, sync *ethpb.SyncAggregate) (state.BeaconState, error) {
|
||||
votedKeys, votedIndices, didntVoteIndices, err := FilterSyncCommitteeVotes(s, sync)
|
||||
if err != nil {
|
||||
|
||||
@@ -3,6 +3,8 @@ package altair
|
||||
import (
|
||||
"context"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/epoch/precompute"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers"
|
||||
@@ -29,6 +31,7 @@ func InitializePrecomputeValidators(ctx context.Context, beaconState state.Beaco
|
||||
// This shouldn't happen with a correct beacon state,
|
||||
// but rather be safe to defend against index out of bound panics.
|
||||
if beaconState.NumValidators() != len(inactivityScores) {
|
||||
log.Errorf("NumValidators=%d inactivityScores=%d", beaconState.NumValidators(), len(inactivityScores))
|
||||
return nil, nil, errors.New("num of validators is different than num of inactivity scores")
|
||||
}
|
||||
if err := beaconState.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
|
||||
@@ -68,9 +71,10 @@ func InitializePrecomputeValidators(ctx context.Context, beaconState state.Beaco
|
||||
// For fully inactive validators and perfect active validators, the effect is the same as before Altair.
|
||||
// For a validator is inactive and the chain fails to finalize, the inactivity score increases by a fixed number, the total loss after N epochs is proportional to N**2/2.
|
||||
// For imperfectly active validators. The inactivity score's behavior is specified by this function:
|
||||
// If a validator fails to submit an attestation with the correct target, their inactivity score goes up by 4.
|
||||
// If they successfully submit an attestation with the correct source and target, their inactivity score drops by 1
|
||||
// If the chain has recently finalized, each validator's score drops by 16.
|
||||
//
|
||||
// If a validator fails to submit an attestation with the correct target, their inactivity score goes up by 4.
|
||||
// If they successfully submit an attestation with the correct source and target, their inactivity score drops by 1
|
||||
// If the chain has recently finalized, each validator's score drops by 16.
|
||||
func ProcessInactivityScores(
|
||||
ctx context.Context,
|
||||
beaconState state.BeaconState,
|
||||
@@ -132,12 +136,13 @@ func ProcessInactivityScores(
|
||||
// it also tracks and updates epoch attesting balances.
|
||||
// Spec code:
|
||||
// if epoch == get_current_epoch(state):
|
||||
// epoch_participation = state.current_epoch_participation
|
||||
// else:
|
||||
// epoch_participation = state.previous_epoch_participation
|
||||
// active_validator_indices = get_active_validator_indices(state, epoch)
|
||||
// participating_indices = [i for i in active_validator_indices if has_flag(epoch_participation[i], flag_index)]
|
||||
// return set(filter(lambda index: not state.validators[index].slashed, participating_indices))
|
||||
//
|
||||
// epoch_participation = state.current_epoch_participation
|
||||
// else:
|
||||
// epoch_participation = state.previous_epoch_participation
|
||||
// active_validator_indices = get_active_validator_indices(state, epoch)
|
||||
// participating_indices = [i for i in active_validator_indices if has_flag(epoch_participation[i], flag_index)]
|
||||
// return set(filter(lambda index: not state.validators[index].slashed, participating_indices))
|
||||
func ProcessEpochParticipation(
|
||||
ctx context.Context,
|
||||
beaconState state.BeaconState,
|
||||
|
||||
@@ -14,10 +14,11 @@ import (
|
||||
//
|
||||
// Spec code:
|
||||
// def process_sync_committee_updates(state: BeaconState) -> None:
|
||||
// next_epoch = get_current_epoch(state) + Epoch(1)
|
||||
// if next_epoch % EPOCHS_PER_SYNC_COMMITTEE_PERIOD == 0:
|
||||
// state.current_sync_committee = state.next_sync_committee
|
||||
// state.next_sync_committee = get_next_sync_committee(state)
|
||||
//
|
||||
// next_epoch = get_current_epoch(state) + Epoch(1)
|
||||
// if next_epoch % EPOCHS_PER_SYNC_COMMITTEE_PERIOD == 0:
|
||||
// state.current_sync_committee = state.next_sync_committee
|
||||
// state.next_sync_committee = get_next_sync_committee(state)
|
||||
func ProcessSyncCommitteeUpdates(ctx context.Context, beaconState state.BeaconState) (state.BeaconState, error) {
|
||||
nextEpoch := time.NextEpoch(beaconState)
|
||||
if nextEpoch%params.BeaconConfig().EpochsPerSyncCommitteePeriod == 0 {
|
||||
@@ -46,8 +47,9 @@ func ProcessSyncCommitteeUpdates(ctx context.Context, beaconState state.BeaconSt
|
||||
//
|
||||
// Spec code:
|
||||
// def process_participation_flag_updates(state: BeaconState) -> None:
|
||||
// state.previous_epoch_participation = state.current_epoch_participation
|
||||
// state.current_epoch_participation = [ParticipationFlags(0b0000_0000) for _ in range(len(state.validators))]
|
||||
//
|
||||
// state.previous_epoch_participation = state.current_epoch_participation
|
||||
// state.current_epoch_participation = [ParticipationFlags(0b0000_0000) for _ in range(len(state.validators))]
|
||||
func ProcessParticipationFlagUpdates(beaconState state.BeaconState) (state.BeaconState, error) {
|
||||
c, err := beaconState.CurrentEpochParticipation()
|
||||
if err != nil {
|
||||
|
||||
@@ -13,16 +13,17 @@ import (
|
||||
// individual validator's base reward.
|
||||
//
|
||||
// Spec code:
|
||||
// def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
|
||||
// """
|
||||
// Return the base reward for the validator defined by ``index`` with respect to the current ``state``.
|
||||
//
|
||||
// Note: An optimally performing validator can earn one base reward per epoch over a long time horizon.
|
||||
// This takes into account both per-epoch (e.g. attestation) and intermittent duties (e.g. block proposal
|
||||
// and sync committees).
|
||||
// """
|
||||
// increments = state.validators[index].effective_balance // EFFECTIVE_BALANCE_INCREMENT
|
||||
// return Gwei(increments * get_base_reward_per_increment(state))
|
||||
// def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
|
||||
// """
|
||||
// Return the base reward for the validator defined by ``index`` with respect to the current ``state``.
|
||||
//
|
||||
// Note: An optimally performing validator can earn one base reward per epoch over a long time horizon.
|
||||
// This takes into account both per-epoch (e.g. attestation) and intermittent duties (e.g. block proposal
|
||||
// and sync committees).
|
||||
// """
|
||||
// increments = state.validators[index].effective_balance // EFFECTIVE_BALANCE_INCREMENT
|
||||
// return Gwei(increments * get_base_reward_per_increment(state))
|
||||
func BaseReward(s state.ReadOnlyBeaconState, index types.ValidatorIndex) (uint64, error) {
|
||||
totalBalance, err := helpers.TotalActiveBalance(s)
|
||||
if err != nil {
|
||||
@@ -50,7 +51,8 @@ func BaseRewardWithTotalBalance(s state.ReadOnlyBeaconState, index types.Validat
|
||||
//
|
||||
// Spec code:
|
||||
// def get_base_reward_per_increment(state: BeaconState) -> Gwei:
|
||||
// return Gwei(EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR // integer_squareroot(get_total_active_balance(state)))
|
||||
//
|
||||
// return Gwei(EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR // integer_squareroot(get_total_active_balance(state)))
|
||||
func BaseRewardPerIncrement(activeBalance uint64) (uint64, error) {
|
||||
if activeBalance == 0 {
|
||||
return 0, errors.New("active balance can't be 0")
|
||||
|
||||
@@ -47,13 +47,14 @@ func ValidateNilSyncContribution(s *ethpb.SignedContributionAndProof) error {
|
||||
//
|
||||
// Spec code:
|
||||
// def get_next_sync_committee(state: BeaconState) -> SyncCommittee:
|
||||
// """
|
||||
// Return the next sync committee, with possible pubkey duplicates.
|
||||
// """
|
||||
// indices = get_next_sync_committee_indices(state)
|
||||
// pubkeys = [state.validators[index].pubkey for index in indices]
|
||||
// aggregate_pubkey = bls.AggregatePKs(pubkeys)
|
||||
// return SyncCommittee(pubkeys=pubkeys, aggregate_pubkey=aggregate_pubkey)
|
||||
//
|
||||
// """
|
||||
// Return the next sync committee, with possible pubkey duplicates.
|
||||
// """
|
||||
// indices = get_next_sync_committee_indices(state)
|
||||
// pubkeys = [state.validators[index].pubkey for index in indices]
|
||||
// aggregate_pubkey = bls.AggregatePKs(pubkeys)
|
||||
// return SyncCommittee(pubkeys=pubkeys, aggregate_pubkey=aggregate_pubkey)
|
||||
func NextSyncCommittee(ctx context.Context, s state.BeaconState) (*ethpb.SyncCommittee, error) {
|
||||
indices, err := NextSyncCommitteeIndices(ctx, s)
|
||||
if err != nil {
|
||||
@@ -78,26 +79,27 @@ func NextSyncCommittee(ctx context.Context, s state.BeaconState) (*ethpb.SyncCom
|
||||
//
|
||||
// Spec code:
|
||||
// def get_next_sync_committee_indices(state: BeaconState) -> Sequence[ValidatorIndex]:
|
||||
// """
|
||||
// Return the sync committee indices, with possible duplicates, for the next sync committee.
|
||||
// """
|
||||
// epoch = Epoch(get_current_epoch(state) + 1)
|
||||
//
|
||||
// MAX_RANDOM_BYTE = 2**8 - 1
|
||||
// active_validator_indices = get_active_validator_indices(state, epoch)
|
||||
// active_validator_count = uint64(len(active_validator_indices))
|
||||
// seed = get_seed(state, epoch, DOMAIN_SYNC_COMMITTEE)
|
||||
// i = 0
|
||||
// sync_committee_indices: List[ValidatorIndex] = []
|
||||
// while len(sync_committee_indices) < SYNC_COMMITTEE_SIZE:
|
||||
// shuffled_index = compute_shuffled_index(uint64(i % active_validator_count), active_validator_count, seed)
|
||||
// candidate_index = active_validator_indices[shuffled_index]
|
||||
// random_byte = hash(seed + uint_to_bytes(uint64(i // 32)))[i % 32]
|
||||
// effective_balance = state.validators[candidate_index].effective_balance
|
||||
// if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte:
|
||||
// sync_committee_indices.append(candidate_index)
|
||||
// i += 1
|
||||
// return sync_committee_indices
|
||||
// """
|
||||
// Return the sync committee indices, with possible duplicates, for the next sync committee.
|
||||
// """
|
||||
// epoch = Epoch(get_current_epoch(state) + 1)
|
||||
//
|
||||
// MAX_RANDOM_BYTE = 2**8 - 1
|
||||
// active_validator_indices = get_active_validator_indices(state, epoch)
|
||||
// active_validator_count = uint64(len(active_validator_indices))
|
||||
// seed = get_seed(state, epoch, DOMAIN_SYNC_COMMITTEE)
|
||||
// i = 0
|
||||
// sync_committee_indices: List[ValidatorIndex] = []
|
||||
// while len(sync_committee_indices) < SYNC_COMMITTEE_SIZE:
|
||||
// shuffled_index = compute_shuffled_index(uint64(i % active_validator_count), active_validator_count, seed)
|
||||
// candidate_index = active_validator_indices[shuffled_index]
|
||||
// random_byte = hash(seed + uint_to_bytes(uint64(i // 32)))[i % 32]
|
||||
// effective_balance = state.validators[candidate_index].effective_balance
|
||||
// if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte:
|
||||
// sync_committee_indices.append(candidate_index)
|
||||
// i += 1
|
||||
// return sync_committee_indices
|
||||
func NextSyncCommitteeIndices(ctx context.Context, s state.BeaconState) ([]types.ValidatorIndex, error) {
|
||||
epoch := coreTime.NextEpoch(s)
|
||||
indices, err := helpers.ActiveValidatorIndices(ctx, s, epoch)
|
||||
@@ -144,18 +146,19 @@ func NextSyncCommitteeIndices(ctx context.Context, s state.BeaconState) ([]types
|
||||
// SyncSubCommitteePubkeys returns the pubkeys participating in a sync subcommittee.
|
||||
//
|
||||
// def get_sync_subcommittee_pubkeys(state: BeaconState, subcommittee_index: uint64) -> Sequence[BLSPubkey]:
|
||||
// # Committees assigned to `slot` sign for `slot - 1`
|
||||
// # This creates the exceptional logic below when transitioning between sync committee periods
|
||||
// next_slot_epoch = compute_epoch_at_slot(Slot(state.slot + 1))
|
||||
// if compute_sync_committee_period(get_current_epoch(state)) == compute_sync_committee_period(next_slot_epoch):
|
||||
// sync_committee = state.current_sync_committee
|
||||
// else:
|
||||
// sync_committee = state.next_sync_committee
|
||||
//
|
||||
// # Return pubkeys for the subcommittee index
|
||||
// sync_subcommittee_size = SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT
|
||||
// i = subcommittee_index * sync_subcommittee_size
|
||||
// return sync_committee.pubkeys[i:i + sync_subcommittee_size]
|
||||
// # Committees assigned to `slot` sign for `slot - 1`
|
||||
// # This creates the exceptional logic below when transitioning between sync committee periods
|
||||
// next_slot_epoch = compute_epoch_at_slot(Slot(state.slot + 1))
|
||||
// if compute_sync_committee_period(get_current_epoch(state)) == compute_sync_committee_period(next_slot_epoch):
|
||||
// sync_committee = state.current_sync_committee
|
||||
// else:
|
||||
// sync_committee = state.next_sync_committee
|
||||
//
|
||||
// # Return pubkeys for the subcommittee index
|
||||
// sync_subcommittee_size = SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT
|
||||
// i = subcommittee_index * sync_subcommittee_size
|
||||
// return sync_committee.pubkeys[i:i + sync_subcommittee_size]
|
||||
func SyncSubCommitteePubkeys(syncCommittee *ethpb.SyncCommittee, subComIdx types.CommitteeIndex) ([][]byte, error) {
|
||||
cfg := params.BeaconConfig()
|
||||
subCommSize := cfg.SyncCommitteeSize / cfg.SyncCommitteeSubnetCount
|
||||
@@ -172,8 +175,9 @@ func SyncSubCommitteePubkeys(syncCommittee *ethpb.SyncCommittee, subComIdx types
|
||||
// aggregator.
|
||||
//
|
||||
// def is_sync_committee_aggregator(signature: BLSSignature) -> bool:
|
||||
// modulo = max(1, SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT // TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE)
|
||||
// return bytes_to_uint64(hash(signature)[0:8]) % modulo == 0
|
||||
//
|
||||
// modulo = max(1, SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT // TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE)
|
||||
// return bytes_to_uint64(hash(signature)[0:8]) % modulo == 0
|
||||
func IsSyncCommitteeAggregator(sig []byte) (bool, error) {
|
||||
if len(sig) != fieldparams.BLSSignatureLength {
|
||||
return false, errors.New("incorrect sig length")
|
||||
|
||||
@@ -15,18 +15,19 @@ import (
|
||||
//
|
||||
// Spec code:
|
||||
// def process_epoch(state: BeaconState) -> None:
|
||||
// process_justification_and_finalization(state) # [Modified in Altair]
|
||||
// process_inactivity_updates(state) # [New in Altair]
|
||||
// process_rewards_and_penalties(state) # [Modified in Altair]
|
||||
// process_registry_updates(state)
|
||||
// process_slashings(state) # [Modified in Altair]
|
||||
// process_eth1_data_reset(state)
|
||||
// process_effective_balance_updates(state)
|
||||
// process_slashings_reset(state)
|
||||
// process_randao_mixes_reset(state)
|
||||
// process_historical_roots_update(state)
|
||||
// process_participation_flag_updates(state) # [New in Altair]
|
||||
// process_sync_committee_updates(state) # [New in Altair]
|
||||
//
|
||||
// process_justification_and_finalization(state) # [Modified in Altair]
|
||||
// process_inactivity_updates(state) # [New in Altair]
|
||||
// process_rewards_and_penalties(state) # [Modified in Altair]
|
||||
// process_registry_updates(state)
|
||||
// process_slashings(state) # [Modified in Altair]
|
||||
// process_eth1_data_reset(state)
|
||||
// process_effective_balance_updates(state)
|
||||
// process_slashings_reset(state)
|
||||
// process_randao_mixes_reset(state)
|
||||
// process_historical_roots_update(state)
|
||||
// process_participation_flag_updates(state) # [New in Altair]
|
||||
// process_sync_committee_updates(state) # [New in Altair]
|
||||
func ProcessEpoch(ctx context.Context, state state.BeaconState) (state.BeaconState, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "altair.ProcessEpoch")
|
||||
defer span.End()
|
||||
|
||||
@@ -16,52 +16,53 @@ import (
|
||||
//
|
||||
// Spec code:
|
||||
// def upgrade_to_altair(pre: phase0.BeaconState) -> BeaconState:
|
||||
// epoch = phase0.get_current_epoch(pre)
|
||||
// post = BeaconState(
|
||||
// # Versioning
|
||||
// genesis_time=pre.genesis_time,
|
||||
// genesis_validators_root=pre.genesis_validators_root,
|
||||
// slot=pre.slot,
|
||||
// fork=Fork(
|
||||
// previous_version=pre.fork.current_version,
|
||||
// current_version=ALTAIR_FORK_VERSION,
|
||||
// epoch=epoch,
|
||||
// ),
|
||||
// # History
|
||||
// latest_block_header=pre.latest_block_header,
|
||||
// block_roots=pre.block_roots,
|
||||
// state_roots=pre.state_roots,
|
||||
// historical_roots=pre.historical_roots,
|
||||
// # Eth1
|
||||
// eth1_data=pre.eth1_data,
|
||||
// eth1_data_votes=pre.eth1_data_votes,
|
||||
// eth1_deposit_index=pre.eth1_deposit_index,
|
||||
// # Registry
|
||||
// validators=pre.validators,
|
||||
// balances=pre.balances,
|
||||
// # Randomness
|
||||
// randao_mixes=pre.randao_mixes,
|
||||
// # Slashings
|
||||
// slashings=pre.slashings,
|
||||
// # Participation
|
||||
// previous_epoch_participation=[ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))],
|
||||
// current_epoch_participation=[ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))],
|
||||
// # Finality
|
||||
// justification_bits=pre.justification_bits,
|
||||
// previous_justified_checkpoint=pre.previous_justified_checkpoint,
|
||||
// current_justified_checkpoint=pre.current_justified_checkpoint,
|
||||
// finalized_checkpoint=pre.finalized_checkpoint,
|
||||
// # Inactivity
|
||||
// inactivity_scores=[uint64(0) for _ in range(len(pre.validators))],
|
||||
// )
|
||||
// # Fill in previous epoch participation from the pre state's pending attestations
|
||||
// translate_participation(post, pre.previous_epoch_attestations)
|
||||
//
|
||||
// # Fill in sync committees
|
||||
// # Note: A duplicate committee is assigned for the current and next committee at the fork boundary
|
||||
// post.current_sync_committee = get_next_sync_committee(post)
|
||||
// post.next_sync_committee = get_next_sync_committee(post)
|
||||
// return post
|
||||
// epoch = phase0.get_current_epoch(pre)
|
||||
// post = BeaconState(
|
||||
// # Versioning
|
||||
// genesis_time=pre.genesis_time,
|
||||
// genesis_validators_root=pre.genesis_validators_root,
|
||||
// slot=pre.slot,
|
||||
// fork=Fork(
|
||||
// previous_version=pre.fork.current_version,
|
||||
// current_version=ALTAIR_FORK_VERSION,
|
||||
// epoch=epoch,
|
||||
// ),
|
||||
// # History
|
||||
// latest_block_header=pre.latest_block_header,
|
||||
// block_roots=pre.block_roots,
|
||||
// state_roots=pre.state_roots,
|
||||
// historical_roots=pre.historical_roots,
|
||||
// # Eth1
|
||||
// eth1_data=pre.eth1_data,
|
||||
// eth1_data_votes=pre.eth1_data_votes,
|
||||
// eth1_deposit_index=pre.eth1_deposit_index,
|
||||
// # Registry
|
||||
// validators=pre.validators,
|
||||
// balances=pre.balances,
|
||||
// # Randomness
|
||||
// randao_mixes=pre.randao_mixes,
|
||||
// # Slashings
|
||||
// slashings=pre.slashings,
|
||||
// # Participation
|
||||
// previous_epoch_participation=[ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))],
|
||||
// current_epoch_participation=[ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))],
|
||||
// # Finality
|
||||
// justification_bits=pre.justification_bits,
|
||||
// previous_justified_checkpoint=pre.previous_justified_checkpoint,
|
||||
// current_justified_checkpoint=pre.current_justified_checkpoint,
|
||||
// finalized_checkpoint=pre.finalized_checkpoint,
|
||||
// # Inactivity
|
||||
// inactivity_scores=[uint64(0) for _ in range(len(pre.validators))],
|
||||
// )
|
||||
// # Fill in previous epoch participation from the pre state's pending attestations
|
||||
// translate_participation(post, pre.previous_epoch_attestations)
|
||||
//
|
||||
// # Fill in sync committees
|
||||
// # Note: A duplicate committee is assigned for the current and next committee at the fork boundary
|
||||
// post.current_sync_committee = get_next_sync_committee(post)
|
||||
// post.next_sync_committee = get_next_sync_committee(post)
|
||||
// return post
|
||||
func UpgradeToAltair(ctx context.Context, state state.BeaconState) (state.BeaconState, error) {
|
||||
epoch := time.CurrentEpoch(state)
|
||||
|
||||
@@ -126,17 +127,18 @@ func UpgradeToAltair(ctx context.Context, state state.BeaconState) (state.Beacon
|
||||
//
|
||||
// Spec code:
|
||||
// def translate_participation(state: BeaconState, pending_attestations: Sequence[phase0.PendingAttestation]) -> None:
|
||||
// for attestation in pending_attestations:
|
||||
// data = attestation.data
|
||||
// inclusion_delay = attestation.inclusion_delay
|
||||
// # Translate attestation inclusion info to flag indices
|
||||
// participation_flag_indices = get_attestation_participation_flag_indices(state, data, inclusion_delay)
|
||||
//
|
||||
// # Apply flags to all attesting validators
|
||||
// epoch_participation = state.previous_epoch_participation
|
||||
// for index in get_attesting_indices(state, data, attestation.aggregation_bits):
|
||||
// for flag_index in participation_flag_indices:
|
||||
// epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
|
||||
// for attestation in pending_attestations:
|
||||
// data = attestation.data
|
||||
// inclusion_delay = attestation.inclusion_delay
|
||||
// # Translate attestation inclusion info to flag indices
|
||||
// participation_flag_indices = get_attestation_participation_flag_indices(state, data, inclusion_delay)
|
||||
//
|
||||
// # Apply flags to all attesting validators
|
||||
// epoch_participation = state.previous_epoch_participation
|
||||
// for index in get_attesting_indices(state, data, attestation.aggregation_bits):
|
||||
// for flag_index in participation_flag_indices:
|
||||
// epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
|
||||
func TranslateParticipation(ctx context.Context, state state.BeaconState, atts []*ethpb.PendingAttestation) (state.BeaconState, error) {
|
||||
epochParticipation, err := state.PreviousEpochParticipation()
|
||||
if err != nil {
|
||||
|
||||
@@ -19,12 +19,7 @@ go_library(
|
||||
"withdrawals.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//testing/util:__pkg__",
|
||||
"//validator:__subpackages__",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/signing:go_default_library",
|
||||
@@ -45,6 +40,7 @@ go_library(
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//math:go_default_library",
|
||||
"//network/forks:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/attestation:go_default_library",
|
||||
"//proto/prysm/v1alpha1/slashings:go_default_library",
|
||||
|
||||
@@ -185,19 +185,20 @@ func VerifyAttestationSignature(ctx context.Context, beaconState state.ReadOnlyB
|
||||
// VerifyIndexedAttestation determines the validity of an indexed attestation.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool:
|
||||
// """
|
||||
// Check if ``indexed_attestation`` is not empty, has sorted and unique indices and has a valid aggregate signature.
|
||||
// """
|
||||
// # Verify indices are sorted and unique
|
||||
// indices = indexed_attestation.attesting_indices
|
||||
// if len(indices) == 0 or not indices == sorted(set(indices)):
|
||||
// return False
|
||||
// # Verify aggregate signature
|
||||
// pubkeys = [state.validators[i].pubkey for i in indices]
|
||||
// domain = get_domain(state, DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch)
|
||||
// signing_root = compute_signing_root(indexed_attestation.data, domain)
|
||||
// return bls.FastAggregateVerify(pubkeys, signing_root, indexed_attestation.signature)
|
||||
//
|
||||
// def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool:
|
||||
// """
|
||||
// Check if ``indexed_attestation`` is not empty, has sorted and unique indices and has a valid aggregate signature.
|
||||
// """
|
||||
// # Verify indices are sorted and unique
|
||||
// indices = indexed_attestation.attesting_indices
|
||||
// if len(indices) == 0 or not indices == sorted(set(indices)):
|
||||
// return False
|
||||
// # Verify aggregate signature
|
||||
// pubkeys = [state.validators[i].pubkey for i in indices]
|
||||
// domain = get_domain(state, DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch)
|
||||
// signing_root = compute_signing_root(indexed_attestation.data, domain)
|
||||
// return bls.FastAggregateVerify(pubkeys, signing_root, indexed_attestation.signature)
|
||||
func VerifyIndexedAttestation(ctx context.Context, beaconState state.ReadOnlyBeaconState, indexedAtt *ethpb.IndexedAttestation) error {
|
||||
ctx, span := trace.StartSpan(ctx, "core.VerifyIndexedAttestation")
|
||||
defer span.End()
|
||||
|
||||
@@ -22,20 +22,21 @@ import (
|
||||
// Casper FFG slashing conditions if any slashable events occurred.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None:
|
||||
// attestation_1 = attester_slashing.attestation_1
|
||||
// attestation_2 = attester_slashing.attestation_2
|
||||
// assert is_slashable_attestation_data(attestation_1.data, attestation_2.data)
|
||||
// assert is_valid_indexed_attestation(state, attestation_1)
|
||||
// assert is_valid_indexed_attestation(state, attestation_2)
|
||||
//
|
||||
// slashed_any = False
|
||||
// indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices)
|
||||
// for index in sorted(indices):
|
||||
// if is_slashable_validator(state.validators[index], get_current_epoch(state)):
|
||||
// slash_validator(state, index)
|
||||
// slashed_any = True
|
||||
// assert slashed_any
|
||||
// def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None:
|
||||
// attestation_1 = attester_slashing.attestation_1
|
||||
// attestation_2 = attester_slashing.attestation_2
|
||||
// assert is_slashable_attestation_data(attestation_1.data, attestation_2.data)
|
||||
// assert is_valid_indexed_attestation(state, attestation_1)
|
||||
// assert is_valid_indexed_attestation(state, attestation_2)
|
||||
//
|
||||
// slashed_any = False
|
||||
// indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices)
|
||||
// for index in sorted(indices):
|
||||
// if is_slashable_validator(state.validators[index], get_current_epoch(state)):
|
||||
// slash_validator(state, index)
|
||||
// slashed_any = True
|
||||
// assert slashed_any
|
||||
func ProcessAttesterSlashings(
|
||||
ctx context.Context,
|
||||
beaconState state.BeaconState,
|
||||
@@ -83,7 +84,7 @@ func ProcessAttesterSlashing(
|
||||
slashingQuotient = cfg.MinSlashingPenaltyQuotient
|
||||
case beaconState.Version() == version.Altair:
|
||||
slashingQuotient = cfg.MinSlashingPenaltyQuotientAltair
|
||||
case beaconState.Version() == version.Bellatrix:
|
||||
case beaconState.Version() == version.Bellatrix, beaconState.Version() == version.Capella:
|
||||
slashingQuotient = cfg.MinSlashingPenaltyQuotientBellatrix
|
||||
default:
|
||||
return nil, errors.New("unknown state version")
|
||||
@@ -132,16 +133,17 @@ func VerifyAttesterSlashing(ctx context.Context, beaconState state.ReadOnlyBeaco
|
||||
// IsSlashableAttestationData verifies a slashing against the Casper Proof of Stake FFG rules.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationData) -> bool:
|
||||
// """
|
||||
// Check if ``data_1`` and ``data_2`` are slashable according to Casper FFG rules.
|
||||
// """
|
||||
// return (
|
||||
// # Double vote
|
||||
// (data_1 != data_2 and data_1.target.epoch == data_2.target.epoch) or
|
||||
// # Surround vote
|
||||
// (data_1.source.epoch < data_2.source.epoch and data_2.target.epoch < data_1.target.epoch)
|
||||
// )
|
||||
//
|
||||
// def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationData) -> bool:
|
||||
// """
|
||||
// Check if ``data_1`` and ``data_2`` are slashable according to Casper FFG rules.
|
||||
// """
|
||||
// return (
|
||||
// # Double vote
|
||||
// (data_1 != data_2 and data_1.target.epoch == data_2.target.epoch) or
|
||||
// # Surround vote
|
||||
// (data_1.source.epoch < data_2.source.epoch and data_2.target.epoch < data_1.target.epoch)
|
||||
// )
|
||||
func IsSlashableAttestationData(data1, data2 *ethpb.AttestationData) bool {
|
||||
if data1 == nil || data2 == nil || data1.Target == nil || data2.Target == nil || data1.Source == nil || data2.Source == nil {
|
||||
return false
|
||||
|
||||
@@ -302,3 +302,72 @@ func TestProcessAttesterSlashings_AppliesCorrectStatusBellatrix(t *testing.T) {
|
||||
require.Equal(t, uint64(31000000000), newState.Balances()[1])
|
||||
require.Equal(t, uint64(32000000000), newState.Balances()[2])
|
||||
}
|
||||
|
||||
func TestProcessAttesterSlashings_AppliesCorrectStatusCapella(t *testing.T) {
|
||||
beaconState, privKeys := util.DeterministicGenesisStateCapella(t, 100)
|
||||
for _, vv := range beaconState.Validators() {
|
||||
vv.WithdrawableEpoch = types.Epoch(params.BeaconConfig().SlotsPerEpoch)
|
||||
}
|
||||
|
||||
att1 := util.HydrateIndexedAttestation(ðpb.IndexedAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 1},
|
||||
},
|
||||
AttestingIndices: []uint64{0, 1},
|
||||
})
|
||||
domain, err := signing.Domain(beaconState.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, beaconState.GenesisValidatorsRoot())
|
||||
require.NoError(t, err)
|
||||
signingRoot, err := signing.ComputeSigningRoot(att1.Data, domain)
|
||||
assert.NoError(t, err, "Could not get signing root of beacon block header")
|
||||
sig0 := privKeys[0].Sign(signingRoot[:])
|
||||
sig1 := privKeys[1].Sign(signingRoot[:])
|
||||
aggregateSig := bls.AggregateSignatures([]bls.Signature{sig0, sig1})
|
||||
att1.Signature = aggregateSig.Marshal()
|
||||
|
||||
att2 := util.HydrateIndexedAttestation(ðpb.IndexedAttestation{
|
||||
AttestingIndices: []uint64{0, 1},
|
||||
})
|
||||
signingRoot, err = signing.ComputeSigningRoot(att2.Data, domain)
|
||||
assert.NoError(t, err, "Could not get signing root of beacon block header")
|
||||
sig0 = privKeys[0].Sign(signingRoot[:])
|
||||
sig1 = privKeys[1].Sign(signingRoot[:])
|
||||
aggregateSig = bls.AggregateSignatures([]bls.Signature{sig0, sig1})
|
||||
att2.Signature = aggregateSig.Marshal()
|
||||
|
||||
slashings := []*ethpb.AttesterSlashing{
|
||||
{
|
||||
Attestation_1: att1,
|
||||
Attestation_2: att2,
|
||||
},
|
||||
}
|
||||
|
||||
currentSlot := 2 * params.BeaconConfig().SlotsPerEpoch
|
||||
require.NoError(t, beaconState.SetSlot(currentSlot))
|
||||
|
||||
b := util.NewBeaconBlock()
|
||||
b.Block = ðpb.BeaconBlock{
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
AttesterSlashings: slashings,
|
||||
},
|
||||
}
|
||||
|
||||
newState, err := blocks.ProcessAttesterSlashings(context.Background(), beaconState, b.Block.Body.AttesterSlashings, v.SlashValidator)
|
||||
require.NoError(t, err)
|
||||
newRegistry := newState.Validators()
|
||||
|
||||
// Given the intersection of slashable indices is [1], only validator
|
||||
// at index 1 should be slashed and exited. We confirm this below.
|
||||
if newRegistry[1].ExitEpoch != beaconState.Validators()[1].ExitEpoch {
|
||||
t.Errorf(
|
||||
`
|
||||
Expected validator at index 1's exit epoch to match
|
||||
%d, received %d instead
|
||||
`,
|
||||
beaconState.Validators()[1].ExitEpoch,
|
||||
newRegistry[1].ExitEpoch,
|
||||
)
|
||||
}
|
||||
|
||||
require.Equal(t, uint64(31000000000), newState.Balances()[1])
|
||||
require.Equal(t, uint64(32000000000), newState.Balances()[2])
|
||||
}
|
||||
|
||||
@@ -71,8 +71,9 @@ func ActivateValidatorWithEffectiveBalance(beaconState state.BeaconState, deposi
|
||||
// into the beacon chain.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// For each deposit in block.body.deposits:
|
||||
// process_deposit(state, deposit)
|
||||
//
|
||||
// For each deposit in block.body.deposits:
|
||||
// process_deposit(state, deposit)
|
||||
func ProcessDeposits(
|
||||
ctx context.Context,
|
||||
beaconState state.BeaconState,
|
||||
@@ -120,40 +121,41 @@ func BatchVerifyDepositsSignatures(ctx context.Context, deposits []*ethpb.Deposi
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
||||
// # Verify the Merkle branch
|
||||
// assert is_valid_merkle_branch(
|
||||
// leaf=hash_tree_root(deposit.data),
|
||||
// branch=deposit.proof,
|
||||
// depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in
|
||||
// index=state.eth1_deposit_index,
|
||||
// root=state.eth1_data.deposit_root,
|
||||
// )
|
||||
//
|
||||
// # Deposits must be processed in order
|
||||
// state.eth1_deposit_index += 1
|
||||
// # Verify the Merkle branch
|
||||
// assert is_valid_merkle_branch(
|
||||
// leaf=hash_tree_root(deposit.data),
|
||||
// branch=deposit.proof,
|
||||
// depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in
|
||||
// index=state.eth1_deposit_index,
|
||||
// root=state.eth1_data.deposit_root,
|
||||
// )
|
||||
//
|
||||
// pubkey = deposit.data.pubkey
|
||||
// amount = deposit.data.amount
|
||||
// validator_pubkeys = [v.pubkey for v in state.validators]
|
||||
// if pubkey not in validator_pubkeys:
|
||||
// # Verify the deposit signature (proof of possession) which is not checked by the deposit contract
|
||||
// deposit_message = DepositMessage(
|
||||
// pubkey=deposit.data.pubkey,
|
||||
// withdrawal_credentials=deposit.data.withdrawal_credentials,
|
||||
// amount=deposit.data.amount,
|
||||
// )
|
||||
// domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks
|
||||
// signing_root = compute_signing_root(deposit_message, domain)
|
||||
// if not bls.Verify(pubkey, signing_root, deposit.data.signature):
|
||||
// return
|
||||
// # Deposits must be processed in order
|
||||
// state.eth1_deposit_index += 1
|
||||
//
|
||||
// # Add validator and balance entries
|
||||
// state.validators.append(get_validator_from_deposit(state, deposit))
|
||||
// state.balances.append(amount)
|
||||
// else:
|
||||
// # Increase balance by deposit amount
|
||||
// index = ValidatorIndex(validator_pubkeys.index(pubkey))
|
||||
// increase_balance(state, index, amount)
|
||||
// pubkey = deposit.data.pubkey
|
||||
// amount = deposit.data.amount
|
||||
// validator_pubkeys = [v.pubkey for v in state.validators]
|
||||
// if pubkey not in validator_pubkeys:
|
||||
// # Verify the deposit signature (proof of possession) which is not checked by the deposit contract
|
||||
// deposit_message = DepositMessage(
|
||||
// pubkey=deposit.data.pubkey,
|
||||
// withdrawal_credentials=deposit.data.withdrawal_credentials,
|
||||
// amount=deposit.data.amount,
|
||||
// )
|
||||
// domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks
|
||||
// signing_root = compute_signing_root(deposit_message, domain)
|
||||
// if not bls.Verify(pubkey, signing_root, deposit.data.signature):
|
||||
// return
|
||||
//
|
||||
// # Add validator and balance entries
|
||||
// state.validators.append(get_validator_from_deposit(state, deposit))
|
||||
// state.balances.append(amount)
|
||||
// else:
|
||||
// # Increase balance by deposit amount
|
||||
// index = ValidatorIndex(validator_pubkeys.index(pubkey))
|
||||
// increase_balance(state, index, amount)
|
||||
func ProcessDeposit(beaconState state.BeaconState, deposit *ethpb.Deposit, verifySignature bool) (state.BeaconState, bool, error) {
|
||||
var newValidator bool
|
||||
if err := verifyDeposit(beaconState, deposit); err != nil {
|
||||
|
||||
@@ -6,3 +6,8 @@ var errNilSignedWithdrawalMessage = errors.New("nil SignedBLSToExecutionChange m
|
||||
var errNilWithdrawalMessage = errors.New("nil BLSToExecutionChange message")
|
||||
var errInvalidBLSPrefix = errors.New("withdrawal credential prefix is not a BLS prefix")
|
||||
var errInvalidWithdrawalCredentials = errors.New("withdrawal credentials do not match")
|
||||
var errInvalidWithdrawalIndex = errors.New("invalid withdrawal index")
|
||||
var errInvalidValidatorIndex = errors.New("invalid validator index")
|
||||
var errInvalidWithdrawalAmount = errors.New("invalid withdrawal amount")
|
||||
var errInvalidExecutionAddress = errors.New("invalid execution address")
|
||||
var errInvalidWithdrawalNumber = errors.New("invalid number of withdrawals")
|
||||
|
||||
@@ -15,10 +15,11 @@ import (
|
||||
// into the beacon state.
|
||||
//
|
||||
// Official spec definition:
|
||||
// def process_eth1_data(state: BeaconState, body: BeaconBlockBody) -> None:
|
||||
// state.eth1_data_votes.append(body.eth1_data)
|
||||
// if state.eth1_data_votes.count(body.eth1_data) * 2 > EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH:
|
||||
// state.eth1_data = body.eth1_data
|
||||
//
|
||||
// def process_eth1_data(state: BeaconState, body: BeaconBlockBody) -> None:
|
||||
// state.eth1_data_votes.append(body.eth1_data)
|
||||
// if state.eth1_data_votes.count(body.eth1_data) * 2 > EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH:
|
||||
// state.eth1_data = body.eth1_data
|
||||
func ProcessEth1DataInBlock(_ context.Context, beaconState state.BeaconState, eth1Data *ethpb.Eth1Data) (state.BeaconState, error) {
|
||||
if beaconState == nil || beaconState.IsNil() {
|
||||
return nil, errors.New("nil state")
|
||||
|
||||
@@ -27,23 +27,24 @@ var ValidatorCannotExitYetMsg = "validator has not been active long enough to ex
|
||||
// should exit the state's validator registry.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
|
||||
// voluntary_exit = signed_voluntary_exit.message
|
||||
// validator = state.validators[voluntary_exit.validator_index]
|
||||
// # Verify the validator is active
|
||||
// assert is_active_validator(validator, get_current_epoch(state))
|
||||
// # Verify exit has not been initiated
|
||||
// assert validator.exit_epoch == FAR_FUTURE_EPOCH
|
||||
// # Exits must specify an epoch when they become valid; they are not valid before then
|
||||
// assert get_current_epoch(state) >= voluntary_exit.epoch
|
||||
// # Verify the validator has been active long enough
|
||||
// assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
|
||||
// # Verify signature
|
||||
// domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
|
||||
// signing_root = compute_signing_root(voluntary_exit, domain)
|
||||
// assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature)
|
||||
// # Initiate exit
|
||||
// initiate_validator_exit(state, voluntary_exit.validator_index)
|
||||
//
|
||||
// def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
|
||||
// voluntary_exit = signed_voluntary_exit.message
|
||||
// validator = state.validators[voluntary_exit.validator_index]
|
||||
// # Verify the validator is active
|
||||
// assert is_active_validator(validator, get_current_epoch(state))
|
||||
// # Verify exit has not been initiated
|
||||
// assert validator.exit_epoch == FAR_FUTURE_EPOCH
|
||||
// # Exits must specify an epoch when they become valid; they are not valid before then
|
||||
// assert get_current_epoch(state) >= voluntary_exit.epoch
|
||||
// # Verify the validator has been active long enough
|
||||
// assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
|
||||
// # Verify signature
|
||||
// domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
|
||||
// signing_root = compute_signing_root(voluntary_exit, domain)
|
||||
// assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature)
|
||||
// # Initiate exit
|
||||
// initiate_validator_exit(state, voluntary_exit.validator_index)
|
||||
func ProcessVoluntaryExits(
|
||||
ctx context.Context,
|
||||
beaconState state.BeaconState,
|
||||
@@ -71,23 +72,24 @@ func ProcessVoluntaryExits(
|
||||
// VerifyExitAndSignature implements the spec defined validation for voluntary exits.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
|
||||
// voluntary_exit = signed_voluntary_exit.message
|
||||
// validator = state.validators[voluntary_exit.validator_index]
|
||||
// # Verify the validator is active
|
||||
// assert is_active_validator(validator, get_current_epoch(state))
|
||||
// # Verify exit has not been initiated
|
||||
// assert validator.exit_epoch == FAR_FUTURE_EPOCH
|
||||
// # Exits must specify an epoch when they become valid; they are not valid before then
|
||||
// assert get_current_epoch(state) >= voluntary_exit.epoch
|
||||
// # Verify the validator has been active long enough
|
||||
// assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
|
||||
// # Verify signature
|
||||
// domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
|
||||
// signing_root = compute_signing_root(voluntary_exit, domain)
|
||||
// assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature)
|
||||
// # Initiate exit
|
||||
// initiate_validator_exit(state, voluntary_exit.validator_index)
|
||||
//
|
||||
// def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
|
||||
// voluntary_exit = signed_voluntary_exit.message
|
||||
// validator = state.validators[voluntary_exit.validator_index]
|
||||
// # Verify the validator is active
|
||||
// assert is_active_validator(validator, get_current_epoch(state))
|
||||
// # Verify exit has not been initiated
|
||||
// assert validator.exit_epoch == FAR_FUTURE_EPOCH
|
||||
// # Exits must specify an epoch when they become valid; they are not valid before then
|
||||
// assert get_current_epoch(state) >= voluntary_exit.epoch
|
||||
// # Verify the validator has been active long enough
|
||||
// assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
|
||||
// # Verify signature
|
||||
// domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
|
||||
// signing_root = compute_signing_root(voluntary_exit, domain)
|
||||
// assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature)
|
||||
// # Initiate exit
|
||||
// initiate_validator_exit(state, voluntary_exit.validator_index)
|
||||
func VerifyExitAndSignature(
|
||||
validator state.ReadOnlyValidator,
|
||||
currentSlot types.Slot,
|
||||
@@ -117,23 +119,24 @@ func VerifyExitAndSignature(
|
||||
// verifyExitConditions implements the spec defined validation for voluntary exits(excluding signatures).
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
|
||||
// voluntary_exit = signed_voluntary_exit.message
|
||||
// validator = state.validators[voluntary_exit.validator_index]
|
||||
// # Verify the validator is active
|
||||
// assert is_active_validator(validator, get_current_epoch(state))
|
||||
// # Verify exit has not been initiated
|
||||
// assert validator.exit_epoch == FAR_FUTURE_EPOCH
|
||||
// # Exits must specify an epoch when they become valid; they are not valid before then
|
||||
// assert get_current_epoch(state) >= voluntary_exit.epoch
|
||||
// # Verify the validator has been active long enough
|
||||
// assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
|
||||
// # Verify signature
|
||||
// domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
|
||||
// signing_root = compute_signing_root(voluntary_exit, domain)
|
||||
// assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature)
|
||||
// # Initiate exit
|
||||
// initiate_validator_exit(state, voluntary_exit.validator_index)
|
||||
//
|
||||
// def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
|
||||
// voluntary_exit = signed_voluntary_exit.message
|
||||
// validator = state.validators[voluntary_exit.validator_index]
|
||||
// # Verify the validator is active
|
||||
// assert is_active_validator(validator, get_current_epoch(state))
|
||||
// # Verify exit has not been initiated
|
||||
// assert validator.exit_epoch == FAR_FUTURE_EPOCH
|
||||
// # Exits must specify an epoch when they become valid; they are not valid before then
|
||||
// assert get_current_epoch(state) >= voluntary_exit.epoch
|
||||
// # Verify the validator has been active long enough
|
||||
// assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
|
||||
// # Verify signature
|
||||
// domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
|
||||
// signing_root = compute_signing_root(voluntary_exit, domain)
|
||||
// assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature)
|
||||
// # Initiate exit
|
||||
// initiate_validator_exit(state, voluntary_exit.validator_index)
|
||||
func verifyExitConditions(validator state.ReadOnlyValidator, currentSlot types.Slot, exit *ethpb.VoluntaryExit) error {
|
||||
currentEpoch := slots.ToEpoch(currentSlot)
|
||||
// Verify the validator is active.
|
||||
|
||||
@@ -3,9 +3,14 @@
|
||||
package blocks
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
@@ -29,3 +34,60 @@ func NewGenesisBlock(stateRoot []byte) *ethpb.SignedBeaconBlock {
|
||||
}
|
||||
return block
|
||||
}
|
||||
|
||||
var ErrUnrecognizedState = errors.New("uknonwn underlying type for state.BeaconState value")
|
||||
|
||||
func NewGenesisBlockForState(root [32]byte, st state.BeaconState) (interfaces.SignedBeaconBlock, error) {
|
||||
ps := st.ToProto()
|
||||
switch ps.(type) {
|
||||
case *ethpb.BeaconState:
|
||||
return blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlock{
|
||||
Block: ðpb.BeaconBlock{
|
||||
ParentRoot: params.BeaconConfig().ZeroHash[:],
|
||||
StateRoot: root[:],
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
RandaoReveal: make([]byte, fieldparams.BLSSignatureLength),
|
||||
Eth1Data: ðpb.Eth1Data{
|
||||
DepositRoot: make([]byte, 32),
|
||||
BlockHash: make([]byte, 32),
|
||||
},
|
||||
Graffiti: make([]byte, 32),
|
||||
},
|
||||
},
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
})
|
||||
case *ethpb.BeaconStateBellatrix:
|
||||
return blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockBellatrix{
|
||||
Block: ðpb.BeaconBlockBellatrix{
|
||||
ParentRoot: params.BeaconConfig().ZeroHash[:],
|
||||
StateRoot: root[:],
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
RandaoReveal: make([]byte, 96),
|
||||
Eth1Data: ðpb.Eth1Data{
|
||||
DepositRoot: make([]byte, 32),
|
||||
BlockHash: make([]byte, 32),
|
||||
},
|
||||
Graffiti: make([]byte, 32),
|
||||
SyncAggregate: ðpb.SyncAggregate{
|
||||
SyncCommitteeBits: make([]byte, fieldparams.SyncCommitteeLength/8),
|
||||
SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
},
|
||||
ExecutionPayload: &enginev1.ExecutionPayload{
|
||||
ParentHash: make([]byte, 32),
|
||||
FeeRecipient: make([]byte, 20),
|
||||
StateRoot: make([]byte, 32),
|
||||
ReceiptsRoot: make([]byte, 32),
|
||||
LogsBloom: make([]byte, 256),
|
||||
PrevRandao: make([]byte, 32),
|
||||
BaseFeePerGas: make([]byte, 32),
|
||||
BlockHash: make([]byte, 32),
|
||||
Transactions: make([][]byte, 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
})
|
||||
default:
|
||||
return nil, ErrUnrecognizedState
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,27 +18,27 @@ import (
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
//
|
||||
// def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
|
||||
// # Verify that the slots match
|
||||
// assert block.slot == state.slot
|
||||
// # Verify that the block is newer than latest block header
|
||||
// assert block.slot > state.latest_block_header.slot
|
||||
// # Verify that proposer index is the correct index
|
||||
// assert block.proposer_index == get_beacon_proposer_index(state)
|
||||
// # Verify that the parent matches
|
||||
// assert block.parent_root == hash_tree_root(state.latest_block_header)
|
||||
// # Cache current block as the new latest block
|
||||
// state.latest_block_header = BeaconBlockHeader(
|
||||
// slot=block.slot,
|
||||
// proposer_index=block.proposer_index,
|
||||
// parent_root=block.parent_root,
|
||||
// state_root=Bytes32(), # Overwritten in the next process_slot call
|
||||
// body_root=hash_tree_root(block.body),
|
||||
// )
|
||||
// def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
|
||||
// # Verify that the slots match
|
||||
// assert block.slot == state.slot
|
||||
// # Verify that the block is newer than latest block header
|
||||
// assert block.slot > state.latest_block_header.slot
|
||||
// # Verify that proposer index is the correct index
|
||||
// assert block.proposer_index == get_beacon_proposer_index(state)
|
||||
// # Verify that the parent matches
|
||||
// assert block.parent_root == hash_tree_root(state.latest_block_header)
|
||||
// # Cache current block as the new latest block
|
||||
// state.latest_block_header = BeaconBlockHeader(
|
||||
// slot=block.slot,
|
||||
// proposer_index=block.proposer_index,
|
||||
// parent_root=block.parent_root,
|
||||
// state_root=Bytes32(), # Overwritten in the next process_slot call
|
||||
// body_root=hash_tree_root(block.body),
|
||||
// )
|
||||
//
|
||||
// # Verify proposer is not slashed
|
||||
// proposer = state.validators[block.proposer_index]
|
||||
// assert not proposer.slashed
|
||||
// # Verify proposer is not slashed
|
||||
// proposer = state.validators[block.proposer_index]
|
||||
// assert not proposer.slashed
|
||||
func ProcessBlockHeader(
|
||||
ctx context.Context,
|
||||
beaconState state.BeaconState,
|
||||
@@ -73,27 +73,28 @@ func ProcessBlockHeader(
|
||||
// using a unsigned block.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
|
||||
// # Verify that the slots match
|
||||
// assert block.slot == state.slot
|
||||
// # Verify that the block is newer than latest block header
|
||||
// assert block.slot > state.latest_block_header.slot
|
||||
// # Verify that proposer index is the correct index
|
||||
// assert block.proposer_index == get_beacon_proposer_index(state)
|
||||
// # Verify that the parent matches
|
||||
// assert block.parent_root == hash_tree_root(state.latest_block_header)
|
||||
// # Cache current block as the new latest block
|
||||
// state.latest_block_header = BeaconBlockHeader(
|
||||
// slot=block.slot,
|
||||
// proposer_index=block.proposer_index,
|
||||
// parent_root=block.parent_root,
|
||||
// state_root=Bytes32(), # Overwritten in the next process_slot call
|
||||
// body_root=hash_tree_root(block.body),
|
||||
// )
|
||||
//
|
||||
// # Verify proposer is not slashed
|
||||
// proposer = state.validators[block.proposer_index]
|
||||
// assert not proposer.slashed
|
||||
// def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
|
||||
// # Verify that the slots match
|
||||
// assert block.slot == state.slot
|
||||
// # Verify that the block is newer than latest block header
|
||||
// assert block.slot > state.latest_block_header.slot
|
||||
// # Verify that proposer index is the correct index
|
||||
// assert block.proposer_index == get_beacon_proposer_index(state)
|
||||
// # Verify that the parent matches
|
||||
// assert block.parent_root == hash_tree_root(state.latest_block_header)
|
||||
// # Cache current block as the new latest block
|
||||
// state.latest_block_header = BeaconBlockHeader(
|
||||
// slot=block.slot,
|
||||
// proposer_index=block.proposer_index,
|
||||
// parent_root=block.parent_root,
|
||||
// state_root=Bytes32(), # Overwritten in the next process_slot call
|
||||
// body_root=hash_tree_root(block.body),
|
||||
// )
|
||||
//
|
||||
// # Verify proposer is not slashed
|
||||
// proposer = state.validators[block.proposer_index]
|
||||
// assert not proposer.slashed
|
||||
func ProcessBlockHeaderNoVerify(
|
||||
ctx context.Context,
|
||||
beaconState state.BeaconState,
|
||||
|
||||
@@ -25,7 +25,8 @@ var (
|
||||
//
|
||||
// Spec code:
|
||||
// def is_merge_transition_complete(state: BeaconState) -> bool:
|
||||
// return state.latest_execution_payload_header != ExecutionPayloadHeader()
|
||||
//
|
||||
// return state.latest_execution_payload_header != ExecutionPayloadHeader()
|
||||
func IsMergeTransitionComplete(st state.BeaconState) (bool, error) {
|
||||
if st == nil {
|
||||
return false, errors.New("nil state")
|
||||
@@ -33,6 +34,9 @@ func IsMergeTransitionComplete(st state.BeaconState) (bool, error) {
|
||||
if IsPreBellatrixVersion(st.Version()) {
|
||||
return false, nil
|
||||
}
|
||||
if st.Version() > version.Bellatrix {
|
||||
return true, nil
|
||||
}
|
||||
h, err := st.LatestExecutionPayloadHeader()
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -48,7 +52,8 @@ func IsMergeTransitionComplete(st state.BeaconState) (bool, error) {
|
||||
//
|
||||
// Spec code:
|
||||
// def is_execution_block(block: BeaconBlock) -> bool:
|
||||
// return block.body.execution_payload != ExecutionPayload()
|
||||
//
|
||||
// return block.body.execution_payload != ExecutionPayload()
|
||||
func IsExecutionBlock(body interfaces.BeaconBlockBody) (bool, error) {
|
||||
if body == nil {
|
||||
return false, errors.New("nil block body")
|
||||
@@ -73,7 +78,8 @@ func IsExecutionBlock(body interfaces.BeaconBlockBody) (bool, error) {
|
||||
//
|
||||
// Spec code:
|
||||
// def is_execution_enabled(state: BeaconState, body: BeaconBlockBody) -> bool:
|
||||
// return is_merge_block(state, body) or is_merge_complete(state)
|
||||
//
|
||||
// return is_merge_block(state, body) or is_merge_complete(state)
|
||||
func IsExecutionEnabled(st state.BeaconState, body interfaces.BeaconBlockBody) (bool, error) {
|
||||
if st == nil || body == nil {
|
||||
return false, errors.New("nil state or block body")
|
||||
@@ -81,6 +87,9 @@ func IsExecutionEnabled(st state.BeaconState, body interfaces.BeaconBlockBody) (
|
||||
if IsPreBellatrixVersion(st.Version()) {
|
||||
return false, nil
|
||||
}
|
||||
if st.Version() > version.Bellatrix {
|
||||
return true, nil
|
||||
}
|
||||
header, err := st.LatestExecutionPayloadHeader()
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -110,9 +119,10 @@ func IsPreBellatrixVersion(v int) bool {
|
||||
// These validation steps ONLY apply to post merge.
|
||||
//
|
||||
// Spec code:
|
||||
// # Verify consistency of the parent hash with respect to the previous execution payload header
|
||||
// if is_merge_complete(state):
|
||||
// assert payload.parent_hash == state.latest_execution_payload_header.block_hash
|
||||
//
|
||||
// # Verify consistency of the parent hash with respect to the previous execution payload header
|
||||
// if is_merge_complete(state):
|
||||
// assert payload.parent_hash == state.latest_execution_payload_header.block_hash
|
||||
func ValidatePayloadWhenMergeCompletes(st state.BeaconState, payload interfaces.ExecutionData) error {
|
||||
complete, err := IsMergeTransitionComplete(st)
|
||||
if err != nil {
|
||||
@@ -121,7 +131,6 @@ func ValidatePayloadWhenMergeCompletes(st state.BeaconState, payload interfaces.
|
||||
if !complete {
|
||||
return nil
|
||||
}
|
||||
|
||||
header, err := st.LatestExecutionPayloadHeader()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -136,10 +145,11 @@ func ValidatePayloadWhenMergeCompletes(st state.BeaconState, payload interfaces.
|
||||
// These validation steps apply to both pre merge and post merge.
|
||||
//
|
||||
// Spec code:
|
||||
// # Verify random
|
||||
// assert payload.random == get_randao_mix(state, get_current_epoch(state))
|
||||
// # Verify timestamp
|
||||
// assert payload.timestamp == compute_timestamp_at_slot(state, state.slot)
|
||||
//
|
||||
// # Verify random
|
||||
// assert payload.random == get_randao_mix(state, get_current_epoch(state))
|
||||
// # Verify timestamp
|
||||
// assert payload.timestamp == compute_timestamp_at_slot(state, state.slot)
|
||||
func ValidatePayload(st state.BeaconState, payload interfaces.ExecutionData) error {
|
||||
random, err := helpers.RandaoMix(st, time.CurrentEpoch(st))
|
||||
if err != nil {
|
||||
@@ -165,48 +175,51 @@ func ValidatePayload(st state.BeaconState, payload interfaces.ExecutionData) err
|
||||
//
|
||||
// Spec code:
|
||||
// def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None:
|
||||
// # Verify consistency of the parent hash with respect to the previous execution payload header
|
||||
// if is_merge_complete(state):
|
||||
// assert payload.parent_hash == state.latest_execution_payload_header.block_hash
|
||||
// # Verify random
|
||||
// assert payload.random == get_randao_mix(state, get_current_epoch(state))
|
||||
// # Verify timestamp
|
||||
// assert payload.timestamp == compute_timestamp_at_slot(state, state.slot)
|
||||
// # Verify the execution payload is valid
|
||||
// assert execution_engine.execute_payload(payload)
|
||||
// # Cache execution payload header
|
||||
// state.latest_execution_payload_header = ExecutionPayloadHeader(
|
||||
// parent_hash=payload.parent_hash,
|
||||
// FeeRecipient=payload.FeeRecipient,
|
||||
// state_root=payload.state_root,
|
||||
// receipt_root=payload.receipt_root,
|
||||
// logs_bloom=payload.logs_bloom,
|
||||
// random=payload.random,
|
||||
// block_number=payload.block_number,
|
||||
// gas_limit=payload.gas_limit,
|
||||
// gas_used=payload.gas_used,
|
||||
// timestamp=payload.timestamp,
|
||||
// extra_data=payload.extra_data,
|
||||
// base_fee_per_gas=payload.base_fee_per_gas,
|
||||
// block_hash=payload.block_hash,
|
||||
// transactions_root=hash_tree_root(payload.transactions),
|
||||
// )
|
||||
//
|
||||
// # Verify consistency of the parent hash with respect to the previous execution payload header
|
||||
// if is_merge_complete(state):
|
||||
// assert payload.parent_hash == state.latest_execution_payload_header.block_hash
|
||||
// # Verify random
|
||||
// assert payload.random == get_randao_mix(state, get_current_epoch(state))
|
||||
// # Verify timestamp
|
||||
// assert payload.timestamp == compute_timestamp_at_slot(state, state.slot)
|
||||
// # Verify the execution payload is valid
|
||||
// assert execution_engine.execute_payload(payload)
|
||||
// # Cache execution payload header
|
||||
// state.latest_execution_payload_header = ExecutionPayloadHeader(
|
||||
// parent_hash=payload.parent_hash,
|
||||
// FeeRecipient=payload.FeeRecipient,
|
||||
// state_root=payload.state_root,
|
||||
// receipt_root=payload.receipt_root,
|
||||
// logs_bloom=payload.logs_bloom,
|
||||
// random=payload.random,
|
||||
// block_number=payload.block_number,
|
||||
// gas_limit=payload.gas_limit,
|
||||
// gas_used=payload.gas_used,
|
||||
// timestamp=payload.timestamp,
|
||||
// extra_data=payload.extra_data,
|
||||
// base_fee_per_gas=payload.base_fee_per_gas,
|
||||
// block_hash=payload.block_hash,
|
||||
// transactions_root=hash_tree_root(payload.transactions),
|
||||
// )
|
||||
func ProcessPayload(st state.BeaconState, payload interfaces.ExecutionData) (state.BeaconState, error) {
|
||||
if st.Version() >= version.Capella {
|
||||
withdrawals, err := payload.Withdrawals()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get payload withdrawals")
|
||||
}
|
||||
st, err = ProcessWithdrawals(st, withdrawals)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process withdrawals")
|
||||
}
|
||||
}
|
||||
if err := ValidatePayloadWhenMergeCompletes(st, payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ValidatePayload(st, payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
header, err := blocks.PayloadToHeader(payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wrappedHeader, err := blocks.WrappedExecutionPayloadHeader(header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := st.SetLatestExecutionPayloadHeader(wrappedHeader); err != nil {
|
||||
if err := st.SetLatestExecutionPayloadHeader(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return st, nil
|
||||
|
||||
@@ -7,12 +7,14 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/time"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
state_native "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/state-native"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
|
||||
consensusblocks "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/ssz"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/util"
|
||||
"github.com/prysmaticlabs/prysm/v3/time/slots"
|
||||
@@ -203,6 +205,13 @@ func Test_IsMergeComplete(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_IsMergeCompleteCapella(t *testing.T) {
|
||||
st, _ := util.DeterministicGenesisStateCapella(t, 1)
|
||||
got, err := blocks.IsMergeTransitionComplete(st)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, got, true)
|
||||
}
|
||||
|
||||
func Test_IsExecutionBlock(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -237,6 +246,16 @@ func Test_IsExecutionBlock(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_IsExecutionBlockCapella(t *testing.T) {
|
||||
blk := util.NewBeaconBlockCapella()
|
||||
blk.Block.Body.ExecutionPayload = emptyPayloadCapella()
|
||||
wrappedBlock, err := consensusblocks.NewBeaconBlock(blk.Block)
|
||||
require.NoError(t, err)
|
||||
got, err := blocks.IsExecutionBlock(wrappedBlock.Body())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, got)
|
||||
}
|
||||
|
||||
func Test_IsExecutionEnabled(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -330,6 +349,15 @@ func Test_IsExecutionEnabled(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
func Test_IsExecutionEnabledCapella(t *testing.T) {
|
||||
st, _ := util.DeterministicGenesisStateCapella(t, 1)
|
||||
blk := util.NewBeaconBlockCapella()
|
||||
body, err := consensusblocks.NewBeaconBlockBody(blk.Block.Body)
|
||||
require.NoError(t, err)
|
||||
got, err := blocks.IsExecutionEnabled(st, body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, got)
|
||||
}
|
||||
|
||||
func Test_IsExecutionEnabledUsingHeader(t *testing.T) {
|
||||
tests := []struct {
|
||||
@@ -583,6 +611,20 @@ func Test_ProcessPayload(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ProcessPayloadCapella(t *testing.T) {
|
||||
spb := ðpb.BeaconStateCapella{}
|
||||
st, err := state_native.InitializeFromProtoCapella(spb)
|
||||
require.NoError(t, err)
|
||||
header, err := emptyPayloadHeaderCapella()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, st.SetLatestExecutionPayloadHeader(header))
|
||||
payload := emptyPayloadCapella()
|
||||
wrapped, err := consensusblocks.WrappedExecutionPayloadCapella(payload)
|
||||
require.NoError(t, err)
|
||||
_, err = blocks.ProcessPayload(st, wrapped)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_ProcessPayloadHeader(t *testing.T) {
|
||||
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
|
||||
random, err := helpers.RandaoMix(st, time.CurrentEpoch(st))
|
||||
@@ -828,6 +870,22 @@ func emptyPayloadHeader() (interfaces.ExecutionData, error) {
|
||||
})
|
||||
}
|
||||
|
||||
func emptyPayloadHeaderCapella() (interfaces.ExecutionData, error) {
|
||||
return consensusblocks.WrappedExecutionPayloadHeaderCapella(&enginev1.ExecutionPayloadHeaderCapella{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
TransactionsRoot: make([]byte, fieldparams.RootLength),
|
||||
WithdrawalsRoot: make([]byte, fieldparams.RootLength),
|
||||
ExtraData: make([]byte, 0),
|
||||
})
|
||||
}
|
||||
|
||||
func emptyPayload() *enginev1.ExecutionPayload {
|
||||
return &enginev1.ExecutionPayload{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
@@ -842,3 +900,19 @@ func emptyPayload() *enginev1.ExecutionPayload {
|
||||
ExtraData: make([]byte, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func emptyPayloadCapella() *enginev1.ExecutionPayloadCapella {
|
||||
return &enginev1.ExecutionPayloadCapella{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
Transactions: make([][]byte, 0),
|
||||
Withdrawals: make([]*enginev1.Withdrawal, 0),
|
||||
ExtraData: make([]byte, 0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,26 +24,27 @@ type slashValidatorFunc func(ctx context.Context, st state.BeaconState, vid type
|
||||
// slashing conditions if any slashable events occurred.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None:
|
||||
// header_1 = proposer_slashing.signed_header_1.message
|
||||
// header_2 = proposer_slashing.signed_header_2.message
|
||||
//
|
||||
// # Verify header slots match
|
||||
// assert header_1.slot == header_2.slot
|
||||
// # Verify header proposer indices match
|
||||
// assert header_1.proposer_index == header_2.proposer_index
|
||||
// # Verify the headers are different
|
||||
// assert header_1 != header_2
|
||||
// # Verify the proposer is slashable
|
||||
// proposer = state.validators[header_1.proposer_index]
|
||||
// assert is_slashable_validator(proposer, get_current_epoch(state))
|
||||
// # Verify signatures
|
||||
// for signed_header in (proposer_slashing.signed_header_1, proposer_slashing.signed_header_2):
|
||||
// domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(signed_header.message.slot))
|
||||
// signing_root = compute_signing_root(signed_header.message, domain)
|
||||
// assert bls.Verify(proposer.pubkey, signing_root, signed_header.signature)
|
||||
// def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None:
|
||||
// header_1 = proposer_slashing.signed_header_1.message
|
||||
// header_2 = proposer_slashing.signed_header_2.message
|
||||
//
|
||||
// slash_validator(state, header_1.proposer_index)
|
||||
// # Verify header slots match
|
||||
// assert header_1.slot == header_2.slot
|
||||
// # Verify header proposer indices match
|
||||
// assert header_1.proposer_index == header_2.proposer_index
|
||||
// # Verify the headers are different
|
||||
// assert header_1 != header_2
|
||||
// # Verify the proposer is slashable
|
||||
// proposer = state.validators[header_1.proposer_index]
|
||||
// assert is_slashable_validator(proposer, get_current_epoch(state))
|
||||
// # Verify signatures
|
||||
// for signed_header in (proposer_slashing.signed_header_1, proposer_slashing.signed_header_2):
|
||||
// domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(signed_header.message.slot))
|
||||
// signing_root = compute_signing_root(signed_header.message, domain)
|
||||
// assert bls.Verify(proposer.pubkey, signing_root, signed_header.signature)
|
||||
//
|
||||
// slash_validator(state, header_1.proposer_index)
|
||||
func ProcessProposerSlashings(
|
||||
ctx context.Context,
|
||||
beaconState state.BeaconState,
|
||||
@@ -81,7 +82,7 @@ func ProcessProposerSlashing(
|
||||
slashingQuotient = cfg.MinSlashingPenaltyQuotient
|
||||
case beaconState.Version() == version.Altair:
|
||||
slashingQuotient = cfg.MinSlashingPenaltyQuotientAltair
|
||||
case beaconState.Version() == version.Bellatrix:
|
||||
case beaconState.Version() == version.Bellatrix, beaconState.Version() == version.Capella:
|
||||
slashingQuotient = cfg.MinSlashingPenaltyQuotientBellatrix
|
||||
default:
|
||||
return nil, errors.New("unknown state version")
|
||||
|
||||
@@ -282,6 +282,54 @@ func TestProcessProposerSlashings_AppliesCorrectStatusBellatrix(t *testing.T) {
|
||||
require.Equal(t, uint64(32000000000), newState.Balances()[2])
|
||||
}
|
||||
|
||||
func TestProcessProposerSlashings_AppliesCorrectStatusCapella(t *testing.T) {
|
||||
// We test the case when data is correct and verify the validator
|
||||
// registry has been updated.
|
||||
beaconState, privKeys := util.DeterministicGenesisStateCapella(t, 100)
|
||||
proposerIdx := types.ValidatorIndex(1)
|
||||
|
||||
header1 := ðpb.SignedBeaconBlockHeader{
|
||||
Header: util.HydrateBeaconHeader(ðpb.BeaconBlockHeader{
|
||||
ProposerIndex: proposerIdx,
|
||||
StateRoot: bytesutil.PadTo([]byte("A"), 32),
|
||||
}),
|
||||
}
|
||||
var err error
|
||||
header1.Signature, err = signing.ComputeDomainAndSign(beaconState, 0, header1.Header, params.BeaconConfig().DomainBeaconProposer, privKeys[proposerIdx])
|
||||
require.NoError(t, err)
|
||||
|
||||
header2 := util.HydrateSignedBeaconHeader(ðpb.SignedBeaconBlockHeader{
|
||||
Header: ðpb.BeaconBlockHeader{
|
||||
ProposerIndex: proposerIdx,
|
||||
StateRoot: bytesutil.PadTo([]byte("B"), 32),
|
||||
},
|
||||
})
|
||||
header2.Signature, err = signing.ComputeDomainAndSign(beaconState, 0, header2.Header, params.BeaconConfig().DomainBeaconProposer, privKeys[proposerIdx])
|
||||
require.NoError(t, err)
|
||||
|
||||
slashings := []*ethpb.ProposerSlashing{
|
||||
{
|
||||
Header_1: header1,
|
||||
Header_2: header2,
|
||||
},
|
||||
}
|
||||
|
||||
block := util.NewBeaconBlock()
|
||||
block.Block.Body.ProposerSlashings = slashings
|
||||
|
||||
newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator)
|
||||
require.NoError(t, err)
|
||||
|
||||
newStateVals := newState.Validators()
|
||||
if newStateVals[1].ExitEpoch != beaconState.Validators()[1].ExitEpoch {
|
||||
t.Errorf("Proposer with index 1 did not correctly exit,"+"wanted slot:%d, got:%d",
|
||||
newStateVals[1].ExitEpoch, beaconState.Validators()[1].ExitEpoch)
|
||||
}
|
||||
|
||||
require.Equal(t, uint64(31000000000), newState.Balances()[1])
|
||||
require.Equal(t, uint64(32000000000), newState.Balances()[2])
|
||||
}
|
||||
|
||||
func TestVerifyProposerSlashing(t *testing.T) {
|
||||
type args struct {
|
||||
beaconState state.BeaconState
|
||||
|
||||
@@ -17,15 +17,16 @@ import (
|
||||
// in the beacon state's latest randao mixes slice.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_randao(state: BeaconState, body: BeaconBlockBody) -> None:
|
||||
// epoch = get_current_epoch(state)
|
||||
// # Verify RANDAO reveal
|
||||
// proposer = state.validators[get_beacon_proposer_index(state)]
|
||||
// signing_root = compute_signing_root(epoch, get_domain(state, DOMAIN_RANDAO))
|
||||
// assert bls.Verify(proposer.pubkey, signing_root, body.randao_reveal)
|
||||
// # Mix in RANDAO reveal
|
||||
// mix = xor(get_randao_mix(state, epoch), hash(body.randao_reveal))
|
||||
// state.randao_mixes[epoch % EPOCHS_PER_HISTORICAL_VECTOR] = mix
|
||||
//
|
||||
// def process_randao(state: BeaconState, body: BeaconBlockBody) -> None:
|
||||
// epoch = get_current_epoch(state)
|
||||
// # Verify RANDAO reveal
|
||||
// proposer = state.validators[get_beacon_proposer_index(state)]
|
||||
// signing_root = compute_signing_root(epoch, get_domain(state, DOMAIN_RANDAO))
|
||||
// assert bls.Verify(proposer.pubkey, signing_root, body.randao_reveal)
|
||||
// # Mix in RANDAO reveal
|
||||
// mix = xor(get_randao_mix(state, epoch), hash(body.randao_reveal))
|
||||
// state.randao_mixes[epoch % EPOCHS_PER_HISTORICAL_VECTOR] = mix
|
||||
func ProcessRandao(
|
||||
ctx context.Context,
|
||||
beaconState state.BeaconState,
|
||||
@@ -56,11 +57,12 @@ func ProcessRandao(
|
||||
// in the beacon state's latest randao mixes slice.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// # Mix it in
|
||||
// state.latest_randao_mixes[get_current_epoch(state) % LATEST_RANDAO_MIXES_LENGTH] = (
|
||||
// xor(get_randao_mix(state, get_current_epoch(state)),
|
||||
// hash(body.randao_reveal))
|
||||
// )
|
||||
//
|
||||
// # Mix it in
|
||||
// state.latest_randao_mixes[get_current_epoch(state) % LATEST_RANDAO_MIXES_LENGTH] = (
|
||||
// xor(get_randao_mix(state, get_current_epoch(state)),
|
||||
// hash(body.randao_reveal))
|
||||
// )
|
||||
func ProcessRandaoNoVerify(
|
||||
beaconState state.BeaconState,
|
||||
randaoReveal []byte,
|
||||
|
||||
@@ -3,11 +3,15 @@ package blocks
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/signing"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v3/crypto/hash/htr"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/time/slots"
|
||||
)
|
||||
@@ -19,22 +23,22 @@ const executionToBLSPadding = 12
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
//
|
||||
//def process_bls_to_execution_change(state: BeaconState, signed_address_change: SignedBLSToExecutionChange) -> None:
|
||||
// validator = state.validators[address_change.validator_index]
|
||||
// def process_bls_to_execution_change(state: BeaconState, signed_address_change: SignedBLSToExecutionChange) -> None:
|
||||
//
|
||||
// assert validator.withdrawal_credentials[:1] == BLS_WITHDRAWAL_PREFIX
|
||||
// assert validator.withdrawal_credentials[1:] == hash(address_change.from_bls_pubkey)[1:]
|
||||
// validator = state.validators[address_change.validator_index]
|
||||
//
|
||||
// domain = get_domain(state, DOMAIN_BLS_TO_EXECUTION_CHANGE)
|
||||
// signing_root = compute_signing_root(address_change, domain)
|
||||
// assert bls.Verify(address_change.from_bls_pubkey, signing_root, signed_address_change.signature)
|
||||
// assert validator.withdrawal_credentials[:1] == BLS_WITHDRAWAL_PREFIX
|
||||
// assert validator.withdrawal_credentials[1:] == hash(address_change.from_bls_pubkey)[1:]
|
||||
//
|
||||
// validator.withdrawal_credentials = (
|
||||
// ETH1_ADDRESS_WITHDRAWAL_PREFIX
|
||||
// + b'\x00' * 11
|
||||
// + address_change.to_execution_address
|
||||
// )
|
||||
// domain = get_domain(state, DOMAIN_BLS_TO_EXECUTION_CHANGE)
|
||||
// signing_root = compute_signing_root(address_change, domain)
|
||||
// assert bls.Verify(address_change.from_bls_pubkey, signing_root, signed_address_change.signature)
|
||||
//
|
||||
// validator.withdrawal_credentials = (
|
||||
// ETH1_ADDRESS_WITHDRAWAL_PREFIX
|
||||
// + b'\x00' * 11
|
||||
// + address_change.to_execution_address
|
||||
// )
|
||||
func ProcessBLSToExecutionChange(st state.BeaconState, signed *ethpb.SignedBLSToExecutionChange) (state.BeaconState, error) {
|
||||
if signed == nil {
|
||||
return st, errNilSignedWithdrawalMessage
|
||||
@@ -76,3 +80,44 @@ func ProcessBLSToExecutionChange(st state.BeaconState, signed *ethpb.SignedBLSTo
|
||||
err = st.UpdateValidatorAtIndex(message.ValidatorIndex, val)
|
||||
return st, err
|
||||
}
|
||||
|
||||
func ProcessWithdrawals(st state.BeaconState, withdrawals []*enginev1.Withdrawal) (state.BeaconState, error) {
|
||||
expected, err := st.ExpectedWithdrawals()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get expected withdrawals")
|
||||
}
|
||||
if len(expected) != len(withdrawals) {
|
||||
return nil, errInvalidWithdrawalNumber
|
||||
}
|
||||
for i, withdrawal := range withdrawals {
|
||||
if withdrawal.WithdrawalIndex != expected[i].WithdrawalIndex {
|
||||
return nil, errInvalidWithdrawalIndex
|
||||
}
|
||||
if withdrawal.ValidatorIndex != expected[i].ValidatorIndex {
|
||||
return nil, errInvalidValidatorIndex
|
||||
}
|
||||
if !bytes.Equal(withdrawal.ExecutionAddress, expected[i].ExecutionAddress) {
|
||||
return nil, errInvalidExecutionAddress
|
||||
}
|
||||
if withdrawal.Amount != expected[i].Amount {
|
||||
return nil, errInvalidWithdrawalAmount
|
||||
}
|
||||
err := helpers.DecreaseBalance(st, withdrawal.ValidatorIndex, withdrawal.Amount)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not decrease balance")
|
||||
}
|
||||
}
|
||||
if len(withdrawals) > 0 {
|
||||
if err := st.SetNextWithdrawalIndex(withdrawals[len(withdrawals)-1].WithdrawalIndex + 1); err != nil {
|
||||
return nil, errors.Wrap(err, "could not set next withdrawal index")
|
||||
}
|
||||
nextValidatorIndex := withdrawals[len(withdrawals)-1].ValidatorIndex + 1
|
||||
if nextValidatorIndex == types.ValidatorIndex(st.NumValidators()) {
|
||||
nextValidatorIndex = 0
|
||||
}
|
||||
if err := st.SetNextWithdrawalValidatorIndex(nextValidatorIndex); err != nil {
|
||||
return nil, errors.Wrap(err, "could not set latest withdrawal validator index")
|
||||
}
|
||||
}
|
||||
return st, nil
|
||||
}
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
package blocks_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/signing"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/time"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
state_native "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/state-native"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v3/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/v3/crypto/hash/htr"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/time/slots"
|
||||
)
|
||||
|
||||
func TestProcessBLSToExecutionChange(t *testing.T) {
|
||||
@@ -191,3 +196,385 @@ func TestProcessBLSToExecutionChange(t *testing.T) {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestProcessWithdrawals(t *testing.T) {
|
||||
const (
|
||||
currentEpoch = types.Epoch(10)
|
||||
epochInFuture = types.Epoch(12)
|
||||
epochInPast = types.Epoch(8)
|
||||
numValidators = 128
|
||||
notWithdrawableIndex = 127
|
||||
notPartiallyWithdrawable = 126
|
||||
)
|
||||
maxEffectiveBalance := params.BeaconConfig().MaxEffectiveBalance
|
||||
|
||||
type args struct {
|
||||
Name string
|
||||
NextWithdrawalValidatorIndex types.ValidatorIndex
|
||||
NextWithdrawalIndex uint64
|
||||
FullWithdrawalIndices []types.ValidatorIndex
|
||||
PartialWithdrawalIndices []types.ValidatorIndex
|
||||
Withdrawals []*enginev1.Withdrawal
|
||||
}
|
||||
type control struct {
|
||||
NextWithdrawalValidatorIndex types.ValidatorIndex
|
||||
NextWithdrawalIndex uint64
|
||||
ExpectedError bool
|
||||
Balances map[uint64]uint64
|
||||
}
|
||||
type Test struct {
|
||||
Args args
|
||||
Control control
|
||||
}
|
||||
executionAddress := func(i types.ValidatorIndex) []byte {
|
||||
wc := make([]byte, 20)
|
||||
wc[19] = byte(i)
|
||||
return wc
|
||||
}
|
||||
withdrawalAmount := func(i types.ValidatorIndex) uint64 {
|
||||
return maxEffectiveBalance + uint64(i)*100000
|
||||
}
|
||||
fullWithdrawal := func(i types.ValidatorIndex, idx uint64) *enginev1.Withdrawal {
|
||||
return &enginev1.Withdrawal{
|
||||
WithdrawalIndex: idx,
|
||||
ValidatorIndex: i,
|
||||
ExecutionAddress: executionAddress(i),
|
||||
Amount: withdrawalAmount(i),
|
||||
}
|
||||
}
|
||||
partialWithdrawal := func(i types.ValidatorIndex, idx uint64) *enginev1.Withdrawal {
|
||||
return &enginev1.Withdrawal{
|
||||
WithdrawalIndex: idx,
|
||||
ValidatorIndex: i,
|
||||
ExecutionAddress: executionAddress(i),
|
||||
Amount: withdrawalAmount(i) - maxEffectiveBalance,
|
||||
}
|
||||
}
|
||||
tests := []Test{
|
||||
{
|
||||
Args: args{
|
||||
Name: "success no withdrawals",
|
||||
NextWithdrawalValidatorIndex: 10,
|
||||
NextWithdrawalIndex: 3,
|
||||
},
|
||||
Control: control{
|
||||
NextWithdrawalValidatorIndex: 10,
|
||||
NextWithdrawalIndex: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: args{
|
||||
Name: "success one full withdrawal",
|
||||
NextWithdrawalIndex: 3,
|
||||
NextWithdrawalValidatorIndex: 5,
|
||||
FullWithdrawalIndices: []types.ValidatorIndex{1},
|
||||
Withdrawals: []*enginev1.Withdrawal{
|
||||
fullWithdrawal(1, 3),
|
||||
},
|
||||
},
|
||||
Control: control{
|
||||
NextWithdrawalValidatorIndex: 2,
|
||||
NextWithdrawalIndex: 4,
|
||||
Balances: map[uint64]uint64{1: 0},
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: args{
|
||||
Name: "success one partial withdrawal",
|
||||
NextWithdrawalIndex: 21,
|
||||
NextWithdrawalValidatorIndex: 37,
|
||||
PartialWithdrawalIndices: []types.ValidatorIndex{7},
|
||||
Withdrawals: []*enginev1.Withdrawal{
|
||||
partialWithdrawal(7, 21),
|
||||
},
|
||||
},
|
||||
Control: control{
|
||||
NextWithdrawalValidatorIndex: 8,
|
||||
NextWithdrawalIndex: 22,
|
||||
Balances: map[uint64]uint64{7: maxEffectiveBalance},
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: args{
|
||||
Name: "success many full withdrawals",
|
||||
NextWithdrawalIndex: 22,
|
||||
NextWithdrawalValidatorIndex: 4,
|
||||
FullWithdrawalIndices: []types.ValidatorIndex{7, 19, 28, 1},
|
||||
Withdrawals: []*enginev1.Withdrawal{
|
||||
fullWithdrawal(7, 22), fullWithdrawal(19, 23), fullWithdrawal(28, 24),
|
||||
fullWithdrawal(1, 25),
|
||||
},
|
||||
},
|
||||
Control: control{
|
||||
NextWithdrawalValidatorIndex: 2,
|
||||
NextWithdrawalIndex: 26,
|
||||
Balances: map[uint64]uint64{7: 0, 19: 0, 28: 0, 1: 0},
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: args{
|
||||
Name: "success many partial withdrawals",
|
||||
NextWithdrawalIndex: 22,
|
||||
NextWithdrawalValidatorIndex: 4,
|
||||
PartialWithdrawalIndices: []types.ValidatorIndex{7, 19, 28, 1},
|
||||
Withdrawals: []*enginev1.Withdrawal{
|
||||
partialWithdrawal(7, 22), partialWithdrawal(19, 23), partialWithdrawal(28, 24),
|
||||
partialWithdrawal(1, 25),
|
||||
},
|
||||
},
|
||||
Control: control{
|
||||
NextWithdrawalValidatorIndex: 2,
|
||||
NextWithdrawalIndex: 26,
|
||||
Balances: map[uint64]uint64{
|
||||
7: maxEffectiveBalance,
|
||||
19: maxEffectiveBalance,
|
||||
28: maxEffectiveBalance,
|
||||
1: maxEffectiveBalance,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: args{
|
||||
Name: "success many withdrawals",
|
||||
NextWithdrawalIndex: 22,
|
||||
NextWithdrawalValidatorIndex: 12,
|
||||
FullWithdrawalIndices: []types.ValidatorIndex{7, 19, 28},
|
||||
PartialWithdrawalIndices: []types.ValidatorIndex{2, 1, 89, 15},
|
||||
Withdrawals: []*enginev1.Withdrawal{
|
||||
partialWithdrawal(15, 22), fullWithdrawal(19, 23), fullWithdrawal(28, 24),
|
||||
partialWithdrawal(89, 25), partialWithdrawal(1, 26), partialWithdrawal(2, 27),
|
||||
fullWithdrawal(7, 28),
|
||||
},
|
||||
},
|
||||
Control: control{
|
||||
NextWithdrawalValidatorIndex: 8,
|
||||
NextWithdrawalIndex: 29,
|
||||
Balances: map[uint64]uint64{
|
||||
7: 0, 19: 0, 28: 0,
|
||||
2: maxEffectiveBalance, 1: maxEffectiveBalance, 89: maxEffectiveBalance,
|
||||
15: maxEffectiveBalance,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: args{
|
||||
Name: "success more than max fully withdrawals",
|
||||
NextWithdrawalIndex: 22,
|
||||
NextWithdrawalValidatorIndex: 0,
|
||||
FullWithdrawalIndices: []types.ValidatorIndex{1, 2, 3, 4, 5, 6, 7, 8, 9, 21, 22, 23, 24, 25, 26, 27, 29, 35, 89},
|
||||
Withdrawals: []*enginev1.Withdrawal{
|
||||
fullWithdrawal(1, 22), fullWithdrawal(2, 23), fullWithdrawal(3, 24),
|
||||
fullWithdrawal(4, 25), fullWithdrawal(5, 26), fullWithdrawal(6, 27),
|
||||
fullWithdrawal(7, 28), fullWithdrawal(8, 29), fullWithdrawal(9, 30),
|
||||
fullWithdrawal(21, 31), fullWithdrawal(22, 32), fullWithdrawal(23, 33),
|
||||
fullWithdrawal(24, 34), fullWithdrawal(25, 35), fullWithdrawal(26, 36),
|
||||
fullWithdrawal(27, 37),
|
||||
},
|
||||
},
|
||||
Control: control{
|
||||
NextWithdrawalValidatorIndex: 28,
|
||||
NextWithdrawalIndex: 38,
|
||||
Balances: map[uint64]uint64{
|
||||
1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0,
|
||||
21: 0, 22: 0, 23: 0, 24: 0, 25: 0, 26: 0, 27: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: args{
|
||||
Name: "success more than max partially withdrawals",
|
||||
NextWithdrawalIndex: 22,
|
||||
NextWithdrawalValidatorIndex: 0,
|
||||
PartialWithdrawalIndices: []types.ValidatorIndex{1, 2, 3, 4, 5, 6, 7, 8, 9, 21, 22, 23, 24, 25, 26, 27, 29, 35, 89},
|
||||
Withdrawals: []*enginev1.Withdrawal{
|
||||
partialWithdrawal(1, 22), partialWithdrawal(2, 23), partialWithdrawal(3, 24),
|
||||
partialWithdrawal(4, 25), partialWithdrawal(5, 26), partialWithdrawal(6, 27),
|
||||
partialWithdrawal(7, 28), partialWithdrawal(8, 29), partialWithdrawal(9, 30),
|
||||
partialWithdrawal(21, 31), partialWithdrawal(22, 32), partialWithdrawal(23, 33),
|
||||
partialWithdrawal(24, 34), partialWithdrawal(25, 35), partialWithdrawal(26, 36),
|
||||
partialWithdrawal(27, 37),
|
||||
},
|
||||
},
|
||||
Control: control{
|
||||
NextWithdrawalValidatorIndex: 28,
|
||||
NextWithdrawalIndex: 38,
|
||||
Balances: map[uint64]uint64{
|
||||
1: maxEffectiveBalance,
|
||||
2: maxEffectiveBalance,
|
||||
3: maxEffectiveBalance,
|
||||
4: maxEffectiveBalance,
|
||||
5: maxEffectiveBalance,
|
||||
6: maxEffectiveBalance,
|
||||
7: maxEffectiveBalance,
|
||||
8: maxEffectiveBalance,
|
||||
9: maxEffectiveBalance,
|
||||
21: maxEffectiveBalance,
|
||||
22: maxEffectiveBalance,
|
||||
23: maxEffectiveBalance,
|
||||
24: maxEffectiveBalance,
|
||||
25: maxEffectiveBalance,
|
||||
26: maxEffectiveBalance,
|
||||
27: maxEffectiveBalance,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: args{
|
||||
Name: "failure wrong number of partial withdrawal",
|
||||
NextWithdrawalIndex: 21,
|
||||
NextWithdrawalValidatorIndex: 37,
|
||||
PartialWithdrawalIndices: []types.ValidatorIndex{7},
|
||||
Withdrawals: []*enginev1.Withdrawal{
|
||||
partialWithdrawal(7, 21), partialWithdrawal(9, 22),
|
||||
},
|
||||
},
|
||||
Control: control{
|
||||
ExpectedError: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: args{
|
||||
Name: "failure invalid withdrawal index",
|
||||
NextWithdrawalIndex: 22,
|
||||
NextWithdrawalValidatorIndex: 4,
|
||||
FullWithdrawalIndices: []types.ValidatorIndex{7, 19, 28, 1},
|
||||
Withdrawals: []*enginev1.Withdrawal{
|
||||
fullWithdrawal(7, 22), fullWithdrawal(19, 23), fullWithdrawal(28, 25),
|
||||
fullWithdrawal(1, 25),
|
||||
},
|
||||
},
|
||||
Control: control{
|
||||
ExpectedError: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: args{
|
||||
Name: "failure invalid validator index",
|
||||
NextWithdrawalIndex: 22,
|
||||
NextWithdrawalValidatorIndex: 4,
|
||||
FullWithdrawalIndices: []types.ValidatorIndex{7, 19, 28, 1},
|
||||
Withdrawals: []*enginev1.Withdrawal{
|
||||
fullWithdrawal(7, 22), fullWithdrawal(19, 23), fullWithdrawal(27, 24),
|
||||
fullWithdrawal(1, 25),
|
||||
},
|
||||
},
|
||||
Control: control{
|
||||
ExpectedError: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: args{
|
||||
Name: "failure invalid withdrawal amount",
|
||||
NextWithdrawalIndex: 22,
|
||||
NextWithdrawalValidatorIndex: 4,
|
||||
FullWithdrawalIndices: []types.ValidatorIndex{7, 19, 28, 1},
|
||||
Withdrawals: []*enginev1.Withdrawal{
|
||||
fullWithdrawal(7, 22), fullWithdrawal(19, 23), partialWithdrawal(28, 24),
|
||||
fullWithdrawal(1, 25),
|
||||
},
|
||||
},
|
||||
Control: control{
|
||||
ExpectedError: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: args{
|
||||
Name: "failure validator not fully withdrawable",
|
||||
NextWithdrawalIndex: 22,
|
||||
NextWithdrawalValidatorIndex: 4,
|
||||
FullWithdrawalIndices: []types.ValidatorIndex{notWithdrawableIndex},
|
||||
Withdrawals: []*enginev1.Withdrawal{
|
||||
fullWithdrawal(notWithdrawableIndex, 22),
|
||||
},
|
||||
},
|
||||
Control: control{
|
||||
ExpectedError: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: args{
|
||||
Name: "failure validator not partially withdrawable",
|
||||
NextWithdrawalIndex: 22,
|
||||
NextWithdrawalValidatorIndex: 4,
|
||||
PartialWithdrawalIndices: []types.ValidatorIndex{notPartiallyWithdrawable},
|
||||
Withdrawals: []*enginev1.Withdrawal{
|
||||
fullWithdrawal(notPartiallyWithdrawable, 22),
|
||||
},
|
||||
},
|
||||
Control: control{
|
||||
ExpectedError: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
checkPostState := func(t *testing.T, expected control, st state.BeaconState) {
|
||||
l, err := st.LastWithdrawalValidatorIndex()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected.NextWithdrawalValidatorIndex, l)
|
||||
|
||||
n, err := st.NextWithdrawalIndex()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected.NextWithdrawalIndex, n)
|
||||
balances := st.Balances()
|
||||
for idx, bal := range expected.Balances {
|
||||
require.Equal(t, bal, balances[idx])
|
||||
}
|
||||
}
|
||||
|
||||
prepareValidators := func(st *ethpb.BeaconStateCapella, arguments args) (state.BeaconState, error) {
|
||||
validators := make([]*ethpb.Validator, numValidators)
|
||||
st.Balances = make([]uint64, numValidators)
|
||||
for i := range validators {
|
||||
v := ðpb.Validator{}
|
||||
v.EffectiveBalance = maxEffectiveBalance
|
||||
v.WithdrawableEpoch = epochInFuture
|
||||
v.WithdrawalCredentials = make([]byte, 32)
|
||||
v.WithdrawalCredentials[31] = byte(i)
|
||||
st.Balances[i] = v.EffectiveBalance - uint64(rand.Intn(1000))
|
||||
validators[i] = v
|
||||
}
|
||||
for _, idx := range arguments.FullWithdrawalIndices {
|
||||
if idx != notWithdrawableIndex {
|
||||
validators[idx].WithdrawableEpoch = epochInPast
|
||||
}
|
||||
st.Balances[idx] = withdrawalAmount(idx)
|
||||
validators[idx].WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
||||
}
|
||||
for _, idx := range arguments.PartialWithdrawalIndices {
|
||||
validators[idx].WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
||||
st.Balances[idx] = withdrawalAmount(idx)
|
||||
}
|
||||
st.Validators = validators
|
||||
return state_native.InitializeFromProtoCapella(st)
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.Args.Name, func(t *testing.T) {
|
||||
if test.Args.Withdrawals == nil {
|
||||
test.Args.Withdrawals = make([]*enginev1.Withdrawal, 0)
|
||||
}
|
||||
if test.Args.FullWithdrawalIndices == nil {
|
||||
test.Args.FullWithdrawalIndices = make([]types.ValidatorIndex, 0)
|
||||
}
|
||||
if test.Args.PartialWithdrawalIndices == nil {
|
||||
test.Args.PartialWithdrawalIndices = make([]types.ValidatorIndex, 0)
|
||||
}
|
||||
slot, err := slots.EpochStart(currentEpoch)
|
||||
require.NoError(t, err)
|
||||
spb := ðpb.BeaconStateCapella{
|
||||
Slot: slot,
|
||||
NextWithdrawalValidatorIndex: test.Args.NextWithdrawalValidatorIndex,
|
||||
NextWithdrawalIndex: test.Args.NextWithdrawalIndex,
|
||||
}
|
||||
st, err := prepareValidators(spb, test.Args)
|
||||
require.NoError(t, err)
|
||||
post, err := blocks.ProcessWithdrawals(st, test.Args.Withdrawals)
|
||||
if test.Control.ExpectedError {
|
||||
require.NotNil(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
checkPostState(t, test.Control, post)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
31
beacon-chain/core/capella/BUILD.bazel
Normal file
31
beacon-chain/core/capella/BUILD.bazel
Normal file
@@ -0,0 +1,31 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["upgrade.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/capella",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/core/time:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/state-native:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["upgrade_test.go"],
|
||||
deps = [
|
||||
":go_default_library",
|
||||
"//beacon-chain/core/time:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"//testing/util:go_default_library",
|
||||
],
|
||||
)
|
||||
96
beacon-chain/core/capella/upgrade.go
Normal file
96
beacon-chain/core/capella/upgrade.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package capella
|
||||
|
||||
import (
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/time"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
state_native "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/state-native"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// UpgradeToCapella updates a generic state to return the version Capella state.
|
||||
func UpgradeToCapella(state state.BeaconState) (state.BeaconState, error) {
|
||||
epoch := time.CurrentEpoch(state)
|
||||
|
||||
currentSyncCommittee, err := state.CurrentSyncCommittee()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nextSyncCommittee, err := state.NextSyncCommittee()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prevEpochParticipation, err := state.PreviousEpochParticipation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentEpochParticipation, err := state.CurrentEpochParticipation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inactivityScores, err := state.InactivityScores()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payloadHeader, err := state.LatestExecutionPayloadHeader()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txRoot, err := payloadHeader.TransactionsRoot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := ðpb.BeaconStateCapella{
|
||||
GenesisTime: state.GenesisTime(),
|
||||
GenesisValidatorsRoot: state.GenesisValidatorsRoot(),
|
||||
Slot: state.Slot(),
|
||||
Fork: ðpb.Fork{
|
||||
PreviousVersion: state.Fork().CurrentVersion,
|
||||
CurrentVersion: params.BeaconConfig().CapellaForkVersion,
|
||||
Epoch: epoch,
|
||||
},
|
||||
LatestBlockHeader: state.LatestBlockHeader(),
|
||||
BlockRoots: state.BlockRoots(),
|
||||
StateRoots: state.StateRoots(),
|
||||
HistoricalRoots: state.HistoricalRoots(),
|
||||
Eth1Data: state.Eth1Data(),
|
||||
Eth1DataVotes: state.Eth1DataVotes(),
|
||||
Eth1DepositIndex: state.Eth1DepositIndex(),
|
||||
Validators: state.Validators(),
|
||||
Balances: state.Balances(),
|
||||
RandaoMixes: state.RandaoMixes(),
|
||||
Slashings: state.Slashings(),
|
||||
PreviousEpochParticipation: prevEpochParticipation,
|
||||
CurrentEpochParticipation: currentEpochParticipation,
|
||||
JustificationBits: state.JustificationBits(),
|
||||
PreviousJustifiedCheckpoint: state.PreviousJustifiedCheckpoint(),
|
||||
CurrentJustifiedCheckpoint: state.CurrentJustifiedCheckpoint(),
|
||||
FinalizedCheckpoint: state.FinalizedCheckpoint(),
|
||||
InactivityScores: inactivityScores,
|
||||
CurrentSyncCommittee: currentSyncCommittee,
|
||||
NextSyncCommittee: nextSyncCommittee,
|
||||
LatestExecutionPayloadHeader: &enginev1.ExecutionPayloadHeaderCapella{
|
||||
ParentHash: payloadHeader.ParentHash(),
|
||||
FeeRecipient: payloadHeader.FeeRecipient(),
|
||||
StateRoot: payloadHeader.StateRoot(),
|
||||
ReceiptsRoot: payloadHeader.ReceiptsRoot(),
|
||||
LogsBloom: payloadHeader.LogsBloom(),
|
||||
PrevRandao: payloadHeader.PrevRandao(),
|
||||
BlockNumber: payloadHeader.BlockNumber(),
|
||||
GasLimit: payloadHeader.GasLimit(),
|
||||
GasUsed: payloadHeader.GasUsed(),
|
||||
Timestamp: payloadHeader.Timestamp(),
|
||||
ExtraData: payloadHeader.ExtraData(),
|
||||
BaseFeePerGas: payloadHeader.BaseFeePerGas(),
|
||||
BlockHash: payloadHeader.BlockHash(),
|
||||
TransactionsRoot: txRoot,
|
||||
WithdrawalsRoot: make([]byte, 32),
|
||||
},
|
||||
NextWithdrawalIndex: 0,
|
||||
NextWithdrawalValidatorIndex: 0,
|
||||
}
|
||||
|
||||
return state_native.InitializeFromProtoUnsafeCapella(s)
|
||||
}
|
||||
101
beacon-chain/core/capella/upgrade_test.go
Normal file
101
beacon-chain/core/capella/upgrade_test.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package capella_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/capella"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/time"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/util"
|
||||
)
|
||||
|
||||
func TestUpgradeToCapella(t *testing.T) {
|
||||
st, _ := util.DeterministicGenesisStateBellatrix(t, params.BeaconConfig().MaxValidatorsPerCommittee)
|
||||
preForkState := st.Copy()
|
||||
mSt, err := capella.UpgradeToCapella(st)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, preForkState.GenesisTime(), mSt.GenesisTime())
|
||||
require.DeepSSZEqual(t, preForkState.GenesisValidatorsRoot(), mSt.GenesisValidatorsRoot())
|
||||
require.Equal(t, preForkState.Slot(), mSt.Slot())
|
||||
require.DeepSSZEqual(t, preForkState.LatestBlockHeader(), mSt.LatestBlockHeader())
|
||||
require.DeepSSZEqual(t, preForkState.BlockRoots(), mSt.BlockRoots())
|
||||
require.DeepSSZEqual(t, preForkState.StateRoots(), mSt.StateRoots())
|
||||
require.DeepSSZEqual(t, preForkState.HistoricalRoots(), mSt.HistoricalRoots())
|
||||
require.DeepSSZEqual(t, preForkState.Eth1Data(), mSt.Eth1Data())
|
||||
require.DeepSSZEqual(t, preForkState.Eth1DataVotes(), mSt.Eth1DataVotes())
|
||||
require.DeepSSZEqual(t, preForkState.Eth1DepositIndex(), mSt.Eth1DepositIndex())
|
||||
require.DeepSSZEqual(t, preForkState.Validators(), mSt.Validators())
|
||||
require.DeepSSZEqual(t, preForkState.Balances(), mSt.Balances())
|
||||
require.DeepSSZEqual(t, preForkState.RandaoMixes(), mSt.RandaoMixes())
|
||||
require.DeepSSZEqual(t, preForkState.Slashings(), mSt.Slashings())
|
||||
require.DeepSSZEqual(t, preForkState.JustificationBits(), mSt.JustificationBits())
|
||||
require.DeepSSZEqual(t, preForkState.PreviousJustifiedCheckpoint(), mSt.PreviousJustifiedCheckpoint())
|
||||
require.DeepSSZEqual(t, preForkState.CurrentJustifiedCheckpoint(), mSt.CurrentJustifiedCheckpoint())
|
||||
require.DeepSSZEqual(t, preForkState.FinalizedCheckpoint(), mSt.FinalizedCheckpoint())
|
||||
numValidators := mSt.NumValidators()
|
||||
p, err := mSt.PreviousEpochParticipation()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, make([]byte, numValidators), p)
|
||||
p, err = mSt.CurrentEpochParticipation()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, make([]byte, numValidators), p)
|
||||
s, err := mSt.InactivityScores()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, make([]uint64, numValidators), s)
|
||||
|
||||
f := mSt.Fork()
|
||||
require.DeepSSZEqual(t, ðpb.Fork{
|
||||
PreviousVersion: st.Fork().CurrentVersion,
|
||||
CurrentVersion: params.BeaconConfig().CapellaForkVersion,
|
||||
Epoch: time.CurrentEpoch(st),
|
||||
}, f)
|
||||
csc, err := mSt.CurrentSyncCommittee()
|
||||
require.NoError(t, err)
|
||||
psc, err := preForkState.CurrentSyncCommittee()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, psc, csc)
|
||||
nsc, err := mSt.NextSyncCommittee()
|
||||
require.NoError(t, err)
|
||||
psc, err = preForkState.NextSyncCommittee()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, psc, nsc)
|
||||
|
||||
header, err := mSt.LatestExecutionPayloadHeader()
|
||||
require.NoError(t, err)
|
||||
protoHeader, ok := header.Proto().(*enginev1.ExecutionPayloadHeaderCapella)
|
||||
require.Equal(t, true, ok)
|
||||
prevHeader, err := preForkState.LatestExecutionPayloadHeader()
|
||||
require.NoError(t, err)
|
||||
txRoot, err := prevHeader.TransactionsRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
wanted := &enginev1.ExecutionPayloadHeaderCapella{
|
||||
ParentHash: prevHeader.ParentHash(),
|
||||
FeeRecipient: prevHeader.FeeRecipient(),
|
||||
StateRoot: prevHeader.StateRoot(),
|
||||
ReceiptsRoot: prevHeader.ReceiptsRoot(),
|
||||
LogsBloom: prevHeader.LogsBloom(),
|
||||
PrevRandao: prevHeader.PrevRandao(),
|
||||
BlockNumber: prevHeader.BlockNumber(),
|
||||
GasLimit: prevHeader.GasLimit(),
|
||||
GasUsed: prevHeader.GasUsed(),
|
||||
Timestamp: prevHeader.Timestamp(),
|
||||
BaseFeePerGas: prevHeader.BaseFeePerGas(),
|
||||
BlockHash: prevHeader.BlockHash(),
|
||||
TransactionsRoot: txRoot,
|
||||
WithdrawalsRoot: make([]byte, 32),
|
||||
}
|
||||
require.DeepEqual(t, wanted, protoHeader)
|
||||
nwi, err := mSt.NextWithdrawalIndex()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(0), nwi)
|
||||
|
||||
lwvi, err := mSt.LastWithdrawalValidatorIndex()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, types.ValidatorIndex(0), lwvi)
|
||||
}
|
||||
@@ -49,12 +49,13 @@ func (s sortableIndices) Less(i, j int) bool {
|
||||
// need to get attesting balance from attestations.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_attesting_balance(state: BeaconState, attestations: Sequence[PendingAttestation]) -> Gwei:
|
||||
// """
|
||||
// Return the combined effective balance of the set of unslashed validators participating in ``attestations``.
|
||||
// Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero.
|
||||
// """
|
||||
// return get_total_balance(state, get_unslashed_attesting_indices(state, attestations))
|
||||
//
|
||||
// def get_attesting_balance(state: BeaconState, attestations: Sequence[PendingAttestation]) -> Gwei:
|
||||
// """
|
||||
// Return the combined effective balance of the set of unslashed validators participating in ``attestations``.
|
||||
// Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero.
|
||||
// """
|
||||
// return get_total_balance(state, get_unslashed_attesting_indices(state, attestations))
|
||||
func AttestingBalance(ctx context.Context, state state.ReadOnlyBeaconState, atts []*ethpb.PendingAttestation) (uint64, error) {
|
||||
indices, err := UnslashedAttestingIndices(ctx, state, atts)
|
||||
if err != nil {
|
||||
@@ -67,25 +68,26 @@ func AttestingBalance(ctx context.Context, state state.ReadOnlyBeaconState, atts
|
||||
// the amount to rotate is determined churn limit.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_registry_updates(state: BeaconState) -> None:
|
||||
// # Process activation eligibility and ejections
|
||||
// for index, validator in enumerate(state.validators):
|
||||
// if is_eligible_for_activation_queue(validator):
|
||||
// validator.activation_eligibility_epoch = get_current_epoch(state) + 1
|
||||
//
|
||||
// if is_active_validator(validator, get_current_epoch(state)) and validator.effective_balance <= EJECTION_BALANCE:
|
||||
// initiate_validator_exit(state, ValidatorIndex(index))
|
||||
// def process_registry_updates(state: BeaconState) -> None:
|
||||
// # Process activation eligibility and ejections
|
||||
// for index, validator in enumerate(state.validators):
|
||||
// if is_eligible_for_activation_queue(validator):
|
||||
// validator.activation_eligibility_epoch = get_current_epoch(state) + 1
|
||||
//
|
||||
// # Queue validators eligible for activation and not yet dequeued for activation
|
||||
// activation_queue = sorted([
|
||||
// index for index, validator in enumerate(state.validators)
|
||||
// if is_eligible_for_activation(state, validator)
|
||||
// # Order by the sequence of activation_eligibility_epoch setting and then index
|
||||
// ], key=lambda index: (state.validators[index].activation_eligibility_epoch, index))
|
||||
// # Dequeued validators for activation up to churn limit
|
||||
// for index in activation_queue[:get_validator_churn_limit(state)]:
|
||||
// validator = state.validators[index]
|
||||
// validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state))
|
||||
// if is_active_validator(validator, get_current_epoch(state)) and validator.effective_balance <= EJECTION_BALANCE:
|
||||
// initiate_validator_exit(state, ValidatorIndex(index))
|
||||
//
|
||||
// # Queue validators eligible for activation and not yet dequeued for activation
|
||||
// activation_queue = sorted([
|
||||
// index for index, validator in enumerate(state.validators)
|
||||
// if is_eligible_for_activation(state, validator)
|
||||
// # Order by the sequence of activation_eligibility_epoch setting and then index
|
||||
// ], key=lambda index: (state.validators[index].activation_eligibility_epoch, index))
|
||||
// # Dequeued validators for activation up to churn limit
|
||||
// for index in activation_queue[:get_validator_churn_limit(state)]:
|
||||
// validator = state.validators[index]
|
||||
// validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state))
|
||||
func ProcessRegistryUpdates(ctx context.Context, state state.BeaconState) (state.BeaconState, error) {
|
||||
currentEpoch := time.CurrentEpoch(state)
|
||||
vals := state.Validators()
|
||||
@@ -155,16 +157,16 @@ func ProcessRegistryUpdates(ctx context.Context, state state.BeaconState) (state
|
||||
|
||||
// ProcessSlashings processes the slashed validators during epoch processing,
|
||||
//
|
||||
// def process_slashings(state: BeaconState) -> None:
|
||||
// epoch = get_current_epoch(state)
|
||||
// total_balance = get_total_active_balance(state)
|
||||
// adjusted_total_slashing_balance = min(sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER, total_balance)
|
||||
// for index, validator in enumerate(state.validators):
|
||||
// if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch:
|
||||
// increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from penalty numerator to avoid uint64 overflow
|
||||
// penalty_numerator = validator.effective_balance // increment * adjusted_total_slashing_balance
|
||||
// penalty = penalty_numerator // total_balance * increment
|
||||
// decrease_balance(state, ValidatorIndex(index), penalty)
|
||||
// def process_slashings(state: BeaconState) -> None:
|
||||
// epoch = get_current_epoch(state)
|
||||
// total_balance = get_total_active_balance(state)
|
||||
// adjusted_total_slashing_balance = min(sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER, total_balance)
|
||||
// for index, validator in enumerate(state.validators):
|
||||
// if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch:
|
||||
// increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from penalty numerator to avoid uint64 overflow
|
||||
// penalty_numerator = validator.effective_balance // increment * adjusted_total_slashing_balance
|
||||
// penalty = penalty_numerator // total_balance * increment
|
||||
// decrease_balance(state, ValidatorIndex(index), penalty)
|
||||
func ProcessSlashings(state state.BeaconState, slashingMultiplier uint64) (state.BeaconState, error) {
|
||||
currentEpoch := time.CurrentEpoch(state)
|
||||
totalBalance, err := helpers.TotalActiveBalance(state)
|
||||
@@ -207,11 +209,12 @@ func ProcessSlashings(state state.BeaconState, slashingMultiplier uint64) (state
|
||||
// ProcessEth1DataReset processes updates to ETH1 data votes during epoch processing.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_eth1_data_reset(state: BeaconState) -> None:
|
||||
// next_epoch = Epoch(get_current_epoch(state) + 1)
|
||||
// # Reset eth1 data votes
|
||||
// if next_epoch % EPOCHS_PER_ETH1_VOTING_PERIOD == 0:
|
||||
// state.eth1_data_votes = []
|
||||
//
|
||||
// def process_eth1_data_reset(state: BeaconState) -> None:
|
||||
// next_epoch = Epoch(get_current_epoch(state) + 1)
|
||||
// # Reset eth1 data votes
|
||||
// if next_epoch % EPOCHS_PER_ETH1_VOTING_PERIOD == 0:
|
||||
// state.eth1_data_votes = []
|
||||
func ProcessEth1DataReset(state state.BeaconState) (state.BeaconState, error) {
|
||||
currentEpoch := time.CurrentEpoch(state)
|
||||
nextEpoch := currentEpoch + 1
|
||||
@@ -229,18 +232,19 @@ func ProcessEth1DataReset(state state.BeaconState) (state.BeaconState, error) {
|
||||
// ProcessEffectiveBalanceUpdates processes effective balance updates during epoch processing.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_effective_balance_updates(state: BeaconState) -> None:
|
||||
// # Update effective balances with hysteresis
|
||||
// for index, validator in enumerate(state.validators):
|
||||
// balance = state.balances[index]
|
||||
// HYSTERESIS_INCREMENT = uint64(EFFECTIVE_BALANCE_INCREMENT // HYSTERESIS_QUOTIENT)
|
||||
// DOWNWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_DOWNWARD_MULTIPLIER
|
||||
// UPWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_UPWARD_MULTIPLIER
|
||||
// if (
|
||||
// balance + DOWNWARD_THRESHOLD < validator.effective_balance
|
||||
// or validator.effective_balance + UPWARD_THRESHOLD < balance
|
||||
// ):
|
||||
// validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE)
|
||||
//
|
||||
// def process_effective_balance_updates(state: BeaconState) -> None:
|
||||
// # Update effective balances with hysteresis
|
||||
// for index, validator in enumerate(state.validators):
|
||||
// balance = state.balances[index]
|
||||
// HYSTERESIS_INCREMENT = uint64(EFFECTIVE_BALANCE_INCREMENT // HYSTERESIS_QUOTIENT)
|
||||
// DOWNWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_DOWNWARD_MULTIPLIER
|
||||
// UPWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_UPWARD_MULTIPLIER
|
||||
// if (
|
||||
// balance + DOWNWARD_THRESHOLD < validator.effective_balance
|
||||
// or validator.effective_balance + UPWARD_THRESHOLD < balance
|
||||
// ):
|
||||
// validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE)
|
||||
func ProcessEffectiveBalanceUpdates(state state.BeaconState) (state.BeaconState, error) {
|
||||
effBalanceInc := params.BeaconConfig().EffectiveBalanceIncrement
|
||||
maxEffBalance := params.BeaconConfig().MaxEffectiveBalance
|
||||
@@ -285,10 +289,11 @@ func ProcessEffectiveBalanceUpdates(state state.BeaconState) (state.BeaconState,
|
||||
// ProcessSlashingsReset processes the total slashing balances updates during epoch processing.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_slashings_reset(state: BeaconState) -> None:
|
||||
// next_epoch = Epoch(get_current_epoch(state) + 1)
|
||||
// # Reset slashings
|
||||
// state.slashings[next_epoch % EPOCHS_PER_SLASHINGS_VECTOR] = Gwei(0)
|
||||
//
|
||||
// def process_slashings_reset(state: BeaconState) -> None:
|
||||
// next_epoch = Epoch(get_current_epoch(state) + 1)
|
||||
// # Reset slashings
|
||||
// state.slashings[next_epoch % EPOCHS_PER_SLASHINGS_VECTOR] = Gwei(0)
|
||||
func ProcessSlashingsReset(state state.BeaconState) (state.BeaconState, error) {
|
||||
currentEpoch := time.CurrentEpoch(state)
|
||||
nextEpoch := currentEpoch + 1
|
||||
@@ -314,11 +319,12 @@ func ProcessSlashingsReset(state state.BeaconState) (state.BeaconState, error) {
|
||||
// ProcessRandaoMixesReset processes the final updates to RANDAO mix during epoch processing.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_randao_mixes_reset(state: BeaconState) -> None:
|
||||
// current_epoch = get_current_epoch(state)
|
||||
// next_epoch = Epoch(current_epoch + 1)
|
||||
// # Set randao mix
|
||||
// state.randao_mixes[next_epoch % EPOCHS_PER_HISTORICAL_VECTOR] = get_randao_mix(state, current_epoch)
|
||||
//
|
||||
// def process_randao_mixes_reset(state: BeaconState) -> None:
|
||||
// current_epoch = get_current_epoch(state)
|
||||
// next_epoch = Epoch(current_epoch + 1)
|
||||
// # Set randao mix
|
||||
// state.randao_mixes[next_epoch % EPOCHS_PER_HISTORICAL_VECTOR] = get_randao_mix(state, current_epoch)
|
||||
func ProcessRandaoMixesReset(state state.BeaconState) (state.BeaconState, error) {
|
||||
currentEpoch := time.CurrentEpoch(state)
|
||||
nextEpoch := currentEpoch + 1
|
||||
@@ -346,12 +352,13 @@ func ProcessRandaoMixesReset(state state.BeaconState) (state.BeaconState, error)
|
||||
// ProcessHistoricalRootsUpdate processes the updates to historical root accumulator during epoch processing.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_historical_roots_update(state: BeaconState) -> None:
|
||||
// # Set historical root accumulator
|
||||
// next_epoch = Epoch(get_current_epoch(state) + 1)
|
||||
// if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0:
|
||||
// historical_batch = HistoricalBatch(block_roots=state.block_roots, state_roots=state.state_roots)
|
||||
// state.historical_roots.append(hash_tree_root(historical_batch))
|
||||
//
|
||||
// def process_historical_roots_update(state: BeaconState) -> None:
|
||||
// # Set historical root accumulator
|
||||
// next_epoch = Epoch(get_current_epoch(state) + 1)
|
||||
// if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0:
|
||||
// historical_batch = HistoricalBatch(block_roots=state.block_roots, state_roots=state.state_roots)
|
||||
// state.historical_roots.append(hash_tree_root(historical_batch))
|
||||
func ProcessHistoricalRootsUpdate(state state.BeaconState) (state.BeaconState, error) {
|
||||
currentEpoch := time.CurrentEpoch(state)
|
||||
nextEpoch := currentEpoch + 1
|
||||
@@ -378,10 +385,11 @@ func ProcessHistoricalRootsUpdate(state state.BeaconState) (state.BeaconState, e
|
||||
// ProcessParticipationRecordUpdates rotates current/previous epoch attestations during epoch processing.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_participation_record_updates(state: BeaconState) -> None:
|
||||
// # Rotate current/previous epoch attestations
|
||||
// state.previous_epoch_attestations = state.current_epoch_attestations
|
||||
// state.current_epoch_attestations = []
|
||||
//
|
||||
// def process_participation_record_updates(state: BeaconState) -> None:
|
||||
// # Rotate current/previous epoch attestations
|
||||
// state.previous_epoch_attestations = state.current_epoch_attestations
|
||||
// state.current_epoch_attestations = []
|
||||
func ProcessParticipationRecordUpdates(state state.BeaconState) (state.BeaconState, error) {
|
||||
if err := state.RotateAttestations(); err != nil {
|
||||
return nil, err
|
||||
@@ -436,12 +444,13 @@ func ProcessFinalUpdates(state state.BeaconState) (state.BeaconState, error) {
|
||||
// it sorts the indices and filters out the slashed ones.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_unslashed_attesting_indices(state: BeaconState,
|
||||
// attestations: Sequence[PendingAttestation]) -> Set[ValidatorIndex]:
|
||||
// output = set() # type: Set[ValidatorIndex]
|
||||
// for a in attestations:
|
||||
// output = output.union(get_attesting_indices(state, a.data, a.aggregation_bits))
|
||||
// return set(filter(lambda index: not state.validators[index].slashed, output))
|
||||
//
|
||||
// def get_unslashed_attesting_indices(state: BeaconState,
|
||||
// attestations: Sequence[PendingAttestation]) -> Set[ValidatorIndex]:
|
||||
// output = set() # type: Set[ValidatorIndex]
|
||||
// for a in attestations:
|
||||
// output = output.union(get_attesting_indices(state, a.data, a.aggregation_bits))
|
||||
// return set(filter(lambda index: not state.validators[index].slashed, output))
|
||||
func UnslashedAttestingIndices(ctx context.Context, state state.ReadOnlyBeaconState, atts []*ethpb.PendingAttestation) ([]types.ValidatorIndex, error) {
|
||||
var setIndices []types.ValidatorIndex
|
||||
seen := make(map[uint64]bool)
|
||||
|
||||
@@ -183,7 +183,7 @@ func UpdateBalance(vp []*Validator, bBal *Balance, stateVersion int) *Balance {
|
||||
if stateVersion == version.Phase0 && v.IsPrevEpochAttester {
|
||||
bBal.PrevEpochAttested += v.CurrentEpochEffectiveBalance
|
||||
}
|
||||
if (stateVersion == version.Altair || stateVersion == version.Bellatrix) && v.IsPrevEpochSourceAttester {
|
||||
if stateVersion >= version.Altair && v.IsPrevEpochSourceAttester {
|
||||
bBal.PrevEpochAttested += v.CurrentEpochEffectiveBalance
|
||||
}
|
||||
if v.IsPrevEpochTargetAttester {
|
||||
|
||||
@@ -70,7 +70,7 @@ func TestUpdateBalance(t *testing.T) {
|
||||
assert.DeepEqual(t, wantedPBal, pBal, "Incorrect balance calculations")
|
||||
}
|
||||
|
||||
func TestUpdateBalanceBellatrixVersion(t *testing.T) {
|
||||
func TestUpdateBalanceDifferentVersions(t *testing.T) {
|
||||
vp := []*precompute.Validator{
|
||||
{IsCurrentEpochAttester: true, CurrentEpochEffectiveBalance: 100 * params.BeaconConfig().EffectiveBalanceIncrement},
|
||||
{IsCurrentEpochTargetAttester: true, IsCurrentEpochAttester: true, CurrentEpochEffectiveBalance: 100 * params.BeaconConfig().EffectiveBalanceIncrement},
|
||||
@@ -92,6 +92,9 @@ func TestUpdateBalanceBellatrixVersion(t *testing.T) {
|
||||
}
|
||||
pBal := precompute.UpdateBalance(vp, &precompute.Balance{}, version.Bellatrix)
|
||||
assert.DeepEqual(t, wantedPBal, pBal, "Incorrect balance calculations")
|
||||
|
||||
pBal = precompute.UpdateBalance(vp, &precompute.Balance{}, version.Capella)
|
||||
assert.DeepEqual(t, wantedPBal, pBal, "Incorrect balance calculations")
|
||||
}
|
||||
|
||||
func TestSameHead(t *testing.T) {
|
||||
|
||||
@@ -42,17 +42,18 @@ func UnrealizedCheckpoints(st state.BeaconState) (uint64, *ethpb.Checkpoint, *et
|
||||
// Note: this is an optimized version by passing in precomputed total and attesting balances.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_justification_and_finalization(state: BeaconState) -> None:
|
||||
// # Initial FFG checkpoint values have a `0x00` stub for `root`.
|
||||
// # Skip FFG updates in the first two epochs to avoid corner cases that might result in modifying this stub.
|
||||
// if get_current_epoch(state) <= GENESIS_EPOCH + 1:
|
||||
// return
|
||||
// previous_attestations = get_matching_target_attestations(state, get_previous_epoch(state))
|
||||
// current_attestations = get_matching_target_attestations(state, get_current_epoch(state))
|
||||
// total_active_balance = get_total_active_balance(state)
|
||||
// previous_target_balance = get_attesting_balance(state, previous_attestations)
|
||||
// current_target_balance = get_attesting_balance(state, current_attestations)
|
||||
// weigh_justification_and_finalization(state, total_active_balance, previous_target_balance, current_target_balance)
|
||||
//
|
||||
// def process_justification_and_finalization(state: BeaconState) -> None:
|
||||
// # Initial FFG checkpoint values have a `0x00` stub for `root`.
|
||||
// # Skip FFG updates in the first two epochs to avoid corner cases that might result in modifying this stub.
|
||||
// if get_current_epoch(state) <= GENESIS_EPOCH + 1:
|
||||
// return
|
||||
// previous_attestations = get_matching_target_attestations(state, get_previous_epoch(state))
|
||||
// current_attestations = get_matching_target_attestations(state, get_current_epoch(state))
|
||||
// total_active_balance = get_total_active_balance(state)
|
||||
// previous_target_balance = get_attesting_balance(state, previous_attestations)
|
||||
// current_target_balance = get_attesting_balance(state, current_attestations)
|
||||
// weigh_justification_and_finalization(state, total_active_balance, previous_target_balance, current_target_balance)
|
||||
func ProcessJustificationAndFinalizationPreCompute(state state.BeaconState, pBal *Balance) (state.BeaconState, error) {
|
||||
canProcessSlot, err := slots.EpochStart(2 /*epoch*/)
|
||||
if err != nil {
|
||||
@@ -113,41 +114,42 @@ func weighJustificationAndFinalization(state state.BeaconState, newBits bitfield
|
||||
// checkpoints at epoch transition
|
||||
// Spec pseudocode definition:
|
||||
// def weigh_justification_and_finalization(state: BeaconState,
|
||||
// total_active_balance: Gwei,
|
||||
// previous_epoch_target_balance: Gwei,
|
||||
// current_epoch_target_balance: Gwei) -> None:
|
||||
// previous_epoch = get_previous_epoch(state)
|
||||
// current_epoch = get_current_epoch(state)
|
||||
// old_previous_justified_checkpoint = state.previous_justified_checkpoint
|
||||
// old_current_justified_checkpoint = state.current_justified_checkpoint
|
||||
//
|
||||
// # Process justifications
|
||||
// state.previous_justified_checkpoint = state.current_justified_checkpoint
|
||||
// state.justification_bits[1:] = state.justification_bits[:JUSTIFICATION_BITS_LENGTH - 1]
|
||||
// state.justification_bits[0] = 0b0
|
||||
// if previous_epoch_target_balance * 3 >= total_active_balance * 2:
|
||||
// state.current_justified_checkpoint = Checkpoint(epoch=previous_epoch,
|
||||
// root=get_block_root(state, previous_epoch))
|
||||
// state.justification_bits[1] = 0b1
|
||||
// if current_epoch_target_balance * 3 >= total_active_balance * 2:
|
||||
// state.current_justified_checkpoint = Checkpoint(epoch=current_epoch,
|
||||
// root=get_block_root(state, current_epoch))
|
||||
// state.justification_bits[0] = 0b1
|
||||
// total_active_balance: Gwei,
|
||||
// previous_epoch_target_balance: Gwei,
|
||||
// current_epoch_target_balance: Gwei) -> None:
|
||||
// previous_epoch = get_previous_epoch(state)
|
||||
// current_epoch = get_current_epoch(state)
|
||||
// old_previous_justified_checkpoint = state.previous_justified_checkpoint
|
||||
// old_current_justified_checkpoint = state.current_justified_checkpoint
|
||||
//
|
||||
// # Process finalizations
|
||||
// bits = state.justification_bits
|
||||
// # The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th as source
|
||||
// if all(bits[1:4]) and old_previous_justified_checkpoint.epoch + 3 == current_epoch:
|
||||
// state.finalized_checkpoint = old_previous_justified_checkpoint
|
||||
// # The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as source
|
||||
// if all(bits[1:3]) and old_previous_justified_checkpoint.epoch + 2 == current_epoch:
|
||||
// state.finalized_checkpoint = old_previous_justified_checkpoint
|
||||
// # The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as source
|
||||
// if all(bits[0:3]) and old_current_justified_checkpoint.epoch + 2 == current_epoch:
|
||||
// state.finalized_checkpoint = old_current_justified_checkpoint
|
||||
// # The 1st/2nd most recent epochs are justified, the 1st using the 2nd as source
|
||||
// if all(bits[0:2]) and old_current_justified_checkpoint.epoch + 1 == current_epoch:
|
||||
// state.finalized_checkpoint = old_current_justified_checkpoint
|
||||
// # Process justifications
|
||||
// state.previous_justified_checkpoint = state.current_justified_checkpoint
|
||||
// state.justification_bits[1:] = state.justification_bits[:JUSTIFICATION_BITS_LENGTH - 1]
|
||||
// state.justification_bits[0] = 0b0
|
||||
// if previous_epoch_target_balance * 3 >= total_active_balance * 2:
|
||||
// state.current_justified_checkpoint = Checkpoint(epoch=previous_epoch,
|
||||
// root=get_block_root(state, previous_epoch))
|
||||
// state.justification_bits[1] = 0b1
|
||||
// if current_epoch_target_balance * 3 >= total_active_balance * 2:
|
||||
// state.current_justified_checkpoint = Checkpoint(epoch=current_epoch,
|
||||
// root=get_block_root(state, current_epoch))
|
||||
// state.justification_bits[0] = 0b1
|
||||
//
|
||||
// # Process finalizations
|
||||
// bits = state.justification_bits
|
||||
// # The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th as source
|
||||
// if all(bits[1:4]) and old_previous_justified_checkpoint.epoch + 3 == current_epoch:
|
||||
// state.finalized_checkpoint = old_previous_justified_checkpoint
|
||||
// # The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as source
|
||||
// if all(bits[1:3]) and old_previous_justified_checkpoint.epoch + 2 == current_epoch:
|
||||
// state.finalized_checkpoint = old_previous_justified_checkpoint
|
||||
// # The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as source
|
||||
// if all(bits[0:3]) and old_current_justified_checkpoint.epoch + 2 == current_epoch:
|
||||
// state.finalized_checkpoint = old_current_justified_checkpoint
|
||||
// # The 1st/2nd most recent epochs are justified, the 1st using the 2nd as source
|
||||
// if all(bits[0:2]) and old_current_justified_checkpoint.epoch + 1 == current_epoch:
|
||||
// state.finalized_checkpoint = old_current_justified_checkpoint
|
||||
func computeCheckpoints(state state.BeaconState, newBits bitfield.Bitvector4) (*ethpb.Checkpoint, *ethpb.Checkpoint, error) {
|
||||
prevEpoch := time.PrevEpoch(state)
|
||||
currentEpoch := time.CurrentEpoch(state)
|
||||
|
||||
@@ -358,10 +358,11 @@ func TestProposerDeltaPrecompute_SlashedCase(t *testing.T) {
|
||||
// individual validator's base reward quotient.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
|
||||
// total_balance = get_total_active_balance(state)
|
||||
// effective_balance = state.validators[index].effective_balance
|
||||
// return Gwei(effective_balance * BASE_REWARD_FACTOR // integer_squareroot(total_balance) // BASE_REWARDS_PER_EPOCH)
|
||||
//
|
||||
// def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
|
||||
// total_balance = get_total_active_balance(state)
|
||||
// effective_balance = state.validators[index].effective_balance
|
||||
// return Gwei(effective_balance * BASE_REWARD_FACTOR // integer_squareroot(total_balance) // BASE_REWARDS_PER_EPOCH)
|
||||
func baseReward(state state.ReadOnlyBeaconState, index types.ValidatorIndex) (uint64, error) {
|
||||
totalBalance, err := helpers.TotalActiveBalance(state)
|
||||
if err != nil {
|
||||
|
||||
@@ -6,6 +6,7 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/execution",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//cmd/prysmctl/testnet:__pkg__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//validator/client:__pkg__",
|
||||
],
|
||||
|
||||
@@ -35,14 +35,15 @@ var (
|
||||
// count.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_committee_count_per_slot(state: BeaconState, epoch: Epoch) -> uint64:
|
||||
// """
|
||||
// Return the number of committees in each slot for the given ``epoch``.
|
||||
// """
|
||||
// return max(uint64(1), min(
|
||||
// MAX_COMMITTEES_PER_SLOT,
|
||||
// uint64(len(get_active_validator_indices(state, epoch))) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE,
|
||||
// ))
|
||||
//
|
||||
// def get_committee_count_per_slot(state: BeaconState, epoch: Epoch) -> uint64:
|
||||
// """
|
||||
// Return the number of committees in each slot for the given ``epoch``.
|
||||
// """
|
||||
// return max(uint64(1), min(
|
||||
// MAX_COMMITTEES_PER_SLOT,
|
||||
// uint64(len(get_active_validator_indices(state, epoch))) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE,
|
||||
// ))
|
||||
func SlotCommitteeCount(activeValidatorCount uint64) uint64 {
|
||||
var committeesPerSlot = activeValidatorCount / uint64(params.BeaconConfig().SlotsPerEpoch) / params.BeaconConfig().TargetCommitteeSize
|
||||
|
||||
@@ -61,18 +62,19 @@ func SlotCommitteeCount(activeValidatorCount uint64) uint64 {
|
||||
// becomes expensive, consider using BeaconCommittee below.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_beacon_committee(state: BeaconState, slot: Slot, index: CommitteeIndex) -> Sequence[ValidatorIndex]:
|
||||
// """
|
||||
// Return the beacon committee at ``slot`` for ``index``.
|
||||
// """
|
||||
// epoch = compute_epoch_at_slot(slot)
|
||||
// committees_per_slot = get_committee_count_per_slot(state, epoch)
|
||||
// return compute_committee(
|
||||
// indices=get_active_validator_indices(state, epoch),
|
||||
// seed=get_seed(state, epoch, DOMAIN_BEACON_ATTESTER),
|
||||
// index=(slot % SLOTS_PER_EPOCH) * committees_per_slot + index,
|
||||
// count=committees_per_slot * SLOTS_PER_EPOCH,
|
||||
// )
|
||||
//
|
||||
// def get_beacon_committee(state: BeaconState, slot: Slot, index: CommitteeIndex) -> Sequence[ValidatorIndex]:
|
||||
// """
|
||||
// Return the beacon committee at ``slot`` for ``index``.
|
||||
// """
|
||||
// epoch = compute_epoch_at_slot(slot)
|
||||
// committees_per_slot = get_committee_count_per_slot(state, epoch)
|
||||
// return compute_committee(
|
||||
// indices=get_active_validator_indices(state, epoch),
|
||||
// seed=get_seed(state, epoch, DOMAIN_BEACON_ATTESTER),
|
||||
// index=(slot % SLOTS_PER_EPOCH) * committees_per_slot + index,
|
||||
// count=committees_per_slot * SLOTS_PER_EPOCH,
|
||||
// )
|
||||
func BeaconCommitteeFromState(ctx context.Context, state state.ReadOnlyBeaconState, slot types.Slot, committeeIndex types.CommitteeIndex) ([]types.ValidatorIndex, error) {
|
||||
epoch := slots.ToEpoch(slot)
|
||||
seed, err := Seed(state, epoch, params.BeaconConfig().DomainBeaconAttester)
|
||||
@@ -101,18 +103,19 @@ func BeaconCommitteeFromState(ctx context.Context, state state.ReadOnlyBeaconSta
|
||||
// from the spec definition. Having them as an argument allows for cheaper computation run time.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_beacon_committee(state: BeaconState, slot: Slot, index: CommitteeIndex) -> Sequence[ValidatorIndex]:
|
||||
// """
|
||||
// Return the beacon committee at ``slot`` for ``index``.
|
||||
// """
|
||||
// epoch = compute_epoch_at_slot(slot)
|
||||
// committees_per_slot = get_committee_count_per_slot(state, epoch)
|
||||
// return compute_committee(
|
||||
// indices=get_active_validator_indices(state, epoch),
|
||||
// seed=get_seed(state, epoch, DOMAIN_BEACON_ATTESTER),
|
||||
// index=(slot % SLOTS_PER_EPOCH) * committees_per_slot + index,
|
||||
// count=committees_per_slot * SLOTS_PER_EPOCH,
|
||||
// )
|
||||
//
|
||||
// def get_beacon_committee(state: BeaconState, slot: Slot, index: CommitteeIndex) -> Sequence[ValidatorIndex]:
|
||||
// """
|
||||
// Return the beacon committee at ``slot`` for ``index``.
|
||||
// """
|
||||
// epoch = compute_epoch_at_slot(slot)
|
||||
// committees_per_slot = get_committee_count_per_slot(state, epoch)
|
||||
// return compute_committee(
|
||||
// indices=get_active_validator_indices(state, epoch),
|
||||
// seed=get_seed(state, epoch, DOMAIN_BEACON_ATTESTER),
|
||||
// index=(slot % SLOTS_PER_EPOCH) * committees_per_slot + index,
|
||||
// count=committees_per_slot * SLOTS_PER_EPOCH,
|
||||
// )
|
||||
func BeaconCommittee(
|
||||
ctx context.Context,
|
||||
validatorIndices []types.ValidatorIndex,
|
||||
@@ -381,16 +384,17 @@ func ClearCache() {
|
||||
// validator indices and seed.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def compute_committee(indices: Sequence[ValidatorIndex],
|
||||
// seed: Bytes32,
|
||||
// index: uint64,
|
||||
// count: uint64) -> Sequence[ValidatorIndex]:
|
||||
// """
|
||||
// Return the committee corresponding to ``indices``, ``seed``, ``index``, and committee ``count``.
|
||||
// """
|
||||
// start = (len(indices) * index) // count
|
||||
// end = (len(indices) * uint64(index + 1)) // count
|
||||
// return [indices[compute_shuffled_index(uint64(i), uint64(len(indices)), seed)] for i in range(start, end)]
|
||||
//
|
||||
// def compute_committee(indices: Sequence[ValidatorIndex],
|
||||
// seed: Bytes32,
|
||||
// index: uint64,
|
||||
// count: uint64) -> Sequence[ValidatorIndex]:
|
||||
// """
|
||||
// Return the committee corresponding to ``indices``, ``seed``, ``index``, and committee ``count``.
|
||||
// """
|
||||
// start = (len(indices) * index) // count
|
||||
// end = (len(indices) * uint64(index + 1)) // count
|
||||
// return [indices[compute_shuffled_index(uint64(i), uint64(len(indices)), seed)] for i in range(start, end)]
|
||||
func computeCommittee(
|
||||
indices []types.ValidatorIndex,
|
||||
seed [32]byte,
|
||||
|
||||
@@ -14,12 +14,13 @@ import (
|
||||
// It returns an error if the requested block root is not within the slot range.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_block_root_at_slot(state: BeaconState, slot: Slot) -> Root:
|
||||
// """
|
||||
// Return the block root at a recent ``slot``.
|
||||
// """
|
||||
// assert slot < state.slot <= slot + SLOTS_PER_HISTORICAL_ROOT
|
||||
// return state.block_roots[slot % SLOTS_PER_HISTORICAL_ROOT]
|
||||
//
|
||||
// def get_block_root_at_slot(state: BeaconState, slot: Slot) -> Root:
|
||||
// """
|
||||
// Return the block root at a recent ``slot``.
|
||||
// """
|
||||
// assert slot < state.slot <= slot + SLOTS_PER_HISTORICAL_ROOT
|
||||
// return state.block_roots[slot % SLOTS_PER_HISTORICAL_ROOT]
|
||||
func BlockRootAtSlot(state state.ReadOnlyBeaconState, slot types.Slot) ([]byte, error) {
|
||||
if math.MaxUint64-slot < params.BeaconConfig().SlotsPerHistoricalRoot {
|
||||
return []byte{}, errors.New("slot overflows uint64")
|
||||
@@ -42,11 +43,12 @@ func StateRootAtSlot(state state.ReadOnlyBeaconState, slot types.Slot) ([]byte,
|
||||
// BlockRoot returns the block root stored in the BeaconState for epoch start slot.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_block_root(state: BeaconState, epoch: Epoch) -> Root:
|
||||
// """
|
||||
// Return the block root at the start of a recent ``epoch``.
|
||||
// """
|
||||
// return get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch))
|
||||
//
|
||||
// def get_block_root(state: BeaconState, epoch: Epoch) -> Root:
|
||||
// """
|
||||
// Return the block root at the start of a recent ``epoch``.
|
||||
// """
|
||||
// return get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch))
|
||||
func BlockRoot(state state.ReadOnlyBeaconState, epoch types.Epoch) ([]byte, error) {
|
||||
s, err := slots.EpochStart(epoch)
|
||||
if err != nil {
|
||||
|
||||
@@ -12,12 +12,13 @@ import (
|
||||
// Seed returns the randao seed used for shuffling of a given epoch.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_seed(state: BeaconState, epoch: Epoch, domain_type: DomainType) -> Bytes32:
|
||||
// """
|
||||
// Return the seed at ``epoch``.
|
||||
// """
|
||||
// mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1)) # Avoid underflow
|
||||
// return hash(domain_type + uint_to_bytes(epoch) + mix)
|
||||
//
|
||||
// def get_seed(state: BeaconState, epoch: Epoch, domain_type: DomainType) -> Bytes32:
|
||||
// """
|
||||
// Return the seed at ``epoch``.
|
||||
// """
|
||||
// mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1)) # Avoid underflow
|
||||
// return hash(domain_type + uint_to_bytes(epoch) + mix)
|
||||
func Seed(state state.ReadOnlyBeaconState, epoch types.Epoch, domain [bls.DomainByteLength]byte) ([32]byte, error) {
|
||||
// See https://github.com/ethereum/consensus-specs/pull/1296 for
|
||||
// rationale on why offset has to look down by 1.
|
||||
@@ -40,11 +41,12 @@ func Seed(state state.ReadOnlyBeaconState, epoch types.Epoch, domain [bls.Domain
|
||||
// of a given slot. It is used to shuffle validators.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_randao_mix(state: BeaconState, epoch: Epoch) -> Bytes32:
|
||||
// """
|
||||
// Return the randao mix at a recent ``epoch``.
|
||||
// """
|
||||
// return state.randao_mixes[epoch % EPOCHS_PER_HISTORICAL_VECTOR]
|
||||
//
|
||||
// def get_randao_mix(state: BeaconState, epoch: Epoch) -> Bytes32:
|
||||
// """
|
||||
// Return the randao mix at a recent ``epoch``.
|
||||
// """
|
||||
// return state.randao_mixes[epoch % EPOCHS_PER_HISTORICAL_VECTOR]
|
||||
func RandaoMix(state state.ReadOnlyBeaconState, epoch types.Epoch) ([]byte, error) {
|
||||
return state.RandaoMixAtIndex(uint64(epoch % params.BeaconConfig().EpochsPerHistoricalVector))
|
||||
}
|
||||
|
||||
@@ -17,13 +17,14 @@ var balanceCache = cache.NewEffectiveBalanceCache()
|
||||
// of input validators.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_total_balance(state: BeaconState, indices: Set[ValidatorIndex]) -> Gwei:
|
||||
// """
|
||||
// Return the combined effective balance of the ``indices``.
|
||||
// ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero.
|
||||
// Math safe up to ~10B ETH, afterwhich this overflows uint64.
|
||||
// """
|
||||
// return Gwei(max(EFFECTIVE_BALANCE_INCREMENT, sum([state.validators[index].effective_balance for index in indices])))
|
||||
//
|
||||
// def get_total_balance(state: BeaconState, indices: Set[ValidatorIndex]) -> Gwei:
|
||||
// """
|
||||
// Return the combined effective balance of the ``indices``.
|
||||
// ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero.
|
||||
// Math safe up to ~10B ETH, afterwhich this overflows uint64.
|
||||
// """
|
||||
// return Gwei(max(EFFECTIVE_BALANCE_INCREMENT, sum([state.validators[index].effective_balance for index in indices])))
|
||||
func TotalBalance(state state.ReadOnlyValidators, indices []types.ValidatorIndex) uint64 {
|
||||
total := uint64(0)
|
||||
|
||||
@@ -47,12 +48,13 @@ func TotalBalance(state state.ReadOnlyValidators, indices []types.ValidatorIndex
|
||||
// of active validators.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_total_active_balance(state: BeaconState) -> Gwei:
|
||||
// """
|
||||
// Return the combined effective balance of the active validators.
|
||||
// Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero.
|
||||
// """
|
||||
// return get_total_balance(state, set(get_active_validator_indices(state, get_current_epoch(state))))
|
||||
//
|
||||
// def get_total_active_balance(state: BeaconState) -> Gwei:
|
||||
// """
|
||||
// Return the combined effective balance of the active validators.
|
||||
// Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero.
|
||||
// """
|
||||
// return get_total_balance(state, set(get_active_validator_indices(state, get_current_epoch(state))))
|
||||
func TotalActiveBalance(s state.ReadOnlyBeaconState) (uint64, error) {
|
||||
bal, err := balanceCache.Get(s)
|
||||
switch {
|
||||
@@ -88,11 +90,12 @@ func TotalActiveBalance(s state.ReadOnlyBeaconState) (uint64, error) {
|
||||
// IncreaseBalance increases validator with the given 'index' balance by 'delta' in Gwei.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
|
||||
// """
|
||||
// Increase the validator balance at index ``index`` by ``delta``.
|
||||
// """
|
||||
// state.balances[index] += delta
|
||||
//
|
||||
// def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
|
||||
// """
|
||||
// Increase the validator balance at index ``index`` by ``delta``.
|
||||
// """
|
||||
// state.balances[index] += delta
|
||||
func IncreaseBalance(state state.BeaconState, idx types.ValidatorIndex, delta uint64) error {
|
||||
balAtIdx, err := state.BalanceAtIndex(idx)
|
||||
if err != nil {
|
||||
@@ -110,11 +113,12 @@ func IncreaseBalance(state state.BeaconState, idx types.ValidatorIndex, delta ui
|
||||
// the post balance.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
|
||||
// """
|
||||
// Increase the validator balance at index ``index`` by ``delta``.
|
||||
// """
|
||||
// state.balances[index] += delta
|
||||
//
|
||||
// def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
|
||||
// """
|
||||
// Increase the validator balance at index ``index`` by ``delta``.
|
||||
// """
|
||||
// state.balances[index] += delta
|
||||
func IncreaseBalanceWithVal(currBalance, delta uint64) (uint64, error) {
|
||||
return mathutil.Add64(currBalance, delta)
|
||||
}
|
||||
@@ -122,11 +126,12 @@ func IncreaseBalanceWithVal(currBalance, delta uint64) (uint64, error) {
|
||||
// DecreaseBalance decreases validator with the given 'index' balance by 'delta' in Gwei.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
|
||||
// """
|
||||
// Decrease the validator balance at index ``index`` by ``delta``, with underflow protection.
|
||||
// """
|
||||
// state.balances[index] = 0 if delta > state.balances[index] else state.balances[index] - delta
|
||||
//
|
||||
// def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
|
||||
// """
|
||||
// Decrease the validator balance at index ``index`` by ``delta``, with underflow protection.
|
||||
// """
|
||||
// state.balances[index] = 0 if delta > state.balances[index] else state.balances[index] - delta
|
||||
func DecreaseBalance(state state.BeaconState, idx types.ValidatorIndex, delta uint64) error {
|
||||
balAtIdx, err := state.BalanceAtIndex(idx)
|
||||
if err != nil {
|
||||
@@ -140,11 +145,12 @@ func DecreaseBalance(state state.BeaconState, idx types.ValidatorIndex, delta ui
|
||||
// the post balance.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
|
||||
// """
|
||||
// Decrease the validator balance at index ``index`` by ``delta``, with underflow protection.
|
||||
// """
|
||||
// state.balances[index] = 0 if delta > state.balances[index] else state.balances[index] - delta
|
||||
//
|
||||
// def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
|
||||
// """
|
||||
// Decrease the validator balance at index ``index`` by ``delta``, with underflow protection.
|
||||
// """
|
||||
// state.balances[index] = 0 if delta > state.balances[index] else state.balances[index] - delta
|
||||
func DecreaseBalanceWithVal(currBalance, delta uint64) uint64 {
|
||||
if delta > currBalance {
|
||||
return 0
|
||||
@@ -156,7 +162,8 @@ func DecreaseBalanceWithVal(currBalance, delta uint64) uint64 {
|
||||
//
|
||||
// Spec code:
|
||||
// def is_in_inactivity_leak(state: BeaconState) -> bool:
|
||||
// return get_finality_delay(state) > MIN_EPOCHS_TO_INACTIVITY_PENALTY
|
||||
//
|
||||
// return get_finality_delay(state) > MIN_EPOCHS_TO_INACTIVITY_PENALTY
|
||||
func IsInInactivityLeak(prevEpoch, finalizedEpoch types.Epoch) bool {
|
||||
return FinalityDelay(prevEpoch, finalizedEpoch) > params.BeaconConfig().MinEpochsToInactivityPenalty
|
||||
}
|
||||
@@ -165,7 +172,8 @@ func IsInInactivityLeak(prevEpoch, finalizedEpoch types.Epoch) bool {
|
||||
//
|
||||
// Spec code:
|
||||
// def get_finality_delay(state: BeaconState) -> uint64:
|
||||
// return get_previous_epoch(state) - state.finalized_checkpoint.epoch
|
||||
//
|
||||
// return get_previous_epoch(state) - state.finalized_checkpoint.epoch
|
||||
func FinalityDelay(prevEpoch, finalizedEpoch types.Epoch) types.Epoch {
|
||||
return prevEpoch - finalizedEpoch
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ func SplitIndices(l []uint64, n uint64) [][]uint64 {
|
||||
return divided
|
||||
}
|
||||
|
||||
// ShuffledIndex returns `p(index)` in a pseudorandom permutation `p` of `0...list_size - 1` with ``seed`` as entropy.
|
||||
// ShuffledIndex returns `p(index)` in a pseudorandom permutation `p` of `0...list_size - 1` with “seed“ as entropy.
|
||||
// We utilize 'swap or not' shuffling in this implementation; we are allocating the memory with the seed that stays
|
||||
// constant between iterations instead of reallocating it each iteration as in the spec. This implementation is based
|
||||
// on the original implementation from protolambda, https://github.com/protolambda/eth2-shuffle
|
||||
@@ -47,28 +47,29 @@ func UnShuffledIndex(index types.ValidatorIndex, indexCount uint64, seed [32]byt
|
||||
|
||||
// ComputeShuffledIndex returns the shuffled validator index corresponding to seed and index count.
|
||||
// Spec pseudocode definition:
|
||||
// def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> uint64:
|
||||
// """
|
||||
// Return the shuffled index corresponding to ``seed`` (and ``index_count``).
|
||||
// """
|
||||
// assert index < index_count
|
||||
//
|
||||
// # Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf)
|
||||
// # See the 'generalized domain' algorithm on page 3
|
||||
// for current_round in range(SHUFFLE_ROUND_COUNT):
|
||||
// pivot = bytes_to_uint64(hash(seed + uint_to_bytes(uint8(current_round)))[0:8]) % index_count
|
||||
// flip = (pivot + index_count - index) % index_count
|
||||
// position = max(index, flip)
|
||||
// source = hash(
|
||||
// seed
|
||||
// + uint_to_bytes(uint8(current_round))
|
||||
// + uint_to_bytes(uint32(position // 256))
|
||||
// )
|
||||
// byte = uint8(source[(position % 256) // 8])
|
||||
// bit = (byte >> (position % 8)) % 2
|
||||
// index = flip if bit else index
|
||||
// def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> uint64:
|
||||
// """
|
||||
// Return the shuffled index corresponding to ``seed`` (and ``index_count``).
|
||||
// """
|
||||
// assert index < index_count
|
||||
//
|
||||
// return index
|
||||
// # Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf)
|
||||
// # See the 'generalized domain' algorithm on page 3
|
||||
// for current_round in range(SHUFFLE_ROUND_COUNT):
|
||||
// pivot = bytes_to_uint64(hash(seed + uint_to_bytes(uint8(current_round)))[0:8]) % index_count
|
||||
// flip = (pivot + index_count - index) % index_count
|
||||
// position = max(index, flip)
|
||||
// source = hash(
|
||||
// seed
|
||||
// + uint_to_bytes(uint8(current_round))
|
||||
// + uint_to_bytes(uint32(position // 256))
|
||||
// )
|
||||
// byte = uint8(source[(position % 256) // 8])
|
||||
// bit = (byte >> (position % 8)) % 2
|
||||
// index = flip if bit else index
|
||||
//
|
||||
// return index
|
||||
func ComputeShuffledIndex(index types.ValidatorIndex, indexCount uint64, seed [32]byte, shuffle bool) (types.ValidatorIndex, error) {
|
||||
if params.BeaconConfig().ShuffleRoundCount == 0 {
|
||||
return index, nil
|
||||
@@ -135,20 +136,21 @@ func ComputeShuffledIndex(index types.ValidatorIndex, indexCount uint64, seed [3
|
||||
return index, nil
|
||||
}
|
||||
|
||||
// ShuffleList returns list of shuffled indexes in a pseudorandom permutation `p` of `0...list_size - 1` with ``seed`` as entropy.
|
||||
// ShuffleList returns list of shuffled indexes in a pseudorandom permutation `p` of `0...list_size - 1` with “seed“ as entropy.
|
||||
// We utilize 'swap or not' shuffling in this implementation; we are allocating the memory with the seed that stays
|
||||
// constant between iterations instead of reallocating it each iteration as in the spec. This implementation is based
|
||||
// on the original implementation from protolambda, https://github.com/protolambda/eth2-shuffle
|
||||
// improvements:
|
||||
// - seed is always the first 32 bytes of the hash input, we just copy it into the buffer one time.
|
||||
// - add round byte to seed and hash that part of the buffer.
|
||||
// - split up the for-loop in two:
|
||||
// 1. Handle the part from 0 (incl) to pivot (incl). This is mirrored around (pivot / 2).
|
||||
// 2. Handle the part from pivot (excl) to N (excl). This is mirrored around ((pivot / 2) + (size/2)).
|
||||
// - hash source every 256 iterations.
|
||||
// - change byteV every 8 iterations.
|
||||
// - we start at the edges, and work back to the mirror point.
|
||||
// this makes us process each pear exactly once (instead of unnecessarily twice, like in the spec).
|
||||
//
|
||||
// improvements:
|
||||
// - seed is always the first 32 bytes of the hash input, we just copy it into the buffer one time.
|
||||
// - add round byte to seed and hash that part of the buffer.
|
||||
// - split up the for-loop in two:
|
||||
// 1. Handle the part from 0 (incl) to pivot (incl). This is mirrored around (pivot / 2).
|
||||
// 2. Handle the part from pivot (excl) to N (excl). This is mirrored around ((pivot / 2) + (size/2)).
|
||||
// - hash source every 256 iterations.
|
||||
// - change byteV every 8 iterations.
|
||||
// - we start at the edges, and work back to the mirror point.
|
||||
// this makes us process each pear exactly once (instead of unnecessarily twice, like in the spec).
|
||||
func ShuffleList(input []types.ValidatorIndex, seed [32]byte) ([]types.ValidatorIndex, error) {
|
||||
return innerShuffleList(input, seed, true /* shuffle */)
|
||||
}
|
||||
|
||||
@@ -28,11 +28,12 @@ var CommitteeCacheInProgressHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
// is active or not.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
|
||||
// """
|
||||
// Check if ``validator`` is active.
|
||||
// """
|
||||
// return validator.activation_epoch <= epoch < validator.exit_epoch
|
||||
//
|
||||
// def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
|
||||
// """
|
||||
// Check if ``validator`` is active.
|
||||
// """
|
||||
// return validator.activation_epoch <= epoch < validator.exit_epoch
|
||||
func IsActiveValidator(validator *ethpb.Validator, epoch types.Epoch) bool {
|
||||
return checkValidatorActiveStatus(validator.ActivationEpoch, validator.ExitEpoch, epoch)
|
||||
}
|
||||
@@ -50,11 +51,12 @@ func checkValidatorActiveStatus(activationEpoch, exitEpoch, epoch types.Epoch) b
|
||||
// is slashable or not.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def is_slashable_validator(validator: Validator, epoch: Epoch) -> bool:
|
||||
// """
|
||||
// Check if ``validator`` is slashable.
|
||||
// """
|
||||
// return (not validator.slashed) and (validator.activation_epoch <= epoch < validator.withdrawable_epoch)
|
||||
//
|
||||
// def is_slashable_validator(validator: Validator, epoch: Epoch) -> bool:
|
||||
// """
|
||||
// Check if ``validator`` is slashable.
|
||||
// """
|
||||
// return (not validator.slashed) and (validator.activation_epoch <= epoch < validator.withdrawable_epoch)
|
||||
func IsSlashableValidator(activationEpoch, withdrawableEpoch types.Epoch, slashed bool, epoch types.Epoch) bool {
|
||||
return checkValidatorSlashable(activationEpoch, withdrawableEpoch, slashed, epoch)
|
||||
}
|
||||
@@ -78,11 +80,12 @@ func checkValidatorSlashable(activationEpoch, withdrawableEpoch types.Epoch, sla
|
||||
// need the active validator indices for some specific reason.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_active_validator_indices(state: BeaconState, epoch: Epoch) -> Sequence[ValidatorIndex]:
|
||||
// """
|
||||
// Return the sequence of active validator indices at ``epoch``.
|
||||
// """
|
||||
// return [ValidatorIndex(i) for i, v in enumerate(state.validators) if is_active_validator(v, epoch)]
|
||||
//
|
||||
// def get_active_validator_indices(state: BeaconState, epoch: Epoch) -> Sequence[ValidatorIndex]:
|
||||
// """
|
||||
// Return the sequence of active validator indices at ``epoch``.
|
||||
// """
|
||||
// return [ValidatorIndex(i) for i, v in enumerate(state.validators) if is_active_validator(v, epoch)]
|
||||
func ActiveValidatorIndices(ctx context.Context, s state.ReadOnlyBeaconState, epoch types.Epoch) ([]types.ValidatorIndex, error) {
|
||||
seed, err := Seed(s, epoch, params.BeaconConfig().DomainBeaconAttester)
|
||||
if err != nil {
|
||||
@@ -186,11 +189,12 @@ func ActiveValidatorCount(ctx context.Context, s state.ReadOnlyBeaconState, epoc
|
||||
// the validator is eligible for activation and exit.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def compute_activation_exit_epoch(epoch: Epoch) -> Epoch:
|
||||
// """
|
||||
// Return the epoch during which validator activations and exits initiated in ``epoch`` take effect.
|
||||
// """
|
||||
// return Epoch(epoch + 1 + MAX_SEED_LOOKAHEAD)
|
||||
//
|
||||
// def compute_activation_exit_epoch(epoch: Epoch) -> Epoch:
|
||||
// """
|
||||
// Return the epoch during which validator activations and exits initiated in ``epoch`` take effect.
|
||||
// """
|
||||
// return Epoch(epoch + 1 + MAX_SEED_LOOKAHEAD)
|
||||
func ActivationExitEpoch(epoch types.Epoch) types.Epoch {
|
||||
return epoch + 1 + params.BeaconConfig().MaxSeedLookahead
|
||||
}
|
||||
@@ -199,12 +203,13 @@ func ActivationExitEpoch(epoch types.Epoch) types.Epoch {
|
||||
// enter and exit validator pool for an epoch.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_validator_churn_limit(state: BeaconState) -> uint64:
|
||||
// """
|
||||
// Return the validator churn limit for the current epoch.
|
||||
// """
|
||||
// active_validator_indices = get_active_validator_indices(state, get_current_epoch(state))
|
||||
// return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT)
|
||||
//
|
||||
// def get_validator_churn_limit(state: BeaconState) -> uint64:
|
||||
// """
|
||||
// Return the validator churn limit for the current epoch.
|
||||
// """
|
||||
// active_validator_indices = get_active_validator_indices(state, get_current_epoch(state))
|
||||
// return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT)
|
||||
func ValidatorChurnLimit(activeValidatorCount uint64) (uint64, error) {
|
||||
churnLimit := activeValidatorCount / params.BeaconConfig().ChurnLimitQuotient
|
||||
if churnLimit < params.BeaconConfig().MinPerEpochChurnLimit {
|
||||
@@ -216,14 +221,15 @@ func ValidatorChurnLimit(activeValidatorCount uint64) (uint64, error) {
|
||||
// BeaconProposerIndex returns proposer index of a current slot.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
|
||||
// """
|
||||
// Return the beacon proposer index at the current slot.
|
||||
// """
|
||||
// epoch = get_current_epoch(state)
|
||||
// seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + uint_to_bytes(state.slot))
|
||||
// indices = get_active_validator_indices(state, epoch)
|
||||
// return compute_proposer_index(state, indices, seed)
|
||||
//
|
||||
// def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
|
||||
// """
|
||||
// Return the beacon proposer index at the current slot.
|
||||
// """
|
||||
// epoch = get_current_epoch(state)
|
||||
// seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + uint_to_bytes(state.slot))
|
||||
// indices = get_active_validator_indices(state, epoch)
|
||||
// return compute_proposer_index(state, indices, seed)
|
||||
func BeaconProposerIndex(ctx context.Context, state state.ReadOnlyBeaconState) (types.ValidatorIndex, error) {
|
||||
e := time.CurrentEpoch(state)
|
||||
// The cache uses the state root of the previous epoch - minimum_seed_lookahead last slot as key. (e.g. Starting epoch 1, slot 32, the key would be block root at slot 31)
|
||||
@@ -274,21 +280,22 @@ func BeaconProposerIndex(ctx context.Context, state state.ReadOnlyBeaconState) (
|
||||
// ComputeProposerIndex returns the index sampled by effective balance, which is used to calculate proposer.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex], seed: Bytes32) -> ValidatorIndex:
|
||||
// """
|
||||
// Return from ``indices`` a random index sampled by effective balance.
|
||||
// """
|
||||
// assert len(indices) > 0
|
||||
// MAX_RANDOM_BYTE = 2**8 - 1
|
||||
// i = uint64(0)
|
||||
// total = uint64(len(indices))
|
||||
// while True:
|
||||
// candidate_index = indices[compute_shuffled_index(i % total, total, seed)]
|
||||
// random_byte = hash(seed + uint_to_bytes(uint64(i // 32)))[i % 32]
|
||||
// effective_balance = state.validators[candidate_index].effective_balance
|
||||
// if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte:
|
||||
// return candidate_index
|
||||
// i += 1
|
||||
//
|
||||
// def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex], seed: Bytes32) -> ValidatorIndex:
|
||||
// """
|
||||
// Return from ``indices`` a random index sampled by effective balance.
|
||||
// """
|
||||
// assert len(indices) > 0
|
||||
// MAX_RANDOM_BYTE = 2**8 - 1
|
||||
// i = uint64(0)
|
||||
// total = uint64(len(indices))
|
||||
// while True:
|
||||
// candidate_index = indices[compute_shuffled_index(i % total, total, seed)]
|
||||
// random_byte = hash(seed + uint_to_bytes(uint64(i // 32)))[i % 32]
|
||||
// effective_balance = state.validators[candidate_index].effective_balance
|
||||
// if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte:
|
||||
// return candidate_index
|
||||
// i += 1
|
||||
func ComputeProposerIndex(bState state.ReadOnlyValidators, activeIndices []types.ValidatorIndex, seed [32]byte) (types.ValidatorIndex, error) {
|
||||
length := uint64(len(activeIndices))
|
||||
if length == 0 {
|
||||
@@ -324,14 +331,15 @@ func ComputeProposerIndex(bState state.ReadOnlyValidators, activeIndices []types
|
||||
// be placed into the activation queue.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def is_eligible_for_activation_queue(validator: Validator) -> bool:
|
||||
// """
|
||||
// Check if ``validator`` is eligible to be placed into the activation queue.
|
||||
// """
|
||||
// return (
|
||||
// validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
|
||||
// and validator.effective_balance == MAX_EFFECTIVE_BALANCE
|
||||
// )
|
||||
//
|
||||
// def is_eligible_for_activation_queue(validator: Validator) -> bool:
|
||||
// """
|
||||
// Check if ``validator`` is eligible to be placed into the activation queue.
|
||||
// """
|
||||
// return (
|
||||
// validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
|
||||
// and validator.effective_balance == MAX_EFFECTIVE_BALANCE
|
||||
// )
|
||||
func IsEligibleForActivationQueue(validator *ethpb.Validator) bool {
|
||||
return isEligibileForActivationQueue(validator.ActivationEligibilityEpoch, validator.EffectiveBalance)
|
||||
}
|
||||
@@ -351,16 +359,17 @@ func isEligibileForActivationQueue(activationEligibilityEpoch types.Epoch, effec
|
||||
// IsEligibleForActivation checks if the validator is eligible for activation.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def is_eligible_for_activation(state: BeaconState, validator: Validator) -> bool:
|
||||
// """
|
||||
// Check if ``validator`` is eligible for activation.
|
||||
// """
|
||||
// return (
|
||||
// # Placement in queue is finalized
|
||||
// validator.activation_eligibility_epoch <= state.finalized_checkpoint.epoch
|
||||
// # Has not yet been activated
|
||||
// and validator.activation_epoch == FAR_FUTURE_EPOCH
|
||||
// )
|
||||
//
|
||||
// def is_eligible_for_activation(state: BeaconState, validator: Validator) -> bool:
|
||||
// """
|
||||
// Check if ``validator`` is eligible for activation.
|
||||
// """
|
||||
// return (
|
||||
// # Placement in queue is finalized
|
||||
// validator.activation_eligibility_epoch <= state.finalized_checkpoint.epoch
|
||||
// # Has not yet been activated
|
||||
// and validator.activation_epoch == FAR_FUTURE_EPOCH
|
||||
// )
|
||||
func IsEligibleForActivation(state state.ReadOnlyCheckpoint, validator *ethpb.Validator) bool {
|
||||
finalizedEpoch := state.FinalizedCheckpointEpoch()
|
||||
return isEligibleForActivation(validator.ActivationEligibilityEpoch, validator.ActivationEpoch, finalizedEpoch)
|
||||
|
||||
@@ -25,36 +25,37 @@ import (
|
||||
// https://github.com/ethereum/consensus-specs/blob/master/specs/phase0/weak-subjectivity.md#calculating-the-weak-subjectivity-period
|
||||
//
|
||||
// def compute_weak_subjectivity_period(state: BeaconState) -> uint64:
|
||||
// """
|
||||
// Returns the weak subjectivity period for the current ``state``.
|
||||
// This computation takes into account the effect of:
|
||||
// - validator set churn (bounded by ``get_validator_churn_limit()`` per epoch), and
|
||||
// - validator balance top-ups (bounded by ``MAX_DEPOSITS * SLOTS_PER_EPOCH`` per epoch).
|
||||
// A detailed calculation can be found at:
|
||||
// https://github.com/runtimeverification/beacon-chain-verification/blob/master/weak-subjectivity/weak-subjectivity-analysis.pdf
|
||||
// """
|
||||
// ws_period = MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
||||
// N = len(get_active_validator_indices(state, get_current_epoch(state)))
|
||||
// t = get_total_active_balance(state) // N // ETH_TO_GWEI
|
||||
// T = MAX_EFFECTIVE_BALANCE // ETH_TO_GWEI
|
||||
// delta = get_validator_churn_limit(state)
|
||||
// Delta = MAX_DEPOSITS * SLOTS_PER_EPOCH
|
||||
// D = SAFETY_DECAY
|
||||
//
|
||||
// if T * (200 + 3 * D) < t * (200 + 12 * D):
|
||||
// epochs_for_validator_set_churn = (
|
||||
// N * (t * (200 + 12 * D) - T * (200 + 3 * D)) // (600 * delta * (2 * t + T))
|
||||
// )
|
||||
// epochs_for_balance_top_ups = (
|
||||
// N * (200 + 3 * D) // (600 * Delta)
|
||||
// )
|
||||
// ws_period += max(epochs_for_validator_set_churn, epochs_for_balance_top_ups)
|
||||
// else:
|
||||
// ws_period += (
|
||||
// 3 * N * D * t // (200 * Delta * (T - t))
|
||||
// )
|
||||
// """
|
||||
// Returns the weak subjectivity period for the current ``state``.
|
||||
// This computation takes into account the effect of:
|
||||
// - validator set churn (bounded by ``get_validator_churn_limit()`` per epoch), and
|
||||
// - validator balance top-ups (bounded by ``MAX_DEPOSITS * SLOTS_PER_EPOCH`` per epoch).
|
||||
// A detailed calculation can be found at:
|
||||
// https://github.com/runtimeverification/beacon-chain-verification/blob/master/weak-subjectivity/weak-subjectivity-analysis.pdf
|
||||
// """
|
||||
// ws_period = MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
||||
// N = len(get_active_validator_indices(state, get_current_epoch(state)))
|
||||
// t = get_total_active_balance(state) // N // ETH_TO_GWEI
|
||||
// T = MAX_EFFECTIVE_BALANCE // ETH_TO_GWEI
|
||||
// delta = get_validator_churn_limit(state)
|
||||
// Delta = MAX_DEPOSITS * SLOTS_PER_EPOCH
|
||||
// D = SAFETY_DECAY
|
||||
//
|
||||
// return ws_period
|
||||
// if T * (200 + 3 * D) < t * (200 + 12 * D):
|
||||
// epochs_for_validator_set_churn = (
|
||||
// N * (t * (200 + 12 * D) - T * (200 + 3 * D)) // (600 * delta * (2 * t + T))
|
||||
// )
|
||||
// epochs_for_balance_top_ups = (
|
||||
// N * (200 + 3 * D) // (600 * Delta)
|
||||
// )
|
||||
// ws_period += max(epochs_for_validator_set_churn, epochs_for_balance_top_ups)
|
||||
// else:
|
||||
// ws_period += (
|
||||
// 3 * N * D * t // (200 * Delta * (T - t))
|
||||
// )
|
||||
//
|
||||
// return ws_period
|
||||
func ComputeWeakSubjectivityPeriod(ctx context.Context, st state.ReadOnlyBeaconState, cfg *params.BeaconChainConfig) (types.Epoch, error) {
|
||||
// Weak subjectivity period cannot be smaller than withdrawal delay.
|
||||
wsp := uint64(cfg.MinValidatorWithdrawabilityDelay)
|
||||
@@ -114,14 +115,15 @@ func ComputeWeakSubjectivityPeriod(ctx context.Context, st state.ReadOnlyBeaconS
|
||||
// https://github.com/ethereum/consensus-specs/blob/master/specs/phase0/weak-subjectivity.md#checking-for-stale-weak-subjectivity-checkpoint
|
||||
//
|
||||
// def is_within_weak_subjectivity_period(store: Store, ws_state: BeaconState, ws_checkpoint: Checkpoint) -> bool:
|
||||
// # Clients may choose to validate the input state against the input Weak Subjectivity Checkpoint
|
||||
// assert ws_state.latest_block_header.state_root == ws_checkpoint.root
|
||||
// assert compute_epoch_at_slot(ws_state.slot) == ws_checkpoint.epoch
|
||||
//
|
||||
// ws_period = compute_weak_subjectivity_period(ws_state)
|
||||
// ws_state_epoch = compute_epoch_at_slot(ws_state.slot)
|
||||
// current_epoch = compute_epoch_at_slot(get_current_slot(store))
|
||||
// return current_epoch <= ws_state_epoch + ws_period
|
||||
// # Clients may choose to validate the input state against the input Weak Subjectivity Checkpoint
|
||||
// assert ws_state.latest_block_header.state_root == ws_checkpoint.root
|
||||
// assert compute_epoch_at_slot(ws_state.slot) == ws_checkpoint.epoch
|
||||
//
|
||||
// ws_period = compute_weak_subjectivity_period(ws_state)
|
||||
// ws_state_epoch = compute_epoch_at_slot(ws_state.slot)
|
||||
// current_epoch = compute_epoch_at_slot(get_current_slot(store))
|
||||
// return current_epoch <= ws_state_epoch + ws_period
|
||||
func IsWithinWeakSubjectivityPeriod(
|
||||
ctx context.Context, currentEpoch types.Epoch, wsState state.ReadOnlyBeaconState, wsStateRoot [fieldparams.RootLength]byte, wsEpoch types.Epoch, cfg *params.BeaconChainConfig) (bool, error) {
|
||||
// Make sure that incoming objects are not nil.
|
||||
|
||||
@@ -10,13 +10,14 @@ import (
|
||||
// Domain returns the domain version for BLS private key to sign and verify.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_domain(state: BeaconState, domain_type: DomainType, epoch: Epoch=None) -> Domain:
|
||||
// """
|
||||
// Return the signature domain (fork version concatenated with domain type) of a message.
|
||||
// """
|
||||
// epoch = get_current_epoch(state) if epoch is None else epoch
|
||||
// fork_version = state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version
|
||||
// return compute_domain(domain_type, fork_version, state.genesis_validators_root)
|
||||
//
|
||||
// def get_domain(state: BeaconState, domain_type: DomainType, epoch: Epoch=None) -> Domain:
|
||||
// """
|
||||
// Return the signature domain (fork version concatenated with domain type) of a message.
|
||||
// """
|
||||
// epoch = get_current_epoch(state) if epoch is None else epoch
|
||||
// fork_version = state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version
|
||||
// return compute_domain(domain_type, fork_version, state.genesis_validators_root)
|
||||
func Domain(fork *eth.Fork, epoch types.Epoch, domainType [bls.DomainByteLength]byte, genesisRoot []byte) ([]byte, error) {
|
||||
if fork == nil {
|
||||
return []byte{}, errors.New("nil fork or domain type")
|
||||
|
||||
@@ -37,14 +37,15 @@ func ComputeDomainAndSign(st state.ReadOnlyBeaconState, epoch types.Epoch, obj f
|
||||
// ComputeSigningRoot computes the root of the object by calculating the hash tree root of the signing data with the given domain.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def compute_signing_root(ssz_object: SSZObject, domain: Domain) -> Root:
|
||||
// """
|
||||
// Return the signing root for the corresponding signing data.
|
||||
// """
|
||||
// return hash_tree_root(SigningData(
|
||||
// object_root=hash_tree_root(ssz_object),
|
||||
// domain=domain,
|
||||
// ))
|
||||
//
|
||||
// def compute_signing_root(ssz_object: SSZObject, domain: Domain) -> Root:
|
||||
// """
|
||||
// Return the signing root for the corresponding signing data.
|
||||
// """
|
||||
// return hash_tree_root(SigningData(
|
||||
// object_root=hash_tree_root(ssz_object),
|
||||
// domain=domain,
|
||||
// ))
|
||||
func ComputeSigningRoot(object fssz.HashRoot, domain []byte) ([32]byte, error) {
|
||||
return signingData(object.HashTreeRoot, domain)
|
||||
}
|
||||
@@ -160,15 +161,16 @@ func BlockSignatureBatch(pub, signature, domain []byte, rootFunc func() ([32]byt
|
||||
// array as the fork version.
|
||||
//
|
||||
// def compute_domain(domain_type: DomainType, fork_version: Version=None, genesis_validators_root: Root=None) -> Domain:
|
||||
// """
|
||||
// Return the domain for the ``domain_type`` and ``fork_version``.
|
||||
// """
|
||||
// if fork_version is None:
|
||||
// fork_version = GENESIS_FORK_VERSION
|
||||
// if genesis_validators_root is None:
|
||||
// genesis_validators_root = Root() # all bytes zero by default
|
||||
// fork_data_root = compute_fork_data_root(fork_version, genesis_validators_root)
|
||||
// return Domain(domain_type + fork_data_root[:28])
|
||||
//
|
||||
// """
|
||||
// Return the domain for the ``domain_type`` and ``fork_version``.
|
||||
// """
|
||||
// if fork_version is None:
|
||||
// fork_version = GENESIS_FORK_VERSION
|
||||
// if genesis_validators_root is None:
|
||||
// genesis_validators_root = Root() # all bytes zero by default
|
||||
// fork_data_root = compute_fork_data_root(fork_version, genesis_validators_root)
|
||||
// return Domain(domain_type + fork_data_root[:28])
|
||||
func ComputeDomain(domainType [DomainByteLength]byte, forkVersion, genesisValidatorsRoot []byte) ([]byte, error) {
|
||||
if forkVersion == nil {
|
||||
forkVersion = params.BeaconConfig().GenesisForkVersion
|
||||
@@ -195,19 +197,20 @@ func domain(domainType [DomainByteLength]byte, forkDataRoot []byte) []byte {
|
||||
return b
|
||||
}
|
||||
|
||||
// this returns the 32byte fork data root for the ``current_version`` and ``genesis_validators_root``.
|
||||
// this returns the 32byte fork data root for the “current_version“ and “genesis_validators_root“.
|
||||
// This is used primarily in signature domains to avoid collisions across forks/chains.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def compute_fork_data_root(current_version: Version, genesis_validators_root: Root) -> Root:
|
||||
// """
|
||||
// Return the 32-byte fork data root for the ``current_version`` and ``genesis_validators_root``.
|
||||
// This is used primarily in signature domains to avoid collisions across forks/chains.
|
||||
// """
|
||||
// return hash_tree_root(ForkData(
|
||||
// current_version=current_version,
|
||||
// genesis_validators_root=genesis_validators_root,
|
||||
// ))
|
||||
//
|
||||
// def compute_fork_data_root(current_version: Version, genesis_validators_root: Root) -> Root:
|
||||
// """
|
||||
// Return the 32-byte fork data root for the ``current_version`` and ``genesis_validators_root``.
|
||||
// This is used primarily in signature domains to avoid collisions across forks/chains.
|
||||
// """
|
||||
// return hash_tree_root(ForkData(
|
||||
// current_version=current_version,
|
||||
// genesis_validators_root=genesis_validators_root,
|
||||
// ))
|
||||
func computeForkDataRoot(version, root []byte) ([32]byte, error) {
|
||||
r, err := (ðpb.ForkData{
|
||||
CurrentVersion: version,
|
||||
@@ -222,13 +225,14 @@ func computeForkDataRoot(version, root []byte) ([32]byte, error) {
|
||||
// ComputeForkDigest returns the fork for the current version and genesis validators root
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def compute_fork_digest(current_version: Version, genesis_validators_root: Root) -> ForkDigest:
|
||||
// """
|
||||
// Return the 4-byte fork digest for the ``current_version`` and ``genesis_validators_root``.
|
||||
// This is a digest primarily used for domain separation on the p2p layer.
|
||||
// 4-bytes suffices for practical separation of forks/chains.
|
||||
// """
|
||||
// return ForkDigest(compute_fork_data_root(current_version, genesis_validators_root)[:4])
|
||||
//
|
||||
// def compute_fork_digest(current_version: Version, genesis_validators_root: Root) -> ForkDigest:
|
||||
// """
|
||||
// Return the 4-byte fork digest for the ``current_version`` and ``genesis_validators_root``.
|
||||
// This is a digest primarily used for domain separation on the p2p layer.
|
||||
// 4-bytes suffices for practical separation of forks/chains.
|
||||
// """
|
||||
// return ForkDigest(compute_fork_data_root(current_version, genesis_validators_root)[:4])
|
||||
func ComputeForkDigest(version, genesisValidatorsRoot []byte) ([4]byte, error) {
|
||||
dataRoot, err := computeForkDataRoot(version, genesisValidatorsRoot)
|
||||
if err != nil {
|
||||
|
||||
@@ -12,11 +12,12 @@ import (
|
||||
// the slot number stored in beacon state.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_current_epoch(state: BeaconState) -> Epoch:
|
||||
// """
|
||||
// Return the current epoch.
|
||||
// """
|
||||
// return compute_epoch_at_slot(state.slot)
|
||||
//
|
||||
// def get_current_epoch(state: BeaconState) -> Epoch:
|
||||
// """
|
||||
// Return the current epoch.
|
||||
// """
|
||||
// return compute_epoch_at_slot(state.slot)
|
||||
func CurrentEpoch(state state.ReadOnlyBeaconState) types.Epoch {
|
||||
return slots.ToEpoch(state.Slot())
|
||||
}
|
||||
@@ -26,12 +27,13 @@ func CurrentEpoch(state state.ReadOnlyBeaconState) types.Epoch {
|
||||
// underflow condition.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_previous_epoch(state: BeaconState) -> Epoch:
|
||||
// """`
|
||||
// Return the previous epoch (unless the current epoch is ``GENESIS_EPOCH``).
|
||||
// """
|
||||
// current_epoch = get_current_epoch(state)
|
||||
// return GENESIS_EPOCH if current_epoch == GENESIS_EPOCH else Epoch(current_epoch - 1)
|
||||
//
|
||||
// def get_previous_epoch(state: BeaconState) -> Epoch:
|
||||
// """`
|
||||
// Return the previous epoch (unless the current epoch is ``GENESIS_EPOCH``).
|
||||
// """
|
||||
// current_epoch = get_current_epoch(state)
|
||||
// return GENESIS_EPOCH if current_epoch == GENESIS_EPOCH else Epoch(current_epoch - 1)
|
||||
func PrevEpoch(state state.ReadOnlyBeaconState) types.Epoch {
|
||||
currentEpoch := CurrentEpoch(state)
|
||||
if currentEpoch == 0 {
|
||||
@@ -70,11 +72,21 @@ func CanUpgradeToBellatrix(slot types.Slot) bool {
|
||||
return epochStart && bellatrixEpoch
|
||||
}
|
||||
|
||||
// CanUpgradeToCapella returns true if the input `slot` can upgrade to Capella.
|
||||
// Spec code:
|
||||
// If state.slot % SLOTS_PER_EPOCH == 0 and compute_epoch_at_slot(state.slot) == CAPELLA_FORK_EPOCH
|
||||
func CanUpgradeToCapella(slot types.Slot) bool {
|
||||
epochStart := slots.IsEpochStart(slot)
|
||||
capellaEpoch := slots.ToEpoch(slot) == params.BeaconConfig().CapellaForkEpoch
|
||||
return epochStart && capellaEpoch
|
||||
}
|
||||
|
||||
// CanProcessEpoch checks the eligibility to process epoch.
|
||||
// The epoch can be processed at the end of the last slot of every epoch.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// If (state.slot + 1) % SLOTS_PER_EPOCH == 0:
|
||||
//
|
||||
// If (state.slot + 1) % SLOTS_PER_EPOCH == 0:
|
||||
func CanProcessEpoch(state state.ReadOnlyBeaconState) bool {
|
||||
return (state.Slot()+1)%params.BeaconConfig().SlotsPerEpoch == 0
|
||||
}
|
||||
|
||||
@@ -263,3 +263,38 @@ func TestAltairCompatible(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanUpgradeToCapella(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
bc := params.BeaconConfig()
|
||||
bc.CapellaForkEpoch = 5
|
||||
params.OverrideBeaconConfig(bc)
|
||||
tests := []struct {
|
||||
name string
|
||||
slot types.Slot
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "not epoch start",
|
||||
slot: 1,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "not capella epoch",
|
||||
slot: params.BeaconConfig().SlotsPerEpoch,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "capella epoch",
|
||||
slot: types.Slot(params.BeaconConfig().CapellaForkEpoch) * params.BeaconConfig().SlotsPerEpoch,
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := time.CanUpgradeToCapella(tt.slot); got != tt.want {
|
||||
t.Errorf("CanUpgradeToCapella() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ go_library(
|
||||
"log.go",
|
||||
"skip_slot_cache.go",
|
||||
"state.go",
|
||||
"state-bellatrix.go",
|
||||
"trailing_slot_state_cache.go",
|
||||
"transition.go",
|
||||
"transition_no_verify_sig.go",
|
||||
@@ -25,6 +26,7 @@ go_library(
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/core/altair:go_default_library",
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/capella:go_default_library",
|
||||
"//beacon-chain/core/epoch:go_default_library",
|
||||
"//beacon-chain/core/epoch/precompute:go_default_library",
|
||||
"//beacon-chain/core/execution:go_default_library",
|
||||
@@ -35,6 +37,7 @@ go_library(
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/state-native:go_default_library",
|
||||
"//beacon-chain/state/stateutil:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
"//consensus-types/interfaces:go_default_library",
|
||||
@@ -44,6 +47,7 @@ go_library(
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//math:go_default_library",
|
||||
"//monitoring/tracing:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
|
||||
@@ -257,3 +257,43 @@ func createFullBellatrixBlockWithOperations(t *testing.T) (state.BeaconState,
|
||||
beaconState, _ := util.DeterministicGenesisStateBellatrix(t, 32)
|
||||
return beaconState, blk
|
||||
}
|
||||
|
||||
func createFullCapellaBlockWithOperations(t *testing.T) (state.BeaconState,
|
||||
*ethpb.SignedBeaconBlockCapella) {
|
||||
_, bellatrixBlk := createFullBellatrixBlockWithOperations(t)
|
||||
blk := ðpb.SignedBeaconBlockCapella{
|
||||
Block: ðpb.BeaconBlockCapella{
|
||||
Slot: bellatrixBlk.Block.Slot,
|
||||
ProposerIndex: bellatrixBlk.Block.ProposerIndex,
|
||||
ParentRoot: bellatrixBlk.Block.ParentRoot,
|
||||
StateRoot: bellatrixBlk.Block.StateRoot,
|
||||
Body: ðpb.BeaconBlockBodyCapella{
|
||||
RandaoReveal: bellatrixBlk.Block.Body.RandaoReveal,
|
||||
Eth1Data: bellatrixBlk.Block.Body.Eth1Data,
|
||||
Graffiti: bellatrixBlk.Block.Body.Graffiti,
|
||||
ProposerSlashings: bellatrixBlk.Block.Body.ProposerSlashings,
|
||||
AttesterSlashings: bellatrixBlk.Block.Body.AttesterSlashings,
|
||||
Attestations: bellatrixBlk.Block.Body.Attestations,
|
||||
Deposits: bellatrixBlk.Block.Body.Deposits,
|
||||
VoluntaryExits: bellatrixBlk.Block.Body.VoluntaryExits,
|
||||
SyncAggregate: bellatrixBlk.Block.Body.SyncAggregate,
|
||||
ExecutionPayload: &enginev1.ExecutionPayloadCapella{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: bytesutil.PadTo([]byte{1, 2, 3, 4}, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
Transactions: make([][]byte, 0),
|
||||
Withdrawals: make([]*enginev1.Withdrawal, 0),
|
||||
ExtraData: make([]byte, 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
Signature: nil,
|
||||
}
|
||||
beaconState, _ := util.DeterministicGenesisStateCapella(t, 32)
|
||||
return beaconState, blk
|
||||
}
|
||||
|
||||
283
beacon-chain/core/transition/state-bellatrix.go
Normal file
283
beacon-chain/core/transition/state-bellatrix.go
Normal file
@@ -0,0 +1,283 @@
|
||||
package transition
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/altair"
|
||||
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
|
||||
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
b "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
state_native "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/state-native"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state/stateutil"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// GenesisBeaconStateBellatrix gets called when MinGenesisActiveValidatorCount count of
|
||||
// full deposits were made to the deposit contract and the ChainStart log gets emitted.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
//
|
||||
// def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32,
|
||||
// eth1_timestamp: uint64,
|
||||
// deposits: Sequence[Deposit]) -> BeaconState:
|
||||
// fork = Fork(
|
||||
// previous_version=GENESIS_FORK_VERSION,
|
||||
// current_version=GENESIS_FORK_VERSION,
|
||||
// epoch=GENESIS_EPOCH,
|
||||
// )
|
||||
// state = BeaconState(
|
||||
// genesis_time=eth1_timestamp + GENESIS_DELAY,
|
||||
// fork=fork,
|
||||
// eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))),
|
||||
// latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())),
|
||||
// randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy
|
||||
// )
|
||||
//
|
||||
// # Process deposits
|
||||
// leaves = list(map(lambda deposit: deposit.data, deposits))
|
||||
// for index, deposit in enumerate(deposits):
|
||||
// deposit_data_list = List[DepositData, 2**DEPOSIT_CONTRACT_TREE_DEPTH](*leaves[:index + 1])
|
||||
// state.eth1_data.deposit_root = hash_tree_root(deposit_data_list)
|
||||
// process_deposit(state, deposit)
|
||||
//
|
||||
// # Process activations
|
||||
// for index, validator in enumerate(state.validators):
|
||||
// balance = state.balances[index]
|
||||
// validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE)
|
||||
// if validator.effective_balance == MAX_EFFECTIVE_BALANCE:
|
||||
// validator.activation_eligibility_epoch = GENESIS_EPOCH
|
||||
// validator.activation_epoch = GENESIS_EPOCH
|
||||
//
|
||||
// # Set genesis validators root for domain separation and chain versioning
|
||||
// state.genesis_validators_root = hash_tree_root(state.validators)
|
||||
//
|
||||
// return state
|
||||
//
|
||||
// This method differs from the spec so as to process deposits beforehand instead of the end of the function.
|
||||
func GenesisBeaconStateBellatrix(ctx context.Context, deposits []*ethpb.Deposit, genesisTime uint64, eth1Data *ethpb.Eth1Data, ep *enginev1.ExecutionPayload) (state.BeaconState, error) {
|
||||
st, err := EmptyGenesisStateBellatrix()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Process initial deposits.
|
||||
st, err = helpers.UpdateGenesisEth1Data(st, deposits, eth1Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
st, err = b.ProcessPreGenesisDeposits(ctx, st, deposits)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process validator deposits")
|
||||
}
|
||||
|
||||
// After deposits have been processed, overwrite eth1data to what is passed in. This allows us to "pre-mine" validators
|
||||
// without the deposit root and count mismatching the real deposit contract.
|
||||
if err := st.SetEth1Data(eth1Data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := st.SetEth1DepositIndex(eth1Data.DepositCount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return OptimizedGenesisBeaconStateBellatrix(genesisTime, st, st.Eth1Data(), ep)
|
||||
}
|
||||
|
||||
// OptimizedGenesisBeaconState is used to create a state that has already processed deposits. This is to efficiently
|
||||
// create a mainnet state at chainstart.
|
||||
func OptimizedGenesisBeaconStateBellatrix(genesisTime uint64, preState state.BeaconState, eth1Data *ethpb.Eth1Data, ep *enginev1.ExecutionPayload) (state.BeaconState, error) {
|
||||
if eth1Data == nil {
|
||||
return nil, errors.New("no eth1data provided for genesis state")
|
||||
}
|
||||
|
||||
randaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
|
||||
for i := 0; i < len(randaoMixes); i++ {
|
||||
h := make([]byte, 32)
|
||||
copy(h, eth1Data.BlockHash)
|
||||
randaoMixes[i] = h
|
||||
}
|
||||
|
||||
zeroHash := params.BeaconConfig().ZeroHash[:]
|
||||
|
||||
activeIndexRoots := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
|
||||
for i := 0; i < len(activeIndexRoots); i++ {
|
||||
activeIndexRoots[i] = zeroHash
|
||||
}
|
||||
|
||||
blockRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
|
||||
for i := 0; i < len(blockRoots); i++ {
|
||||
blockRoots[i] = zeroHash
|
||||
}
|
||||
|
||||
stateRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
|
||||
for i := 0; i < len(stateRoots); i++ {
|
||||
stateRoots[i] = zeroHash
|
||||
}
|
||||
|
||||
slashings := make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector)
|
||||
|
||||
genesisValidatorsRoot, err := stateutil.ValidatorRegistryRoot(preState.Validators())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not hash tree root genesis validators %v", err)
|
||||
}
|
||||
|
||||
scores, err := preState.InactivityScores()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scoresMissing := len(preState.Validators()) - len(scores)
|
||||
if scoresMissing > 0 {
|
||||
for i := 0; i < scoresMissing; i++ {
|
||||
scores = append(scores, 0)
|
||||
}
|
||||
}
|
||||
wep, err := blocks.WrappedExecutionPayload(ep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eph, err := blocks.PayloadToHeader(wep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
st := ðpb.BeaconStateBellatrix{
|
||||
// Misc fields.
|
||||
Slot: 0,
|
||||
GenesisTime: genesisTime,
|
||||
GenesisValidatorsRoot: genesisValidatorsRoot[:],
|
||||
|
||||
Fork: ðpb.Fork{
|
||||
PreviousVersion: params.BeaconConfig().AltairForkVersion,
|
||||
CurrentVersion: params.BeaconConfig().BellatrixForkVersion,
|
||||
Epoch: 0,
|
||||
},
|
||||
|
||||
// Validator registry fields.
|
||||
Validators: preState.Validators(),
|
||||
Balances: preState.Balances(),
|
||||
|
||||
// Randomness and committees.
|
||||
RandaoMixes: randaoMixes,
|
||||
|
||||
// Finality.
|
||||
PreviousJustifiedCheckpoint: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
Root: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
CurrentJustifiedCheckpoint: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
Root: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
JustificationBits: []byte{0},
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
Root: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
|
||||
HistoricalRoots: [][]byte{},
|
||||
BlockRoots: blockRoots,
|
||||
StateRoots: stateRoots,
|
||||
Slashings: slashings,
|
||||
|
||||
// Eth1 data.
|
||||
Eth1Data: eth1Data,
|
||||
Eth1DataVotes: []*ethpb.Eth1Data{},
|
||||
Eth1DepositIndex: preState.Eth1DepositIndex(),
|
||||
LatestExecutionPayloadHeader: eph,
|
||||
InactivityScores: scores,
|
||||
}
|
||||
|
||||
bodyRoot, err := (ðpb.BeaconBlockBodyBellatrix{
|
||||
RandaoReveal: make([]byte, 96),
|
||||
Eth1Data: ðpb.Eth1Data{
|
||||
DepositRoot: make([]byte, 32),
|
||||
BlockHash: make([]byte, 32),
|
||||
},
|
||||
Graffiti: make([]byte, 32),
|
||||
SyncAggregate: ðpb.SyncAggregate{
|
||||
SyncCommitteeBits: make([]byte, fieldparams.SyncCommitteeLength/8),
|
||||
SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
},
|
||||
ExecutionPayload: &enginev1.ExecutionPayload{
|
||||
ParentHash: make([]byte, 32),
|
||||
FeeRecipient: make([]byte, 20),
|
||||
StateRoot: make([]byte, 32),
|
||||
ReceiptsRoot: make([]byte, 32),
|
||||
LogsBloom: make([]byte, 256),
|
||||
PrevRandao: make([]byte, 32),
|
||||
BaseFeePerGas: make([]byte, 32),
|
||||
BlockHash: make([]byte, 32),
|
||||
Transactions: make([][]byte, 0),
|
||||
},
|
||||
}).HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not hash tree root empty block body")
|
||||
}
|
||||
|
||||
st.LatestBlockHeader = ðpb.BeaconBlockHeader{
|
||||
ParentRoot: zeroHash,
|
||||
StateRoot: zeroHash,
|
||||
BodyRoot: bodyRoot[:],
|
||||
}
|
||||
|
||||
ist, err := state_native.InitializeFromProtoBellatrix(st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sc, err := altair.NextSyncCommittee(context.Background(), ist)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ist.SetNextSyncCommittee(sc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ist.SetCurrentSyncCommittee(sc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ist, nil
|
||||
}
|
||||
|
||||
// EmptyGenesisState returns an empty beacon state object.
|
||||
func EmptyGenesisStateBellatrix() (state.BeaconState, error) {
|
||||
st := ðpb.BeaconStateBellatrix{
|
||||
// Misc fields.
|
||||
Slot: 0,
|
||||
Fork: ðpb.Fork{
|
||||
PreviousVersion: params.BeaconConfig().AltairForkVersion,
|
||||
CurrentVersion: params.BeaconConfig().BellatrixForkVersion,
|
||||
Epoch: 0,
|
||||
},
|
||||
// Validator registry fields.
|
||||
Validators: []*ethpb.Validator{},
|
||||
Balances: []uint64{},
|
||||
|
||||
JustificationBits: []byte{0},
|
||||
HistoricalRoots: [][]byte{},
|
||||
|
||||
// Eth1 data.
|
||||
Eth1Data: ðpb.Eth1Data{},
|
||||
Eth1DataVotes: []*ethpb.Eth1Data{},
|
||||
Eth1DepositIndex: 0,
|
||||
LatestExecutionPayloadHeader: &enginev1.ExecutionPayloadHeader{
|
||||
ParentHash: make([]byte, 32),
|
||||
FeeRecipient: make([]byte, 20),
|
||||
StateRoot: make([]byte, 32),
|
||||
ReceiptsRoot: make([]byte, 32),
|
||||
LogsBloom: make([]byte, 256),
|
||||
PrevRandao: make([]byte, 32),
|
||||
BaseFeePerGas: make([]byte, 32),
|
||||
BlockHash: make([]byte, 32),
|
||||
TransactionsRoot: make([]byte, 32),
|
||||
},
|
||||
}
|
||||
|
||||
return state_native.InitializeFromProtoBellatrix(st)
|
||||
}
|
||||
@@ -17,41 +17,43 @@ import (
|
||||
// full deposits were made to the deposit contract and the ChainStart log gets emitted.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32,
|
||||
// eth1_timestamp: uint64,
|
||||
// deposits: Sequence[Deposit]) -> BeaconState:
|
||||
// fork = Fork(
|
||||
// previous_version=GENESIS_FORK_VERSION,
|
||||
// current_version=GENESIS_FORK_VERSION,
|
||||
// epoch=GENESIS_EPOCH,
|
||||
// )
|
||||
// state = BeaconState(
|
||||
// genesis_time=eth1_timestamp + GENESIS_DELAY,
|
||||
// fork=fork,
|
||||
// eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))),
|
||||
// latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())),
|
||||
// randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy
|
||||
// )
|
||||
//
|
||||
// # Process deposits
|
||||
// leaves = list(map(lambda deposit: deposit.data, deposits))
|
||||
// for index, deposit in enumerate(deposits):
|
||||
// deposit_data_list = List[DepositData, 2**DEPOSIT_CONTRACT_TREE_DEPTH](*leaves[:index + 1])
|
||||
// state.eth1_data.deposit_root = hash_tree_root(deposit_data_list)
|
||||
// process_deposit(state, deposit)
|
||||
// def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32,
|
||||
// eth1_timestamp: uint64,
|
||||
// deposits: Sequence[Deposit]) -> BeaconState:
|
||||
// fork = Fork(
|
||||
// previous_version=GENESIS_FORK_VERSION,
|
||||
// current_version=GENESIS_FORK_VERSION,
|
||||
// epoch=GENESIS_EPOCH,
|
||||
// )
|
||||
// state = BeaconState(
|
||||
// genesis_time=eth1_timestamp + GENESIS_DELAY,
|
||||
// fork=fork,
|
||||
// eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))),
|
||||
// latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())),
|
||||
// randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy
|
||||
// )
|
||||
//
|
||||
// # Process activations
|
||||
// for index, validator in enumerate(state.validators):
|
||||
// balance = state.balances[index]
|
||||
// validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE)
|
||||
// if validator.effective_balance == MAX_EFFECTIVE_BALANCE:
|
||||
// validator.activation_eligibility_epoch = GENESIS_EPOCH
|
||||
// validator.activation_epoch = GENESIS_EPOCH
|
||||
// # Process deposits
|
||||
// leaves = list(map(lambda deposit: deposit.data, deposits))
|
||||
// for index, deposit in enumerate(deposits):
|
||||
// deposit_data_list = List[DepositData, 2**DEPOSIT_CONTRACT_TREE_DEPTH](*leaves[:index + 1])
|
||||
// state.eth1_data.deposit_root = hash_tree_root(deposit_data_list)
|
||||
// process_deposit(state, deposit)
|
||||
//
|
||||
// # Set genesis validators root for domain separation and chain versioning
|
||||
// state.genesis_validators_root = hash_tree_root(state.validators)
|
||||
// # Process activations
|
||||
// for index, validator in enumerate(state.validators):
|
||||
// balance = state.balances[index]
|
||||
// validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE)
|
||||
// if validator.effective_balance == MAX_EFFECTIVE_BALANCE:
|
||||
// validator.activation_eligibility_epoch = GENESIS_EPOCH
|
||||
// validator.activation_epoch = GENESIS_EPOCH
|
||||
//
|
||||
// # Set genesis validators root for domain separation and chain versioning
|
||||
// state.genesis_validators_root = hash_tree_root(state.validators)
|
||||
//
|
||||
// return state
|
||||
//
|
||||
// return state
|
||||
// This method differs from the spec so as to process deposits beforehand instead of the end of the function.
|
||||
func GenesisBeaconState(ctx context.Context, deposits []*ethpb.Deposit, genesisTime uint64, eth1Data *ethpb.Eth1Data) (state.BeaconState, error) {
|
||||
st, err := EmptyGenesisState()
|
||||
@@ -211,12 +213,14 @@ func EmptyGenesisState() (state.BeaconState, error) {
|
||||
// if the minimum genesis time arrived already.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def is_valid_genesis_state(state: BeaconState) -> bool:
|
||||
// if state.genesis_time < MIN_GENESIS_TIME:
|
||||
// return False
|
||||
// if len(get_active_validator_indices(state, GENESIS_EPOCH)) < MIN_GENESIS_ACTIVE_VALIDATOR_COUNT:
|
||||
// return False
|
||||
// return True
|
||||
//
|
||||
// def is_valid_genesis_state(state: BeaconState) -> bool:
|
||||
// if state.genesis_time < MIN_GENESIS_TIME:
|
||||
// return False
|
||||
// if len(get_active_validator_indices(state, GENESIS_EPOCH)) < MIN_GENESIS_ACTIVE_VALIDATOR_COUNT:
|
||||
// return False
|
||||
// return True
|
||||
//
|
||||
// This method has been modified from the spec to allow whole states not to be saved
|
||||
// but instead only cache the relevant information.
|
||||
func IsValidGenesisState(chainStartDepositCount, currentTime uint64) bool {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/altair"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/capella"
|
||||
e "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/epoch"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/epoch/precompute"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/execution"
|
||||
@@ -32,18 +33,19 @@ import (
|
||||
// See: ExecuteStateTransitionNoVerifyAnySig
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def state_transition(state: BeaconState, signed_block: SignedBeaconBlock, validate_result: bool=True) -> None:
|
||||
// block = signed_block.message
|
||||
// # Process slots (including those with no blocks) since block
|
||||
// process_slots(state, block.slot)
|
||||
// # Verify signature
|
||||
// if validate_result:
|
||||
// assert verify_block_signature(state, signed_block)
|
||||
// # Process block
|
||||
// process_block(state, block)
|
||||
// # Verify state root
|
||||
// if validate_result:
|
||||
// assert block.state_root == hash_tree_root(state)
|
||||
//
|
||||
// def state_transition(state: BeaconState, signed_block: SignedBeaconBlock, validate_result: bool=True) -> None:
|
||||
// block = signed_block.message
|
||||
// # Process slots (including those with no blocks) since block
|
||||
// process_slots(state, block.slot)
|
||||
// # Verify signature
|
||||
// if validate_result:
|
||||
// assert verify_block_signature(state, signed_block)
|
||||
// # Process block
|
||||
// process_block(state, block)
|
||||
// # Verify state root
|
||||
// if validate_result:
|
||||
// assert block.state_root == hash_tree_root(state)
|
||||
func ExecuteStateTransition(
|
||||
ctx context.Context,
|
||||
state state.BeaconState,
|
||||
@@ -79,16 +81,16 @@ func ExecuteStateTransition(
|
||||
// It happens regardless if there's an incoming block or not.
|
||||
// Spec pseudocode definition:
|
||||
//
|
||||
// def process_slot(state: BeaconState) -> None:
|
||||
// # Cache state root
|
||||
// previous_state_root = hash_tree_root(state)
|
||||
// state.state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_state_root
|
||||
// # Cache latest block header state root
|
||||
// if state.latest_block_header.state_root == Bytes32():
|
||||
// state.latest_block_header.state_root = previous_state_root
|
||||
// # Cache block root
|
||||
// previous_block_root = hash_tree_root(state.latest_block_header)
|
||||
// state.block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_block_root
|
||||
// def process_slot(state: BeaconState) -> None:
|
||||
// # Cache state root
|
||||
// previous_state_root = hash_tree_root(state)
|
||||
// state.state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_state_root
|
||||
// # Cache latest block header state root
|
||||
// if state.latest_block_header.state_root == Bytes32():
|
||||
// state.latest_block_header.state_root = previous_state_root
|
||||
// # Cache block root
|
||||
// previous_block_root = hash_tree_root(state.latest_block_header)
|
||||
// state.block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_block_root
|
||||
func ProcessSlot(ctx context.Context, state state.BeaconState) (state.BeaconState, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "core.state.ProcessSlot")
|
||||
defer span.End()
|
||||
@@ -176,14 +178,15 @@ func ProcessSlotsIfPossible(ctx context.Context, state state.BeaconState, target
|
||||
// ProcessSlots process through skip slots and apply epoch transition when it's needed
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_slots(state: BeaconState, slot: Slot) -> None:
|
||||
// assert state.slot < slot
|
||||
// while state.slot < slot:
|
||||
// process_slot(state)
|
||||
// # Process epoch on the start slot of the next epoch
|
||||
// if (state.slot + 1) % SLOTS_PER_EPOCH == 0:
|
||||
// process_epoch(state)
|
||||
// state.slot = Slot(state.slot + 1)
|
||||
//
|
||||
// def process_slots(state: BeaconState, slot: Slot) -> None:
|
||||
// assert state.slot < slot
|
||||
// while state.slot < slot:
|
||||
// process_slot(state)
|
||||
// # Process epoch on the start slot of the next epoch
|
||||
// if (state.slot + 1) % SLOTS_PER_EPOCH == 0:
|
||||
// process_epoch(state)
|
||||
// state.slot = Slot(state.slot + 1)
|
||||
func ProcessSlots(ctx context.Context, state state.BeaconState, slot types.Slot) (state.BeaconState, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "core.state.ProcessSlots")
|
||||
defer span.End()
|
||||
@@ -255,7 +258,7 @@ func ProcessSlots(ctx context.Context, state state.BeaconState, slot types.Slot)
|
||||
tracing.AnnotateError(span, err)
|
||||
return nil, errors.Wrap(err, "could not process epoch with optimizations")
|
||||
}
|
||||
case version.Altair, version.Bellatrix:
|
||||
case version.Altair, version.Bellatrix, version.Capella:
|
||||
state, err = altair.ProcessEpoch(ctx, state)
|
||||
if err != nil {
|
||||
tracing.AnnotateError(span, err)
|
||||
@@ -285,6 +288,14 @@ func ProcessSlots(ctx context.Context, state state.BeaconState, slot types.Slot)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if time.CanUpgradeToCapella(state.Slot()) {
|
||||
state, err = capella.UpgradeToCapella(state)
|
||||
if err != nil {
|
||||
tracing.AnnotateError(span, err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if highestSlot < state.Slot() {
|
||||
|
||||
@@ -28,18 +28,19 @@ import (
|
||||
// This method also modifies the passed in state.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def state_transition(state: BeaconState, signed_block: SignedBeaconBlock, validate_result: bool=True) -> None:
|
||||
// block = signed_block.message
|
||||
// # Process slots (including those with no blocks) since block
|
||||
// process_slots(state, block.slot)
|
||||
// # Verify signature
|
||||
// if validate_result:
|
||||
// assert verify_block_signature(state, signed_block)
|
||||
// # Process block
|
||||
// process_block(state, block)
|
||||
// # Verify state root
|
||||
// if validate_result:
|
||||
// assert block.state_root == hash_tree_root(state)
|
||||
//
|
||||
// def state_transition(state: BeaconState, signed_block: SignedBeaconBlock, validate_result: bool=True) -> None:
|
||||
// block = signed_block.message
|
||||
// # Process slots (including those with no blocks) since block
|
||||
// process_slots(state, block.slot)
|
||||
// # Verify signature
|
||||
// if validate_result:
|
||||
// assert verify_block_signature(state, signed_block)
|
||||
// # Process block
|
||||
// process_block(state, block)
|
||||
// # Verify state root
|
||||
// if validate_result:
|
||||
// assert block.state_root == hash_tree_root(state)
|
||||
func ExecuteStateTransitionNoVerifyAnySig(
|
||||
ctx context.Context,
|
||||
st state.BeaconState,
|
||||
@@ -94,18 +95,19 @@ func ExecuteStateTransitionNoVerifyAnySig(
|
||||
// This is used for proposer to compute state root before proposing a new block, and this does not modify state.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def state_transition(state: BeaconState, signed_block: SignedBeaconBlock, validate_result: bool=True) -> None:
|
||||
// block = signed_block.message
|
||||
// # Process slots (including those with no blocks) since block
|
||||
// process_slots(state, block.slot)
|
||||
// # Verify signature
|
||||
// if validate_result:
|
||||
// assert verify_block_signature(state, signed_block)
|
||||
// # Process block
|
||||
// process_block(state, block)
|
||||
// # Verify state root
|
||||
// if validate_result:
|
||||
// assert block.state_root == hash_tree_root(state)
|
||||
//
|
||||
// def state_transition(state: BeaconState, signed_block: SignedBeaconBlock, validate_result: bool=True) -> None:
|
||||
// block = signed_block.message
|
||||
// # Process slots (including those with no blocks) since block
|
||||
// process_slots(state, block.slot)
|
||||
// # Verify signature
|
||||
// if validate_result:
|
||||
// assert verify_block_signature(state, signed_block)
|
||||
// # Process block
|
||||
// process_block(state, block)
|
||||
// # Verify state root
|
||||
// if validate_result:
|
||||
// assert block.state_root == hash_tree_root(state)
|
||||
func CalculateStateRoot(
|
||||
ctx context.Context,
|
||||
state state.BeaconState,
|
||||
@@ -151,11 +153,11 @@ func CalculateStateRoot(
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
//
|
||||
// def process_block(state: BeaconState, block: BeaconBlock) -> None:
|
||||
// process_block_header(state, block)
|
||||
// process_randao(state, block.body)
|
||||
// process_eth1_data(state, block.body)
|
||||
// process_operations(state, block.body)
|
||||
// def process_block(state: BeaconState, block: BeaconBlock) -> None:
|
||||
// process_block_header(state, block)
|
||||
// process_randao(state, block.body)
|
||||
// process_eth1_data(state, block.body)
|
||||
// process_operations(state, block.body)
|
||||
func ProcessBlockNoVerifyAnySig(
|
||||
ctx context.Context,
|
||||
st state.BeaconState,
|
||||
@@ -209,19 +211,19 @@ func ProcessBlockNoVerifyAnySig(
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
//
|
||||
// def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
|
||||
// # Verify that outstanding deposits are processed up to the maximum number of deposits
|
||||
// assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index)
|
||||
// def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
|
||||
// # Verify that outstanding deposits are processed up to the maximum number of deposits
|
||||
// assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index)
|
||||
//
|
||||
// def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None:
|
||||
// for operation in operations:
|
||||
// fn(state, operation)
|
||||
// def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None:
|
||||
// for operation in operations:
|
||||
// fn(state, operation)
|
||||
//
|
||||
// for_ops(body.proposer_slashings, process_proposer_slashing)
|
||||
// for_ops(body.attester_slashings, process_attester_slashing)
|
||||
// for_ops(body.attestations, process_attestation)
|
||||
// for_ops(body.deposits, process_deposit)
|
||||
// for_ops(body.voluntary_exits, process_voluntary_exit)
|
||||
// for_ops(body.proposer_slashings, process_proposer_slashing)
|
||||
// for_ops(body.attester_slashings, process_attester_slashing)
|
||||
// for_ops(body.attestations, process_attestation)
|
||||
// for_ops(body.deposits, process_deposit)
|
||||
// for_ops(body.voluntary_exits, process_voluntary_exit)
|
||||
func ProcessOperationsNoVerifyAttsSigs(
|
||||
ctx context.Context,
|
||||
state state.BeaconState,
|
||||
@@ -243,7 +245,7 @@ func ProcessOperationsNoVerifyAttsSigs(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case version.Altair, version.Bellatrix:
|
||||
case version.Altair, version.Bellatrix, version.Capella:
|
||||
state, err = altairOperations(ctx, state, signedBeaconBlock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -260,13 +262,14 @@ func ProcessOperationsNoVerifyAttsSigs(
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_block(state: BeaconState, block: BeaconBlock) -> None:
|
||||
// process_block_header(state, block)
|
||||
// if is_execution_enabled(state, block.body):
|
||||
// process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [New in Bellatrix]
|
||||
// process_randao(state, block.body)
|
||||
// process_eth1_data(state, block.body)
|
||||
// process_operations(state, block.body)
|
||||
// process_sync_aggregate(state, block.body.sync_aggregate)
|
||||
//
|
||||
// process_block_header(state, block)
|
||||
// if is_execution_enabled(state, block.body):
|
||||
// process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [New in Bellatrix]
|
||||
// process_randao(state, block.body)
|
||||
// process_eth1_data(state, block.body)
|
||||
// process_operations(state, block.body)
|
||||
// process_sync_aggregate(state, block.body.sync_aggregate)
|
||||
func ProcessBlockForStateRoot(
|
||||
ctx context.Context,
|
||||
state state.BeaconState,
|
||||
|
||||
@@ -171,6 +171,16 @@ func TestProcessOperationsNoVerifyAttsSigsBellatrix_OK(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestProcessOperationsNoVerifyAttsSigsCapella_OK(t *testing.T) {
|
||||
beaconState, block := createFullCapellaBlockWithOperations(t)
|
||||
wsb, err := blocks.NewSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
beaconState, err = transition.ProcessSlots(context.Background(), beaconState, wsb.Block().Slot())
|
||||
require.NoError(t, err)
|
||||
_, err = transition.ProcessOperationsNoVerifyAttsSigs(context.Background(), beaconState, wsb)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestCalculateStateRootAltair_OK(t *testing.T) {
|
||||
beaconState, block := createFullAltairBlockWithOperations(t)
|
||||
wsb, err := blocks.NewSignedBeaconBlock(block)
|
||||
|
||||
@@ -22,25 +22,26 @@ import (
|
||||
// validator with correct voluntary exit parameters.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:
|
||||
// """
|
||||
// Initiate the exit of the validator with index ``index``.
|
||||
// """
|
||||
// # Return if validator already initiated exit
|
||||
// validator = state.validators[index]
|
||||
// if validator.exit_epoch != FAR_FUTURE_EPOCH:
|
||||
// return
|
||||
//
|
||||
// # Compute exit queue epoch
|
||||
// exit_epochs = [v.exit_epoch for v in state.validators if v.exit_epoch != FAR_FUTURE_EPOCH]
|
||||
// exit_queue_epoch = max(exit_epochs + [compute_activation_exit_epoch(get_current_epoch(state))])
|
||||
// exit_queue_churn = len([v for v in state.validators if v.exit_epoch == exit_queue_epoch])
|
||||
// if exit_queue_churn >= get_validator_churn_limit(state):
|
||||
// exit_queue_epoch += Epoch(1)
|
||||
// def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:
|
||||
// """
|
||||
// Initiate the exit of the validator with index ``index``.
|
||||
// """
|
||||
// # Return if validator already initiated exit
|
||||
// validator = state.validators[index]
|
||||
// if validator.exit_epoch != FAR_FUTURE_EPOCH:
|
||||
// return
|
||||
//
|
||||
// # Set validator exit epoch and withdrawable epoch
|
||||
// validator.exit_epoch = exit_queue_epoch
|
||||
// validator.withdrawable_epoch = Epoch(validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY)
|
||||
// # Compute exit queue epoch
|
||||
// exit_epochs = [v.exit_epoch for v in state.validators if v.exit_epoch != FAR_FUTURE_EPOCH]
|
||||
// exit_queue_epoch = max(exit_epochs + [compute_activation_exit_epoch(get_current_epoch(state))])
|
||||
// exit_queue_churn = len([v for v in state.validators if v.exit_epoch == exit_queue_epoch])
|
||||
// if exit_queue_churn >= get_validator_churn_limit(state):
|
||||
// exit_queue_epoch += Epoch(1)
|
||||
//
|
||||
// # Set validator exit epoch and withdrawable epoch
|
||||
// validator.exit_epoch = exit_queue_epoch
|
||||
// validator.withdrawable_epoch = Epoch(validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY)
|
||||
func InitiateValidatorExit(ctx context.Context, s state.BeaconState, idx types.ValidatorIndex) (state.BeaconState, error) {
|
||||
validator, err := s.ValidatorAtIndex(idx)
|
||||
if err != nil {
|
||||
@@ -114,28 +115,29 @@ func InitiateValidatorExit(ctx context.Context, s state.BeaconState, idx types.V
|
||||
// the whistleblower's balance.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def slash_validator(state: BeaconState,
|
||||
// slashed_index: ValidatorIndex,
|
||||
// whistleblower_index: ValidatorIndex=None) -> None:
|
||||
// """
|
||||
// Slash the validator with index ``slashed_index``.
|
||||
// """
|
||||
// epoch = get_current_epoch(state)
|
||||
// initiate_validator_exit(state, slashed_index)
|
||||
// validator = state.validators[slashed_index]
|
||||
// validator.slashed = True
|
||||
// validator.withdrawable_epoch = max(validator.withdrawable_epoch, Epoch(epoch + EPOCHS_PER_SLASHINGS_VECTOR))
|
||||
// state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
|
||||
// decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT)
|
||||
//
|
||||
// # Apply proposer and whistleblower rewards
|
||||
// proposer_index = get_beacon_proposer_index(state)
|
||||
// if whistleblower_index is None:
|
||||
// whistleblower_index = proposer_index
|
||||
// whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
|
||||
// proposer_reward = Gwei(whistleblower_reward // PROPOSER_REWARD_QUOTIENT)
|
||||
// increase_balance(state, proposer_index, proposer_reward)
|
||||
// increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
|
||||
// def slash_validator(state: BeaconState,
|
||||
// slashed_index: ValidatorIndex,
|
||||
// whistleblower_index: ValidatorIndex=None) -> None:
|
||||
// """
|
||||
// Slash the validator with index ``slashed_index``.
|
||||
// """
|
||||
// epoch = get_current_epoch(state)
|
||||
// initiate_validator_exit(state, slashed_index)
|
||||
// validator = state.validators[slashed_index]
|
||||
// validator.slashed = True
|
||||
// validator.withdrawable_epoch = max(validator.withdrawable_epoch, Epoch(epoch + EPOCHS_PER_SLASHINGS_VECTOR))
|
||||
// state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
|
||||
// decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT)
|
||||
//
|
||||
// # Apply proposer and whistleblower rewards
|
||||
// proposer_index = get_beacon_proposer_index(state)
|
||||
// if whistleblower_index is None:
|
||||
// whistleblower_index = proposer_index
|
||||
// whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
|
||||
// proposer_reward = Gwei(whistleblower_reward // PROPOSER_REWARD_QUOTIENT)
|
||||
// increase_balance(state, proposer_index, proposer_reward)
|
||||
// increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
|
||||
func SlashValidator(
|
||||
ctx context.Context,
|
||||
s state.BeaconState,
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
// For example, one can specify a filter query for data by start epoch + end epoch + shard
|
||||
// for attestations, build a filter as follows, and respond to it accordingly:
|
||||
//
|
||||
// f := filters.NewFilter().SetStartEpoch(3).SetEndEpoch(5)
|
||||
// for k, v := range f.Filters() {
|
||||
// switch k {
|
||||
// case filters.StartEpoch:
|
||||
// // Verify data matches filter criteria...
|
||||
// case filters.EndEpoch:
|
||||
// // Verify data matches filter criteria...
|
||||
// }
|
||||
// }
|
||||
// f := filters.NewFilter().SetStartEpoch(3).SetEndEpoch(5)
|
||||
// for k, v := range f.Filters() {
|
||||
// switch k {
|
||||
// case filters.StartEpoch:
|
||||
// // Verify data matches filter criteria...
|
||||
// case filters.EndEpoch:
|
||||
// // Verify data matches filter criteria...
|
||||
// }
|
||||
// }
|
||||
package filters
|
||||
|
||||
import types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
|
||||
@@ -30,10 +30,7 @@ go_library(
|
||||
"wss.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/kv",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//tools:__subpackages__",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/db/filters:go_default_library",
|
||||
|
||||
@@ -789,6 +789,16 @@ func unmarshalBlock(_ context.Context, enc []byte) (interfaces.SignedBeaconBlock
|
||||
if err := rawBlock.UnmarshalSSZ(enc[len(bellatrixBlindKey):]); err != nil {
|
||||
return nil, errors.Wrap(err, "could not unmarshal blinded Bellatrix block")
|
||||
}
|
||||
case hasCapellaKey(enc):
|
||||
rawBlock = ðpb.SignedBeaconBlockCapella{}
|
||||
if err := rawBlock.UnmarshalSSZ(enc[len(capellaKey):]); err != nil {
|
||||
return nil, errors.Wrap(err, "could not unmarshal Capella block")
|
||||
}
|
||||
case hasCapellaBlindKey(enc):
|
||||
rawBlock = ðpb.SignedBlindedBeaconBlockCapella{}
|
||||
if err := rawBlock.UnmarshalSSZ(enc[len(capellaBlindKey):]); err != nil {
|
||||
return nil, errors.Wrap(err, "could not unmarshal blinded Capella block")
|
||||
}
|
||||
default:
|
||||
// Marshal block bytes to phase 0 beacon block.
|
||||
rawBlock = ðpb.SignedBeaconBlock{}
|
||||
@@ -828,6 +838,11 @@ func marshalBlock(_ context.Context, blk interfaces.SignedBeaconBlock) ([]byte,
|
||||
}
|
||||
}
|
||||
switch blockToSave.Version() {
|
||||
case version.Capella:
|
||||
if blockToSave.IsBlinded() {
|
||||
return snappy.Encode(nil, append(capellaBlindKey, encodedBlock...)), nil
|
||||
}
|
||||
return snappy.Encode(nil, append(capellaKey, encodedBlock...)), nil
|
||||
case version.Bellatrix:
|
||||
if blockToSave.IsBlinded() {
|
||||
return snappy.Encode(nil, append(bellatrixBlindKey, encodedBlock...)), nil
|
||||
|
||||
@@ -67,6 +67,28 @@ var blockTests = []struct {
|
||||
return blocks.NewSignedBeaconBlock(b)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "capella",
|
||||
newBlock: func(slot types.Slot, root []byte) (interfaces.SignedBeaconBlock, error) {
|
||||
b := util.NewBeaconBlockCapella()
|
||||
b.Block.Slot = slot
|
||||
if root != nil {
|
||||
b.Block.ParentRoot = root
|
||||
}
|
||||
return blocks.NewSignedBeaconBlock(b)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "capella blind",
|
||||
newBlock: func(slot types.Slot, root []byte) (interfaces.SignedBeaconBlock, error) {
|
||||
b := util.NewBlindedBeaconBlockCapella()
|
||||
b.Block.Slot = slot
|
||||
if root != nil {
|
||||
b.Block.ParentRoot = root
|
||||
}
|
||||
return blocks.NewSignedBeaconBlock(b)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestStore_SaveBackfillBlockRoot(t *testing.T) {
|
||||
@@ -332,6 +354,10 @@ func TestStore_BlocksCRUD_NoCache(t *testing.T) {
|
||||
wanted, err = blk.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if _, err := blk.PbCapellaBlock(); err == nil {
|
||||
wanted, err = blk.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
wantedPb, err := wanted.Proto()
|
||||
require.NoError(t, err)
|
||||
retrievedPb, err := retrievedBlock.Proto()
|
||||
@@ -551,6 +577,10 @@ func TestStore_SaveBlock_CanGetHighestAt(t *testing.T) {
|
||||
wanted, err = wanted.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if _, err := block1.PbCapellaBlock(); err == nil {
|
||||
wanted, err = wanted.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
wantedPb, err := wanted.Proto()
|
||||
require.NoError(t, err)
|
||||
bPb, err := b.Proto()
|
||||
@@ -569,6 +599,10 @@ func TestStore_SaveBlock_CanGetHighestAt(t *testing.T) {
|
||||
wanted2, err = block2.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if _, err := block2.PbCapellaBlock(); err == nil {
|
||||
wanted2, err = block2.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
wanted2Pb, err := wanted2.Proto()
|
||||
require.NoError(t, err)
|
||||
bPb, err = b.Proto()
|
||||
@@ -587,6 +621,10 @@ func TestStore_SaveBlock_CanGetHighestAt(t *testing.T) {
|
||||
wanted, err = wanted.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if _, err := block3.PbCapellaBlock(); err == nil {
|
||||
wanted, err = wanted.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
wantedPb, err = wanted.Proto()
|
||||
require.NoError(t, err)
|
||||
bPb, err = b.Proto()
|
||||
@@ -623,6 +661,10 @@ func TestStore_GenesisBlock_CanGetHighestAt(t *testing.T) {
|
||||
wanted, err = block1.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if _, err := block1.PbCapellaBlock(); err == nil {
|
||||
wanted, err = block1.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
wantedPb, err := wanted.Proto()
|
||||
require.NoError(t, err)
|
||||
bPb, err := b.Proto()
|
||||
@@ -640,6 +682,10 @@ func TestStore_GenesisBlock_CanGetHighestAt(t *testing.T) {
|
||||
wanted, err = genesisBlock.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if _, err := genesisBlock.PbCapellaBlock(); err == nil {
|
||||
wanted, err = genesisBlock.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
wantedPb, err = wanted.Proto()
|
||||
require.NoError(t, err)
|
||||
bPb, err = b.Proto()
|
||||
@@ -657,6 +703,10 @@ func TestStore_GenesisBlock_CanGetHighestAt(t *testing.T) {
|
||||
wanted, err = genesisBlock.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if _, err := genesisBlock.PbCapellaBlock(); err == nil {
|
||||
wanted, err = genesisBlock.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
wantedPb, err = wanted.Proto()
|
||||
require.NoError(t, err)
|
||||
bPb, err = b.Proto()
|
||||
@@ -753,6 +803,10 @@ func TestStore_BlocksBySlot_BlockRootsBySlot(t *testing.T) {
|
||||
wanted, err = b1.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if _, err := b1.PbCapellaBlock(); err == nil {
|
||||
wanted, err = b1.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
retrieved0Pb, err := retrievedBlocks[0].Proto()
|
||||
require.NoError(t, err)
|
||||
wantedPb, err := wanted.Proto()
|
||||
@@ -769,6 +823,10 @@ func TestStore_BlocksBySlot_BlockRootsBySlot(t *testing.T) {
|
||||
wanted, err = b2.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if _, err := b2.PbCapellaBlock(); err == nil {
|
||||
wanted, err = b2.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
retrieved0Pb, err = retrievedBlocks[0].Proto()
|
||||
require.NoError(t, err)
|
||||
wantedPb, err = wanted.Proto()
|
||||
@@ -779,6 +837,10 @@ func TestStore_BlocksBySlot_BlockRootsBySlot(t *testing.T) {
|
||||
wanted, err = b3.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if _, err := b3.PbCapellaBlock(); err == nil {
|
||||
wanted, err = b3.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
retrieved1Pb, err := retrievedBlocks[1].Proto()
|
||||
require.NoError(t, err)
|
||||
wantedPb, err = wanted.Proto()
|
||||
|
||||
@@ -76,7 +76,9 @@ func TestStore_IsFinalizedBlockGenesis(t *testing.T) {
|
||||
// Example:
|
||||
// 0 1 2 3 4 5 6 slot
|
||||
// a <- b <-- d <- e <- f <- g roots
|
||||
// ^- c
|
||||
//
|
||||
// ^- c
|
||||
//
|
||||
// Imagine that epochs are 2 slots and that epoch 1, 2, and 3 are finalized. Checkpoint roots would
|
||||
// be c, e, and g. In this scenario, c was a finalized checkpoint root but no block built upon it so
|
||||
// it should not be considered "final and canonical" in the view at slot 6.
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/ssz/detect"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks"
|
||||
dbIface "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/iface"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
state_native "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/state-native"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
consensusblocks "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
@@ -19,17 +16,25 @@ import (
|
||||
func (s *Store) SaveGenesisData(ctx context.Context, genesisState state.BeaconState) error {
|
||||
stateRoot, err := genesisState.HashTreeRoot(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "HashTreeRoot")
|
||||
}
|
||||
genesisBlk := blocks.NewGenesisBlock(stateRoot[:])
|
||||
genesisBlkRoot, err := genesisBlk.Block.HashTreeRoot()
|
||||
wsb, err := blocks.NewGenesisBlockForState(stateRoot, genesisState)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "NewGenesisBlockForState")
|
||||
}
|
||||
genesisBlkRoot, err := wsb.Block().HashTreeRoot()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get genesis block root")
|
||||
}
|
||||
wsb, err := consensusblocks.NewSignedBeaconBlock(genesisBlk)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not wrap genesis block")
|
||||
}
|
||||
/*
|
||||
lbhr, err := genesisState.LatestBlockHeader().HashTreeRoot()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to compute HTR of latest block header from genesis state")
|
||||
}
|
||||
if genesisBlkRoot != lbhr {
|
||||
return fmt.Errorf("state.latest_block_header=%#x does not match derived genessis block root=%#x", genesisBlkRoot, lbhr)
|
||||
}
|
||||
*/
|
||||
if err := s.SaveBlock(ctx, wsb); err != nil {
|
||||
return errors.Wrap(err, "could not save genesis block")
|
||||
}
|
||||
@@ -54,11 +59,11 @@ func (s *Store) SaveGenesisData(ctx context.Context, genesisState state.BeaconSt
|
||||
|
||||
// LoadGenesis loads a genesis state from a ssz-serialized byte slice, if no genesis exists already.
|
||||
func (s *Store) LoadGenesis(ctx context.Context, sb []byte) error {
|
||||
st := ðpb.BeaconState{}
|
||||
if err := st.UnmarshalSSZ(sb); err != nil {
|
||||
vu, err := detect.FromState(sb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gs, err := state_native.InitializeFromProtoUnsafePhase0(st)
|
||||
gs, err := vu.UnmarshalBeaconState(sb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -83,10 +88,6 @@ func (s *Store) LoadGenesis(ctx context.Context, sb []byte) error {
|
||||
return dbIface.ErrExistingGenesisState
|
||||
}
|
||||
|
||||
if !bytes.Equal(gs.Fork().CurrentVersion, params.BeaconConfig().GenesisForkVersion) {
|
||||
return fmt.Errorf("loaded genesis fork version (%#x) does not match config genesis "+
|
||||
"fork version (%#x)", gs.Fork().CurrentVersion, params.BeaconConfig().GenesisForkVersion)
|
||||
}
|
||||
return s.SaveGenesisData(ctx, gs)
|
||||
}
|
||||
|
||||
|
||||
@@ -30,3 +30,10 @@ func hasCapellaKey(enc []byte) bool {
|
||||
}
|
||||
return bytes.Equal(enc[:len(capellaKey)], capellaKey)
|
||||
}
|
||||
|
||||
func hasCapellaBlindKey(enc []byte) bool {
|
||||
if len(capellaBlindKey) >= len(enc) {
|
||||
return false
|
||||
}
|
||||
return bytes.Equal(enc[:len(capellaBlindKey)], capellaBlindKey)
|
||||
}
|
||||
|
||||
@@ -98,6 +98,38 @@ func KVStoreDatafilePath(dirPath string) string {
|
||||
return path.Join(dirPath, DatabaseFileName)
|
||||
}
|
||||
|
||||
var Buckets = [][]byte{
|
||||
attestationsBucket,
|
||||
blocksBucket,
|
||||
stateBucket,
|
||||
proposerSlashingsBucket,
|
||||
attesterSlashingsBucket,
|
||||
voluntaryExitsBucket,
|
||||
chainMetadataBucket,
|
||||
checkpointBucket,
|
||||
powchainBucket,
|
||||
stateSummaryBucket,
|
||||
stateValidatorsBucket,
|
||||
// Indices buckets.
|
||||
attestationHeadBlockRootBucket,
|
||||
attestationSourceRootIndicesBucket,
|
||||
attestationSourceEpochIndicesBucket,
|
||||
attestationTargetRootIndicesBucket,
|
||||
attestationTargetEpochIndicesBucket,
|
||||
blockSlotIndicesBucket,
|
||||
stateSlotIndicesBucket,
|
||||
blockParentRootIndicesBucket,
|
||||
finalizedBlockRootsIndexBucket,
|
||||
blockRootValidatorHashesBucket,
|
||||
// State management service bucket.
|
||||
newStateServiceCompatibleBucket,
|
||||
// Migrations
|
||||
migrationsBucket,
|
||||
|
||||
feeRecipientBucket,
|
||||
registrationBucket,
|
||||
}
|
||||
|
||||
// NewKVStore initializes a new boltDB key-value store at the directory
|
||||
// path specified, creates the kv-buckets based on the schema, and stores
|
||||
// an open connection db object as a property of the Store struct.
|
||||
@@ -155,38 +187,7 @@ func NewKVStore(ctx context.Context, dirPath string) (*Store, error) {
|
||||
ctx: ctx,
|
||||
}
|
||||
if err := kv.db.Update(func(tx *bolt.Tx) error {
|
||||
return createBuckets(
|
||||
tx,
|
||||
attestationsBucket,
|
||||
blocksBucket,
|
||||
stateBucket,
|
||||
proposerSlashingsBucket,
|
||||
attesterSlashingsBucket,
|
||||
voluntaryExitsBucket,
|
||||
chainMetadataBucket,
|
||||
checkpointBucket,
|
||||
powchainBucket,
|
||||
stateSummaryBucket,
|
||||
stateValidatorsBucket,
|
||||
// Indices buckets.
|
||||
attestationHeadBlockRootBucket,
|
||||
attestationSourceRootIndicesBucket,
|
||||
attestationSourceEpochIndicesBucket,
|
||||
attestationTargetRootIndicesBucket,
|
||||
attestationTargetEpochIndicesBucket,
|
||||
blockSlotIndicesBucket,
|
||||
stateSlotIndicesBucket,
|
||||
blockParentRootIndicesBucket,
|
||||
finalizedBlockRootsIndexBucket,
|
||||
blockRootValidatorHashesBucket,
|
||||
// State management service bucket.
|
||||
newStateServiceCompatibleBucket,
|
||||
// Migrations
|
||||
migrationsBucket,
|
||||
|
||||
feeRecipientBucket,
|
||||
registrationBucket,
|
||||
)
|
||||
return createBuckets(tx, Buckets...)
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -53,6 +53,8 @@ var (
|
||||
bellatrixKey = []byte("merge")
|
||||
bellatrixBlindKey = []byte("blind-bellatrix")
|
||||
capellaKey = []byte("capella")
|
||||
capellaBlindKey = []byte("blind-capella")
|
||||
|
||||
// block root included in the beacon state used by weak subjectivity initial sync
|
||||
originCheckpointBlockRootKey = []byte("origin-checkpoint-block-root")
|
||||
// block root tracking the progress of backfill, or pointing at genesis if backfill has not been initiated
|
||||
|
||||
@@ -129,7 +129,6 @@ go_test(
|
||||
"@com_github_ethereum_go_ethereum//core/beacon:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//trie:go_default_library",
|
||||
"@com_github_holiman_uint256//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
gethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types"
|
||||
@@ -133,14 +132,11 @@ func (c *headerCache) HeaderInfoByHeight(height *big.Int) (bool, *types.HeaderIn
|
||||
// size limit. This method should be called in sequential header number order if
|
||||
// the desired behavior is that the blocks with the highest header number should
|
||||
// be present in the cache.
|
||||
func (c *headerCache) AddHeader(hdr *gethTypes.Header) error {
|
||||
func (c *headerCache) AddHeader(hdr *types.HeaderInfo) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
hInfo, err := types.HeaderToHeaderInfo(hdr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hInfo := hdr.Copy()
|
||||
|
||||
if err := c.hashCache.AddIfNotPresent(hInfo); err != nil {
|
||||
return err
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
gethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
)
|
||||
@@ -44,34 +44,30 @@ func TestHeightKeyFn_InvalidObj(t *testing.T) {
|
||||
func TestBlockCache_byHash(t *testing.T) {
|
||||
cache := newHeaderCache()
|
||||
|
||||
header := &gethTypes.Header{
|
||||
ParentHash: common.HexToHash("0x12345"),
|
||||
Number: big.NewInt(55),
|
||||
header := &types.HeaderInfo{
|
||||
Number: big.NewInt(55),
|
||||
}
|
||||
|
||||
exists, _, err := cache.HeaderInfoByHash(header.Hash())
|
||||
exists, _, err := cache.HeaderInfoByHash(header.Hash)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, exists, "Expected block info not to exist in empty cache")
|
||||
|
||||
err = cache.AddHeader(header)
|
||||
require.NoError(t, err)
|
||||
|
||||
exists, fetchedInfo, err := cache.HeaderInfoByHash(header.Hash())
|
||||
exists, fetchedInfo, err := cache.HeaderInfoByHash(header.Hash)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, exists, "Expected headerInfo to exist")
|
||||
assert.Equal(t, 0, fetchedInfo.Number.Cmp(header.Number), "Expected fetched info number to be equal")
|
||||
assert.Equal(t, header.Hash(), fetchedInfo.Hash, "Expected hash to be equal")
|
||||
assert.Equal(t, header.Hash, fetchedInfo.Hash, "Expected hash to be equal")
|
||||
|
||||
}
|
||||
|
||||
func TestBlockCache_byHeight(t *testing.T) {
|
||||
cache := newHeaderCache()
|
||||
|
||||
header := &gethTypes.Header{
|
||||
ParentHash: common.HexToHash("0x12345"),
|
||||
Number: big.NewInt(55),
|
||||
header := &types.HeaderInfo{
|
||||
Number: big.NewInt(55),
|
||||
}
|
||||
|
||||
exists, _, err := cache.HeaderInfoByHeight(header.Number)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, exists, "Expected block info not to exist in empty cache")
|
||||
@@ -84,7 +80,7 @@ func TestBlockCache_byHeight(t *testing.T) {
|
||||
assert.Equal(t, true, exists, "Expected headerInfo to exist")
|
||||
|
||||
assert.Equal(t, 0, fetchedInfo.Number.Cmp(header.Number), "Expected fetched info number to be equal")
|
||||
assert.Equal(t, header.Hash(), fetchedInfo.Hash, "Expected hash to be equal")
|
||||
assert.Equal(t, header.Hash, fetchedInfo.Hash, "Expected hash to be equal")
|
||||
|
||||
}
|
||||
|
||||
@@ -92,8 +88,9 @@ func TestBlockCache_maxSize(t *testing.T) {
|
||||
cache := newHeaderCache()
|
||||
|
||||
for i := int64(0); i < int64(maxCacheSize+10); i++ {
|
||||
header := &gethTypes.Header{
|
||||
header := &types.HeaderInfo{
|
||||
Number: big.NewInt(i),
|
||||
Hash: common.Hash(bytesutil.ToBytes32(bytesutil.Bytes32(uint64(i)))),
|
||||
}
|
||||
err := cache.AddHeader(header)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -35,7 +35,7 @@ func (s *Service) BlockExists(ctx context.Context, hash common.Hash) (bool, *big
|
||||
return true, hdrInfo.Number, nil
|
||||
}
|
||||
span.AddAttributes(trace.BoolAttribute("blockCacheHit", false))
|
||||
header, err := s.eth1DataFetcher.HeaderByHash(ctx, hash)
|
||||
header, err := s.HeaderByHash(ctx, hash)
|
||||
if err != nil {
|
||||
return false, big.NewInt(0), errors.Wrap(err, "could not query block with given hash")
|
||||
}
|
||||
@@ -61,33 +61,33 @@ func (s *Service) BlockHashByHeight(ctx context.Context, height *big.Int) (commo
|
||||
}
|
||||
span.AddAttributes(trace.BoolAttribute("headerCacheHit", false))
|
||||
|
||||
if s.eth1DataFetcher == nil {
|
||||
err := errors.New("nil eth1DataFetcher")
|
||||
if s.rpcClient == nil {
|
||||
err := errors.New("nil rpc client")
|
||||
tracing.AnnotateError(span, err)
|
||||
return [32]byte{}, err
|
||||
}
|
||||
|
||||
header, err := s.eth1DataFetcher.HeaderByNumber(ctx, height)
|
||||
header, err := s.HeaderByNumber(ctx, height)
|
||||
if err != nil {
|
||||
return [32]byte{}, errors.Wrap(err, fmt.Sprintf("could not query header with height %d", height.Uint64()))
|
||||
}
|
||||
if err := s.headerCache.AddHeader(header); err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
return header.Hash(), nil
|
||||
return header.Hash, nil
|
||||
}
|
||||
|
||||
// BlockTimeByHeight fetches an eth1 block timestamp by its height.
|
||||
func (s *Service) BlockTimeByHeight(ctx context.Context, height *big.Int) (uint64, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "powchain.BlockTimeByHeight")
|
||||
defer span.End()
|
||||
if s.eth1DataFetcher == nil {
|
||||
err := errors.New("nil eth1DataFetcher")
|
||||
if s.rpcClient == nil {
|
||||
err := errors.New("nil rpc client")
|
||||
tracing.AnnotateError(span, err)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
header, err := s.eth1DataFetcher.HeaderByNumber(ctx, height)
|
||||
header, err := s.HeaderByNumber(ctx, height)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, fmt.Sprintf("could not query block with height %d", height.Uint64()))
|
||||
}
|
||||
@@ -106,7 +106,11 @@ func (s *Service) BlockByTimestamp(ctx context.Context, time uint64) (*types.Hea
|
||||
latestBlkTime := s.latestEth1Data.BlockTime
|
||||
s.latestEth1DataLock.RUnlock()
|
||||
|
||||
log.WithField("time (req val)", time).WithField("latestBlockTime", latestBlkTime).WithField("latestBlockHeight", s.latestEth1Data.BlockHeight).Info("BlockByTimestamp")
|
||||
if time > latestBlkTime {
|
||||
if latestBlkHeight < params.BeaconConfig().Eth1FollowDistance {
|
||||
|
||||
}
|
||||
return nil, errors.Wrap(errBlockTimeTooLate, fmt.Sprintf("(%d > %d)", time, latestBlkTime))
|
||||
}
|
||||
// Initialize a pointer to eth1 chain's history to start our search from.
|
||||
@@ -207,20 +211,17 @@ func (s *Service) retrieveHeaderInfo(ctx context.Context, bNum uint64) (*types.H
|
||||
return nil, err
|
||||
}
|
||||
if !exists {
|
||||
blk, err := s.eth1DataFetcher.HeaderByNumber(ctx, bn)
|
||||
hdr, err := s.HeaderByNumber(ctx, bn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if blk == nil {
|
||||
if hdr == nil {
|
||||
return nil, errors.Errorf("header with the number %d does not exist", bNum)
|
||||
}
|
||||
if err := s.headerCache.AddHeader(blk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info, err = types.HeaderToHeaderInfo(blk)
|
||||
if err != nil {
|
||||
if err := s.headerCache.AddHeader(hdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info = hdr
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
gethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
dbutil "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/testing"
|
||||
mockExecution "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/testing"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types"
|
||||
contracts "github.com/prysmaticlabs/prysm/v3/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/v3/contracts/deposit/mock"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
)
|
||||
|
||||
func setDefaultMocks(service *Service) *Service {
|
||||
service.eth1DataFetcher = &goodFetcher{}
|
||||
service.httpLogger = &goodLogger{}
|
||||
service.cfg.stateNotifier = &goodNotifier{}
|
||||
return service
|
||||
@@ -44,7 +43,6 @@ func TestLatestMainchainInfo_OK(t *testing.T) {
|
||||
|
||||
web3Service = setDefaultMocks(web3Service)
|
||||
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
|
||||
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
|
||||
|
||||
web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend)
|
||||
require.NoError(t, err)
|
||||
@@ -57,7 +55,7 @@ func TestLatestMainchainInfo_OK(t *testing.T) {
|
||||
<-exitRoutine
|
||||
}()
|
||||
|
||||
header, err := web3Service.eth1DataFetcher.HeaderByNumber(web3Service.ctx, nil)
|
||||
header, err := web3Service.HeaderByNumber(web3Service.ctx, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
tickerChan := make(chan time.Time)
|
||||
@@ -67,7 +65,7 @@ func TestLatestMainchainInfo_OK(t *testing.T) {
|
||||
exitRoutine <- true
|
||||
|
||||
assert.Equal(t, web3Service.latestEth1Data.BlockHeight, header.Number.Uint64())
|
||||
assert.Equal(t, hexutil.Encode(web3Service.latestEth1Data.BlockHash), header.Hash().Hex())
|
||||
assert.Equal(t, hexutil.Encode(web3Service.latestEth1Data.BlockHash), header.Hash.Hex())
|
||||
assert.Equal(t, web3Service.latestEth1Data.BlockTime, header.Time)
|
||||
}
|
||||
|
||||
@@ -85,6 +83,7 @@ func TestBlockHashByHeight_ReturnsHash(t *testing.T) {
|
||||
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
|
||||
|
||||
web3Service = setDefaultMocks(web3Service)
|
||||
web3Service.rpcClient = &mockExecution.RPCClient{}
|
||||
ctx := context.Background()
|
||||
|
||||
header := &gethTypes.Header{
|
||||
@@ -117,15 +116,17 @@ func TestBlockHashByHeight_ReturnsError_WhenNoEth1Client(t *testing.T) {
|
||||
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
|
||||
|
||||
web3Service = setDefaultMocks(web3Service)
|
||||
web3Service.eth1DataFetcher = nil
|
||||
web3Service.rpcClient = nil
|
||||
ctx := context.Background()
|
||||
|
||||
_, err = web3Service.BlockHashByHeight(ctx, big.NewInt(0))
|
||||
require.ErrorContains(t, "nil eth1DataFetcher", err)
|
||||
require.ErrorContains(t, "nil rpc client", err)
|
||||
}
|
||||
|
||||
func TestBlockExists_ValidHash(t *testing.T) {
|
||||
beaconDB := dbutil.SetupDB(t)
|
||||
testAcc, err := mock.Setup()
|
||||
require.NoError(t, err, "Unable to set up simulated backend")
|
||||
server, endpoint, err := mockExecution.SetupRPCServer()
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
@@ -138,16 +139,10 @@ func TestBlockExists_ValidHash(t *testing.T) {
|
||||
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
|
||||
|
||||
web3Service = setDefaultMocks(web3Service)
|
||||
|
||||
block := gethTypes.NewBlock(
|
||||
&gethTypes.Header{
|
||||
Number: big.NewInt(0),
|
||||
},
|
||||
[]*gethTypes.Transaction{},
|
||||
[]*gethTypes.Header{},
|
||||
[]*gethTypes.Receipt{},
|
||||
new(trie.Trie),
|
||||
)
|
||||
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
|
||||
testAcc.Backend.Commit()
|
||||
block, err := testAcc.Backend.BlockByNumber(context.Background(), big.NewInt(0))
|
||||
assert.NoError(t, err)
|
||||
|
||||
exists, height, err := web3Service.BlockExists(context.Background(), block.Hash())
|
||||
require.NoError(t, err, "Could not get block hash with given height")
|
||||
@@ -191,17 +186,15 @@ func TestBlockExists_UsesCachedBlockInfo(t *testing.T) {
|
||||
WithDatabase(beaconDB),
|
||||
)
|
||||
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
|
||||
// nil eth1DataFetcher would panic if cached value not used
|
||||
web3Service.eth1DataFetcher = nil
|
||||
|
||||
header := &gethTypes.Header{
|
||||
header := &types.HeaderInfo{
|
||||
Number: big.NewInt(0),
|
||||
}
|
||||
|
||||
err = web3Service.headerCache.AddHeader(header)
|
||||
require.NoError(t, err)
|
||||
|
||||
exists, height, err := web3Service.BlockExists(context.Background(), header.Hash())
|
||||
exists, height, err := web3Service.BlockExists(context.Background(), header.Hash)
|
||||
require.NoError(t, err, "Could not get block hash with given height")
|
||||
require.Equal(t, true, exists)
|
||||
require.Equal(t, 0, height.Cmp(header.Number))
|
||||
@@ -222,7 +215,7 @@ func TestService_BlockNumberByTimestamp(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, err)
|
||||
web3Service = setDefaultMocks(web3Service)
|
||||
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
|
||||
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
|
||||
|
||||
for i := 0; i < 200; i++ {
|
||||
testAcc.Backend.Commit()
|
||||
@@ -254,7 +247,7 @@ func TestService_BlockNumberByTimestampLessTargetTime(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, err)
|
||||
web3Service = setDefaultMocks(web3Service)
|
||||
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
|
||||
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
|
||||
|
||||
for i := 0; i < 200; i++ {
|
||||
testAcc.Backend.Commit()
|
||||
@@ -292,7 +285,7 @@ func TestService_BlockNumberByTimestampMoreTargetTime(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, err)
|
||||
web3Service = setDefaultMocks(web3Service)
|
||||
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
|
||||
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
|
||||
|
||||
for i := 0; i < 200; i++ {
|
||||
testAcc.Backend.Commit()
|
||||
@@ -329,9 +322,9 @@ func TestService_BlockTimeByHeight_ReturnsError_WhenNoEth1Client(t *testing.T) {
|
||||
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
|
||||
|
||||
web3Service = setDefaultMocks(web3Service)
|
||||
web3Service.eth1DataFetcher = nil
|
||||
web3Service.rpcClient = nil
|
||||
ctx := context.Background()
|
||||
|
||||
_, err = web3Service.BlockTimeByHeight(ctx, big.NewInt(0))
|
||||
require.ErrorContains(t, "nil eth1DataFetcher", err)
|
||||
require.ErrorContains(t, "nil rpc client", err)
|
||||
}
|
||||
|
||||
@@ -8,11 +8,13 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
gethRPC "github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
|
||||
@@ -212,15 +214,16 @@ func (s *Service) ExchangeTransitionConfiguration(
|
||||
//
|
||||
// Spec code:
|
||||
// def get_pow_block_at_terminal_total_difficulty(pow_chain: Dict[Hash32, PowBlock]) -> Optional[PowBlock]:
|
||||
// # `pow_chain` abstractly represents all blocks in the PoW chain
|
||||
// for block in pow_chain:
|
||||
// parent = pow_chain[block.parent_hash]
|
||||
// block_reached_ttd = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
||||
// parent_reached_ttd = parent.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
||||
// if block_reached_ttd and not parent_reached_ttd:
|
||||
// return block
|
||||
//
|
||||
// return None
|
||||
// # `pow_chain` abstractly represents all blocks in the PoW chain
|
||||
// for block in pow_chain:
|
||||
// parent = pow_chain[block.parent_hash]
|
||||
// block_reached_ttd = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
||||
// parent_reached_ttd = parent.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
||||
// if block_reached_ttd and not parent_reached_ttd:
|
||||
// return block
|
||||
//
|
||||
// return None
|
||||
func (s *Service) GetTerminalBlockHash(ctx context.Context, transitionTime uint64) ([]byte, bool, error) {
|
||||
ttd := new(big.Int)
|
||||
ttd.SetString(params.BeaconConfig().TerminalTotalDifficulty, 10)
|
||||
@@ -354,6 +357,26 @@ func (s *Service) ExecutionBlocksByHashes(ctx context.Context, hashes []common.H
|
||||
return execBlks, nil
|
||||
}
|
||||
|
||||
// HeaderByHash returns the relevant header details for the provided block hash.
|
||||
func (s *Service) HeaderByHash(ctx context.Context, hash common.Hash) (*types.HeaderInfo, error) {
|
||||
var hdr *types.HeaderInfo
|
||||
err := s.rpcClient.CallContext(ctx, &hdr, ExecutionBlockByHashMethod, hash, false /* no transactions */)
|
||||
if err == nil && hdr == nil {
|
||||
err = ethereum.NotFound
|
||||
}
|
||||
return hdr, err
|
||||
}
|
||||
|
||||
// HeaderByNumber returns the relevant header details for the provided block number.
|
||||
func (s *Service) HeaderByNumber(ctx context.Context, number *big.Int) (*types.HeaderInfo, error) {
|
||||
var hdr *types.HeaderInfo
|
||||
err := s.rpcClient.CallContext(ctx, &hdr, ExecutionBlockByNumberMethod, toBlockNumArg(number), false /* no transactions */)
|
||||
if err == nil && hdr == nil {
|
||||
err = ethereum.NotFound
|
||||
}
|
||||
return hdr, err
|
||||
}
|
||||
|
||||
// ReconstructFullBellatrixBlock takes in a blinded beacon block and reconstructs
|
||||
// a beacon block with a full execution payload via the engine API.
|
||||
func (s *Service) ReconstructFullBellatrixBlock(
|
||||
@@ -610,3 +633,22 @@ func buildEmptyExecutionPayload() *pb.ExecutionPayload {
|
||||
ExtraData: make([]byte, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func toBlockNumArg(number *big.Int) string {
|
||||
if number == nil {
|
||||
return "latest"
|
||||
}
|
||||
pending := big.NewInt(-1)
|
||||
if number.Cmp(pending) == 0 {
|
||||
return "pending"
|
||||
}
|
||||
finalized := big.NewInt(int64(gethRPC.FinalizedBlockNumber))
|
||||
if number.Cmp(finalized) == 0 {
|
||||
return "finalized"
|
||||
}
|
||||
safe := big.NewInt(int64(gethRPC.SafeBlockNumber))
|
||||
if number.Cmp(safe) == 0 {
|
||||
return "safe"
|
||||
}
|
||||
return hexutil.EncodeBig(number)
|
||||
}
|
||||
|
||||
@@ -11,10 +11,12 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
gethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
gethRPC "github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/pkg/errors"
|
||||
mocks "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/testing"
|
||||
@@ -24,6 +26,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
pb "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/util"
|
||||
"google.golang.org/protobuf/proto"
|
||||
@@ -36,6 +39,18 @@ var (
|
||||
_ = EngineCaller(&mocks.EngineClient{})
|
||||
)
|
||||
|
||||
type RPCClientBad struct {
|
||||
}
|
||||
|
||||
func (RPCClientBad) Close() {}
|
||||
func (RPCClientBad) BatchCall([]gethRPC.BatchElem) error {
|
||||
return errors.New("rpc client is not initialized")
|
||||
}
|
||||
|
||||
func (RPCClientBad) CallContext(context.Context, interface{}, string, ...interface{}) error {
|
||||
return ethereum.NotFound
|
||||
}
|
||||
|
||||
func TestClient_IPC(t *testing.T) {
|
||||
server := newTestIPCServer(t)
|
||||
defer server.Stop()
|
||||
@@ -1215,6 +1230,78 @@ func Test_fullPayloadFromExecutionBlock(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeaderByHash_NotFound(t *testing.T) {
|
||||
srv := &Service{}
|
||||
srv.rpcClient = RPCClientBad{}
|
||||
|
||||
_, err := srv.HeaderByHash(context.Background(), common.Hash([32]byte{}))
|
||||
assert.Equal(t, ethereum.NotFound, err)
|
||||
}
|
||||
|
||||
func TestHeaderByNumber_NotFound(t *testing.T) {
|
||||
srv := &Service{}
|
||||
srv.rpcClient = RPCClientBad{}
|
||||
|
||||
_, err := srv.HeaderByNumber(context.Background(), big.NewInt(100))
|
||||
assert.Equal(t, ethereum.NotFound, err)
|
||||
}
|
||||
|
||||
func TestToBlockNumArg(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
number *big.Int
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "genesis",
|
||||
number: big.NewInt(0),
|
||||
want: "0x0",
|
||||
},
|
||||
{
|
||||
name: "near genesis block",
|
||||
number: big.NewInt(300),
|
||||
want: "0x12c",
|
||||
},
|
||||
{
|
||||
name: "current block",
|
||||
number: big.NewInt(15838075),
|
||||
want: "0xf1ab7b",
|
||||
},
|
||||
{
|
||||
name: "far off block",
|
||||
number: big.NewInt(12032894823020),
|
||||
want: "0xaf1a06bea6c",
|
||||
},
|
||||
{
|
||||
name: "latest block",
|
||||
number: nil,
|
||||
want: "latest",
|
||||
},
|
||||
{
|
||||
name: "pending block",
|
||||
number: big.NewInt(-1),
|
||||
want: "pending",
|
||||
},
|
||||
{
|
||||
name: "finalized block",
|
||||
number: big.NewInt(-3),
|
||||
want: "finalized",
|
||||
},
|
||||
{
|
||||
name: "safe block",
|
||||
number: big.NewInt(-4),
|
||||
want: "safe",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := toBlockNumArg(tt.number); got != tt.want {
|
||||
t.Errorf("toBlockNumArg() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type testEngineService struct{}
|
||||
|
||||
func (*testEngineService) NoArgsRets() {}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
statefeed "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/feed/state"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers"
|
||||
coreState "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/transition"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types"
|
||||
statenative "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/state-native"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
contracts "github.com/prysmaticlabs/prysm/v3/contracts/deposit"
|
||||
@@ -284,16 +285,16 @@ func (s *Service) processPastLogs(ctx context.Context) error {
|
||||
currentBlockNum = deploymentBlock
|
||||
}
|
||||
// To store all blocks.
|
||||
headersMap := make(map[uint64]*gethtypes.Header)
|
||||
headersMap := make(map[uint64]*types.HeaderInfo)
|
||||
rawLogCount, err := s.depositContractCaller.GetDepositCount(&bind.CallOpts{})
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "GetDepositCount")
|
||||
}
|
||||
logCount := binary.LittleEndian.Uint64(rawLogCount)
|
||||
|
||||
latestFollowHeight, err := s.followedBlockHeight(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "followedBlockHeight")
|
||||
}
|
||||
|
||||
batchSize := s.cfg.eth1HeaderReqLimit
|
||||
@@ -302,7 +303,7 @@ func (s *Service) processPastLogs(ctx context.Context) error {
|
||||
for currentBlockNum < latestFollowHeight {
|
||||
currentBlockNum, batchSize, err = s.processBlockInBatch(ctx, currentBlockNum, latestFollowHeight, batchSize, additiveFactor, logCount, headersMap)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrapf(err, "processBlockInBatch, num=%d, height=%d", currentBlockNum, latestFollowHeight)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +313,7 @@ func (s *Service) processPastLogs(ctx context.Context) error {
|
||||
|
||||
c, err := s.cfg.beaconDB.FinalizedCheckpoint(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "FinalizedCheckpoint")
|
||||
}
|
||||
fRoot := bytesutil.ToBytes32(c.Root)
|
||||
// Return if no checkpoint exists yet.
|
||||
@@ -332,7 +333,7 @@ func (s *Service) processPastLogs(ctx context.Context) error {
|
||||
if isNil || slots.ToEpoch(fState.Slot()) != c.Epoch {
|
||||
fState, err = s.cfg.stateGen.StateByRoot(ctx, fRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrapf(err, "StateByRoot=%#x", fRoot)
|
||||
}
|
||||
}
|
||||
if fState != nil && !fState.IsNil() && fState.Eth1DepositIndex() > 0 {
|
||||
@@ -341,7 +342,7 @@ func (s *Service) processPastLogs(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) processBlockInBatch(ctx context.Context, currentBlockNum uint64, latestFollowHeight uint64, batchSize uint64, additiveFactor uint64, logCount uint64, headersMap map[uint64]*gethtypes.Header) (uint64, uint64, error) {
|
||||
func (s *Service) processBlockInBatch(ctx context.Context, currentBlockNum uint64, latestFollowHeight uint64, batchSize uint64, additiveFactor uint64, logCount uint64, headersMap map[uint64]*types.HeaderInfo) (uint64, uint64, error) {
|
||||
// Batch request the desired headers and store them in a
|
||||
// map for quick access.
|
||||
requestHeaders := func(startBlk uint64, endBlk uint64) error {
|
||||
@@ -499,11 +500,11 @@ func (s *Service) processChainStartFromBlockNum(ctx context.Context, blkNum *big
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) processChainStartFromHeader(ctx context.Context, header *gethtypes.Header) {
|
||||
s.processChainStartIfReady(ctx, header.Hash(), header.Number, header.Time)
|
||||
func (s *Service) processChainStartFromHeader(ctx context.Context, header *types.HeaderInfo) {
|
||||
s.processChainStartIfReady(ctx, header.Hash, header.Number, header.Time)
|
||||
}
|
||||
|
||||
func (s *Service) checkHeaderRange(ctx context.Context, start, end uint64, headersMap map[uint64]*gethtypes.Header,
|
||||
func (s *Service) checkHeaderRange(ctx context.Context, start, end uint64, headersMap map[uint64]*types.HeaderInfo,
|
||||
requestHeaders func(uint64, uint64) error) error {
|
||||
for i := start; i <= end; i++ {
|
||||
if !s.chainStartData.Chainstarted {
|
||||
|
||||
@@ -308,6 +308,7 @@ func TestProcessETH2GenesisLog(t *testing.T) {
|
||||
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
|
||||
web3Service = setDefaultMocks(web3Service)
|
||||
web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend)
|
||||
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
|
||||
require.NoError(t, err)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
bConfig := params.MinimalSpecConfig().Copy()
|
||||
@@ -403,7 +404,6 @@ func TestProcessETH2GenesisLog_CorrectNumOfDeposits(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
|
||||
web3Service.httpLogger = testAcc.Backend
|
||||
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
|
||||
web3Service.latestEth1Data.LastRequestedBlock = 0
|
||||
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64()
|
||||
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time()
|
||||
@@ -501,7 +501,6 @@ func TestProcessETH2GenesisLog_LargePeriodOfNoLogs(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
|
||||
web3Service.httpLogger = testAcc.Backend
|
||||
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
|
||||
web3Service.latestEth1Data.LastRequestedBlock = 0
|
||||
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64()
|
||||
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time()
|
||||
@@ -613,7 +612,6 @@ func newPowchainService(t *testing.T, eth1Backend *mock.TestAccount, beaconDB db
|
||||
require.NoError(t, err)
|
||||
|
||||
web3Service.rpcClient = &mockExecution.RPCClient{Backend: eth1Backend.Backend}
|
||||
web3Service.eth1DataFetcher = &goodFetcher{backend: eth1Backend.Backend}
|
||||
web3Service.httpLogger = &goodLogger{backend: eth1Backend.Backend}
|
||||
params.SetupTestConfigCleanup(t)
|
||||
bConfig := params.MinimalSpecConfig().Copy()
|
||||
|
||||
@@ -26,7 +26,6 @@ func (s *Service) setupExecutionClientConnections(ctx context.Context, currEndpo
|
||||
fetcher := ethclient.NewClient(client)
|
||||
s.rpcClient = client
|
||||
s.httpLogger = fetcher
|
||||
s.eth1DataFetcher = fetcher
|
||||
|
||||
depositContractCaller, err := contracts.NewDepositContractCaller(s.cfg.depositContractAddr, fetcher)
|
||||
if err != nil {
|
||||
@@ -46,7 +45,7 @@ func (s *Service) setupExecutionClientConnections(ctx context.Context, currEndpo
|
||||
}
|
||||
return errors.Wrap(err, errStr)
|
||||
}
|
||||
s.updateConnectedETH1(true)
|
||||
s.updateConnectedETH1(true, "execution chain connection established")
|
||||
s.runError = nil
|
||||
return nil
|
||||
}
|
||||
@@ -89,13 +88,14 @@ func (s *Service) pollConnectionStatus(ctx context.Context) {
|
||||
|
||||
// Forces to retry an execution client connection.
|
||||
func (s *Service) retryExecutionClientConnection(ctx context.Context, err error) {
|
||||
s.runError = err
|
||||
s.updateConnectedETH1(false)
|
||||
log.Debugf("retryExecutionClientConnection, err=%s", err.Error())
|
||||
s.runError = errors.Wrap(err, "retryExecutionClientConnection")
|
||||
s.updateConnectedETH1(false, "retryExecutionClientConnection")
|
||||
// Back off for a while before redialing.
|
||||
time.Sleep(backOffPeriod)
|
||||
currClient := s.rpcClient
|
||||
if err := s.setupExecutionClientConnections(ctx, s.cfg.currHttpEndpoint); err != nil {
|
||||
s.runError = err
|
||||
s.runError = errors.Wrap(err, "setupExecutionClientConnections")
|
||||
return
|
||||
}
|
||||
// Close previous client, if connection was successful.
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
gethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
gethRPC "github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -97,14 +96,6 @@ type Chain interface {
|
||||
POWBlockFetcher
|
||||
}
|
||||
|
||||
// RPCDataFetcher defines a subset of methods conformed to by ETH1.0 RPC clients for
|
||||
// fetching eth1 data from the clients.
|
||||
type RPCDataFetcher interface {
|
||||
Close()
|
||||
HeaderByNumber(ctx context.Context, number *big.Int) (*gethTypes.Header, error)
|
||||
HeaderByHash(ctx context.Context, hash common.Hash) (*gethTypes.Header, error)
|
||||
}
|
||||
|
||||
// RPCClient defines the rpc methods required to interact with the eth1 node.
|
||||
type RPCClient interface {
|
||||
Close()
|
||||
@@ -154,7 +145,6 @@ type Service struct {
|
||||
cancel context.CancelFunc
|
||||
eth1HeadTicker *time.Ticker
|
||||
httpLogger bind.ContractFilterer
|
||||
eth1DataFetcher RPCDataFetcher
|
||||
rpcClient RPCClient
|
||||
headerCache *headerCache // cache to store block hash/block height.
|
||||
latestEth1Data *ethpb.LatestETH1Data
|
||||
@@ -264,9 +254,6 @@ func (s *Service) Stop() error {
|
||||
if s.rpcClient != nil {
|
||||
s.rpcClient.Close()
|
||||
}
|
||||
if s.eth1DataFetcher != nil {
|
||||
s.eth1DataFetcher.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -320,7 +307,8 @@ func (s *Service) updateBeaconNodeStats() {
|
||||
s.cfg.beaconNodeStatsUpdater.Update(bs)
|
||||
}
|
||||
|
||||
func (s *Service) updateConnectedETH1(state bool) {
|
||||
func (s *Service) updateConnectedETH1(state bool, reason string) {
|
||||
log.Infof("updateConnectedETH1 - %s", reason)
|
||||
s.connectedETH1 = state
|
||||
s.updateBeaconNodeStats()
|
||||
}
|
||||
@@ -332,10 +320,14 @@ func (s *Service) followedBlockHeight(ctx context.Context) (uint64, error) {
|
||||
latestBlockTime := uint64(0)
|
||||
if s.latestEth1Data.BlockTime > followTime {
|
||||
latestBlockTime = s.latestEth1Data.BlockTime - followTime
|
||||
if s.latestEth1Data.BlockHeight < params.BeaconConfig().Eth1FollowDistance {
|
||||
latestBlockTime = s.latestEth1Data.BlockTime
|
||||
}
|
||||
}
|
||||
log.WithField("distance", params.BeaconConfig().Eth1FollowDistance).WithField("followTime", followTime).WithField("BlockTime", s.latestEth1Data.BlockTime).WithField("BlockHeight", s.latestEth1Data.BlockHeight).WithField("latestBlockTime", latestBlockTime).Info("followedBlockHeight")
|
||||
blk, err := s.BlockByTimestamp(ctx, latestBlockTime)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, errors.Wrapf(err, "BlockByTimestamp=%d", latestBlockTime)
|
||||
}
|
||||
return blk.Number.Uint64(), nil
|
||||
}
|
||||
@@ -398,36 +390,35 @@ func (s *Service) initDepositCaches(ctx context.Context, ctrs []*ethpb.DepositCo
|
||||
|
||||
// processBlockHeader adds a newly observed eth1 block to the block cache and
|
||||
// updates the latest blockHeight, blockHash, and blockTime properties of the service.
|
||||
func (s *Service) processBlockHeader(header *gethTypes.Header) {
|
||||
func (s *Service) processBlockHeader(header *types.HeaderInfo) {
|
||||
defer safelyHandlePanic()
|
||||
blockNumberGauge.Set(float64(header.Number.Int64()))
|
||||
s.latestEth1DataLock.Lock()
|
||||
s.latestEth1Data.BlockHeight = header.Number.Uint64()
|
||||
s.latestEth1Data.BlockHash = header.Hash().Bytes()
|
||||
s.latestEth1Data.BlockHash = header.Hash.Bytes()
|
||||
s.latestEth1Data.BlockTime = header.Time
|
||||
s.latestEth1DataLock.Unlock()
|
||||
log.WithFields(logrus.Fields{
|
||||
"blockNumber": s.latestEth1Data.BlockHeight,
|
||||
"blockHash": hexutil.Encode(s.latestEth1Data.BlockHash),
|
||||
"difficulty": header.Difficulty.String(),
|
||||
}).Debug("Latest eth1 chain event")
|
||||
}
|
||||
|
||||
// batchRequestHeaders requests the block range specified in the arguments. Instead of requesting
|
||||
// each block in one call, it batches all requests into a single rpc call.
|
||||
func (s *Service) batchRequestHeaders(startBlock, endBlock uint64) ([]*gethTypes.Header, error) {
|
||||
func (s *Service) batchRequestHeaders(startBlock, endBlock uint64) ([]*types.HeaderInfo, error) {
|
||||
if startBlock > endBlock {
|
||||
return nil, fmt.Errorf("start block height %d cannot be > end block height %d", startBlock, endBlock)
|
||||
}
|
||||
requestRange := (endBlock - startBlock) + 1
|
||||
elems := make([]gethRPC.BatchElem, 0, requestRange)
|
||||
headers := make([]*gethTypes.Header, 0, requestRange)
|
||||
headers := make([]*types.HeaderInfo, 0, requestRange)
|
||||
errs := make([]error, 0, requestRange)
|
||||
if requestRange == 0 {
|
||||
return headers, nil
|
||||
}
|
||||
for i := startBlock; i <= endBlock; i++ {
|
||||
header := &gethTypes.Header{}
|
||||
header := &types.HeaderInfo{}
|
||||
err := error(nil)
|
||||
elems = append(elems, gethRPC.BatchElem{
|
||||
Method: "eth_getBlockByNumber",
|
||||
@@ -481,21 +472,23 @@ func (s *Service) handleETH1FollowDistance() {
|
||||
}
|
||||
if !s.chainStartData.Chainstarted {
|
||||
if err := s.processChainStartFromBlockNum(ctx, big.NewInt(int64(s.latestEth1Data.LastRequestedBlock))); err != nil {
|
||||
s.runError = err
|
||||
s.runError = errors.Wrap(err, "processChainStartFromBlockNum")
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If the last requested block has not changed,
|
||||
// we do not request batched logs as this means there are no new
|
||||
// logs for the powchain service to process. Also it is a potential
|
||||
// failure condition as would mean we have not respected the protocol threshold.
|
||||
if s.latestEth1Data.LastRequestedBlock == s.latestEth1Data.BlockHeight {
|
||||
log.Error("Beacon node is not respecting the follow distance")
|
||||
// TODO this is usually an error but temporarily setting it to warn because its noisy when starting from genesis
|
||||
log.Warn("Beacon node is not respecting the follow distance")
|
||||
return
|
||||
}
|
||||
if err := s.requestBatchedHeadersAndLogs(ctx); err != nil {
|
||||
s.runError = err
|
||||
s.runError = errors.Wrap(err, "requestBatchedHeadersAndLogs")
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
@@ -523,8 +516,9 @@ func (s *Service) initPOWService() {
|
||||
return
|
||||
default:
|
||||
ctx := s.ctx
|
||||
header, err := s.eth1DataFetcher.HeaderByNumber(ctx, nil)
|
||||
header, err := s.HeaderByNumber(ctx, nil)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "HeaderByNumber")
|
||||
s.retryExecutionClientConnection(ctx, err)
|
||||
errorLogger(err, "Unable to retrieve latest execution client header")
|
||||
continue
|
||||
@@ -532,11 +526,12 @@ func (s *Service) initPOWService() {
|
||||
|
||||
s.latestEth1DataLock.Lock()
|
||||
s.latestEth1Data.BlockHeight = header.Number.Uint64()
|
||||
s.latestEth1Data.BlockHash = header.Hash().Bytes()
|
||||
s.latestEth1Data.BlockHash = header.Hash.Bytes()
|
||||
s.latestEth1Data.BlockTime = header.Time
|
||||
s.latestEth1DataLock.Unlock()
|
||||
|
||||
if err := s.processPastLogs(ctx); err != nil {
|
||||
err = errors.Wrap(err, "processPastLogs")
|
||||
s.retryExecutionClientConnection(ctx, err)
|
||||
errorLogger(
|
||||
err,
|
||||
@@ -546,6 +541,7 @@ func (s *Service) initPOWService() {
|
||||
}
|
||||
// Cache eth1 headers from our voting period.
|
||||
if err := s.cacheHeadersForEth1DataVote(ctx); err != nil {
|
||||
err = errors.Wrap(err, "cacheHeadersForEth1DataVote")
|
||||
s.retryExecutionClientConnection(ctx, err)
|
||||
if errors.Is(err, errBlockTimeTooLate) {
|
||||
log.WithError(err).Debug("Unable to cache headers for execution client votes")
|
||||
@@ -562,8 +558,9 @@ func (s *Service) initPOWService() {
|
||||
// In the event our provided chainstart data references a non-existent block hash,
|
||||
// we assume the genesis block to be 0.
|
||||
if genHash != [32]byte{} {
|
||||
genHeader, err := s.eth1DataFetcher.HeaderByHash(ctx, genHash)
|
||||
genHeader, err := s.HeaderByHash(ctx, genHash)
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "HeaderByHash, hash=%#x", genHash)
|
||||
s.retryExecutionClientConnection(ctx, err)
|
||||
errorLogger(err, "Unable to retrieve proof-of-stake genesis block data")
|
||||
continue
|
||||
@@ -572,6 +569,7 @@ func (s *Service) initPOWService() {
|
||||
}
|
||||
s.chainStartData.GenesisBlock = genBlock
|
||||
if err := s.savePowchainData(ctx); err != nil {
|
||||
err = errors.Wrap(err, "savePowchainData")
|
||||
s.retryExecutionClientConnection(ctx, err)
|
||||
errorLogger(err, "Unable to save execution client data")
|
||||
continue
|
||||
@@ -597,11 +595,11 @@ func (s *Service) run(done <-chan struct{}) {
|
||||
s.isRunning = false
|
||||
s.runError = nil
|
||||
s.rpcClient.Close()
|
||||
s.updateConnectedETH1(false)
|
||||
s.updateConnectedETH1(false, "context canceled in run()")
|
||||
log.Debug("Context closed, exiting goroutine")
|
||||
return
|
||||
case <-s.eth1HeadTicker.C:
|
||||
head, err := s.eth1DataFetcher.HeaderByNumber(s.ctx, nil)
|
||||
head, err := s.HeaderByNumber(s.ctx, nil)
|
||||
if err != nil {
|
||||
s.pollConnectionStatus(s.ctx)
|
||||
log.WithError(err).Debug("Could not fetch latest eth1 header")
|
||||
@@ -655,11 +653,11 @@ func (s *Service) cacheHeadersForEth1DataVote(ctx context.Context) error {
|
||||
// Find the end block to request from.
|
||||
end, err := s.followedBlockHeight(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "followedBlockHeight")
|
||||
}
|
||||
start, err := s.determineEarliestVotingBlock(ctx, end)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrapf(err, "determineEarliestVotingBlock=%d", end)
|
||||
}
|
||||
return s.cacheBlockHeaders(start, end)
|
||||
}
|
||||
@@ -691,7 +689,7 @@ func (s *Service) cacheBlockHeaders(start, end uint64) error {
|
||||
}
|
||||
continue
|
||||
}
|
||||
return err
|
||||
return errors.Wrapf(err, "cacheBlockHeaders, start=%d, end=%d", startReq, endReq)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -710,12 +708,22 @@ func (s *Service) determineEarliestVotingBlock(ctx context.Context, followBlock
|
||||
}
|
||||
return earliestBlk, nil
|
||||
}
|
||||
if s.latestEth1Data.BlockHeight < params.BeaconConfig().Eth1FollowDistance {
|
||||
return 0, nil
|
||||
}
|
||||
votingTime := slots.VotingPeriodStartTime(genesisTime, currSlot)
|
||||
followBackDist := 2 * params.BeaconConfig().SecondsPerETH1Block * params.BeaconConfig().Eth1FollowDistance
|
||||
if followBackDist > votingTime {
|
||||
return 0, errors.Errorf("invalid genesis time provided. %d > %d", followBackDist, votingTime)
|
||||
}
|
||||
earliestValidTime := votingTime - followBackDist
|
||||
/*
|
||||
if earliestValidTime > s.latestEth1Data.BlockTime {
|
||||
return 0, nil
|
||||
}
|
||||
log.WithField("earliestValidTime", earliestValidTime).Info("calling BlockByTimestamp")
|
||||
*/
|
||||
log.WithField("distance", params.BeaconConfig().Eth1FollowDistance).WithField("earliestValidTime", earliestValidTime).WithField("BlockTime", s.latestEth1Data.BlockTime).WithField("BlockHeight", s.latestEth1Data.BlockHeight).WithField("followBlock", followBlock).Info("determineEarliestVotingBlock")
|
||||
hdr, err := s.BlockByTimestamp(ctx, earliestValidTime)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package execution
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -20,6 +18,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/cache/depositcache"
|
||||
dbutil "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/testing"
|
||||
mockExecution "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/testing"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types"
|
||||
doublylinkedtree "github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice/doubly-linked-tree"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state/stategen"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
@@ -80,56 +79,6 @@ func (g *goodNotifier) StateFeed() *event.Feed {
|
||||
return g.MockStateFeed
|
||||
}
|
||||
|
||||
type goodFetcher struct {
|
||||
backend *backends.SimulatedBackend
|
||||
blockNumMap map[uint64]*gethTypes.Header
|
||||
}
|
||||
|
||||
func (_ *goodFetcher) Close() {}
|
||||
|
||||
func (g *goodFetcher) HeaderByHash(_ context.Context, hash common.Hash) (*gethTypes.Header, error) {
|
||||
if bytes.Equal(hash.Bytes(), common.BytesToHash([]byte{0}).Bytes()) {
|
||||
return nil, fmt.Errorf("expected block hash to be nonzero %v", hash)
|
||||
}
|
||||
if g.backend == nil {
|
||||
return &gethTypes.Header{
|
||||
Number: big.NewInt(0),
|
||||
}, nil
|
||||
}
|
||||
header := g.backend.Blockchain().GetHeaderByHash(hash)
|
||||
if header == nil {
|
||||
return nil, errors.New("nil header returned")
|
||||
}
|
||||
return header, nil
|
||||
|
||||
}
|
||||
|
||||
func (g *goodFetcher) HeaderByNumber(_ context.Context, number *big.Int) (*gethTypes.Header, error) {
|
||||
if g.backend == nil && g.blockNumMap == nil {
|
||||
return &gethTypes.Header{
|
||||
Number: big.NewInt(15),
|
||||
Time: 150,
|
||||
}, nil
|
||||
}
|
||||
if g.blockNumMap != nil {
|
||||
return g.blockNumMap[number.Uint64()], nil
|
||||
}
|
||||
var header *gethTypes.Header
|
||||
if number == nil {
|
||||
header = g.backend.Blockchain().CurrentHeader()
|
||||
} else {
|
||||
header = g.backend.Blockchain().GetHeaderByNumber(number.Uint64())
|
||||
}
|
||||
if header == nil {
|
||||
return nil, errors.New("nil header returned")
|
||||
}
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func (_ *goodFetcher) SyncProgress(_ context.Context) (*ethereum.SyncProgress, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var depositsReqForChainStart = 64
|
||||
|
||||
func TestStart_OK(t *testing.T) {
|
||||
@@ -229,7 +178,6 @@ func TestService_Eth1Synced(t *testing.T) {
|
||||
web3Service = setDefaultMocks(web3Service)
|
||||
web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend)
|
||||
require.NoError(t, err)
|
||||
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
|
||||
|
||||
currTime := testAcc.Backend.Blockchain().CurrentHeader().Time
|
||||
now := time.Now()
|
||||
@@ -261,7 +209,7 @@ func TestFollowBlock_OK(t *testing.T) {
|
||||
params.OverrideBeaconConfig(conf)
|
||||
|
||||
web3Service = setDefaultMocks(web3Service)
|
||||
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
|
||||
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
|
||||
baseHeight := testAcc.Backend.Blockchain().CurrentBlock().NumberU64()
|
||||
// process follow_distance blocks
|
||||
for i := 0; i < int(params.BeaconConfig().Eth1FollowDistance); i++ {
|
||||
@@ -330,7 +278,7 @@ func TestHandlePanic_OK(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
|
||||
// nil eth1DataFetcher would panic if cached value not used
|
||||
web3Service.eth1DataFetcher = nil
|
||||
web3Service.rpcClient = nil
|
||||
web3Service.processBlockHeader(nil)
|
||||
require.LogsContain(t, hook, "Panicked when handling data from ETH 1.0 Chain!")
|
||||
}
|
||||
@@ -371,7 +319,6 @@ func TestLogTillGenesis_OK(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
|
||||
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
|
||||
web3Service.httpLogger = testAcc.Backend
|
||||
for i := 0; i < 30; i++ {
|
||||
testAcc.Backend.Commit()
|
||||
@@ -506,7 +453,6 @@ func TestNewService_EarliestVotingBlock(t *testing.T) {
|
||||
WithDatabase(beaconDB),
|
||||
)
|
||||
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
|
||||
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
|
||||
// simulated backend sets eth1 block
|
||||
// time as 10 seconds
|
||||
params.SetupTestConfigCleanup(t)
|
||||
@@ -800,18 +746,23 @@ func TestService_CacheBlockHeaders(t *testing.T) {
|
||||
func TestService_FollowBlock(t *testing.T) {
|
||||
followTime := params.BeaconConfig().Eth1FollowDistance * params.BeaconConfig().SecondsPerETH1Block
|
||||
followTime += 10000
|
||||
bMap := make(map[uint64]*gethTypes.Header)
|
||||
bMap := make(map[uint64]*types.HeaderInfo)
|
||||
for i := uint64(3000); i > 0; i-- {
|
||||
bMap[i] = &gethTypes.Header{
|
||||
h := &gethTypes.Header{
|
||||
Number: big.NewInt(int64(i)),
|
||||
Time: followTime + (i * 40),
|
||||
}
|
||||
bMap[i] = &types.HeaderInfo{
|
||||
Number: h.Number,
|
||||
Hash: h.Hash(),
|
||||
Time: h.Time,
|
||||
}
|
||||
}
|
||||
s := &Service{
|
||||
cfg: &config{eth1HeaderReqLimit: 1000},
|
||||
eth1DataFetcher: &goodFetcher{blockNumMap: bMap},
|
||||
headerCache: newHeaderCache(),
|
||||
latestEth1Data: ðpb.LatestETH1Data{BlockTime: (3000 * 40) + followTime, BlockHeight: 3000},
|
||||
cfg: &config{eth1HeaderReqLimit: 1000},
|
||||
rpcClient: &mockExecution.RPCClient{BlockNumMap: bMap},
|
||||
headerCache: newHeaderCache(),
|
||||
latestEth1Data: ðpb.LatestETH1Data{BlockTime: (3000 * 40) + followTime, BlockHeight: 3000},
|
||||
}
|
||||
h, err := s.followedBlockHeight(context.Background())
|
||||
assert.NoError(t, err)
|
||||
@@ -839,7 +790,7 @@ func (s *slowRPCClient) BatchCall(b []rpc.BatchElem) error {
|
||||
return err
|
||||
}
|
||||
h := &gethTypes.Header{Number: num}
|
||||
*e.Result.(*gethTypes.Header) = *h
|
||||
*e.Result.(*types.HeaderInfo) = types.HeaderInfo{Number: h.Number, Hash: h.Hash()}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,13 +4,14 @@ go_library(
|
||||
name = "go_default_library",
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"genesis.go",
|
||||
"mock_engine_client.go",
|
||||
"mock_execution_chain.go",
|
||||
"mock_faulty_powchain.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/testing",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//visibility:public",
|
||||
],
|
||||
deps = [
|
||||
"//async/event:go_default_library",
|
||||
@@ -26,7 +27,9 @@ go_library(
|
||||
"@com_github_ethereum_go_ethereum//accounts/abi/bind/backends:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//params:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
|
||||
"@com_github_holiman_uint256//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
|
||||
202
beacon-chain/execution/testing/genesis.go
Normal file
202
beacon-chain/execution/testing/genesis.go
Normal file
File diff suppressed because one or more lines are too long
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
gethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/async/event"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
@@ -140,12 +141,92 @@ func (m *Chain) ETH1ConnectionErrors() []error {
|
||||
|
||||
// RPCClient defines the mock rpc client.
|
||||
type RPCClient struct {
|
||||
Backend *backends.SimulatedBackend
|
||||
Backend *backends.SimulatedBackend
|
||||
BlockNumMap map[uint64]*types.HeaderInfo
|
||||
}
|
||||
|
||||
func (*RPCClient) Close() {}
|
||||
|
||||
func (*RPCClient) CallContext(_ context.Context, _ interface{}, _ string, _ ...interface{}) error {
|
||||
func (r *RPCClient) CallContext(ctx context.Context, obj interface{}, methodName string, args ...interface{}) error {
|
||||
if r.BlockNumMap != nil && methodName == "eth_getBlockByNumber" {
|
||||
val, ok := args[0].(string)
|
||||
if !ok {
|
||||
return errors.Errorf("wrong argument type provided: %T", args[0])
|
||||
}
|
||||
num, err := hexutil.DecodeBig(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b := r.BlockNumMap[num.Uint64()]
|
||||
assertedObj, ok := obj.(**types.HeaderInfo)
|
||||
if !ok {
|
||||
return errors.Errorf("wrong argument type provided: %T", obj)
|
||||
}
|
||||
*assertedObj = b
|
||||
return nil
|
||||
}
|
||||
if r.Backend == nil && methodName == "eth_getBlockByNumber" {
|
||||
h := &gethTypes.Header{
|
||||
Number: big.NewInt(15),
|
||||
Time: 150,
|
||||
}
|
||||
assertedObj, ok := obj.(**types.HeaderInfo)
|
||||
if !ok {
|
||||
return errors.Errorf("wrong argument type provided: %T", obj)
|
||||
}
|
||||
*assertedObj = &types.HeaderInfo{
|
||||
Hash: h.Hash(),
|
||||
Number: h.Number,
|
||||
Time: h.Time,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
switch methodName {
|
||||
case "eth_getBlockByNumber":
|
||||
val, ok := args[0].(string)
|
||||
if !ok {
|
||||
return errors.Errorf("wrong argument type provided: %T", args[0])
|
||||
}
|
||||
var num *big.Int
|
||||
var err error
|
||||
if val != "latest" {
|
||||
num, err = hexutil.DecodeBig(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
h, err := r.Backend.HeaderByNumber(ctx, num)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
assertedObj, ok := obj.(**types.HeaderInfo)
|
||||
if !ok {
|
||||
return errors.Errorf("wrong argument type provided: %T", obj)
|
||||
}
|
||||
*assertedObj = &types.HeaderInfo{
|
||||
Hash: h.Hash(),
|
||||
Number: h.Number,
|
||||
Time: h.Time,
|
||||
}
|
||||
case "eth_getBlockByHash":
|
||||
val, ok := args[0].(common.Hash)
|
||||
if !ok {
|
||||
return errors.Errorf("wrong argument type provided: %T", args[0])
|
||||
}
|
||||
h, err := r.Backend.HeaderByHash(ctx, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
assertedObj, ok := obj.(**types.HeaderInfo)
|
||||
if !ok {
|
||||
return errors.Errorf("wrong argument type provided: %T", obj)
|
||||
}
|
||||
*assertedObj = &types.HeaderInfo{
|
||||
Hash: h.Hash(),
|
||||
Number: h.Number,
|
||||
Time: h.Time,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -164,7 +245,7 @@ func (r *RPCClient) BatchCall(b []rpc.BatchElem) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e.Result.(*gethTypes.Header) = *h
|
||||
*e.Result.(*types.HeaderInfo) = types.HeaderInfo{Number: h.Number, Time: h.Time, Hash: h.Hash()}
|
||||
|
||||
}
|
||||
return nil
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user