mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-02-01 00:28:16 -05:00
Compare commits
94 Commits
e2e-debugg
...
pcli-epbs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb2281f6b4 | ||
|
|
b19a181e4f | ||
|
|
4839039975 | ||
|
|
fc539ba3cc | ||
|
|
41b09baf30 | ||
|
|
61c36c0801 | ||
|
|
d69664d376 | ||
|
|
c1b4f27492 | ||
|
|
0c606d5d4a | ||
|
|
5f6081eada | ||
|
|
e4a2c77154 | ||
|
|
cbaa8e15b7 | ||
|
|
b1679b6913 | ||
|
|
cc375a1282 | ||
|
|
b21fb45306 | ||
|
|
86569127a8 | ||
|
|
a1ab658d51 | ||
|
|
451680b2c5 | ||
|
|
d89becf7fc | ||
|
|
1c829a9567 | ||
|
|
cd85a4f7ca | ||
|
|
596b972696 | ||
|
|
da470d76aa | ||
|
|
3a324d9a8c | ||
|
|
d41ecd0c0a | ||
|
|
1bdd79e16b | ||
|
|
bf17020e01 | ||
|
|
fd5111d30d | ||
|
|
6523fb328d | ||
|
|
f968baeb71 | ||
|
|
6b47bd81f8 | ||
|
|
ed801af186 | ||
|
|
13aba9d129 | ||
|
|
338e14d3af | ||
|
|
9f16a17b8c | ||
|
|
7d8ab438c6 | ||
|
|
6600cec6b3 | ||
|
|
4a4d8284cc | ||
|
|
28cd5072b5 | ||
|
|
2d6fb117d5 | ||
|
|
1525dde40e | ||
|
|
df9921efe5 | ||
|
|
3098757724 | ||
|
|
6b8409601c | ||
|
|
50352072bc | ||
|
|
ccdafe3838 | ||
|
|
09d97c548b | ||
|
|
17e04f72f6 | ||
|
|
c82e2368f2 | ||
|
|
c9d257baf1 | ||
|
|
3cc776b7f2 | ||
|
|
7bf0379a78 | ||
|
|
fb21cf77cc | ||
|
|
8a5967d0aa | ||
|
|
b63d7cfffd | ||
|
|
66853d936a | ||
|
|
d5479ce098 | ||
|
|
aefa4eeb06 | ||
|
|
4d209bdccc | ||
|
|
2ad8ef30b1 | ||
|
|
f466c2e1fa | ||
|
|
83ee0b298a | ||
|
|
ea9969ec7a | ||
|
|
69f1a56d06 | ||
|
|
78d74a5313 | ||
|
|
bfd0a22260 | ||
|
|
790ce1cef9 | ||
|
|
2b6ddd5887 | ||
|
|
63370855ce | ||
|
|
6d3e10a335 | ||
|
|
082edda8fa | ||
|
|
bfe4109068 | ||
|
|
99da0bec2c | ||
|
|
1aa7bb6c72 | ||
|
|
875cce2639 | ||
|
|
037d91f750 | ||
|
|
5079887351 | ||
|
|
e8b8d64bfa | ||
|
|
e50c60f783 | ||
|
|
00fb633c39 | ||
|
|
3afb4d05db | ||
|
|
1974636dc6 | ||
|
|
c7c6226b4f | ||
|
|
a54e1e8d94 | ||
|
|
233eae8681 | ||
|
|
bed94f37f5 | ||
|
|
62202f8459 | ||
|
|
06bd2042be | ||
|
|
7e27bd5615 | ||
|
|
6930f722d5 | ||
|
|
2e7ab9104e | ||
|
|
2ef1e712de | ||
|
|
a6cef5ef3f | ||
|
|
2512dbe580 |
@@ -4,11 +4,13 @@ go_library(
|
|||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = [
|
srcs = [
|
||||||
"block.go",
|
"block.go",
|
||||||
|
"block_epbs.go",
|
||||||
"conversions.go",
|
"conversions.go",
|
||||||
"conversions_blob.go",
|
"conversions_blob.go",
|
||||||
"conversions_block.go",
|
"conversions_block.go",
|
||||||
"conversions_lightclient.go",
|
"conversions_lightclient.go",
|
||||||
"conversions_state.go",
|
"conversions_state.go",
|
||||||
|
"converstions_block_epbs.go",
|
||||||
"endpoints_beacon.go",
|
"endpoints_beacon.go",
|
||||||
"endpoints_blob.go",
|
"endpoints_blob.go",
|
||||||
"endpoints_builder.go",
|
"endpoints_builder.go",
|
||||||
|
|||||||
72
api/server/structs/block_epbs.go
Normal file
72
api/server/structs/block_epbs.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package structs
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Epbs
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
type SignedBeaconBlockEpbs struct {
|
||||||
|
Message *BeaconBlockEpbs `json:"message"`
|
||||||
|
Signature string `json:"signature"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ SignedMessageJsoner = &SignedBeaconBlockElectra{}
|
||||||
|
|
||||||
|
func (s *SignedBeaconBlockEpbs) MessageRawJson() ([]byte, error) {
|
||||||
|
return json.Marshal(s.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SignedBeaconBlockEpbs) SigString() string {
|
||||||
|
return s.Signature
|
||||||
|
}
|
||||||
|
|
||||||
|
type BeaconBlockEpbs struct {
|
||||||
|
Slot string `json:"slot"`
|
||||||
|
ProposerIndex string `json:"proposer_index"`
|
||||||
|
ParentRoot string `json:"parent_root"`
|
||||||
|
StateRoot string `json:"state_root"`
|
||||||
|
Body *BeaconBlockBodyEpbs `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BeaconBlockBodyEpbs struct {
|
||||||
|
RandaoReveal string `json:"randao_reveal"`
|
||||||
|
Eth1Data *Eth1Data `json:"eth1_data"`
|
||||||
|
Graffiti string `json:"graffiti"`
|
||||||
|
ProposerSlashings []*ProposerSlashing `json:"proposer_slashings"`
|
||||||
|
AttesterSlashings []*AttesterSlashingElectra `json:"attester_slashings"`
|
||||||
|
Attestations []*AttestationElectra `json:"attestations"`
|
||||||
|
Deposits []*Deposit `json:"deposits"`
|
||||||
|
VoluntaryExits []*SignedVoluntaryExit `json:"voluntary_exits"`
|
||||||
|
SyncAggregate *SyncAggregate `json:"sync_aggregate"`
|
||||||
|
BLSToExecutionChanges []*SignedBLSToExecutionChange `json:"bls_to_execution_changes"`
|
||||||
|
SignedExecutionPayloadHeader *SignedExecutionPayloadHeader `json:"signed_execution_payload_header"`
|
||||||
|
PayloadAttestations []*PayloadAttestation `json:"payload_attestations"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignedExecutionPayloadHeader struct {
|
||||||
|
Message *ExecutionPayloadHeaderEPBS `json:"message"`
|
||||||
|
Signature string `json:"signature"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExecutionPayloadHeaderEPBS struct {
|
||||||
|
ParentBlockHash string `json:"parent_block_hash"`
|
||||||
|
ParentBlockRoot string `json:"parent_block_root"`
|
||||||
|
BlockHash string `json:"block_hash"`
|
||||||
|
GasLimit string `json:"gas_limit"`
|
||||||
|
BuilderIndex string `json:"builder_index"`
|
||||||
|
Slot string `json:"slot"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
BlobKzgCommitmentsRoot string `json:"blob_kzg_commitments_root"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PayloadAttestation struct {
|
||||||
|
AggregationBits string `json:"aggregation_bits"`
|
||||||
|
Data *PayloadAttestationData `json:"data"`
|
||||||
|
Signature string `json:"signature"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PayloadAttestationData struct {
|
||||||
|
BeaconBlockRoot string `json:"beacon_block_root"`
|
||||||
|
Slot string `json:"slot"`
|
||||||
|
PayloadStatus string `json:"payload_status"`
|
||||||
|
}
|
||||||
@@ -269,6 +269,8 @@ func SignedBeaconBlockMessageJsoner(block interfaces.ReadOnlySignedBeaconBlock)
|
|||||||
return SignedBlindedBeaconBlockFuluFromConsensus(pbStruct)
|
return SignedBlindedBeaconBlockFuluFromConsensus(pbStruct)
|
||||||
case *eth.SignedBeaconBlockFulu:
|
case *eth.SignedBeaconBlockFulu:
|
||||||
return SignedBeaconBlockFuluFromConsensus(pbStruct)
|
return SignedBeaconBlockFuluFromConsensus(pbStruct)
|
||||||
|
case *eth.SignedBeaconBlockEpbs:
|
||||||
|
return SignedBeaconBlockEpbsFromConsensus(pbStruct)
|
||||||
default:
|
default:
|
||||||
return nil, ErrUnsupportedConversion
|
return nil, ErrUnsupportedConversion
|
||||||
}
|
}
|
||||||
|
|||||||
364
api/server/structs/converstions_block_epbs.go
Normal file
364
api/server/structs/converstions_block_epbs.go
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
package structs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/api/server"
|
||||||
|
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||||
|
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||||
|
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Epbs
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// nolint:gocognit
|
||||||
|
func (b *BeaconBlockEpbs) ToConsensus() (*eth.BeaconBlockEpbs, error) {
|
||||||
|
if b == nil {
|
||||||
|
return nil, errNilValue
|
||||||
|
}
|
||||||
|
if b.Body == nil {
|
||||||
|
return nil, server.NewDecodeError(errNilValue, "Body")
|
||||||
|
}
|
||||||
|
if b.Body.Eth1Data == nil {
|
||||||
|
return nil, server.NewDecodeError(errNilValue, "Body.Eth1Data")
|
||||||
|
}
|
||||||
|
if b.Body.SyncAggregate == nil {
|
||||||
|
return nil, server.NewDecodeError(errNilValue, "Body.SyncAggregate")
|
||||||
|
}
|
||||||
|
slot, err := strconv.ParseUint(b.Slot, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Slot")
|
||||||
|
}
|
||||||
|
proposerIndex, err := strconv.ParseUint(b.ProposerIndex, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "ProposerIndex")
|
||||||
|
}
|
||||||
|
parentRoot, err := bytesutil.DecodeHexWithLength(b.ParentRoot, fieldparams.RootLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "ParentRoot")
|
||||||
|
}
|
||||||
|
stateRoot, err := bytesutil.DecodeHexWithLength(b.StateRoot, fieldparams.RootLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "StateRoot")
|
||||||
|
}
|
||||||
|
randaoReveal, err := bytesutil.DecodeHexWithLength(b.Body.RandaoReveal, fieldparams.BLSSignatureLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Body.RandaoReveal")
|
||||||
|
}
|
||||||
|
depositRoot, err := bytesutil.DecodeHexWithLength(b.Body.Eth1Data.DepositRoot, fieldparams.RootLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Body.Eth1Data.DepositRoot")
|
||||||
|
}
|
||||||
|
depositCount, err := strconv.ParseUint(b.Body.Eth1Data.DepositCount, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Body.Eth1Data.DepositCount")
|
||||||
|
}
|
||||||
|
blockHash, err := bytesutil.DecodeHexWithLength(b.Body.Eth1Data.BlockHash, common.HashLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Body.Eth1Data.BlockHash")
|
||||||
|
}
|
||||||
|
graffiti, err := bytesutil.DecodeHexWithLength(b.Body.Graffiti, fieldparams.RootLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Body.Graffiti")
|
||||||
|
}
|
||||||
|
proposerSlashings, err := ProposerSlashingsToConsensus(b.Body.ProposerSlashings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Body.ProposerSlashings")
|
||||||
|
}
|
||||||
|
attesterSlashings, err := AttesterSlashingsElectraToConsensus(b.Body.AttesterSlashings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Body.AttesterSlashings")
|
||||||
|
}
|
||||||
|
atts, err := AttsElectraToConsensus(b.Body.Attestations)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Body.Attestations")
|
||||||
|
}
|
||||||
|
deposits, err := DepositsToConsensus(b.Body.Deposits)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Body.Deposits")
|
||||||
|
}
|
||||||
|
exits, err := SignedExitsToConsensus(b.Body.VoluntaryExits)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Body.VoluntaryExits")
|
||||||
|
}
|
||||||
|
syncCommitteeBits, err := bytesutil.DecodeHexWithLength(b.Body.SyncAggregate.SyncCommitteeBits, fieldparams.SyncAggregateSyncCommitteeBytesLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Body.SyncAggregate.SyncCommitteeBits")
|
||||||
|
}
|
||||||
|
syncCommitteeSig, err := bytesutil.DecodeHexWithLength(b.Body.SyncAggregate.SyncCommitteeSignature, fieldparams.BLSSignatureLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Body.SyncAggregate.SyncCommitteeSignature")
|
||||||
|
}
|
||||||
|
signedPayloadHeader, err := b.Body.SignedExecutionPayloadHeader.ToConsensus()
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Body.SignedExecutionPayloadHeader")
|
||||||
|
}
|
||||||
|
|
||||||
|
blsChanges, err := SignedBLSChangesToConsensus(b.Body.BLSToExecutionChanges)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Body.BLSToExecutionChanges")
|
||||||
|
}
|
||||||
|
payloadAttestations := make([]*eth.PayloadAttestation, len(b.Body.PayloadAttestations))
|
||||||
|
for i, p := range b.Body.PayloadAttestations {
|
||||||
|
payloadAttestations[i], err = p.ToConsensus()
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, fmt.Sprintf("Body.PayloadAttestations[%d]", i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ð.BeaconBlockEpbs{
|
||||||
|
Slot: primitives.Slot(slot),
|
||||||
|
ProposerIndex: primitives.ValidatorIndex(proposerIndex),
|
||||||
|
ParentRoot: parentRoot,
|
||||||
|
StateRoot: stateRoot,
|
||||||
|
Body: ð.BeaconBlockBodyEpbs{
|
||||||
|
RandaoReveal: randaoReveal,
|
||||||
|
Eth1Data: ð.Eth1Data{
|
||||||
|
DepositRoot: depositRoot,
|
||||||
|
DepositCount: depositCount,
|
||||||
|
BlockHash: blockHash,
|
||||||
|
},
|
||||||
|
Graffiti: graffiti,
|
||||||
|
ProposerSlashings: proposerSlashings,
|
||||||
|
AttesterSlashings: attesterSlashings,
|
||||||
|
Attestations: atts,
|
||||||
|
Deposits: deposits,
|
||||||
|
VoluntaryExits: exits,
|
||||||
|
SyncAggregate: ð.SyncAggregate{
|
||||||
|
SyncCommitteeBits: syncCommitteeBits,
|
||||||
|
SyncCommitteeSignature: syncCommitteeSig,
|
||||||
|
},
|
||||||
|
BlsToExecutionChanges: blsChanges,
|
||||||
|
SignedExecutionPayloadHeader: signedPayloadHeader,
|
||||||
|
PayloadAttestations: payloadAttestations,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PayloadAttestation) ToConsensus() (*eth.PayloadAttestation, error) {
|
||||||
|
if p == nil {
|
||||||
|
return nil, errNilValue
|
||||||
|
}
|
||||||
|
aggregationBits, err := bytesutil.DecodeHexWithLength(p.AggregationBits, fieldparams.PTCSize/8)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "AggregationBits")
|
||||||
|
}
|
||||||
|
data, err := p.Data.ToConsensus()
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Data")
|
||||||
|
}
|
||||||
|
sig, err := bytesutil.DecodeHexWithLength(p.Signature, fieldparams.BLSSignatureLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Signature")
|
||||||
|
}
|
||||||
|
return ð.PayloadAttestation{
|
||||||
|
AggregationBits: aggregationBits,
|
||||||
|
Data: data,
|
||||||
|
Signature: sig,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PayloadAttestationData) ToConsensus() (*eth.PayloadAttestationData, error) {
|
||||||
|
if p == nil {
|
||||||
|
return nil, errNilValue
|
||||||
|
}
|
||||||
|
beaconBlockRoot, err := bytesutil.DecodeHexWithLength(p.BeaconBlockRoot, fieldparams.RootLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "BeaconBlockRoot")
|
||||||
|
}
|
||||||
|
slot, err := strconv.ParseUint(p.Slot, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Slot")
|
||||||
|
}
|
||||||
|
payloadStatus, err := strconv.ParseUint(p.PayloadStatus, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "PayloadStatus")
|
||||||
|
}
|
||||||
|
return ð.PayloadAttestationData{
|
||||||
|
BeaconBlockRoot: beaconBlockRoot,
|
||||||
|
Slot: primitives.Slot(slot),
|
||||||
|
PayloadStatus: primitives.PTCStatus(payloadStatus),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SignedExecutionPayloadHeader) ToConsensus() (*enginev1.SignedExecutionPayloadHeader, error) {
|
||||||
|
if p == nil {
|
||||||
|
return nil, errNilValue
|
||||||
|
}
|
||||||
|
sig, err := bytesutil.DecodeHexWithLength(p.Signature, fieldparams.BLSSignatureLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Signature")
|
||||||
|
}
|
||||||
|
header, err := p.Message.ToConsensus()
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Header")
|
||||||
|
}
|
||||||
|
return &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: header,
|
||||||
|
Signature: sig,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ExecutionPayloadHeaderEPBS) ToConsensus() (*enginev1.ExecutionPayloadHeaderEPBS, error) {
|
||||||
|
parentBlockHash, err := bytesutil.DecodeHexWithLength(p.ParentBlockHash, common.HashLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "ParentBlockHash")
|
||||||
|
}
|
||||||
|
parentBlockRoot, err := bytesutil.DecodeHexWithLength(p.ParentBlockRoot, common.HashLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "ParentBlockRoot")
|
||||||
|
}
|
||||||
|
blockHash, err := bytesutil.DecodeHexWithLength(p.BlockHash, common.HashLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "BlockHash")
|
||||||
|
}
|
||||||
|
gasLimit, err := strconv.ParseUint(p.GasLimit, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "GasLimit")
|
||||||
|
}
|
||||||
|
builderIndex, err := strconv.ParseUint(p.BuilderIndex, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "BuilderIndex")
|
||||||
|
}
|
||||||
|
slot, err := strconv.ParseUint(p.Slot, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Slot")
|
||||||
|
}
|
||||||
|
value, err := strconv.ParseUint(p.Value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Value")
|
||||||
|
}
|
||||||
|
blobKzgCommitmentsRoot, err := bytesutil.DecodeHexWithLength(p.BlobKzgCommitmentsRoot, fieldparams.RootLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "BlobKzgCommitmentsRoot")
|
||||||
|
}
|
||||||
|
return &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
ParentBlockHash: parentBlockHash,
|
||||||
|
ParentBlockRoot: parentBlockRoot,
|
||||||
|
BlockHash: blockHash,
|
||||||
|
GasLimit: gasLimit,
|
||||||
|
BuilderIndex: primitives.ValidatorIndex(builderIndex),
|
||||||
|
Slot: primitives.Slot(slot),
|
||||||
|
Value: value,
|
||||||
|
BlobKzgCommitmentsRoot: blobKzgCommitmentsRoot,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *SignedBeaconBlockEpbs) ToConsensus() (*eth.SignedBeaconBlockEpbs, error) {
|
||||||
|
if b == nil {
|
||||||
|
return nil, errNilValue
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := bytesutil.DecodeHexWithLength(b.Signature, fieldparams.BLSSignatureLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Signature")
|
||||||
|
}
|
||||||
|
block, err := b.Message.ToConsensus()
|
||||||
|
if err != nil {
|
||||||
|
return nil, server.NewDecodeError(err, "Message")
|
||||||
|
}
|
||||||
|
return ð.SignedBeaconBlockEpbs{
|
||||||
|
Block: block,
|
||||||
|
Signature: sig,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func BeaconBlockEpbsFromConsensus(b *eth.BeaconBlockEpbs) (*BeaconBlockEpbs, error) {
|
||||||
|
signedPayloadHeader, err := SignedExecutionPayloadHeaderFromConsensus(b.Body.SignedExecutionPayloadHeader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payloadAttestations, err := PayloadAttestationsFromConsensus(b.Body.PayloadAttestations)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &BeaconBlockEpbs{
|
||||||
|
Slot: fmt.Sprintf("%d", b.Slot),
|
||||||
|
ProposerIndex: fmt.Sprintf("%d", b.ProposerIndex),
|
||||||
|
ParentRoot: hexutil.Encode(b.ParentRoot),
|
||||||
|
StateRoot: hexutil.Encode(b.StateRoot),
|
||||||
|
Body: &BeaconBlockBodyEpbs{
|
||||||
|
RandaoReveal: hexutil.Encode(b.Body.RandaoReveal),
|
||||||
|
Eth1Data: Eth1DataFromConsensus(b.Body.Eth1Data),
|
||||||
|
Graffiti: hexutil.Encode(b.Body.Graffiti),
|
||||||
|
ProposerSlashings: ProposerSlashingsFromConsensus(b.Body.ProposerSlashings),
|
||||||
|
AttesterSlashings: AttesterSlashingsElectraFromConsensus(b.Body.AttesterSlashings),
|
||||||
|
Attestations: AttsElectraFromConsensus(b.Body.Attestations),
|
||||||
|
Deposits: DepositsFromConsensus(b.Body.Deposits),
|
||||||
|
VoluntaryExits: SignedExitsFromConsensus(b.Body.VoluntaryExits),
|
||||||
|
SyncAggregate: &SyncAggregate{
|
||||||
|
SyncCommitteeBits: hexutil.Encode(b.Body.SyncAggregate.SyncCommitteeBits),
|
||||||
|
SyncCommitteeSignature: hexutil.Encode(b.Body.SyncAggregate.SyncCommitteeSignature),
|
||||||
|
},
|
||||||
|
BLSToExecutionChanges: SignedBLSChangesFromConsensus(b.Body.BlsToExecutionChanges),
|
||||||
|
SignedExecutionPayloadHeader: signedPayloadHeader,
|
||||||
|
PayloadAttestations: payloadAttestations,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SignedBeaconBlockEpbsFromConsensus(b *eth.SignedBeaconBlockEpbs) (*SignedBeaconBlockEpbs, error) {
|
||||||
|
block, err := BeaconBlockEpbsFromConsensus(b.Block)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &SignedBeaconBlockEpbs{
|
||||||
|
Message: block,
|
||||||
|
Signature: hexutil.Encode(b.Signature),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SignedExecutionPayloadHeaderFromConsensus(b *enginev1.SignedExecutionPayloadHeader) (*SignedExecutionPayloadHeader, error) {
|
||||||
|
header, err := ExecutionPayloadHeaderEPBSFromConsensus(b.Message)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &SignedExecutionPayloadHeader{
|
||||||
|
Message: header,
|
||||||
|
Signature: hexutil.Encode(b.Signature),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecutionPayloadHeaderEPBSFromConsensus(b *enginev1.ExecutionPayloadHeaderEPBS) (*ExecutionPayloadHeaderEPBS, error) {
|
||||||
|
return &ExecutionPayloadHeaderEPBS{
|
||||||
|
ParentBlockHash: hexutil.Encode(b.ParentBlockHash),
|
||||||
|
ParentBlockRoot: hexutil.Encode(b.ParentBlockRoot),
|
||||||
|
BlockHash: hexutil.Encode(b.BlockHash),
|
||||||
|
GasLimit: fmt.Sprintf("%d", b.GasLimit),
|
||||||
|
BuilderIndex: fmt.Sprintf("%d", b.BuilderIndex),
|
||||||
|
Slot: fmt.Sprintf("%d", b.Slot),
|
||||||
|
Value: fmt.Sprintf("%d", b.Value),
|
||||||
|
BlobKzgCommitmentsRoot: hexutil.Encode(b.BlobKzgCommitmentsRoot),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PayloadAttestationsFromConsensus(b []*eth.PayloadAttestation) ([]*PayloadAttestation, error) {
|
||||||
|
payloadAttestations := make([]*PayloadAttestation, len(b))
|
||||||
|
for i, p := range b {
|
||||||
|
data, err := PayloadAttestationDataFromConsensus(p.Data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payloadAttestations[i] = &PayloadAttestation{
|
||||||
|
AggregationBits: hexutil.Encode(p.AggregationBits),
|
||||||
|
Data: data,
|
||||||
|
Signature: hexutil.Encode(p.Signature),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return payloadAttestations, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PayloadAttestationDataFromConsensus(b *eth.PayloadAttestationData) (*PayloadAttestationData, error) {
|
||||||
|
return &PayloadAttestationData{
|
||||||
|
BeaconBlockRoot: hexutil.Encode(b.BeaconBlockRoot),
|
||||||
|
Slot: fmt.Sprintf("%d", b.Slot),
|
||||||
|
PayloadStatus: fmt.Sprintf("%d", b.PayloadStatus),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -6,9 +6,11 @@ go_library(
|
|||||||
"chain_info.go",
|
"chain_info.go",
|
||||||
"chain_info_forkchoice.go",
|
"chain_info_forkchoice.go",
|
||||||
"currently_syncing_block.go",
|
"currently_syncing_block.go",
|
||||||
|
"currently_syncing_execution_payload_envelope.go",
|
||||||
"defragment.go",
|
"defragment.go",
|
||||||
"error.go",
|
"error.go",
|
||||||
"execution_engine.go",
|
"execution_engine.go",
|
||||||
|
"execution_engine_epbs.go",
|
||||||
"forkchoice_update_execution.go",
|
"forkchoice_update_execution.go",
|
||||||
"head.go",
|
"head.go",
|
||||||
"head_sync_committee_info.go",
|
"head_sync_committee_info.go",
|
||||||
@@ -25,6 +27,8 @@ go_library(
|
|||||||
"receive_attestation.go",
|
"receive_attestation.go",
|
||||||
"receive_blob.go",
|
"receive_blob.go",
|
||||||
"receive_block.go",
|
"receive_block.go",
|
||||||
|
"receive_execution_payload_envelope.go",
|
||||||
|
"receive_payload_attestation_message.go",
|
||||||
"service.go",
|
"service.go",
|
||||||
"tracked_proposer.go",
|
"tracked_proposer.go",
|
||||||
"weak_subjectivity_checks.go",
|
"weak_subjectivity_checks.go",
|
||||||
@@ -43,6 +47,7 @@ go_library(
|
|||||||
"//beacon-chain/cache:go_default_library",
|
"//beacon-chain/cache:go_default_library",
|
||||||
"//beacon-chain/core/altair:go_default_library",
|
"//beacon-chain/core/altair:go_default_library",
|
||||||
"//beacon-chain/core/blocks:go_default_library",
|
"//beacon-chain/core/blocks:go_default_library",
|
||||||
|
"//beacon-chain/core/epbs:go_default_library",
|
||||||
"//beacon-chain/core/epoch/precompute:go_default_library",
|
"//beacon-chain/core/epoch/precompute:go_default_library",
|
||||||
"//beacon-chain/core/feed:go_default_library",
|
"//beacon-chain/core/feed:go_default_library",
|
||||||
"//beacon-chain/core/feed/state:go_default_library",
|
"//beacon-chain/core/feed/state:go_default_library",
|
||||||
@@ -96,6 +101,7 @@ go_library(
|
|||||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||||
"@com_github_sirupsen_logrus//:go_default_library",
|
"@com_github_sirupsen_logrus//:go_default_library",
|
||||||
|
"@io_opencensus_go//trace:go_default_library",
|
||||||
"@org_golang_x_sync//errgroup:go_default_library",
|
"@org_golang_x_sync//errgroup:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -108,6 +114,7 @@ go_test(
|
|||||||
"chain_info_norace_test.go",
|
"chain_info_norace_test.go",
|
||||||
"chain_info_test.go",
|
"chain_info_test.go",
|
||||||
"checktags_test.go",
|
"checktags_test.go",
|
||||||
|
"epbs_test.go",
|
||||||
"error_test.go",
|
"error_test.go",
|
||||||
"execution_engine_test.go",
|
"execution_engine_test.go",
|
||||||
"forkchoice_update_execution_test.go",
|
"forkchoice_update_execution_test.go",
|
||||||
@@ -123,6 +130,7 @@ go_test(
|
|||||||
"process_block_test.go",
|
"process_block_test.go",
|
||||||
"receive_attestation_test.go",
|
"receive_attestation_test.go",
|
||||||
"receive_block_test.go",
|
"receive_block_test.go",
|
||||||
|
"receive_execution_payload_envelope_test.go",
|
||||||
"service_norace_test.go",
|
"service_norace_test.go",
|
||||||
"service_test.go",
|
"service_test.go",
|
||||||
"setup_test.go",
|
"setup_test.go",
|
||||||
@@ -177,6 +185,7 @@ go_test(
|
|||||||
"//testing/assert:go_default_library",
|
"//testing/assert:go_default_library",
|
||||||
"//testing/require:go_default_library",
|
"//testing/require:go_default_library",
|
||||||
"//testing/util:go_default_library",
|
"//testing/util:go_default_library",
|
||||||
|
"//testing/util/random:go_default_library",
|
||||||
"//time:go_default_library",
|
"//time:go_default_library",
|
||||||
"//time/slots:go_default_library",
|
"//time/slots:go_default_library",
|
||||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ type ForkchoiceFetcher interface {
|
|||||||
GetProposerHead() [32]byte
|
GetProposerHead() [32]byte
|
||||||
SetForkChoiceGenesisTime(uint64)
|
SetForkChoiceGenesisTime(uint64)
|
||||||
UpdateHead(context.Context, primitives.Slot)
|
UpdateHead(context.Context, primitives.Slot)
|
||||||
HighestReceivedBlockSlot() primitives.Slot
|
HighestReceivedBlockSlotRoot() (primitives.Slot, [32]byte)
|
||||||
ReceivedBlocksLastEpoch() (uint64, error)
|
ReceivedBlocksLastEpoch() (uint64, error)
|
||||||
InsertNode(context.Context, state.BeaconState, consensus_blocks.ROBlock) error
|
InsertNode(context.Context, state.BeaconState, consensus_blocks.ROBlock) error
|
||||||
ForkChoiceDump(context.Context) (*forkchoice.Dump, error)
|
ForkChoiceDump(context.Context) (*forkchoice.Dump, error)
|
||||||
@@ -51,6 +51,8 @@ type ForkchoiceFetcher interface {
|
|||||||
ProposerBoost() [32]byte
|
ProposerBoost() [32]byte
|
||||||
RecentBlockSlot(root [32]byte) (primitives.Slot, error)
|
RecentBlockSlot(root [32]byte) (primitives.Slot, error)
|
||||||
IsCanonical(ctx context.Context, blockRoot [32]byte) (bool, error)
|
IsCanonical(ctx context.Context, blockRoot [32]byte) (bool, error)
|
||||||
|
GetPTCVote(root [32]byte) primitives.PTCStatus
|
||||||
|
HashForBlockRoot(root [32]byte) [32]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// TimeFetcher retrieves the Ethereum consensus data that's related to time.
|
// TimeFetcher retrieves the Ethereum consensus data that's related to time.
|
||||||
@@ -119,6 +121,12 @@ type OptimisticModeFetcher interface {
|
|||||||
IsOptimisticForRoot(ctx context.Context, root [32]byte) (bool, error)
|
IsOptimisticForRoot(ctx context.Context, root [32]byte) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExecutionPayloadFetcher defines a common interface that returns forkchoice
|
||||||
|
// information about payload block hashes
|
||||||
|
type ExecutionPayloadFetcher interface {
|
||||||
|
HashInForkchoice([32]byte) bool
|
||||||
|
}
|
||||||
|
|
||||||
// FinalizedCheckpt returns the latest finalized checkpoint from chain store.
|
// FinalizedCheckpt returns the latest finalized checkpoint from chain store.
|
||||||
func (s *Service) FinalizedCheckpt() *ethpb.Checkpoint {
|
func (s *Service) FinalizedCheckpt() *ethpb.Checkpoint {
|
||||||
s.cfg.ForkChoiceStore.RLock()
|
s.cfg.ForkChoiceStore.RLock()
|
||||||
@@ -400,6 +408,14 @@ func (s *Service) InForkchoice(root [32]byte) bool {
|
|||||||
return s.cfg.ForkChoiceStore.HasNode(root)
|
return s.cfg.ForkChoiceStore.HasNode(root)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HashInForkchoice returns true if the given payload block hash is found in
|
||||||
|
// forkchoice
|
||||||
|
func (s *Service) HashInForkchoice(hash [32]byte) bool {
|
||||||
|
s.cfg.ForkChoiceStore.RLock()
|
||||||
|
defer s.cfg.ForkChoiceStore.RUnlock()
|
||||||
|
return s.cfg.ForkChoiceStore.HasHash(hash)
|
||||||
|
}
|
||||||
|
|
||||||
// IsOptimisticForRoot takes the root as argument instead of the current head
|
// IsOptimisticForRoot takes the root as argument instead of the current head
|
||||||
// and returns true if it is optimistic.
|
// and returns true if it is optimistic.
|
||||||
func (s *Service) IsOptimisticForRoot(ctx context.Context, root [32]byte) (bool, error) {
|
func (s *Service) IsOptimisticForRoot(ctx context.Context, root [32]byte) (bool, error) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||||
consensus_blocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
consensus_blocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
||||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/forkchoice"
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/forkchoice"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,11 +31,11 @@ func (s *Service) SetForkChoiceGenesisTime(timestamp uint64) {
|
|||||||
s.cfg.ForkChoiceStore.SetGenesisTime(timestamp)
|
s.cfg.ForkChoiceStore.SetGenesisTime(timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HighestReceivedBlockSlot returns the corresponding value from forkchoice
|
// HighestReceivedBlockSlotRoot returns the corresponding value from forkchoice
|
||||||
func (s *Service) HighestReceivedBlockSlot() primitives.Slot {
|
func (s *Service) HighestReceivedBlockSlotRoot() (primitives.Slot, [32]byte) {
|
||||||
s.cfg.ForkChoiceStore.RLock()
|
s.cfg.ForkChoiceStore.RLock()
|
||||||
defer s.cfg.ForkChoiceStore.RUnlock()
|
defer s.cfg.ForkChoiceStore.RUnlock()
|
||||||
return s.cfg.ForkChoiceStore.HighestReceivedBlockSlot()
|
return s.cfg.ForkChoiceStore.HighestReceivedBlockSlotRoot()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReceivedBlocksLastEpoch returns the corresponding value from forkchoice
|
// ReceivedBlocksLastEpoch returns the corresponding value from forkchoice
|
||||||
@@ -100,3 +101,37 @@ func (s *Service) ParentRoot(root [32]byte) ([32]byte, error) {
|
|||||||
defer s.cfg.ForkChoiceStore.RUnlock()
|
defer s.cfg.ForkChoiceStore.RUnlock()
|
||||||
return s.cfg.ForkChoiceStore.ParentRoot(root)
|
return s.cfg.ForkChoiceStore.ParentRoot(root)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HashForBlockRoot wraps a call to the corresponding method in forkchoice
|
||||||
|
func (s *Service) HashForBlockRoot(root [32]byte) [32]byte {
|
||||||
|
s.cfg.ForkChoiceStore.RLock()
|
||||||
|
defer s.cfg.ForkChoiceStore.RUnlock()
|
||||||
|
return s.cfg.ForkChoiceStore.HashForBlockRoot(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPTCVote wraps a call to the corresponding method in forkchoice and checks
|
||||||
|
// the currently syncing status
|
||||||
|
// Warning: this method will return the current PTC status regardless of
|
||||||
|
// timeliness. A client MUST call this method when about to submit a PTC
|
||||||
|
// attestation, that is exactly at the threshold to submit the attestation.
|
||||||
|
func (s *Service) GetPTCVote(root [32]byte) primitives.PTCStatus {
|
||||||
|
s.cfg.ForkChoiceStore.RLock()
|
||||||
|
f := s.cfg.ForkChoiceStore.GetPTCVote()
|
||||||
|
s.cfg.ForkChoiceStore.RUnlock()
|
||||||
|
if f != primitives.PAYLOAD_ABSENT {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
f, isSyncing := s.payloadBeingSynced.isSyncing(root)
|
||||||
|
if isSyncing {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
return primitives.PAYLOAD_ABSENT
|
||||||
|
}
|
||||||
|
|
||||||
|
// insertPayloadEnvelope wraps a locked call to the corresponding method in
|
||||||
|
// forkchoice
|
||||||
|
func (s *Service) insertPayloadEnvelope(envelope interfaces.ROExecutionPayloadEnvelope) error {
|
||||||
|
s.cfg.ForkChoiceStore.Lock()
|
||||||
|
defer s.cfg.ForkChoiceStore.Unlock()
|
||||||
|
return s.cfg.ForkChoiceStore.InsertPayloadEnvelope(envelope)
|
||||||
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ func prepareForkchoiceState(
|
|||||||
blockRoot [32]byte,
|
blockRoot [32]byte,
|
||||||
parentRoot [32]byte,
|
parentRoot [32]byte,
|
||||||
payloadHash [32]byte,
|
payloadHash [32]byte,
|
||||||
|
parentHash [32]byte,
|
||||||
justified *ethpb.Checkpoint,
|
justified *ethpb.Checkpoint,
|
||||||
finalized *ethpb.Checkpoint,
|
finalized *ethpb.Checkpoint,
|
||||||
) (state.BeaconState, blocks.ROBlock, error) {
|
) (state.BeaconState, blocks.ROBlock, error) {
|
||||||
@@ -68,7 +69,8 @@ func prepareForkchoiceState(
|
|||||||
ParentRoot: parentRoot[:],
|
ParentRoot: parentRoot[:],
|
||||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||||
ExecutionPayload: &enginev1.ExecutionPayload{
|
ExecutionPayload: &enginev1.ExecutionPayload{
|
||||||
BlockHash: payloadHash[:],
|
BlockHash: payloadHash[:],
|
||||||
|
ParentHash: parentHash[:],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -141,7 +143,7 @@ func TestUnrealizedJustifiedBlockHash(t *testing.T) {
|
|||||||
service := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}}
|
service := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}}
|
||||||
ojc := ðpb.Checkpoint{Root: []byte{'j'}}
|
ojc := ðpb.Checkpoint{Root: []byte{'j'}}
|
||||||
ofc := ðpb.Checkpoint{Root: []byte{'f'}}
|
ofc := ðpb.Checkpoint{Root: []byte{'f'}}
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
st, roblock, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
||||||
service.cfg.ForkChoiceStore.SetBalancesByRooter(func(_ context.Context, _ [32]byte) ([]uint64, error) { return []uint64{}, nil })
|
service.cfg.ForkChoiceStore.SetBalancesByRooter(func(_ context.Context, _ [32]byte) ([]uint64, error) { return []uint64{}, nil })
|
||||||
@@ -335,22 +337,22 @@ func TestService_ChainHeads(t *testing.T) {
|
|||||||
c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}}
|
c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}}
|
||||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
st, roblock, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
st, roblock, err = prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, [32]byte{'A'}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
st, roblock, err = prepareForkchoiceState(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, [32]byte{'A'}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
st, roblock, err = prepareForkchoiceState(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, [32]byte{'C'}, [32]byte{'B'}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 103, [32]byte{'d'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
st, roblock, err = prepareForkchoiceState(ctx, 103, [32]byte{'d'}, [32]byte{'a'}, [32]byte{'D'}, [32]byte{'C'}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 104, [32]byte{'e'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
st, roblock, err = prepareForkchoiceState(ctx, 104, [32]byte{'e'}, [32]byte{'b'}, [32]byte{'E'}, [32]byte{'D'}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
||||||
|
|
||||||
@@ -432,10 +434,10 @@ func TestService_IsOptimistic(t *testing.T) {
|
|||||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}, head: &head{root: [32]byte{'b'}}}
|
c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}, head: &head{root: [32]byte{'b'}}}
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
st, roblock, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
st, roblock, err = prepareForkchoiceState(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'A'}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
||||||
|
|
||||||
@@ -468,10 +470,10 @@ func TestService_IsOptimisticForRoot(t *testing.T) {
|
|||||||
c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}, head: &head{root: [32]byte{'b'}}}
|
c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}, head: &head{root: [32]byte{'b'}}}
|
||||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
st, roblock, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, [32]byte{'A'}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
st, roblock, err = prepareForkchoiceState(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package blockchain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
|
)
|
||||||
|
|
||||||
|
type currentlySyncingPayload struct {
|
||||||
|
sync.Mutex
|
||||||
|
roots map[[32]byte]primitives.PTCStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *currentlySyncingPayload) set(envelope interfaces.ROExecutionPayloadEnvelope) {
|
||||||
|
b.Lock()
|
||||||
|
defer b.Unlock()
|
||||||
|
b.roots[envelope.BeaconBlockRoot()] = primitives.PAYLOAD_PRESENT
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *currentlySyncingPayload) unset(root [32]byte) {
|
||||||
|
b.Lock()
|
||||||
|
defer b.Unlock()
|
||||||
|
delete(b.roots, root)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *currentlySyncingPayload) isSyncing(root [32]byte) (status primitives.PTCStatus, isSyncing bool) {
|
||||||
|
b.Lock()
|
||||||
|
defer b.Unlock()
|
||||||
|
status, isSyncing = b.roots[root]
|
||||||
|
return
|
||||||
|
}
|
||||||
18
beacon-chain/blockchain/epbs_test.go
Normal file
18
beacon-chain/blockchain/epbs_test.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package blockchain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
doublylinkedtree "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/doubly-linked-tree"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServiceGetPTCVote(t *testing.T) {
|
||||||
|
c := ¤tlySyncingPayload{roots: make(map[[32]byte]primitives.PTCStatus)}
|
||||||
|
s := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}, payloadBeingSynced: c}
|
||||||
|
r := [32]byte{'r'}
|
||||||
|
require.Equal(t, primitives.PAYLOAD_ABSENT, s.GetPTCVote(r))
|
||||||
|
c.roots[r] = primitives.PAYLOAD_WITHHELD
|
||||||
|
require.Equal(t, primitives.PAYLOAD_WITHHELD, s.GetPTCVote(r))
|
||||||
|
}
|
||||||
@@ -30,6 +30,9 @@ var (
|
|||||||
ErrNotCheckpoint = errors.New("not a checkpoint in forkchoice")
|
ErrNotCheckpoint = errors.New("not a checkpoint in forkchoice")
|
||||||
// ErrNilHead is returned when no head is present in the blockchain service.
|
// ErrNilHead is returned when no head is present in the blockchain service.
|
||||||
ErrNilHead = errors.New("nil head")
|
ErrNilHead = errors.New("nil head")
|
||||||
|
// errInvalidValidatorIndex is returned when a validator index is
|
||||||
|
// invalid or unexpected
|
||||||
|
errInvalidValidatorIndex = errors.New("invalid validator index")
|
||||||
)
|
)
|
||||||
|
|
||||||
var errMaxBlobsExceeded = errors.New("Expected commitments in block exceeds MAX_BLOBS_PER_BLOCK")
|
var errMaxBlobsExceeded = errors.New("Expected commitments in block exceeds MAX_BLOBS_PER_BLOCK")
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package blockchain
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
@@ -30,6 +31,8 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const blobCommitmentVersionKZG uint8 = 0x01
|
||||||
|
|
||||||
var defaultLatestValidHash = bytesutil.PadTo([]byte{0xff}, 32)
|
var defaultLatestValidHash = bytesutil.PadTo([]byte{0xff}, 32)
|
||||||
|
|
||||||
// notifyForkchoiceUpdate signals execution engine the fork choice updates. Execution engine should:
|
// notifyForkchoiceUpdate signals execution engine the fork choice updates. Execution engine should:
|
||||||
@@ -95,6 +98,14 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *fcuConfig) (*
|
|||||||
log.WithError(err).Error("Could not set head root to invalid")
|
log.WithError(err).Error("Could not set head root to invalid")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
if len(invalidRoots) == 0 {
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"slot": headBlk.Slot(),
|
||||||
|
"blockRoot": fmt.Sprintf("%#x", bytesutil.Trunc(headRoot[:])),
|
||||||
|
}).Warn("invalid payload")
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
if err := s.removeInvalidBlockAndState(ctx, invalidRoots); err != nil {
|
if err := s.removeInvalidBlockAndState(ctx, invalidRoots); err != nil {
|
||||||
log.WithError(err).Error("Could not remove invalid block and state")
|
log.WithError(err).Error("Could not remove invalid block and state")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -109,6 +120,7 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *fcuConfig) (*
|
|||||||
}).Warn("Pruned invalid blocks, could not update head root")
|
}).Warn("Pruned invalid blocks, could not update head root")
|
||||||
return nil, invalidBlock{error: ErrInvalidPayload, root: arg.headRoot, invalidAncestorRoots: invalidRoots}
|
return nil, invalidBlock{error: ErrInvalidPayload, root: arg.headRoot, invalidAncestorRoots: invalidRoots}
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := s.getBlock(ctx, r)
|
b, err := s.getBlock(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("Could not get head block")
|
log.WithError(err).Error("Could not get head block")
|
||||||
@@ -456,7 +468,13 @@ func kzgCommitmentsToVersionedHashes(body interfaces.ReadOnlyBeaconBlockBody) ([
|
|||||||
|
|
||||||
versionedHashes := make([]common.Hash, len(commitments))
|
versionedHashes := make([]common.Hash, len(commitments))
|
||||||
for i, commitment := range commitments {
|
for i, commitment := range commitments {
|
||||||
versionedHashes[i] = primitives.ConvertKzgCommitmentToVersionedHash(commitment)
|
versionedHashes[i] = ConvertKzgCommitmentToVersionedHash(commitment)
|
||||||
}
|
}
|
||||||
return versionedHashes, nil
|
return versionedHashes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ConvertKzgCommitmentToVersionedHash(commitment []byte) common.Hash {
|
||||||
|
versionedHash := sha256.Sum256(commitment)
|
||||||
|
versionedHash[0] = blobCommitmentVersionKZG
|
||||||
|
return versionedHash
|
||||||
|
}
|
||||||
|
|||||||
62
beacon-chain/blockchain/execution_engine_epbs.go
Normal file
62
beacon-chain/blockchain/execution_engine_epbs.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package blockchain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/execution"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/config/features"
|
||||||
|
payloadattribute "github.com/prysmaticlabs/prysm/v5/consensus-types/payload-attribute"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace"
|
||||||
|
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// notifyForkchoiceUpdate signals execution engine the fork choice updates. Execution engine should:
|
||||||
|
// 1. Re-organizes the execution payload chain and corresponding state to make head_block_hash the head.
|
||||||
|
// 2. Applies finality to the execution state: it irreversibly persists the chain of all execution payloads and corresponding state, up to and including finalized_block_hash.
|
||||||
|
func (s *Service) notifyForkchoiceUpdateEPBS(ctx context.Context, blockhash [32]byte, attributes payloadattribute.Attributer) (*enginev1.PayloadIDBytes, error) {
|
||||||
|
ctx, span := trace.StartSpan(ctx, "blockChain.notifyForkchoiceUpdateEPBS")
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
finalizedHash := s.cfg.ForkChoiceStore.FinalizedPayloadBlockHash()
|
||||||
|
justifiedHash := s.cfg.ForkChoiceStore.UnrealizedJustifiedPayloadBlockHash()
|
||||||
|
fcs := &enginev1.ForkchoiceState{
|
||||||
|
HeadBlockHash: blockhash[:],
|
||||||
|
SafeBlockHash: justifiedHash[:],
|
||||||
|
FinalizedBlockHash: finalizedHash[:],
|
||||||
|
}
|
||||||
|
if attributes == nil {
|
||||||
|
attributes = payloadattribute.EmptyWithVersion(version.EPBS)
|
||||||
|
}
|
||||||
|
payloadID, lastValidHash, err := s.cfg.ExecutionEngineCaller.ForkchoiceUpdated(ctx, fcs, attributes)
|
||||||
|
if err != nil {
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, execution.ErrAcceptedSyncingPayloadStatus):
|
||||||
|
forkchoiceUpdatedOptimisticNodeCount.Inc()
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"headPayloadBlockHash": fmt.Sprintf("%#x", bytesutil.Trunc(blockhash[:])),
|
||||||
|
"finalizedPayloadBlockHash": fmt.Sprintf("%#x", bytesutil.Trunc(finalizedHash[:])),
|
||||||
|
}).Info("Called fork choice updated with optimistic block")
|
||||||
|
return payloadID, nil
|
||||||
|
case errors.Is(err, execution.ErrInvalidPayloadStatus):
|
||||||
|
log.WithError(err).Info("forkchoice updated to invalid block")
|
||||||
|
return nil, invalidBlock{error: ErrInvalidPayload, root: [32]byte(lastValidHash)}
|
||||||
|
default:
|
||||||
|
log.WithError(err).Error(ErrUndefinedExecutionEngineError)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
forkchoiceUpdatedValidNodeCount.Inc()
|
||||||
|
// If the forkchoice update call has an attribute, update the payload ID cache.
|
||||||
|
hasAttr := attributes != nil && !attributes.IsEmpty()
|
||||||
|
if hasAttr && payloadID == nil && !features.Get().PrepareAllPayloads {
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"blockHash": fmt.Sprintf("%#x", blockhash[:]),
|
||||||
|
}).Error("Received nil payload ID on VALID engine response")
|
||||||
|
}
|
||||||
|
return payloadID, nil
|
||||||
|
}
|
||||||
@@ -46,13 +46,13 @@ func Test_NotifyForkchoiceUpdate_GetPayloadAttrErrorCanContinue(t *testing.T) {
|
|||||||
|
|
||||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 1, altairBlkRoot, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 1, altairBlkRoot, [32]byte{}, [32]byte{'A'}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 2, bellatrixBlkRoot, altairBlkRoot, params.BeaconConfig().ZeroHash, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 2, bellatrixBlkRoot, altairBlkRoot, [32]byte{'B'}, [32]byte{'A'}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
|
|
||||||
@@ -104,13 +104,13 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
|
|||||||
|
|
||||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 1, altairBlkRoot, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 1, altairBlkRoot, [32]byte{}, [32]byte{'A'}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 2, bellatrixBlkRoot, altairBlkRoot, params.BeaconConfig().ZeroHash, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 2, bellatrixBlkRoot, altairBlkRoot, [32]byte{'B'}, [32]byte{'A'}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
|
|
||||||
@@ -287,16 +287,16 @@ func Test_NotifyForkchoiceUpdate_NIlLVH(t *testing.T) {
|
|||||||
require.NoError(t, fcs.UpdateJustifiedCheckpoint(ctx, &forkchoicetypes.Checkpoint{}))
|
require.NoError(t, fcs.UpdateJustifiedCheckpoint(ctx, &forkchoicetypes.Checkpoint{}))
|
||||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, 1, bra, [32]byte{}, [32]byte{'A'}, ojc, ofc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, 1, bra, [32]byte{}, [32]byte{'A'}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 2, brb, bra, [32]byte{'B'}, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 2, brb, bra, [32]byte{'B'}, [32]byte{'A'}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 3, brc, brb, [32]byte{'C'}, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 3, brc, brb, [32]byte{'C'}, [32]byte{'B'}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 4, brd, brc, [32]byte{'D'}, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 4, brd, brc, [32]byte{'D'}, [32]byte{'C'}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
|
|
||||||
@@ -316,10 +316,8 @@ func Test_NotifyForkchoiceUpdate_NIlLVH(t *testing.T) {
|
|||||||
headRoot: brd,
|
headRoot: brd,
|
||||||
}
|
}
|
||||||
_, err = service.notifyForkchoiceUpdate(ctx, a)
|
_, err = service.notifyForkchoiceUpdate(ctx, a)
|
||||||
require.Equal(t, true, IsInvalidBlock(err))
|
// The incoming block is not invalid because the empty node is still valid on ePBS.
|
||||||
require.Equal(t, brd, InvalidBlockRoot(err))
|
require.Equal(t, false, IsInvalidBlock(err))
|
||||||
require.Equal(t, brd, InvalidAncestorRoots(err)[0])
|
|
||||||
require.Equal(t, 1, len(InvalidAncestorRoots(err)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -398,28 +396,28 @@ func Test_NotifyForkchoiceUpdateRecursive_DoublyLinkedTree(t *testing.T) {
|
|||||||
require.NoError(t, fcs.UpdateJustifiedCheckpoint(ctx, &forkchoicetypes.Checkpoint{}))
|
require.NoError(t, fcs.UpdateJustifiedCheckpoint(ctx, &forkchoicetypes.Checkpoint{}))
|
||||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, 1, bra, [32]byte{}, [32]byte{'A'}, ojc, ofc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, 1, bra, [32]byte{}, [32]byte{'A'}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
bState, _ := util.DeterministicGenesisState(t, 10)
|
bState, _ := util.DeterministicGenesisState(t, 10)
|
||||||
require.NoError(t, beaconDB.SaveState(ctx, bState, bra))
|
require.NoError(t, beaconDB.SaveState(ctx, bState, bra))
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 2, brb, bra, [32]byte{'B'}, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 2, brb, bra, [32]byte{'B'}, [32]byte{'A'}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 3, brc, brb, [32]byte{'C'}, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 3, brc, brb, [32]byte{'C'}, [32]byte{'B'}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 4, brd, brc, [32]byte{'D'}, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 4, brd, brc, [32]byte{'D'}, [32]byte{'C'}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 5, bre, brb, [32]byte{'E'}, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 5, bre, brb, [32]byte{'E'}, [32]byte{'D'}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 6, brf, bre, [32]byte{'F'}, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 6, brf, bre, [32]byte{'F'}, [32]byte{'E'}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 7, brg, bre, [32]byte{'G'}, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 7, brg, bre, [32]byte{'G'}, [32]byte{'F'}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
|
|
||||||
@@ -512,10 +510,10 @@ func Test_NotifyNewPayload(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 1, r, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 1, r, [32]byte{}, [32]byte{'A'}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
|
|
||||||
@@ -692,7 +690,7 @@ func Test_NotifyNewPayload(t *testing.T) {
|
|||||||
}
|
}
|
||||||
service.cfg.ExecutionEngineCaller = e
|
service.cfg.ExecutionEngineCaller = e
|
||||||
root := [32]byte{'a'}
|
root := [32]byte{'a'}
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, 0, root, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, 0, root, [32]byte{}, params.BeaconConfig().ZeroHash, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
postVersion, postHeader, err := getStateVersionAndPayload(tt.postState)
|
postVersion, postHeader, err := getStateVersionAndPayload(tt.postState)
|
||||||
@@ -759,17 +757,17 @@ func Test_reportInvalidBlock(t *testing.T) {
|
|||||||
service, tr := minimalTestService(t)
|
service, tr := minimalTestService(t)
|
||||||
ctx, _, fcs := tr.ctx, tr.db, tr.fcs
|
ctx, _, fcs := tr.ctx, tr.db, tr.fcs
|
||||||
jcp := ðpb.Checkpoint{}
|
jcp := ðpb.Checkpoint{}
|
||||||
st, root, err := prepareForkchoiceState(ctx, 0, [32]byte{'A'}, [32]byte{}, [32]byte{'a'}, jcp, jcp)
|
st, root, err := prepareForkchoiceState(ctx, 0, [32]byte{'A'}, [32]byte{}, [32]byte{'a'}, [32]byte{}, jcp, jcp)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, st, root))
|
require.NoError(t, fcs.InsertNode(ctx, st, root))
|
||||||
st, root, err = prepareForkchoiceState(ctx, 1, [32]byte{'B'}, [32]byte{'A'}, [32]byte{'b'}, jcp, jcp)
|
st, root, err = prepareForkchoiceState(ctx, 1, [32]byte{'B'}, [32]byte{'A'}, [32]byte{'b'}, [32]byte{'a'}, jcp, jcp)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, st, root))
|
require.NoError(t, fcs.InsertNode(ctx, st, root))
|
||||||
st, root, err = prepareForkchoiceState(ctx, 2, [32]byte{'C'}, [32]byte{'B'}, [32]byte{'c'}, jcp, jcp)
|
st, root, err = prepareForkchoiceState(ctx, 2, [32]byte{'C'}, [32]byte{'B'}, [32]byte{'c'}, [32]byte{'b'}, jcp, jcp)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, st, root))
|
require.NoError(t, fcs.InsertNode(ctx, st, root))
|
||||||
|
|
||||||
st, root, err = prepareForkchoiceState(ctx, 3, [32]byte{'D'}, [32]byte{'C'}, [32]byte{'d'}, jcp, jcp)
|
st, root, err = prepareForkchoiceState(ctx, 3, [32]byte{'D'}, [32]byte{'C'}, [32]byte{'d'}, [32]byte{'c'}, jcp, jcp)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, st, root))
|
require.NoError(t, fcs.InsertNode(ctx, st, root))
|
||||||
|
|
||||||
@@ -931,7 +929,7 @@ func Test_UpdateLastValidatedCheckpoint(t *testing.T) {
|
|||||||
fjc := &forkchoicetypes.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash}
|
fjc := &forkchoicetypes.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash}
|
||||||
require.NoError(t, fcs.UpdateJustifiedCheckpoint(ctx, fjc))
|
require.NoError(t, fcs.UpdateJustifiedCheckpoint(ctx, fjc))
|
||||||
require.NoError(t, fcs.UpdateFinalizedCheckpoint(fjc))
|
require.NoError(t, fcs.UpdateFinalizedCheckpoint(fjc))
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, 0, genesisRoot, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, ojc, ofc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, 0, genesisRoot, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
fcs.SetOriginRoot(genesisRoot)
|
fcs.SetOriginRoot(genesisRoot)
|
||||||
@@ -965,7 +963,7 @@ func Test_UpdateLastValidatedCheckpoint(t *testing.T) {
|
|||||||
require.NoError(t, beaconDB.SaveStateSummary(ctx, opStateSummary))
|
require.NoError(t, beaconDB.SaveStateSummary(ctx, opStateSummary))
|
||||||
tenjc := ðpb.Checkpoint{Epoch: 10, Root: genesisRoot[:]}
|
tenjc := ðpb.Checkpoint{Epoch: 10, Root: genesisRoot[:]}
|
||||||
tenfc := ðpb.Checkpoint{Epoch: 10, Root: genesisRoot[:]}
|
tenfc := ðpb.Checkpoint{Epoch: 10, Root: genesisRoot[:]}
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 320, opRoot, genesisRoot, params.BeaconConfig().ZeroHash, tenjc, tenfc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 320, opRoot, genesisRoot, params.BeaconConfig().ZeroHash, [32]byte{}, tenjc, tenfc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
assert.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, opRoot))
|
assert.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, opRoot))
|
||||||
@@ -994,7 +992,7 @@ func Test_UpdateLastValidatedCheckpoint(t *testing.T) {
|
|||||||
require.NoError(t, beaconDB.SaveStateSummary(ctx, validSummary))
|
require.NoError(t, beaconDB.SaveStateSummary(ctx, validSummary))
|
||||||
twentyjc := ðpb.Checkpoint{Epoch: 20, Root: validRoot[:]}
|
twentyjc := ðpb.Checkpoint{Epoch: 20, Root: validRoot[:]}
|
||||||
twentyfc := ðpb.Checkpoint{Epoch: 20, Root: validRoot[:]}
|
twentyfc := ðpb.Checkpoint{Epoch: 20, Root: validRoot[:]}
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 640, validRoot, genesisRoot, params.BeaconConfig().ZeroHash, twentyjc, twentyfc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 640, validRoot, genesisRoot, params.BeaconConfig().ZeroHash, [32]byte{}, twentyjc, twentyfc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
fcs.SetBalancesByRooter(func(_ context.Context, _ [32]byte) ([]uint64, error) { return []uint64{}, nil })
|
fcs.SetBalancesByRooter(func(_ context.Context, _ [32]byte) ([]uint64, error) { return []uint64{}, nil })
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
@@ -1056,8 +1054,8 @@ func TestService_removeInvalidBlockAndState(t *testing.T) {
|
|||||||
|
|
||||||
require.NoError(t, service.removeInvalidBlockAndState(ctx, [][32]byte{r1, r2}))
|
require.NoError(t, service.removeInvalidBlockAndState(ctx, [][32]byte{r1, r2}))
|
||||||
|
|
||||||
require.Equal(t, false, service.hasBlock(ctx, r1))
|
require.Equal(t, false, service.chainHasBlock(ctx, r1))
|
||||||
require.Equal(t, false, service.hasBlock(ctx, r2))
|
require.Equal(t, false, service.chainHasBlock(ctx, r2))
|
||||||
require.Equal(t, false, service.cfg.BeaconDB.HasStateSummary(ctx, r1))
|
require.Equal(t, false, service.cfg.BeaconDB.HasStateSummary(ctx, r1))
|
||||||
require.Equal(t, false, service.cfg.BeaconDB.HasStateSummary(ctx, r2))
|
require.Equal(t, false, service.cfg.BeaconDB.HasStateSummary(ctx, r2))
|
||||||
has, err := service.cfg.StateGen.HasState(ctx, r1)
|
has, err := service.cfg.StateGen.HasState(ctx, r1)
|
||||||
|
|||||||
@@ -122,13 +122,13 @@ func TestService_forkchoiceUpdateWithExecution_SameHeadRootNewProposer(t *testin
|
|||||||
|
|
||||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 1, altairBlkRoot, [32]byte{}, params.BeaconConfig().ZeroHash, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 1, altairBlkRoot, [32]byte{}, params.BeaconConfig().ZeroHash, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 2, bellatrixBlkRoot, altairBlkRoot, params.BeaconConfig().ZeroHash, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 2, bellatrixBlkRoot, altairBlkRoot, params.BeaconConfig().ZeroHash, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||||
|
|
||||||
@@ -164,10 +164,10 @@ func TestShouldOverrideFCU(t *testing.T) {
|
|||||||
headRoot := [32]byte{'b'}
|
headRoot := [32]byte{'b'}
|
||||||
parentRoot := [32]byte{'a'}
|
parentRoot := [32]byte{'a'}
|
||||||
ojc := ðpb.Checkpoint{}
|
ojc := ðpb.Checkpoint{}
|
||||||
st, root, err := prepareForkchoiceState(ctx, 1, parentRoot, [32]byte{}, [32]byte{}, ojc, ojc)
|
st, root, err := prepareForkchoiceState(ctx, 1, parentRoot, [32]byte{}, [32]byte{}, [32]byte{}, ojc, ojc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, st, root))
|
require.NoError(t, fcs.InsertNode(ctx, st, root))
|
||||||
st, root, err = prepareForkchoiceState(ctx, 2, headRoot, parentRoot, [32]byte{}, ojc, ojc)
|
st, root, err = prepareForkchoiceState(ctx, 2, headRoot, parentRoot, [32]byte{}, [32]byte{}, ojc, ojc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, st, root))
|
require.NoError(t, fcs.InsertNode(ctx, st, root))
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func TestSaveHead_Different(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, oldBlock.Block().Slot(), oldRoot, oldBlock.Block().ParentRoot(), [32]byte{}, ojc, ofc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, oldBlock.Block().Slot(), oldRoot, oldBlock.Block().ParentRoot(), [32]byte{}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
service.head = &head{
|
service.head = &head{
|
||||||
@@ -63,11 +63,11 @@ func TestSaveHead_Different(t *testing.T) {
|
|||||||
wsb := util.SaveBlock(t, context.Background(), service.cfg.BeaconDB, newHeadSignedBlock)
|
wsb := util.SaveBlock(t, context.Background(), service.cfg.BeaconDB, newHeadSignedBlock)
|
||||||
newRoot, err := newHeadBlock.HashTreeRoot()
|
newRoot, err := newHeadBlock.HashTreeRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, slots.PrevSlot(wsb.Block().Slot()), wsb.Block().ParentRoot(), service.cfg.ForkChoiceStore.CachedHeadRoot(), [32]byte{}, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, slots.PrevSlot(wsb.Block().Slot()), wsb.Block().ParentRoot(), service.cfg.ForkChoiceStore.CachedHeadRoot(), [32]byte{}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
|
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, wsb.Block().Slot(), newRoot, wsb.Block().ParentRoot(), [32]byte{}, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, wsb.Block().Slot(), newRoot, wsb.Block().ParentRoot(), [32]byte{}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
headState, err := util.NewBeaconState()
|
headState, err := util.NewBeaconState()
|
||||||
@@ -101,7 +101,7 @@ func TestSaveHead_Different_Reorg(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, oldBlock.Block().Slot(), oldRoot, oldBlock.Block().ParentRoot(), [32]byte{}, ojc, ofc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, oldBlock.Block().Slot(), oldRoot, oldBlock.Block().ParentRoot(), [32]byte{}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
service.head = &head{
|
service.head = &head{
|
||||||
@@ -110,7 +110,7 @@ func TestSaveHead_Different_Reorg(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reorgChainParent := [32]byte{'B'}
|
reorgChainParent := [32]byte{'B'}
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 0, reorgChainParent, oldRoot, oldBlock.Block().ParentRoot(), ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, 0, reorgChainParent, oldRoot, oldBlock.Block().ParentRoot(), [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ func TestSaveHead_Different_Reorg(t *testing.T) {
|
|||||||
wsb := util.SaveBlock(t, context.Background(), service.cfg.BeaconDB, newHeadSignedBlock)
|
wsb := util.SaveBlock(t, context.Background(), service.cfg.BeaconDB, newHeadSignedBlock)
|
||||||
newRoot, err := newHeadBlock.HashTreeRoot()
|
newRoot, err := newHeadBlock.HashTreeRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, wsb.Block().Slot(), newRoot, wsb.Block().ParentRoot(), [32]byte{}, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, wsb.Block().Slot(), newRoot, wsb.Block().ParentRoot(), [32]byte{}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
headState, err := util.NewBeaconState()
|
headState, err := util.NewBeaconState()
|
||||||
@@ -238,11 +238,11 @@ func TestRetrieveHead_ReadOnly(t *testing.T) {
|
|||||||
wsb := util.SaveBlock(t, context.Background(), service.cfg.BeaconDB, newHeadSignedBlock)
|
wsb := util.SaveBlock(t, context.Background(), service.cfg.BeaconDB, newHeadSignedBlock)
|
||||||
newRoot, err := newHeadBlock.HashTreeRoot()
|
newRoot, err := newHeadBlock.HashTreeRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, slots.PrevSlot(wsb.Block().Slot()), wsb.Block().ParentRoot(), service.cfg.ForkChoiceStore.CachedHeadRoot(), [32]byte{}, ojc, ofc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, slots.PrevSlot(wsb.Block().Slot()), wsb.Block().ParentRoot(), service.cfg.ForkChoiceStore.CachedHeadRoot(), [32]byte{}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
|
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, wsb.Block().Slot(), newRoot, wsb.Block().ParentRoot(), [32]byte{}, ojc, ofc)
|
state, blkRoot, err = prepareForkchoiceState(ctx, wsb.Block().Slot(), newRoot, wsb.Block().ParentRoot(), [32]byte{}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
headState, err := util.NewBeaconState()
|
headState, err := util.NewBeaconState()
|
||||||
@@ -304,7 +304,7 @@ func TestSaveOrphanedAtts(t *testing.T) {
|
|||||||
for _, blk := range []*ethpb.SignedBeaconBlock{blkG, blk1, blk2, blk3, blk4} {
|
for _, blk := range []*ethpb.SignedBeaconBlock{blkG, blk1, blk2, blk3, blk4} {
|
||||||
r, err := blk.Block.HashTreeRoot()
|
r, err := blk.Block.HashTreeRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, blk.Block.Slot, r, bytesutil.ToBytes32(blk.Block.ParentRoot), [32]byte{}, ojc, ofc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, blk.Block.Slot, r, bytesutil.ToBytes32(blk.Block.ParentRoot), [32]byte{}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
util.SaveBlock(t, ctx, beaconDB, blk)
|
util.SaveBlock(t, ctx, beaconDB, blk)
|
||||||
@@ -381,7 +381,7 @@ func TestSaveOrphanedOps(t *testing.T) {
|
|||||||
for _, blk := range []*ethpb.SignedBeaconBlock{blkG, blk1, blk2, blk3, blk4} {
|
for _, blk := range []*ethpb.SignedBeaconBlock{blkG, blk1, blk2, blk3, blk4} {
|
||||||
r, err := blk.Block.HashTreeRoot()
|
r, err := blk.Block.HashTreeRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, blk.Block.Slot, r, bytesutil.ToBytes32(blk.Block.ParentRoot), [32]byte{}, ojc, ofc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, blk.Block.Slot, r, bytesutil.ToBytes32(blk.Block.ParentRoot), [32]byte{}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
util.SaveBlock(t, ctx, beaconDB, blk)
|
util.SaveBlock(t, ctx, beaconDB, blk)
|
||||||
@@ -451,7 +451,7 @@ func TestSaveOrphanedAtts_CanFilter(t *testing.T) {
|
|||||||
for _, blk := range []*ethpb.SignedBeaconBlockCapella{blkG, blk1, blk2, blk4} {
|
for _, blk := range []*ethpb.SignedBeaconBlockCapella{blkG, blk1, blk2, blk4} {
|
||||||
r, err := blk.Block.HashTreeRoot()
|
r, err := blk.Block.HashTreeRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, blk.Block.Slot, r, bytesutil.ToBytes32(blk.Block.ParentRoot), [32]byte{}, ojc, ofc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, blk.Block.Slot, r, bytesutil.ToBytes32(blk.Block.ParentRoot), [32]byte{}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
util.SaveBlock(t, ctx, beaconDB, blk)
|
util.SaveBlock(t, ctx, beaconDB, blk)
|
||||||
@@ -509,7 +509,7 @@ func TestSaveOrphanedAtts_DoublyLinkedTrie(t *testing.T) {
|
|||||||
for _, blk := range []*ethpb.SignedBeaconBlock{blkG, blk1, blk2, blk3, blk4} {
|
for _, blk := range []*ethpb.SignedBeaconBlock{blkG, blk1, blk2, blk3, blk4} {
|
||||||
r, err := blk.Block.HashTreeRoot()
|
r, err := blk.Block.HashTreeRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, blk.Block.Slot, r, bytesutil.ToBytes32(blk.Block.ParentRoot), [32]byte{}, ojc, ofc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, blk.Block.Slot, r, bytesutil.ToBytes32(blk.Block.ParentRoot), [32]byte{}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
util.SaveBlock(t, ctx, beaconDB, blk)
|
util.SaveBlock(t, ctx, beaconDB, blk)
|
||||||
@@ -568,7 +568,7 @@ func TestSaveOrphanedAtts_CanFilter_DoublyLinkedTrie(t *testing.T) {
|
|||||||
for _, blk := range []*ethpb.SignedBeaconBlock{blkG, blk1, blk2, blk4} {
|
for _, blk := range []*ethpb.SignedBeaconBlock{blkG, blk1, blk2, blk4} {
|
||||||
r, err := blk.Block.HashTreeRoot()
|
r, err := blk.Block.HashTreeRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, blk.Block.Slot, r, bytesutil.ToBytes32(blk.Block.ParentRoot), [32]byte{}, ojc, ofc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, blk.Block.Slot, r, bytesutil.ToBytes32(blk.Block.ParentRoot), [32]byte{}, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
util.SaveBlock(t, ctx, beaconDB, blk)
|
util.SaveBlock(t, ctx, beaconDB, blk)
|
||||||
@@ -583,7 +583,7 @@ func TestUpdateHead_noSavedChanges(t *testing.T) {
|
|||||||
ctx, beaconDB, fcs := tr.ctx, tr.db, tr.fcs
|
ctx, beaconDB, fcs := tr.ctx, tr.db, tr.fcs
|
||||||
|
|
||||||
ojp := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ojp := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
st, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, [32]byte{}, ojp, ojp)
|
st, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, [32]byte{}, [32]byte{}, ojp, ojp)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, st, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, st, blkRoot))
|
||||||
|
|
||||||
@@ -603,7 +603,7 @@ func TestUpdateHead_noSavedChanges(t *testing.T) {
|
|||||||
headRoot := service.headRoot()
|
headRoot := service.headRoot()
|
||||||
require.Equal(t, [32]byte{}, headRoot)
|
require.Equal(t, [32]byte{}, headRoot)
|
||||||
|
|
||||||
st, blkRoot, err = prepareForkchoiceState(ctx, 0, bellatrixBlkRoot, [32]byte{}, [32]byte{}, fcp, fcp)
|
st, blkRoot, err = prepareForkchoiceState(ctx, 0, bellatrixBlkRoot, [32]byte{}, [32]byte{}, [32]byte{}, fcp, fcp)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, st, blkRoot))
|
require.NoError(t, fcs.InsertNode(ctx, st, blkRoot))
|
||||||
fcs.SetBalancesByRooter(func(context.Context, [32]byte) ([]uint64, error) { return []uint64{1, 2}, nil })
|
fcs.SetBalancesByRooter(func(context.Context, [32]byte) ([]uint64, error) { return []uint64{1, 2}, nil })
|
||||||
|
|||||||
@@ -45,28 +45,44 @@ func logStateTransitionData(b interfaces.ReadOnlyBeaconBlock) error {
|
|||||||
}
|
}
|
||||||
log = log.WithField("syncBitsCount", agg.SyncCommitteeBits.Count())
|
log = log.WithField("syncBitsCount", agg.SyncCommitteeBits.Count())
|
||||||
}
|
}
|
||||||
if b.Version() >= version.Bellatrix {
|
if b.Version() >= version.EPBS {
|
||||||
p, err := b.Body().Execution()
|
sh, err := b.Body().SignedExecutionPayloadHeader()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log = log.WithField("payloadHash", fmt.Sprintf("%#x", bytesutil.Trunc(p.BlockHash())))
|
header, err := sh.Header()
|
||||||
txs, err := p.Transactions()
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, consensus_types.ErrUnsupportedField):
|
|
||||||
case err != nil:
|
|
||||||
return err
|
|
||||||
default:
|
|
||||||
log = log.WithField("txCount", len(txs))
|
|
||||||
txsPerSlotCount.Set(float64(len(txs)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if b.Version() >= version.Deneb {
|
|
||||||
kzgs, err := b.Body().BlobKzgCommitments()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("Failed to get blob KZG commitments")
|
return err
|
||||||
} else if len(kzgs) > 0 {
|
}
|
||||||
log = log.WithField("kzgCommitmentCount", len(kzgs))
|
log = log.WithFields(logrus.Fields{"payloadHash": fmt.Sprintf("%#x", header.BlockHash()),
|
||||||
|
"builderIndex": header.BuilderIndex(),
|
||||||
|
"value": header.Value(),
|
||||||
|
"blobKzgCommitmentsRoot": fmt.Sprintf("%#x", header.BlobKzgCommitmentsRoot()),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if b.Version() >= version.Bellatrix {
|
||||||
|
p, err := b.Body().Execution()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log = log.WithField("payloadHash", fmt.Sprintf("%#x", bytesutil.Trunc(p.BlockHash())))
|
||||||
|
txs, err := p.Transactions()
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, consensus_types.ErrUnsupportedField):
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
log = log.WithField("txCount", len(txs))
|
||||||
|
txsPerSlotCount.Set(float64(len(txs)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if b.Version() >= version.Deneb {
|
||||||
|
kzgs, err := b.Body().BlobKzgCommitments()
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("Failed to get blob KZG commitments")
|
||||||
|
} else if len(kzgs) > 0 {
|
||||||
|
log = log.WithField("kzgCommitmentCount", len(kzgs))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Info("Finished applying state transition")
|
log.Info("Finished applying state transition")
|
||||||
@@ -97,6 +113,18 @@ func logBlockSyncStatus(block interfaces.ReadOnlyBeaconBlock, blockRoot [32]byte
|
|||||||
"dataAvailabilityWaitedTime": daWaitedTime,
|
"dataAvailabilityWaitedTime": daWaitedTime,
|
||||||
"deposits": len(block.Body().Deposits()),
|
"deposits": len(block.Body().Deposits()),
|
||||||
}
|
}
|
||||||
|
if block.Version() >= version.EPBS {
|
||||||
|
ph, err := block.Body().SignedExecutionPayloadHeader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
header, err := ph.Header()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hash := header.ParentBlockHash()
|
||||||
|
lf["parentHash"] = fmt.Sprintf("0x%s...", hex.EncodeToString(hash[:])[:8])
|
||||||
|
}
|
||||||
log.WithFields(lf).Debug("Synced new block")
|
log.WithFields(lf).Debug("Synced new block")
|
||||||
} else {
|
} else {
|
||||||
log.WithFields(logrus.Fields{
|
log.WithFields(logrus.Fields{
|
||||||
@@ -112,6 +140,9 @@ func logBlockSyncStatus(block interfaces.ReadOnlyBeaconBlock, blockRoot [32]byte
|
|||||||
|
|
||||||
// logs payload related data every slot.
|
// logs payload related data every slot.
|
||||||
func logPayload(block interfaces.ReadOnlyBeaconBlock) error {
|
func logPayload(block interfaces.ReadOnlyBeaconBlock) error {
|
||||||
|
if block.Version() >= version.EPBS {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
isExecutionBlk, err := blocks.IsExecutionBlock(block.Body())
|
isExecutionBlk, err := blocks.IsExecutionBlock(block.Body())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "could not determine if block is execution block")
|
return errors.Wrap(err, "could not determine if block is execution block")
|
||||||
|
|||||||
@@ -182,6 +182,10 @@ var (
|
|||||||
Name: "chain_service_processing_milliseconds",
|
Name: "chain_service_processing_milliseconds",
|
||||||
Help: "Total time to call a chain service in ReceiveBlock()",
|
Help: "Total time to call a chain service in ReceiveBlock()",
|
||||||
})
|
})
|
||||||
|
executionEngineProcessingTime = promauto.NewSummary(prometheus.SummaryOpts{
|
||||||
|
Name: "execution_engine_processing_milliseconds",
|
||||||
|
Help: "Total time to process an execution payload envelope in ReceiveExecutionPayloadEnvelope()",
|
||||||
|
})
|
||||||
dataAvailWaitedTime = promauto.NewSummary(prometheus.SummaryOpts{
|
dataAvailWaitedTime = promauto.NewSummary(prometheus.SummaryOpts{
|
||||||
Name: "da_waited_time_milliseconds",
|
Name: "da_waited_time_milliseconds",
|
||||||
Help: "Total time spent waiting for a data availability check in ReceiveBlock()",
|
Help: "Total time spent waiting for a data availability check in ReceiveBlock()",
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package blockchain
|
package blockchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/prysmaticlabs/prysm/v5/async/event"
|
"github.com/prysmaticlabs/prysm/v5/async/event"
|
||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/cache"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/cache"
|
||||||
statefeed "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/state"
|
statefeed "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/state"
|
||||||
@@ -69,6 +71,22 @@ func WithDepositCache(c cache.DepositCache) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithPayloadAttestationCache for payload attestation cache.
|
||||||
|
func WithPayloadAttestationCache(c *cache.PayloadAttestationCache) Option {
|
||||||
|
return func(s *Service) error {
|
||||||
|
s.cfg.PayloadAttestationCache = c
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPayloadEnvelopeCache for payload envelope cache.
|
||||||
|
func WithPayloadEnvelopeCache(c *sync.Map) Option {
|
||||||
|
return func(s *Service) error {
|
||||||
|
s.cfg.PayloadEnvelopeCache = c
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithPayloadIDCache for payload ID cache.
|
// WithPayloadIDCache for payload ID cache.
|
||||||
func WithPayloadIDCache(c *cache.PayloadIDCache) Option {
|
func WithPayloadIDCache(c *cache.PayloadIDCache) Option {
|
||||||
return func(s *Service) error {
|
return func(s *Service) error {
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ func (s *Service) OnAttestation(ctx context.Context, a ethpb.Att, disparity time
|
|||||||
// We assume trusted attestation in this function has verified signature.
|
// We assume trusted attestation in this function has verified signature.
|
||||||
|
|
||||||
// Update forkchoice store with the new attestation for updating weight.
|
// Update forkchoice store with the new attestation for updating weight.
|
||||||
s.cfg.ForkChoiceStore.ProcessAttestation(ctx, indexedAtt.GetAttestingIndices(), bytesutil.ToBytes32(a.GetData().BeaconBlockRoot), a.GetData().Target.Epoch)
|
s.cfg.ForkChoiceStore.ProcessAttestation(ctx, indexedAtt.GetAttestingIndices(), bytesutil.ToBytes32(a.GetData().BeaconBlockRoot), a.GetData().Slot)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func TestStore_OnAttestation_ErrorConditions(t *testing.T) {
|
|||||||
util.SaveBlock(t, ctx, beaconDB, blkWithoutState)
|
util.SaveBlock(t, ctx, beaconDB, blkWithoutState)
|
||||||
|
|
||||||
cp := ðpb.Checkpoint{}
|
cp := ðpb.Checkpoint{}
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, cp, cp)
|
st, roblock, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, [32]byte{}, cp, cp)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ func TestStore_OnAttestation_ErrorConditions(t *testing.T) {
|
|||||||
r, err := blkWithStateBadAtt.Block.HashTreeRoot()
|
r, err := blkWithStateBadAtt.Block.HashTreeRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
cp = ðpb.Checkpoint{Root: r[:]}
|
cp = ðpb.Checkpoint{Root: r[:]}
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, blkWithStateBadAtt.Block.Slot, r, [32]byte{}, params.BeaconConfig().ZeroHash, cp, cp)
|
st, roblock, err = prepareForkchoiceState(ctx, blkWithStateBadAtt.Block.Slot, r, [32]byte{}, params.BeaconConfig().ZeroHash, [32]byte{}, cp, cp)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
||||||
util.SaveBlock(t, ctx, beaconDB, blkWithStateBadAtt)
|
util.SaveBlock(t, ctx, beaconDB, blkWithStateBadAtt)
|
||||||
@@ -139,7 +139,7 @@ func TestStore_OnAttestation_Ok_DoublyLinkedTree(t *testing.T) {
|
|||||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, copied, tRoot))
|
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, copied, tRoot))
|
||||||
ojc := ðpb.Checkpoint{Epoch: 0, Root: tRoot[:]}
|
ojc := ðpb.Checkpoint{Epoch: 0, Root: tRoot[:]}
|
||||||
ofc := ðpb.Checkpoint{Epoch: 0, Root: tRoot[:]}
|
ofc := ðpb.Checkpoint{Epoch: 0, Root: tRoot[:]}
|
||||||
state, roblock, err := prepareForkchoiceState(ctx, 0, tRoot, tRoot, params.BeaconConfig().ZeroHash, ojc, ofc)
|
state, roblock, err := prepareForkchoiceState(ctx, 0, tRoot, tRoot, params.BeaconConfig().ZeroHash, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, roblock))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, roblock))
|
||||||
require.NoError(t, service.OnAttestation(ctx, att[0], 0))
|
require.NoError(t, service.OnAttestation(ctx, att[0], 0))
|
||||||
@@ -170,7 +170,7 @@ func TestService_GetRecentPreState(t *testing.T) {
|
|||||||
err = s.SetFinalizedCheckpoint(cp0)
|
err = s.SetFinalizedCheckpoint(cp0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
st, root, err := prepareForkchoiceState(ctx, 31, [32]byte(ckRoot), [32]byte{}, [32]byte{'R'}, cp0, cp0)
|
st, root, err := prepareForkchoiceState(ctx, 31, [32]byte(ckRoot), [32]byte{}, [32]byte{'R'}, [32]byte{}, cp0, cp0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, root))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, root))
|
||||||
service.head = &head{
|
service.head = &head{
|
||||||
@@ -202,7 +202,7 @@ func TestService_GetAttPreState_Concurrency(t *testing.T) {
|
|||||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, s, bytesutil.ToBytes32([]byte{'A'})))
|
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, s, bytesutil.ToBytes32([]byte{'A'})))
|
||||||
require.NoError(t, service.cfg.BeaconDB.SaveStateSummary(ctx, ðpb.StateSummary{Root: ckRoot}))
|
require.NoError(t, service.cfg.BeaconDB.SaveStateSummary(ctx, ðpb.StateSummary{Root: ckRoot}))
|
||||||
|
|
||||||
st, root, err := prepareForkchoiceState(ctx, 100, [32]byte(cp1.Root), [32]byte{}, [32]byte{'R'}, cp1, cp1)
|
st, root, err := prepareForkchoiceState(ctx, 100, [32]byte(cp1.Root), [32]byte{}, [32]byte{'R'}, [32]byte{}, cp1, cp1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, root))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, root))
|
||||||
|
|
||||||
@@ -259,7 +259,7 @@ func TestStore_SaveCheckpointState(t *testing.T) {
|
|||||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, s, bytesutil.ToBytes32([]byte{'A'})))
|
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, s, bytesutil.ToBytes32([]byte{'A'})))
|
||||||
require.NoError(t, service.cfg.BeaconDB.SaveStateSummary(ctx, ðpb.StateSummary{Root: bytesutil.PadTo([]byte{'A'}, fieldparams.RootLength)}))
|
require.NoError(t, service.cfg.BeaconDB.SaveStateSummary(ctx, ðpb.StateSummary{Root: bytesutil.PadTo([]byte{'A'}, fieldparams.RootLength)}))
|
||||||
|
|
||||||
st, root, err := prepareForkchoiceState(ctx, 1, [32]byte(cp1.Root), [32]byte{}, [32]byte{'R'}, cp1, cp1)
|
st, root, err := prepareForkchoiceState(ctx, 1, [32]byte(cp1.Root), [32]byte{}, [32]byte{'R'}, [32]byte{}, cp1, cp1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, root))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, root))
|
||||||
s1, err := service.getAttPreState(ctx, cp1)
|
s1, err := service.getAttPreState(ctx, cp1)
|
||||||
@@ -273,7 +273,7 @@ func TestStore_SaveCheckpointState(t *testing.T) {
|
|||||||
_, err = service.getAttPreState(ctx, cp2)
|
_, err = service.getAttPreState(ctx, cp2)
|
||||||
require.ErrorContains(t, "epoch 2 root 0x4200000000000000000000000000000000000000000000000000000000000000: not a checkpoint in forkchoice", err)
|
require.ErrorContains(t, "epoch 2 root 0x4200000000000000000000000000000000000000000000000000000000000000: not a checkpoint in forkchoice", err)
|
||||||
|
|
||||||
st, root, err = prepareForkchoiceState(ctx, 33, [32]byte(cp2.Root), [32]byte(cp1.Root), [32]byte{'R'}, cp2, cp2)
|
st, root, err = prepareForkchoiceState(ctx, 33, [32]byte(cp2.Root), [32]byte(cp1.Root), [32]byte{'R'}, [32]byte{}, cp2, cp2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, root))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, root))
|
||||||
|
|
||||||
@@ -298,7 +298,7 @@ func TestStore_SaveCheckpointState(t *testing.T) {
|
|||||||
cp3 := ðpb.Checkpoint{Epoch: 1, Root: bytesutil.PadTo([]byte{'C'}, fieldparams.RootLength)}
|
cp3 := ðpb.Checkpoint{Epoch: 1, Root: bytesutil.PadTo([]byte{'C'}, fieldparams.RootLength)}
|
||||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, s, bytesutil.ToBytes32([]byte{'C'})))
|
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, s, bytesutil.ToBytes32([]byte{'C'})))
|
||||||
require.NoError(t, service.cfg.BeaconDB.SaveStateSummary(ctx, ðpb.StateSummary{Root: bytesutil.PadTo([]byte{'C'}, fieldparams.RootLength)}))
|
require.NoError(t, service.cfg.BeaconDB.SaveStateSummary(ctx, ðpb.StateSummary{Root: bytesutil.PadTo([]byte{'C'}, fieldparams.RootLength)}))
|
||||||
st, root, err = prepareForkchoiceState(ctx, 31, [32]byte(cp3.Root), [32]byte(cp2.Root), [32]byte{'P'}, cp2, cp2)
|
st, root, err = prepareForkchoiceState(ctx, 31, [32]byte(cp3.Root), [32]byte(cp2.Root), [32]byte{'P'}, [32]byte{}, cp2, cp2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, root))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, root))
|
||||||
|
|
||||||
@@ -318,7 +318,7 @@ func TestStore_UpdateCheckpointState(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
checkpoint := ðpb.Checkpoint{Epoch: epoch, Root: r1[:]}
|
checkpoint := ðpb.Checkpoint{Epoch: epoch, Root: r1[:]}
|
||||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, baseState, bytesutil.ToBytes32(checkpoint.Root)))
|
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, baseState, bytesutil.ToBytes32(checkpoint.Root)))
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, blk.Block.Slot, r1, [32]byte{}, params.BeaconConfig().ZeroHash, checkpoint, checkpoint)
|
st, roblock, err := prepareForkchoiceState(ctx, blk.Block.Slot, r1, [32]byte{}, params.BeaconConfig().ZeroHash, [32]byte{}, checkpoint, checkpoint)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
||||||
returned, err := service.getAttPreState(ctx, checkpoint)
|
returned, err := service.getAttPreState(ctx, checkpoint)
|
||||||
@@ -336,7 +336,7 @@ func TestStore_UpdateCheckpointState(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
newCheckpoint := ðpb.Checkpoint{Epoch: epoch, Root: r2[:]}
|
newCheckpoint := ðpb.Checkpoint{Epoch: epoch, Root: r2[:]}
|
||||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, baseState, bytesutil.ToBytes32(newCheckpoint.Root)))
|
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, baseState, bytesutil.ToBytes32(newCheckpoint.Root)))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, blk.Block.Slot, r2, r1, params.BeaconConfig().ZeroHash, newCheckpoint, newCheckpoint)
|
st, roblock, err = prepareForkchoiceState(ctx, blk.Block.Slot, r2, r1, params.BeaconConfig().ZeroHash, [32]byte{}, newCheckpoint, newCheckpoint)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, roblock))
|
||||||
returned, err = service.getAttPreState(ctx, newCheckpoint)
|
returned, err = service.getAttPreState(ctx, newCheckpoint)
|
||||||
|
|||||||
@@ -64,7 +64,9 @@ func (s *Service) postBlockProcess(cfg *postBlockProcessConfig) error {
|
|||||||
fcuArgs := &fcuConfig{}
|
fcuArgs := &fcuConfig{}
|
||||||
|
|
||||||
if s.inRegularSync() {
|
if s.inRegularSync() {
|
||||||
defer s.handleSecondFCUCall(cfg, fcuArgs)
|
if cfg.roblock.Version() < version.EPBS {
|
||||||
|
defer s.handleSecondFCUCall(cfg, fcuArgs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if features.Get().EnableLightClient && slots.ToEpoch(s.CurrentSlot()) >= params.BeaconConfig().AltairForkEpoch {
|
if features.Get().EnableLightClient && slots.ToEpoch(s.CurrentSlot()) >= params.BeaconConfig().AltairForkEpoch {
|
||||||
defer s.processLightClientUpdates(cfg)
|
defer s.processLightClientUpdates(cfg)
|
||||||
@@ -102,6 +104,18 @@ func (s *Service) postBlockProcess(cfg *postBlockProcessConfig) error {
|
|||||||
s.logNonCanonicalBlockReceived(cfg.roblock.Root(), cfg.headRoot)
|
s.logNonCanonicalBlockReceived(cfg.roblock.Root(), cfg.headRoot)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if cfg.roblock.Version() >= version.EPBS {
|
||||||
|
if err := s.saveHead(ctx, cfg.headRoot, cfg.roblock, cfg.postState); err != nil {
|
||||||
|
log.WithError(err).Error("could not save head")
|
||||||
|
}
|
||||||
|
if err := s.pruneAttsFromPool(cfg.roblock); err != nil {
|
||||||
|
log.WithError(err).Error("could not prune attestations from pool")
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the NSC and handle epoch boundaries here since we do
|
||||||
|
// not send FCU at all
|
||||||
|
return s.updateCachesPostBlockProcessing(cfg)
|
||||||
|
}
|
||||||
if err := s.getFCUArgs(cfg, fcuArgs); err != nil {
|
if err := s.getFCUArgs(cfg, fcuArgs); err != nil {
|
||||||
log.WithError(err).Error("Could not get forkchoice update argument")
|
log.WithError(err).Error("Could not get forkchoice update argument")
|
||||||
return nil
|
return nil
|
||||||
@@ -378,7 +392,7 @@ func (s *Service) handleBlockAttestations(ctx context.Context, blk interfaces.Re
|
|||||||
}
|
}
|
||||||
r := bytesutil.ToBytes32(a.GetData().BeaconBlockRoot)
|
r := bytesutil.ToBytes32(a.GetData().BeaconBlockRoot)
|
||||||
if s.cfg.ForkChoiceStore.HasNode(r) {
|
if s.cfg.ForkChoiceStore.HasNode(r) {
|
||||||
s.cfg.ForkChoiceStore.ProcessAttestation(ctx, indices, r, a.GetData().Target.Epoch)
|
s.cfg.ForkChoiceStore.ProcessAttestation(ctx, indices, r, a.GetData().Slot)
|
||||||
} else if features.Get().EnableExperimentalAttestationPool {
|
} else if features.Get().EnableExperimentalAttestationPool {
|
||||||
if err = s.cfg.AttestationCache.Add(a); err != nil {
|
if err = s.cfg.AttestationCache.Add(a); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -488,9 +502,15 @@ func (s *Service) runLateBlockTasks() {
|
|||||||
|
|
||||||
attThreshold := params.BeaconConfig().SecondsPerSlot / 3
|
attThreshold := params.BeaconConfig().SecondsPerSlot / 3
|
||||||
ticker := slots.NewSlotTickerWithOffset(s.genesisTime, time.Duration(attThreshold)*time.Second, params.BeaconConfig().SecondsPerSlot)
|
ticker := slots.NewSlotTickerWithOffset(s.genesisTime, time.Duration(attThreshold)*time.Second, params.BeaconConfig().SecondsPerSlot)
|
||||||
|
epbs := params.BeaconConfig().EPBSForkEpoch
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C():
|
case slot := <-ticker.C():
|
||||||
|
if slots.ToEpoch(slot) == epbs && slot%32 == 0 {
|
||||||
|
ticker.Done()
|
||||||
|
attThreshold := params.BeaconConfig().SecondsPerSlot / 4
|
||||||
|
ticker = slots.NewSlotTickerWithOffset(s.genesisTime, time.Duration(attThreshold)*time.Second, params.BeaconConfig().SecondsPerSlot)
|
||||||
|
}
|
||||||
s.lateBlockTasks(s.ctx)
|
s.lateBlockTasks(s.ctx)
|
||||||
case <-s.ctx.Done():
|
case <-s.ctx.Done():
|
||||||
log.Debug("Context closed, exiting routine")
|
log.Debug("Context closed, exiting routine")
|
||||||
@@ -666,24 +686,36 @@ func (s *Service) lateBlockTasks(ctx context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.headLock.RLock()
|
if headState.Version() >= version.EPBS {
|
||||||
headBlock, err := s.headBlock()
|
bh, err := headState.LatestBlockHash()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.WithError(err).Debug("could not perform late block tasks: failed to retrieve latest block hash")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = s.notifyForkchoiceUpdateEPBS(ctx, [32]byte(bh), attribute)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Debug("could not perform late block tasks: failed to update forkchoice with engine")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.headLock.RLock()
|
||||||
|
headBlock, err := s.headBlock()
|
||||||
|
if err != nil {
|
||||||
|
s.headLock.RUnlock()
|
||||||
|
log.WithError(err).Debug("could not perform late block tasks: failed to retrieve head block")
|
||||||
|
return
|
||||||
|
}
|
||||||
s.headLock.RUnlock()
|
s.headLock.RUnlock()
|
||||||
log.WithError(err).Debug("could not perform late block tasks: failed to retrieve head block")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.headLock.RUnlock()
|
|
||||||
|
|
||||||
fcuArgs := &fcuConfig{
|
fcuArgs := &fcuConfig{
|
||||||
headState: headState,
|
headState: headState,
|
||||||
headRoot: headRoot,
|
headRoot: headRoot,
|
||||||
headBlock: headBlock,
|
headBlock: headBlock,
|
||||||
attributes: attribute,
|
attributes: attribute,
|
||||||
}
|
}
|
||||||
_, err = s.notifyForkchoiceUpdate(ctx, fcuArgs)
|
_, err = s.notifyForkchoiceUpdate(ctx, fcuArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Debug("could not perform late block tasks: failed to update forkchoice with engine")
|
log.WithError(err).Debug("could not perform late block tasks: failed to update forkchoice with engine")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -368,7 +368,31 @@ func (s *Service) getBlockPreState(ctx context.Context, b interfaces.ReadOnlyBea
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
preState, err := s.cfg.StateGen.StateByRoot(ctx, b.ParentRoot())
|
parentRoot := b.ParentRoot()
|
||||||
|
s.ForkChoicer().RLock()
|
||||||
|
slot, err := s.ForkChoicer().Slot(parentRoot)
|
||||||
|
s.ForkChoicer().RUnlock()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "could not get slot for parent root")
|
||||||
|
}
|
||||||
|
if slots.ToEpoch(slot) >= params.BeaconConfig().EPBSForkEpoch {
|
||||||
|
s.ForkChoicer().RLock()
|
||||||
|
parentHash := s.ForkChoicer().HashForBlockRoot(parentRoot)
|
||||||
|
s.ForkChoicer().RUnlock()
|
||||||
|
signedBid, err := b.Body().SignedExecutionPayloadHeader()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "could not get signed execution payload header")
|
||||||
|
}
|
||||||
|
bid, err := signedBid.Header()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "could not get execution payload header")
|
||||||
|
}
|
||||||
|
if parentHash == bid.ParentBlockHash() {
|
||||||
|
// It's based on full, use the state by hash
|
||||||
|
parentRoot = parentHash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
preState, err := s.cfg.StateGen.StateByRoot(ctx, parentRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "could not get pre state for slot %d", b.Slot())
|
return nil, errors.Wrapf(err, "could not get pre state for slot %d", b.Slot())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ func TestFillForkChoiceMissingBlocks_CanSave(t *testing.T) {
|
|||||||
// the parent of the last block inserted is the tree node.
|
// the parent of the last block inserted is the tree node.
|
||||||
fcp := ðpb.Checkpoint{Epoch: 0, Root: service.originBlockRoot[:]}
|
fcp := ðpb.Checkpoint{Epoch: 0, Root: service.originBlockRoot[:]}
|
||||||
r0 := bytesutil.ToBytes32(roots[0])
|
r0 := bytesutil.ToBytes32(roots[0])
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, 0, r0, service.originBlockRoot, [32]byte{}, fcp, fcp)
|
state, blkRoot, err := prepareForkchoiceState(ctx, 0, r0, service.originBlockRoot, [32]byte{}, [32]byte{}, fcp, fcp)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
fcp2 := &forkchoicetypes.Checkpoint{Epoch: 0, Root: r0}
|
fcp2 := &forkchoicetypes.Checkpoint{Epoch: 0, Root: r0}
|
||||||
@@ -184,7 +184,7 @@ func TestFillForkChoiceMissingBlocks_RootsMatch(t *testing.T) {
|
|||||||
// the parent of the last block inserted is the tree node.
|
// the parent of the last block inserted is the tree node.
|
||||||
fcp := ðpb.Checkpoint{Epoch: 0, Root: service.originBlockRoot[:]}
|
fcp := ðpb.Checkpoint{Epoch: 0, Root: service.originBlockRoot[:]}
|
||||||
r0 := bytesutil.ToBytes32(roots[0])
|
r0 := bytesutil.ToBytes32(roots[0])
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, 0, r0, service.originBlockRoot, [32]byte{}, fcp, fcp)
|
state, blkRoot, err := prepareForkchoiceState(ctx, 0, r0, service.originBlockRoot, [32]byte{}, [32]byte{}, fcp, fcp)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
fcp2 := &forkchoicetypes.Checkpoint{Epoch: 0, Root: r0}
|
fcp2 := &forkchoicetypes.Checkpoint{Epoch: 0, Root: r0}
|
||||||
@@ -464,7 +464,7 @@ func TestAncestor_CanUseForkchoice(t *testing.T) {
|
|||||||
beaconBlock.Block.ParentRoot = bytesutil.PadTo(b.Block.ParentRoot, 32)
|
beaconBlock.Block.ParentRoot = bytesutil.PadTo(b.Block.ParentRoot, 32)
|
||||||
r, err := b.Block.HashTreeRoot()
|
r, err := b.Block.HashTreeRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
st, blkRoot, err := prepareForkchoiceState(context.Background(), b.Block.Slot, r, bytesutil.ToBytes32(b.Block.ParentRoot), params.BeaconConfig().ZeroHash, ojc, ofc)
|
st, blkRoot, err := prepareForkchoiceState(context.Background(), b.Block.Slot, r, bytesutil.ToBytes32(b.Block.ParentRoot), params.BeaconConfig().ZeroHash, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot))
|
||||||
}
|
}
|
||||||
@@ -504,7 +504,7 @@ func TestAncestor_CanUseDB(t *testing.T) {
|
|||||||
util.SaveBlock(t, context.Background(), beaconDB, beaconBlock)
|
util.SaveBlock(t, context.Background(), beaconDB, beaconBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
st, blkRoot, err := prepareForkchoiceState(context.Background(), 200, r200, r200, params.BeaconConfig().ZeroHash, ojc, ofc)
|
st, blkRoot, err := prepareForkchoiceState(context.Background(), 200, r200, r200, params.BeaconConfig().ZeroHash, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot))
|
||||||
|
|
||||||
@@ -1153,7 +1153,7 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) {
|
|||||||
logHook := logTest.NewGlobal()
|
logHook := logTest.NewGlobal()
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
fc := ðpb.Checkpoint{}
|
fc := ðpb.Checkpoint{}
|
||||||
st, blkRoot, err := prepareForkchoiceState(ctx, 0, wsb1.Block().ParentRoot(), [32]byte{}, [32]byte{}, fc, fc)
|
st, blkRoot, err := prepareForkchoiceState(ctx, 0, wsb1.Block().ParentRoot(), [32]byte{}, [32]byte{}, [32]byte{}, fc, fc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, st, blkRoot))
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||||
"github.com/prysmaticlabs/prysm/v5/config/features"
|
"github.com/prysmaticlabs/prysm/v5/config/features"
|
||||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||||
|
payloadattribute "github.com/prysmaticlabs/prysm/v5/consensus-types/payload-attribute"
|
||||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||||
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace"
|
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace"
|
||||||
@@ -148,15 +149,35 @@ func (s *Service) UpdateHead(ctx context.Context, proposingSlot primitives.Slot)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
newAttHeadElapsedTime.Observe(float64(time.Since(start).Milliseconds()))
|
newAttHeadElapsedTime.Observe(float64(time.Since(start).Milliseconds()))
|
||||||
|
var attributes payloadattribute.Attributer
|
||||||
|
if s.inRegularSync() {
|
||||||
|
attributes = s.getPayloadAttribute(ctx, headState, proposingSlot, newHeadRoot[:])
|
||||||
|
}
|
||||||
|
if headState.Version() >= version.EPBS {
|
||||||
|
bh, err := headState.LatestBlockHash()
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("could not get latest block hash")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = s.notifyForkchoiceUpdateEPBS(ctx, [32]byte(bh), attributes)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("could not notify forkchoice update")
|
||||||
|
}
|
||||||
|
if err := s.saveHead(ctx, newHeadRoot, headBlock, headState); err != nil {
|
||||||
|
log.WithError(err).Error("could not save head")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := s.pruneAttsFromPool(headBlock); err != nil {
|
||||||
|
log.WithError(err).Error("could not prune attestations from pool")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
fcuArgs := &fcuConfig{
|
fcuArgs := &fcuConfig{
|
||||||
headState: headState,
|
headState: headState,
|
||||||
headRoot: newHeadRoot,
|
headRoot: newHeadRoot,
|
||||||
headBlock: headBlock,
|
headBlock: headBlock,
|
||||||
proposingSlot: proposingSlot,
|
proposingSlot: proposingSlot,
|
||||||
}
|
}
|
||||||
if s.inRegularSync() {
|
|
||||||
fcuArgs.attributes = s.getPayloadAttribute(ctx, headState, proposingSlot, newHeadRoot[:])
|
|
||||||
}
|
|
||||||
if fcuArgs.attributes != nil && s.shouldOverrideFCU(newHeadRoot, proposingSlot) {
|
if fcuArgs.attributes != nil && s.shouldOverrideFCU(newHeadRoot, proposingSlot) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -184,7 +205,7 @@ func (s *Service) processAttestations(ctx context.Context, disparity time.Durati
|
|||||||
}
|
}
|
||||||
|
|
||||||
hasState := s.cfg.BeaconDB.HasStateSummary(ctx, bytesutil.ToBytes32(a.GetData().BeaconBlockRoot))
|
hasState := s.cfg.BeaconDB.HasStateSummary(ctx, bytesutil.ToBytes32(a.GetData().BeaconBlockRoot))
|
||||||
hasBlock := s.hasBlock(ctx, bytesutil.ToBytes32(a.GetData().BeaconBlockRoot))
|
hasBlock := s.chainHasBlock(ctx, bytesutil.ToBytes32(a.GetData().BeaconBlockRoot))
|
||||||
if !(hasState && hasBlock) {
|
if !(hasState && hasBlock) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,11 +42,11 @@ func TestVerifyLMDFFGConsistent(t *testing.T) {
|
|||||||
|
|
||||||
f := service.cfg.ForkChoiceStore
|
f := service.cfg.ForkChoiceStore
|
||||||
fc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
fc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
state, r32, err := prepareForkchoiceState(ctx, 32, [32]byte{'a'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, fc, fc)
|
state, r32, err := prepareForkchoiceState(ctx, 32, [32]byte{'a'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, [32]byte{}, fc, fc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, r32))
|
require.NoError(t, f.InsertNode(ctx, state, r32))
|
||||||
|
|
||||||
state, r33, err := prepareForkchoiceState(ctx, 33, [32]byte{'b'}, r32.Root(), params.BeaconConfig().ZeroHash, fc, fc)
|
state, r33, err := prepareForkchoiceState(ctx, 33, [32]byte{'b'}, r32.Root(), params.BeaconConfig().ZeroHash, [32]byte{}, fc, fc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, r33))
|
require.NoError(t, f.InsertNode(ctx, state, r33))
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ func TestProcessAttestations_Ok(t *testing.T) {
|
|||||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, copied, tRoot))
|
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, copied, tRoot))
|
||||||
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, 0, tRoot, tRoot, params.BeaconConfig().ZeroHash, ojc, ofc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, 0, tRoot, tRoot, params.BeaconConfig().ZeroHash, [32]byte{}, ojc, ofc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
attsToSave := make([]ethpb.Att, len(atts))
|
attsToSave := make([]ethpb.Att, len(atts))
|
||||||
@@ -142,7 +142,7 @@ func TestService_ProcessAttestationsAndUpdateHead(t *testing.T) {
|
|||||||
r, err := b.Block.HashTreeRoot()
|
r, err := b.Block.HashTreeRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
util.SaveBlock(t, ctx, service.cfg.BeaconDB, b)
|
util.SaveBlock(t, ctx, service.cfg.BeaconDB, b)
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, 2, r, service.originBlockRoot, [32]byte{'b'}, ojc, ojc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, 2, r, service.originBlockRoot, [32]byte{'b'}, [32]byte{}, ojc, ojc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
require.Equal(t, 3, fcs.NodeCount())
|
require.Equal(t, 3, fcs.NodeCount())
|
||||||
@@ -191,7 +191,7 @@ func TestService_UpdateHead_NoAtts(t *testing.T) {
|
|||||||
r, err := b.Block.HashTreeRoot()
|
r, err := b.Block.HashTreeRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
util.SaveBlock(t, ctx, service.cfg.BeaconDB, b)
|
util.SaveBlock(t, ctx, service.cfg.BeaconDB, b)
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, 2, r, service.originBlockRoot, [32]byte{'b'}, ojc, ojc)
|
state, blkRoot, err := prepareForkchoiceState(ctx, 2, r, service.originBlockRoot, [32]byte{'b'}, [32]byte{}, ojc, ojc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||||
require.Equal(t, 3, fcs.NodeCount())
|
require.Equal(t, 3, fcs.NodeCount())
|
||||||
|
|||||||
@@ -39,18 +39,30 @@ var epochsSinceFinalityExpandCache = primitives.Epoch(4)
|
|||||||
// BlockReceiver interface defines the methods of chain service for receiving and processing new blocks.
|
// BlockReceiver interface defines the methods of chain service for receiving and processing new blocks.
|
||||||
type BlockReceiver interface {
|
type BlockReceiver interface {
|
||||||
ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock, blockRoot [32]byte, avs das.AvailabilityStore) error
|
ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock, blockRoot [32]byte, avs das.AvailabilityStore) error
|
||||||
|
ReceiveExecutionPayloadEnvelope(ctx context.Context, env interfaces.ROSignedExecutionPayloadEnvelope, avs das.AvailabilityStore) error
|
||||||
ReceiveBlockBatch(ctx context.Context, blocks []blocks.ROBlock, avs das.AvailabilityStore) error
|
ReceiveBlockBatch(ctx context.Context, blocks []blocks.ROBlock, avs das.AvailabilityStore) error
|
||||||
HasBlock(ctx context.Context, root [32]byte) bool
|
HasBlock(ctx context.Context, root [32]byte) bool
|
||||||
RecentBlockSlot(root [32]byte) (primitives.Slot, error)
|
RecentBlockSlot(root [32]byte) (primitives.Slot, error)
|
||||||
BlockBeingSynced([32]byte) bool
|
BlockBeingSynced([32]byte) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PayloadAttestationReceiver defines methods of the chain service for receiving
|
||||||
|
// and processing new payload attestations and payload attestation messages
|
||||||
|
type PayloadAttestationReceiver interface {
|
||||||
|
ReceivePayloadAttestationMessage(ctx context.Context, a *ethpb.PayloadAttestationMessage) error
|
||||||
|
}
|
||||||
|
|
||||||
// BlobReceiver interface defines the methods of chain service for receiving new
|
// BlobReceiver interface defines the methods of chain service for receiving new
|
||||||
// blobs
|
// blobs
|
||||||
type BlobReceiver interface {
|
type BlobReceiver interface {
|
||||||
ReceiveBlob(context.Context, blocks.VerifiedROBlob) error
|
ReceiveBlob(context.Context, blocks.VerifiedROBlob) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExecutionPayloadReceiver interface defines the methods of chain service for receiving `ROExecutionPayloadEnvelope`.
|
||||||
|
type ExecutionPayloadReceiver interface {
|
||||||
|
ReceiveExecutionPayloadEnvelope(ctx context.Context, envelope interfaces.ROSignedExecutionPayloadEnvelope, _ das.AvailabilityStore) error
|
||||||
|
}
|
||||||
|
|
||||||
// SlashingReceiver interface defines the methods of chain service for receiving validated slashing over the wire.
|
// SlashingReceiver interface defines the methods of chain service for receiving validated slashing over the wire.
|
||||||
type SlashingReceiver interface {
|
type SlashingReceiver interface {
|
||||||
ReceiveAttesterSlashing(ctx context.Context, slashing ethpb.AttSlashing)
|
ReceiveAttesterSlashing(ctx context.Context, slashing ethpb.AttSlashing)
|
||||||
@@ -87,14 +99,30 @@ func (s *Service) ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySig
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
var postState state.BeaconState
|
||||||
postState, isValidPayload, err := s.validateExecutionAndConsensus(ctx, preState, roblock)
|
var isValidPayload bool
|
||||||
if err != nil {
|
var daWaitedTime time.Duration
|
||||||
return err
|
if blockCopy.Version() >= version.EPBS {
|
||||||
}
|
postState, err = s.validateStateTransition(ctx, preState, roblock)
|
||||||
daWaitedTime, err := s.handleDA(ctx, blockCopy, blockRoot, avs)
|
if err != nil {
|
||||||
if err != nil {
|
return errors.Wrap(err, "could not validate state transition")
|
||||||
return err
|
}
|
||||||
|
optimistic, err := s.IsOptimisticForRoot(ctx, roblock.Block().ParentRoot())
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not check if parent is optimistic")
|
||||||
|
}
|
||||||
|
// if the parent is not optimistic then we can set the block as
|
||||||
|
// not optimistic.
|
||||||
|
isValidPayload = !optimistic
|
||||||
|
} else {
|
||||||
|
postState, isValidPayload, err = s.validateExecutionAndConsensus(ctx, preState, roblock)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
daWaitedTime, err = s.handleDA(ctx, blockCopy, blockRoot, avs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Defragment the state before continuing block processing.
|
// Defragment the state before continuing block processing.
|
||||||
s.defragmentState(postState)
|
s.defragmentState(postState)
|
||||||
|
|||||||
242
beacon-chain/blockchain/receive_execution_payload_envelope.go
Normal file
242
beacon-chain/blockchain/receive_execution_payload_envelope.go
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
package blockchain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epbs"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/das"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/execution"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"go.opencensus.io/trace"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReceiveExecutionPayloadEnvelope is a function that defines the operations (minus pubsub)
|
||||||
|
// that are performed on a received execution payload envelope. The operations consist of:
|
||||||
|
// 1. Validate the payload, apply state transition.
|
||||||
|
// 2. Apply fork choice to the processed payload
|
||||||
|
// 3. Save latest head info
|
||||||
|
func (s *Service) ReceiveExecutionPayloadEnvelope(ctx context.Context, signed interfaces.ROSignedExecutionPayloadEnvelope, _ das.AvailabilityStore) error {
|
||||||
|
receivedTime := time.Now()
|
||||||
|
envelope, err := signed.Envelope()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Info("Receiving execution payload envelope")
|
||||||
|
root := envelope.BeaconBlockRoot()
|
||||||
|
s.payloadBeingSynced.set(envelope)
|
||||||
|
defer s.payloadBeingSynced.unset(root)
|
||||||
|
|
||||||
|
preState, err := s.getPayloadEnvelopePrestate(ctx, envelope)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not get prestate")
|
||||||
|
}
|
||||||
|
|
||||||
|
eg, _ := errgroup.WithContext(ctx)
|
||||||
|
eg.Go(func() error {
|
||||||
|
if err := epbs.ValidatePayloadStateTransition(ctx, preState, envelope); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to validate consensus state transition function")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
var isValidPayload bool
|
||||||
|
eg.Go(func() error {
|
||||||
|
var err error
|
||||||
|
isValidPayload, err = s.validateExecutionOnEnvelope(ctx, envelope)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not notify the engine of the new payload")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := eg.Wait(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
daStartTime := time.Now()
|
||||||
|
// TODO: Add DA check
|
||||||
|
daWaitedTime := time.Since(daStartTime)
|
||||||
|
dataAvailWaitedTime.Observe(float64(daWaitedTime.Milliseconds()))
|
||||||
|
if err := s.savePostPayload(ctx, signed, preState); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.insertPayloadEnvelope(envelope); err != nil {
|
||||||
|
return errors.Wrap(err, "could not insert payload to forkchoice")
|
||||||
|
}
|
||||||
|
if isValidPayload {
|
||||||
|
s.ForkChoicer().Lock()
|
||||||
|
if err := s.ForkChoicer().SetOptimisticToValid(ctx, root); err != nil {
|
||||||
|
s.ForkChoicer().Unlock()
|
||||||
|
return errors.Wrap(err, "could not set optimistic payload to valid")
|
||||||
|
}
|
||||||
|
s.ForkChoicer().Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
headRoot, err := s.HeadRoot(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("could not get headroot to compute attributes")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if bytes.Equal(headRoot, root[:]) {
|
||||||
|
attr := s.getPayloadAttribute(ctx, preState, envelope.Slot()+1, headRoot)
|
||||||
|
execution, err := envelope.Execution()
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("could not get execution data")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
blockHash := [32]byte(execution.BlockHash())
|
||||||
|
payloadID, err := s.notifyForkchoiceUpdateEPBS(ctx, blockHash, attr)
|
||||||
|
if err != nil {
|
||||||
|
if IsInvalidBlock(err) {
|
||||||
|
// TODO handle the lvh here
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if attr != nil && !attr.IsEmpty() && payloadID != nil {
|
||||||
|
var pid [8]byte
|
||||||
|
copy(pid[:], payloadID[:])
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"blockRoot": fmt.Sprintf("%#x", bytesutil.Trunc(headRoot)),
|
||||||
|
"headSlot": envelope.Slot(),
|
||||||
|
"payloadID": fmt.Sprintf("%#x", bytesutil.Trunc(payloadID[:])),
|
||||||
|
}).Info("Forkchoice updated with payload attributes for proposal")
|
||||||
|
s.cfg.PayloadIDCache.Set(envelope.Slot()+1, root, pid)
|
||||||
|
}
|
||||||
|
// simply update the headstate in head
|
||||||
|
s.headLock.Lock()
|
||||||
|
s.head.state = preState.Copy()
|
||||||
|
s.headLock.Unlock()
|
||||||
|
// update the NSC with the hash for the full block, we use the block root as the key
|
||||||
|
if err := transition.UpdateNextSlotCache(ctx, root[:], preState); err != nil {
|
||||||
|
log.WithError(err).Error("could not update next slot cache with payload")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
timeWithoutDaWait := time.Since(receivedTime) - daWaitedTime
|
||||||
|
executionEngineProcessingTime.Observe(float64(timeWithoutDaWait.Milliseconds()))
|
||||||
|
ex, err := envelope.Execution()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not get execution data")
|
||||||
|
}
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"slot": envelope.Slot(),
|
||||||
|
"blockRoot": fmt.Sprintf("%#x", bytesutil.Trunc(root[:])),
|
||||||
|
"blockHash": fmt.Sprintf("%#x", bytesutil.Trunc(ex.BlockHash())),
|
||||||
|
"ParentHash": fmt.Sprintf("%#x", bytesutil.Trunc(ex.ParentHash())),
|
||||||
|
}).Info("Processed execution payload envelope")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// notifyNewPayload signals execution engine on a new payload.
|
||||||
|
// It returns true if the EL has returned VALID for the block
|
||||||
|
func (s *Service) notifyNewEnvelope(ctx context.Context, envelope interfaces.ROExecutionPayloadEnvelope) (bool, error) {
|
||||||
|
ctx, span := trace.StartSpan(ctx, "blockChain.notifyNewPayload")
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
payload, err := envelope.Execution()
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "could not get execution payload")
|
||||||
|
}
|
||||||
|
|
||||||
|
versionedHashes := envelope.VersionedHashes()
|
||||||
|
root := envelope.BeaconBlockRoot()
|
||||||
|
parentRoot, err := s.ParentRoot(root)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "could not get parent block root")
|
||||||
|
}
|
||||||
|
pr := common.Hash(parentRoot)
|
||||||
|
requests := envelope.ExecutionRequests()
|
||||||
|
lastValidHash, err := s.cfg.ExecutionEngineCaller.NewPayload(ctx, payload, versionedHashes, &pr, requests)
|
||||||
|
switch {
|
||||||
|
case err == nil:
|
||||||
|
newPayloadValidNodeCount.Inc()
|
||||||
|
return true, nil
|
||||||
|
case errors.Is(err, execution.ErrAcceptedSyncingPayloadStatus):
|
||||||
|
newPayloadOptimisticNodeCount.Inc()
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"payloadBlockHash": fmt.Sprintf("%#x", bytesutil.Trunc(payload.BlockHash())),
|
||||||
|
}).Info("Called new payload with optimistic block")
|
||||||
|
return false, nil
|
||||||
|
case errors.Is(err, execution.ErrInvalidPayloadStatus):
|
||||||
|
lvh := bytesutil.ToBytes32(lastValidHash)
|
||||||
|
return false, invalidBlock{
|
||||||
|
error: ErrInvalidPayload,
|
||||||
|
lastValidHash: lvh,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false, errors.WithMessage(ErrUndefinedExecutionEngineError, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateExecutionOnEnvelope notifies the engine of the incoming execution payload and returns true if the payload is valid
|
||||||
|
func (s *Service) validateExecutionOnEnvelope(ctx context.Context, e interfaces.ROExecutionPayloadEnvelope) (bool, error) {
|
||||||
|
isValidPayload, err := s.notifyNewEnvelope(ctx, e)
|
||||||
|
if err == nil {
|
||||||
|
return isValidPayload, nil
|
||||||
|
}
|
||||||
|
blockRoot := e.BeaconBlockRoot()
|
||||||
|
parentRoot, rootErr := s.ParentRoot(blockRoot)
|
||||||
|
if rootErr != nil {
|
||||||
|
return false, errors.Wrap(rootErr, "could not get parent block root")
|
||||||
|
}
|
||||||
|
s.cfg.ForkChoiceStore.Lock()
|
||||||
|
err = s.handleInvalidExecutionError(ctx, err, blockRoot, parentRoot)
|
||||||
|
s.cfg.ForkChoiceStore.Unlock()
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) getPayloadEnvelopePrestate(ctx context.Context, e interfaces.ROExecutionPayloadEnvelope) (state.BeaconState, error) {
|
||||||
|
ctx, span := trace.StartSpan(ctx, "blockChain.getPayloadEnvelopePreState")
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
// Verify incoming payload has a valid pre state.
|
||||||
|
root := e.BeaconBlockRoot()
|
||||||
|
// Verify the referred block is known to forkchoice
|
||||||
|
if !s.InForkchoice(root) {
|
||||||
|
return nil, errors.New("Cannot import execution payload envelope for unknown block")
|
||||||
|
}
|
||||||
|
if err := s.verifyBlkPreState(ctx, root); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "could not verify payload prestate")
|
||||||
|
}
|
||||||
|
|
||||||
|
preState, err := s.cfg.StateGen.StateByRoot(ctx, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "could not get pre state")
|
||||||
|
}
|
||||||
|
if preState == nil || preState.IsNil() {
|
||||||
|
return nil, errors.Wrap(err, "nil pre state")
|
||||||
|
}
|
||||||
|
return preState, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) savePostPayload(ctx context.Context, signed interfaces.ROSignedExecutionPayloadEnvelope, st state.BeaconState) error {
|
||||||
|
if err := s.cfg.BeaconDB.SaveBlindPayloadEnvelope(ctx, signed); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
envelope, err := signed.Envelope()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
execution, err := envelope.Execution()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r := envelope.BeaconBlockRoot()
|
||||||
|
if err := s.cfg.StateGen.SaveState(ctx, [32]byte(execution.BlockHash()), st); err != nil {
|
||||||
|
log.Warnf("Rolling back insertion of block with root %#x", r)
|
||||||
|
if err := s.cfg.BeaconDB.DeleteBlock(ctx, r); err != nil {
|
||||||
|
log.WithError(err).Errorf("Could not delete block with block root %#x", r)
|
||||||
|
}
|
||||||
|
return errors.Wrap(err, "could not save state")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package blockchain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/cache"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/das"
|
||||||
|
mockExecution "github.com/prysmaticlabs/prysm/v5/beacon-chain/execution/testing"
|
||||||
|
forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
||||||
|
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/util"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/util/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_getPayloadEnvelopePrestate(t *testing.T) {
|
||||||
|
service, tr := minimalTestService(t)
|
||||||
|
ctx, fcs := tr.ctx, tr.fcs
|
||||||
|
|
||||||
|
gs, _ := util.DeterministicGenesisStateEpbs(t, 32)
|
||||||
|
require.NoError(t, service.saveGenesisData(ctx, gs))
|
||||||
|
require.NoError(t, fcs.UpdateFinalizedCheckpoint(&forkchoicetypes.Checkpoint{Root: service.originBlockRoot}))
|
||||||
|
|
||||||
|
p := random.ExecutionPayloadEnvelope(t)
|
||||||
|
p.BeaconBlockRoot = service.originBlockRoot[:]
|
||||||
|
e, err := blocks.WrappedROExecutionPayloadEnvelope(p)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = service.getPayloadEnvelopePrestate(ctx, e)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_notifyNewEnvelope(t *testing.T) {
|
||||||
|
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
|
||||||
|
ctx, fcs := tr.ctx, tr.fcs
|
||||||
|
gs, _ := util.DeterministicGenesisStateEpbs(t, 32)
|
||||||
|
require.NoError(t, service.saveGenesisData(ctx, gs))
|
||||||
|
require.NoError(t, fcs.UpdateFinalizedCheckpoint(&forkchoicetypes.Checkpoint{Root: service.originBlockRoot}))
|
||||||
|
p := random.ExecutionPayloadEnvelope(t)
|
||||||
|
p.BeaconBlockRoot = service.originBlockRoot[:]
|
||||||
|
e, err := blocks.WrappedROExecutionPayloadEnvelope(p)
|
||||||
|
require.NoError(t, err)
|
||||||
|
engine := &mockExecution.EngineClient{}
|
||||||
|
service.cfg.ExecutionEngineCaller = engine
|
||||||
|
isValidPayload, err := service.notifyNewEnvelope(ctx, e)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, true, isValidPayload)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_validateExecutionOnEnvelope(t *testing.T) {
|
||||||
|
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
|
||||||
|
ctx, fcs := tr.ctx, tr.fcs
|
||||||
|
gs, _ := util.DeterministicGenesisStateEpbs(t, 32)
|
||||||
|
require.NoError(t, service.saveGenesisData(ctx, gs))
|
||||||
|
require.NoError(t, fcs.UpdateFinalizedCheckpoint(&forkchoicetypes.Checkpoint{Root: service.originBlockRoot}))
|
||||||
|
p := random.ExecutionPayloadEnvelope(t)
|
||||||
|
p.BeaconBlockRoot = service.originBlockRoot[:]
|
||||||
|
e, err := blocks.WrappedROExecutionPayloadEnvelope(p)
|
||||||
|
require.NoError(t, err)
|
||||||
|
engine := &mockExecution.EngineClient{}
|
||||||
|
service.cfg.ExecutionEngineCaller = engine
|
||||||
|
isValidPayload, err := service.validateExecutionOnEnvelope(ctx, e)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, true, isValidPayload)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ReceiveExecutionPayloadEnvelope(t *testing.T) {
|
||||||
|
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
|
||||||
|
ctx, fcs := tr.ctx, tr.fcs
|
||||||
|
gs, _ := util.DeterministicGenesisStateEpbs(t, 32)
|
||||||
|
require.NoError(t, service.saveGenesisData(ctx, gs))
|
||||||
|
require.NoError(t, fcs.UpdateFinalizedCheckpoint(&forkchoicetypes.Checkpoint{Root: service.originBlockRoot}))
|
||||||
|
post := gs.Copy()
|
||||||
|
p := &enginev1.ExecutionPayloadEnvelope{
|
||||||
|
Payload: &enginev1.ExecutionPayloadDeneb{
|
||||||
|
ParentHash: make([]byte, 32),
|
||||||
|
BlockHash: make([]byte, 32),
|
||||||
|
},
|
||||||
|
BeaconBlockRoot: service.originBlockRoot[:],
|
||||||
|
BlobKzgCommitments: make([][]byte, 0),
|
||||||
|
StateRoot: make([]byte, 32),
|
||||||
|
ExecutionRequests: &enginev1.ExecutionRequests{},
|
||||||
|
}
|
||||||
|
sp := &enginev1.SignedExecutionPayloadEnvelope{
|
||||||
|
Message: p,
|
||||||
|
}
|
||||||
|
e, err := blocks.WrappedROSignedExecutionPayloadEnvelope(sp)
|
||||||
|
require.NoError(t, err)
|
||||||
|
das := &das.MockAvailabilityStore{}
|
||||||
|
|
||||||
|
blockHeader := post.LatestBlockHeader()
|
||||||
|
prevStateRoot, err := post.HashTreeRoot(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
blockHeader.StateRoot = prevStateRoot[:]
|
||||||
|
require.NoError(t, post.SetLatestBlockHeader(blockHeader))
|
||||||
|
stRoot, err := post.HashTreeRoot(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
p.StateRoot = stRoot[:]
|
||||||
|
engine := &mockExecution.EngineClient{}
|
||||||
|
service.cfg.ExecutionEngineCaller = engine
|
||||||
|
require.NoError(t, service.ReceiveExecutionPayloadEnvelope(ctx, e, das))
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package blockchain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
|
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Service) ReceivePayloadAttestationMessage(ctx context.Context, a *eth.PayloadAttestationMessage) error {
|
||||||
|
if err := helpers.ValidateNilPayloadAttestationMessage(a); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
root := [32]byte(a.Data.BeaconBlockRoot)
|
||||||
|
st, err := s.HeadStateReadOnly(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ptc, err := helpers.GetPayloadTimelinessCommittee(ctx, st, a.Data.Slot)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
idx := slices.Index(ptc, a.ValidatorIndex)
|
||||||
|
if idx == -1 {
|
||||||
|
return errInvalidValidatorIndex
|
||||||
|
}
|
||||||
|
if s.cfg.PayloadAttestationCache.Seen(root, uint64(primitives.ValidatorIndex(idx))) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return s.cfg.PayloadAttestationCache.Add(a, uint64(idx))
|
||||||
|
}
|
||||||
@@ -47,24 +47,26 @@ import (
|
|||||||
// Service represents a service that handles the internal
|
// Service represents a service that handles the internal
|
||||||
// logic of managing the full PoS beacon chain.
|
// logic of managing the full PoS beacon chain.
|
||||||
type Service struct {
|
type Service struct {
|
||||||
cfg *config
|
cfg *config
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
genesisTime time.Time
|
genesisTime time.Time
|
||||||
head *head
|
head *head
|
||||||
headLock sync.RWMutex
|
headLock sync.RWMutex
|
||||||
originBlockRoot [32]byte // genesis root, or weak subjectivity checkpoint root, depending on how the node is initialized
|
originBlockRoot [32]byte // genesis root, or weak subjectivity checkpoint root, depending on how the node is initialized
|
||||||
boundaryRoots [][32]byte
|
boundaryRoots [][32]byte
|
||||||
checkpointStateCache *cache.CheckpointStateCache
|
checkpointStateCache *cache.CheckpointStateCache
|
||||||
initSyncBlocks map[[32]byte]interfaces.ReadOnlySignedBeaconBlock
|
initSyncBlocks map[[32]byte]interfaces.ReadOnlySignedBeaconBlock
|
||||||
initSyncBlocksLock sync.RWMutex
|
initSyncBlocksLock sync.RWMutex
|
||||||
wsVerifier *WeakSubjectivityVerifier
|
wsVerifier *WeakSubjectivityVerifier
|
||||||
clockSetter startup.ClockSetter
|
clockSetter startup.ClockSetter
|
||||||
clockWaiter startup.ClockWaiter
|
clockWaiter startup.ClockWaiter
|
||||||
syncComplete chan struct{}
|
syncComplete chan struct{}
|
||||||
blobNotifiers *blobNotifierMap
|
blobNotifiers *blobNotifierMap
|
||||||
blockBeingSynced *currentlySyncingBlock
|
blockBeingSynced *currentlySyncingBlock
|
||||||
blobStorage *filesystem.BlobStorage
|
lastPublishedLightClientEpoch primitives.Epoch
|
||||||
|
blobStorage *filesystem.BlobStorage
|
||||||
|
payloadBeingSynced *currentlySyncingPayload
|
||||||
}
|
}
|
||||||
|
|
||||||
// config options for the service.
|
// config options for the service.
|
||||||
@@ -73,6 +75,8 @@ type config struct {
|
|||||||
ChainStartFetcher execution.ChainStartFetcher
|
ChainStartFetcher execution.ChainStartFetcher
|
||||||
BeaconDB db.HeadAccessDatabase
|
BeaconDB db.HeadAccessDatabase
|
||||||
DepositCache cache.DepositCache
|
DepositCache cache.DepositCache
|
||||||
|
PayloadAttestationCache *cache.PayloadAttestationCache
|
||||||
|
PayloadEnvelopeCache *sync.Map
|
||||||
PayloadIDCache *cache.PayloadIDCache
|
PayloadIDCache *cache.PayloadIDCache
|
||||||
TrackedValidatorsCache *cache.TrackedValidatorsCache
|
TrackedValidatorsCache *cache.TrackedValidatorsCache
|
||||||
AttestationCache *cache.AttestationCache
|
AttestationCache *cache.AttestationCache
|
||||||
@@ -183,6 +187,7 @@ func NewService(ctx context.Context, opts ...Option) (*Service, error) {
|
|||||||
blobNotifiers: bn,
|
blobNotifiers: bn,
|
||||||
cfg: &config{},
|
cfg: &config{},
|
||||||
blockBeingSynced: ¤tlySyncingBlock{roots: make(map[[32]byte]struct{})},
|
blockBeingSynced: ¤tlySyncingBlock{roots: make(map[[32]byte]struct{})},
|
||||||
|
payloadBeingSynced: ¤tlySyncingPayload{roots: make(map[[32]byte]primitives.PTCStatus)},
|
||||||
}
|
}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
if err := opt(srv); err != nil {
|
if err := opt(srv); err != nil {
|
||||||
@@ -558,7 +563,7 @@ func (s *Service) saveGenesisData(ctx context.Context, genesisState state.Beacon
|
|||||||
// 2.) Check DB.
|
// 2.) Check DB.
|
||||||
// Checking 1.) is ten times faster than checking 2.)
|
// Checking 1.) is ten times faster than checking 2.)
|
||||||
// this function requires a lock in forkchoice
|
// this function requires a lock in forkchoice
|
||||||
func (s *Service) hasBlock(ctx context.Context, root [32]byte) bool {
|
func (s *Service) chainHasBlock(ctx context.Context, root [32]byte) bool {
|
||||||
if s.cfg.ForkChoiceStore.HasNode(root) {
|
if s.cfg.ForkChoiceStore.HasNode(root) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -386,8 +386,8 @@ func TestHasBlock_ForkChoiceAndDB_DoublyLinkedTree(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, s.cfg.ForkChoiceStore.InsertNode(ctx, beaconState, roblock))
|
require.NoError(t, s.cfg.ForkChoiceStore.InsertNode(ctx, beaconState, roblock))
|
||||||
|
|
||||||
assert.Equal(t, false, s.hasBlock(ctx, [32]byte{}), "Should not have block")
|
assert.Equal(t, false, s.chainHasBlock(ctx, [32]byte{}), "Should not have block")
|
||||||
assert.Equal(t, true, s.hasBlock(ctx, r), "Should have block")
|
assert.Equal(t, true, s.chainHasBlock(ctx, r), "Should have block")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServiceStop_SaveCachedBlocks(t *testing.T) {
|
func TestServiceStop_SaveCachedBlocks(t *testing.T) {
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ load("@prysm//tools/go:def.bzl", "go_library")
|
|||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
testonly = True,
|
testonly = True,
|
||||||
srcs = ["mock.go"],
|
srcs = [
|
||||||
|
"mock.go",
|
||||||
|
"mock_epbs.go",
|
||||||
|
],
|
||||||
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing",
|
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing",
|
||||||
visibility = [
|
visibility = [
|
||||||
"//beacon-chain:__subpackages__",
|
"//beacon-chain:__subpackages__",
|
||||||
|
|||||||
@@ -37,44 +37,50 @@ var ErrNilState = errors.New("nil state")
|
|||||||
|
|
||||||
// ChainService defines the mock interface for testing
|
// ChainService defines the mock interface for testing
|
||||||
type ChainService struct {
|
type ChainService struct {
|
||||||
NotFinalized bool
|
NotFinalized bool
|
||||||
Optimistic bool
|
Optimistic bool
|
||||||
ValidAttestation bool
|
ValidAttestation bool
|
||||||
ValidatorsRoot [32]byte
|
ValidatorsRoot [32]byte
|
||||||
PublicKey [fieldparams.BLSPubkeyLength]byte
|
PublicKey [fieldparams.BLSPubkeyLength]byte
|
||||||
FinalizedCheckPoint *ethpb.Checkpoint
|
FinalizedCheckPoint *ethpb.Checkpoint
|
||||||
CurrentJustifiedCheckPoint *ethpb.Checkpoint
|
CurrentJustifiedCheckPoint *ethpb.Checkpoint
|
||||||
PreviousJustifiedCheckPoint *ethpb.Checkpoint
|
PreviousJustifiedCheckPoint *ethpb.Checkpoint
|
||||||
Slot *primitives.Slot // Pointer because 0 is a useful value, so checking against it can be incorrect.
|
Slot *primitives.Slot // Pointer because 0 is a useful value, so checking against it can be incorrect.
|
||||||
Balance *precompute.Balance
|
Balance *precompute.Balance
|
||||||
CanonicalRoots map[[32]byte]bool
|
CanonicalRoots map[[32]byte]bool
|
||||||
Fork *ethpb.Fork
|
Fork *ethpb.Fork
|
||||||
ETH1Data *ethpb.Eth1Data
|
ETH1Data *ethpb.Eth1Data
|
||||||
InitSyncBlockRoots map[[32]byte]bool
|
InitSyncBlockRoots map[[32]byte]bool
|
||||||
DB db.Database
|
DB db.Database
|
||||||
State state.BeaconState
|
State state.BeaconState
|
||||||
Block interfaces.ReadOnlySignedBeaconBlock
|
Block interfaces.ReadOnlySignedBeaconBlock
|
||||||
VerifyBlkDescendantErr error
|
ExecutionPayloadEnvelope interfaces.ROExecutionPayloadEnvelope
|
||||||
stateNotifier statefeed.Notifier
|
VerifyBlkDescendantErr error
|
||||||
BlocksReceived []interfaces.ReadOnlySignedBeaconBlock
|
stateNotifier statefeed.Notifier
|
||||||
SyncCommitteeIndices []primitives.CommitteeIndex
|
BlocksReceived []interfaces.ReadOnlySignedBeaconBlock
|
||||||
blockNotifier blockfeed.Notifier
|
SyncCommitteeIndices []primitives.CommitteeIndex
|
||||||
opNotifier opfeed.Notifier
|
blockNotifier blockfeed.Notifier
|
||||||
Root []byte
|
opNotifier opfeed.Notifier
|
||||||
SyncCommitteeDomain []byte
|
Root []byte
|
||||||
SyncSelectionProofDomain []byte
|
SyncCommitteeDomain []byte
|
||||||
SyncContributionProofDomain []byte
|
SyncSelectionProofDomain []byte
|
||||||
SyncCommitteePubkeys [][]byte
|
SyncContributionProofDomain []byte
|
||||||
Genesis time.Time
|
SyncCommitteePubkeys [][]byte
|
||||||
ForkChoiceStore forkchoice.ForkChoicer
|
Genesis time.Time
|
||||||
ReceiveBlockMockErr error
|
ForkChoiceStore forkchoice.ForkChoicer
|
||||||
OptimisticCheckRootReceived [32]byte
|
ReceiveBlockMockErr error
|
||||||
FinalizedRoots map[[32]byte]bool
|
ReceiveEnvelopeMockErr error
|
||||||
OptimisticRoots map[[32]byte]bool
|
OptimisticCheckRootReceived [32]byte
|
||||||
BlockSlot primitives.Slot
|
FinalizedRoots map[[32]byte]bool
|
||||||
SyncingRoot [32]byte
|
OptimisticRoots map[[32]byte]bool
|
||||||
Blobs []blocks.VerifiedROBlob
|
BlockSlot primitives.Slot
|
||||||
TargetRoot [32]byte
|
SyncingRoot [32]byte
|
||||||
|
Blobs []blocks.VerifiedROBlob
|
||||||
|
TargetRoot [32]byte
|
||||||
|
HighestReceivedSlot primitives.Slot
|
||||||
|
HighestReceivedRoot [32]byte
|
||||||
|
PayloadStatus primitives.PTCStatus
|
||||||
|
ReceivePayloadAttestationMessageErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ChainService) Ancestor(ctx context.Context, root []byte, slot primitives.Slot) ([]byte, error) {
|
func (s *ChainService) Ancestor(ctx context.Context, root []byte, slot primitives.Slot) ([]byte, error) {
|
||||||
@@ -641,12 +647,12 @@ func (s *ChainService) ReceivedBlocksLastEpoch() (uint64, error) {
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HighestReceivedBlockSlot mocks the same method in the chain service
|
// HighestReceivedBlockSlotRoot mocks the same method in the chain service
|
||||||
func (s *ChainService) HighestReceivedBlockSlot() primitives.Slot {
|
func (s *ChainService) HighestReceivedBlockSlotRoot() (primitives.Slot, [32]byte) {
|
||||||
if s.ForkChoiceStore != nil {
|
if s.ForkChoiceStore != nil {
|
||||||
return s.ForkChoiceStore.HighestReceivedBlockSlot()
|
return s.ForkChoiceStore.HighestReceivedBlockSlotRoot()
|
||||||
}
|
}
|
||||||
return 0
|
return s.HighestReceivedSlot, s.HighestReceivedRoot
|
||||||
}
|
}
|
||||||
|
|
||||||
// InsertNode mocks the same method in the chain service
|
// InsertNode mocks the same method in the chain service
|
||||||
@@ -706,3 +712,21 @@ func (c *ChainService) ReceiveBlob(_ context.Context, b blocks.VerifiedROBlob) e
|
|||||||
func (c *ChainService) TargetRootForEpoch(_ [32]byte, _ primitives.Epoch) ([32]byte, error) {
|
func (c *ChainService) TargetRootForEpoch(_ [32]byte, _ primitives.Epoch) ([32]byte, error) {
|
||||||
return c.TargetRoot, nil
|
return c.TargetRoot, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HashInForkchoice mocks the same method in the chain service
|
||||||
|
func (c *ChainService) HashInForkchoice([32]byte) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReceivePayloadAttestationMessage mocks the same method in the chain service
|
||||||
|
func (c *ChainService) ReceivePayloadAttestationMessage(_ context.Context, _ *ethpb.PayloadAttestationMessage) error {
|
||||||
|
return c.ReceivePayloadAttestationMessageErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChainService) GetPTCVote(root [32]byte) primitives.PTCStatus {
|
||||||
|
return c.PayloadStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChainService) HashForBlockRoot(root [32]byte) [32]byte {
|
||||||
|
return root
|
||||||
|
}
|
||||||
|
|||||||
30
beacon-chain/blockchain/testing/mock_epbs.go
Normal file
30
beacon-chain/blockchain/testing/mock_epbs.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/das"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReceiveExecutionPayloadEnvelope mocks the method in chain service.
|
||||||
|
func (s *ChainService) ReceiveExecutionPayloadEnvelope(ctx context.Context, env interfaces.ROSignedExecutionPayloadEnvelope, _ das.AvailabilityStore) error {
|
||||||
|
if s.ReceiveBlockMockErr != nil {
|
||||||
|
return s.ReceiveBlockMockErr
|
||||||
|
}
|
||||||
|
if s.State == nil {
|
||||||
|
return ErrNilState
|
||||||
|
}
|
||||||
|
e, err := env.Envelope()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.State.Slot() == e.Slot() {
|
||||||
|
if err := s.State.SetLatestFullSlot(s.State.Slot()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.ExecutionPayloadEnvelope = e
|
||||||
|
return nil
|
||||||
|
}
|
||||||
7
beacon-chain/cache/BUILD.bazel
vendored
7
beacon-chain/cache/BUILD.bazel
vendored
@@ -16,11 +16,13 @@ go_library(
|
|||||||
"doc.go",
|
"doc.go",
|
||||||
"error.go",
|
"error.go",
|
||||||
"interfaces.go",
|
"interfaces.go",
|
||||||
|
"payload_attestation.go",
|
||||||
"payload_id.go",
|
"payload_id.go",
|
||||||
"proposer_indices.go",
|
"proposer_indices.go",
|
||||||
"proposer_indices_disabled.go", # keep
|
"proposer_indices_disabled.go", # keep
|
||||||
"proposer_indices_type.go",
|
"proposer_indices_type.go",
|
||||||
"registration.go",
|
"registration.go",
|
||||||
|
"signed_execution_header.go",
|
||||||
"skip_slot_cache.go",
|
"skip_slot_cache.go",
|
||||||
"subnet_ids.go",
|
"subnet_ids.go",
|
||||||
"sync_committee.go",
|
"sync_committee.go",
|
||||||
@@ -50,6 +52,7 @@ go_library(
|
|||||||
"//encoding/bytesutil:go_default_library",
|
"//encoding/bytesutil:go_default_library",
|
||||||
"//math:go_default_library",
|
"//math:go_default_library",
|
||||||
"//monitoring/tracing/trace:go_default_library",
|
"//monitoring/tracing/trace:go_default_library",
|
||||||
|
"//proto/engine/v1:go_default_library",
|
||||||
"//proto/prysm/v1alpha1:go_default_library",
|
"//proto/prysm/v1alpha1:go_default_library",
|
||||||
"//proto/prysm/v1alpha1/attestation:go_default_library",
|
"//proto/prysm/v1alpha1/attestation:go_default_library",
|
||||||
"//runtime/version:go_default_library",
|
"//runtime/version:go_default_library",
|
||||||
@@ -75,10 +78,12 @@ go_test(
|
|||||||
"checkpoint_state_test.go",
|
"checkpoint_state_test.go",
|
||||||
"committee_fuzz_test.go",
|
"committee_fuzz_test.go",
|
||||||
"committee_test.go",
|
"committee_test.go",
|
||||||
|
"payload_attestation_test.go",
|
||||||
"payload_id_test.go",
|
"payload_id_test.go",
|
||||||
"private_access_test.go",
|
"private_access_test.go",
|
||||||
"proposer_indices_test.go",
|
"proposer_indices_test.go",
|
||||||
"registration_test.go",
|
"registration_test.go",
|
||||||
|
"signed_execution_header_test.go",
|
||||||
"skip_slot_cache_test.go",
|
"skip_slot_cache_test.go",
|
||||||
"subnet_ids_test.go",
|
"subnet_ids_test.go",
|
||||||
"sync_committee_head_state_test.go",
|
"sync_committee_head_state_test.go",
|
||||||
@@ -93,8 +98,10 @@ go_test(
|
|||||||
"//config/fieldparams:go_default_library",
|
"//config/fieldparams:go_default_library",
|
||||||
"//config/params:go_default_library",
|
"//config/params:go_default_library",
|
||||||
"//consensus-types/primitives:go_default_library",
|
"//consensus-types/primitives:go_default_library",
|
||||||
|
"//crypto/bls:go_default_library",
|
||||||
"//crypto/bls/blst:go_default_library",
|
"//crypto/bls/blst:go_default_library",
|
||||||
"//encoding/bytesutil:go_default_library",
|
"//encoding/bytesutil:go_default_library",
|
||||||
|
"//proto/engine/v1:go_default_library",
|
||||||
"//proto/prysm/v1alpha1:go_default_library",
|
"//proto/prysm/v1alpha1:go_default_library",
|
||||||
"//proto/prysm/v1alpha1/attestation:go_default_library",
|
"//proto/prysm/v1alpha1/attestation:go_default_library",
|
||||||
"//testing/assert:go_default_library",
|
"//testing/assert:go_default_library",
|
||||||
|
|||||||
132
beacon-chain/cache/payload_attestation.go
vendored
Normal file
132
beacon-chain/cache/payload_attestation.go
vendored
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||||
|
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errNilPayloadAttestationMessage = errors.New("nil Payload Attestation Message")
|
||||||
|
|
||||||
|
// PayloadAttestationCache keeps a map of all the PTC votes that were seen,
|
||||||
|
// already aggregated. The key is the beacon block root.
|
||||||
|
type PayloadAttestationCache struct {
|
||||||
|
root [32]byte
|
||||||
|
attestations [primitives.PAYLOAD_INVALID_STATUS]*eth.PayloadAttestation
|
||||||
|
sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seen returns true if a vote for the given Beacon Block Root has already been processed
|
||||||
|
// for this Payload Timeliness Committee index. This will return true even if
|
||||||
|
// the Payload status differs.
|
||||||
|
func (p *PayloadAttestationCache) Seen(root [32]byte, idx uint64) bool {
|
||||||
|
p.Lock()
|
||||||
|
defer p.Unlock()
|
||||||
|
if p.root != root {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, agg := range p.attestations {
|
||||||
|
if agg == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if agg.AggregationBits.BitAt(idx) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// messageToPayloadAttestation creates a PayloadAttestation with a single
|
||||||
|
// aggregated bit from the passed PayloadAttestationMessage
|
||||||
|
func messageToPayloadAttestation(att *eth.PayloadAttestationMessage, idx uint64) *eth.PayloadAttestation {
|
||||||
|
bits := primitives.NewPayloadAttestationAggregationBits()
|
||||||
|
bits.SetBitAt(idx, true)
|
||||||
|
data := ð.PayloadAttestationData{
|
||||||
|
BeaconBlockRoot: bytesutil.SafeCopyBytes(att.Data.BeaconBlockRoot),
|
||||||
|
Slot: att.Data.Slot,
|
||||||
|
PayloadStatus: att.Data.PayloadStatus,
|
||||||
|
}
|
||||||
|
return ð.PayloadAttestation{
|
||||||
|
AggregationBits: bits,
|
||||||
|
Data: data,
|
||||||
|
Signature: bytesutil.SafeCopyBytes(att.Signature),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// aggregateSigFromMessage returns the aggregated signature from a Payload
|
||||||
|
// Attestation by adding the passed signature in the PayloadAttestationMessage,
|
||||||
|
// no signature validation is performed.
|
||||||
|
func aggregateSigFromMessage(aggregated *eth.PayloadAttestation, message *eth.PayloadAttestationMessage) ([]byte, error) {
|
||||||
|
aggSig, err := bls.SignatureFromBytesNoValidation(aggregated.Signature)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sig, err := bls.SignatureFromBytesNoValidation(message.Signature)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bls.AggregateSignatures([]bls.Signature{aggSig, sig}).Marshal(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a PayloadAttestationMessage to the internal cache of aggregated
|
||||||
|
// PayloadAttestations.
|
||||||
|
// If the index has already been seen for this attestation status the function does nothing.
|
||||||
|
// If the root is not the cached root, the function will clear the previous cache
|
||||||
|
// This function assumes that the message has already been validated. In
|
||||||
|
// particular that the signature is valid and that the block root corresponds to
|
||||||
|
// the given slot in the attestation data.
|
||||||
|
func (p *PayloadAttestationCache) Add(att *eth.PayloadAttestationMessage, idx uint64) error {
|
||||||
|
if att == nil || att.Data == nil || att.Data.BeaconBlockRoot == nil {
|
||||||
|
return errNilPayloadAttestationMessage
|
||||||
|
}
|
||||||
|
p.Lock()
|
||||||
|
defer p.Unlock()
|
||||||
|
root := [32]byte(att.Data.BeaconBlockRoot)
|
||||||
|
if p.root != root {
|
||||||
|
p.root = root
|
||||||
|
p.attestations = [primitives.PAYLOAD_INVALID_STATUS]*eth.PayloadAttestation{}
|
||||||
|
}
|
||||||
|
agg := p.attestations[att.Data.PayloadStatus]
|
||||||
|
if agg == nil {
|
||||||
|
p.attestations[att.Data.PayloadStatus] = messageToPayloadAttestation(att, idx)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if agg.AggregationBits.BitAt(idx) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
sig, err := aggregateSigFromMessage(agg, att)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
agg.Signature = sig
|
||||||
|
agg.AggregationBits.SetBitAt(idx, true)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the aggregated PayloadAttestation for the given root and status
|
||||||
|
// if the root doesn't exist or status is invalid, the function returns nil.
|
||||||
|
func (p *PayloadAttestationCache) Get(root [32]byte, status primitives.PTCStatus) *eth.PayloadAttestation {
|
||||||
|
p.Lock()
|
||||||
|
defer p.Unlock()
|
||||||
|
|
||||||
|
if p.root != root {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if status >= primitives.PAYLOAD_INVALID_STATUS {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return eth.CopyPayloadAttestation(p.attestations[status])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear clears the internal map
|
||||||
|
func (p *PayloadAttestationCache) Clear() {
|
||||||
|
p.Lock()
|
||||||
|
defer p.Unlock()
|
||||||
|
p.root = [32]byte{}
|
||||||
|
p.attestations = [primitives.PAYLOAD_INVALID_STATUS]*eth.PayloadAttestation{}
|
||||||
|
}
|
||||||
143
beacon-chain/cache/payload_attestation_test.go
vendored
Normal file
143
beacon-chain/cache/payload_attestation_test.go
vendored
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||||
|
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPayloadAttestationCache(t *testing.T) {
|
||||||
|
p := &PayloadAttestationCache{}
|
||||||
|
|
||||||
|
//Test Has seen
|
||||||
|
root := [32]byte{'r'}
|
||||||
|
idx := uint64(5)
|
||||||
|
require.Equal(t, false, p.Seen(root, idx))
|
||||||
|
|
||||||
|
// Test Add
|
||||||
|
msg := ð.PayloadAttestationMessage{
|
||||||
|
Signature: bls.NewAggregateSignature().Marshal(),
|
||||||
|
Data: ð.PayloadAttestationData{
|
||||||
|
BeaconBlockRoot: root[:],
|
||||||
|
Slot: 1,
|
||||||
|
PayloadStatus: primitives.PAYLOAD_PRESENT,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new root
|
||||||
|
require.NoError(t, p.Add(msg, idx))
|
||||||
|
require.Equal(t, true, p.Seen(root, idx))
|
||||||
|
require.Equal(t, root, p.root)
|
||||||
|
att := p.attestations[primitives.PAYLOAD_PRESENT]
|
||||||
|
indices := att.AggregationBits.BitIndices()
|
||||||
|
require.DeepEqual(t, []int{int(idx)}, indices)
|
||||||
|
singleSig := bytesutil.SafeCopyBytes(msg.Signature)
|
||||||
|
require.DeepEqual(t, singleSig, att.Signature)
|
||||||
|
|
||||||
|
// Test Seen
|
||||||
|
require.Equal(t, true, p.Seen(root, idx))
|
||||||
|
require.Equal(t, false, p.Seen(root, idx+1))
|
||||||
|
|
||||||
|
// Add another attestation on the same data
|
||||||
|
msg2 := ð.PayloadAttestationMessage{
|
||||||
|
Signature: bls.NewAggregateSignature().Marshal(),
|
||||||
|
Data: att.Data,
|
||||||
|
}
|
||||||
|
idx2 := uint64(7)
|
||||||
|
require.NoError(t, p.Add(msg2, idx2))
|
||||||
|
att = p.attestations[primitives.PAYLOAD_PRESENT]
|
||||||
|
indices = att.AggregationBits.BitIndices()
|
||||||
|
require.DeepEqual(t, []int{int(idx), int(idx2)}, indices)
|
||||||
|
require.DeepNotEqual(t, att.Signature, msg.Signature)
|
||||||
|
|
||||||
|
// Try again the same index
|
||||||
|
require.NoError(t, p.Add(msg2, idx2))
|
||||||
|
att2 := p.attestations[primitives.PAYLOAD_PRESENT]
|
||||||
|
indices = att.AggregationBits.BitIndices()
|
||||||
|
require.DeepEqual(t, []int{int(idx), int(idx2)}, indices)
|
||||||
|
require.DeepEqual(t, att, att2)
|
||||||
|
|
||||||
|
// Test Seen
|
||||||
|
require.Equal(t, true, p.Seen(root, idx2))
|
||||||
|
require.Equal(t, false, p.Seen(root, idx2+1))
|
||||||
|
|
||||||
|
// Add another payload status for a different payload status
|
||||||
|
msg3 := ð.PayloadAttestationMessage{
|
||||||
|
Signature: bls.NewAggregateSignature().Marshal(),
|
||||||
|
Data: ð.PayloadAttestationData{
|
||||||
|
BeaconBlockRoot: root[:],
|
||||||
|
Slot: 1,
|
||||||
|
PayloadStatus: primitives.PAYLOAD_WITHHELD,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
idx3 := uint64(17)
|
||||||
|
|
||||||
|
require.NoError(t, p.Add(msg3, idx3))
|
||||||
|
att3 := p.attestations[primitives.PAYLOAD_WITHHELD]
|
||||||
|
indices3 := att3.AggregationBits.BitIndices()
|
||||||
|
require.DeepEqual(t, []int{int(idx3)}, indices3)
|
||||||
|
require.DeepEqual(t, singleSig, att3.Signature)
|
||||||
|
|
||||||
|
// Add a different root
|
||||||
|
root2 := [32]byte{'s'}
|
||||||
|
msg.Data.BeaconBlockRoot = root2[:]
|
||||||
|
require.NoError(t, p.Add(msg, idx))
|
||||||
|
require.Equal(t, root2, p.root)
|
||||||
|
require.Equal(t, true, p.Seen(root2, idx))
|
||||||
|
require.Equal(t, false, p.Seen(root, idx))
|
||||||
|
att = p.attestations[primitives.PAYLOAD_PRESENT]
|
||||||
|
indices = att.AggregationBits.BitIndices()
|
||||||
|
require.DeepEqual(t, []int{int(idx)}, indices)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPayloadAttestationCache_Get(t *testing.T) {
|
||||||
|
root := [32]byte{1, 2, 3}
|
||||||
|
wrongRoot := [32]byte{4, 5, 6}
|
||||||
|
status := primitives.PAYLOAD_PRESENT
|
||||||
|
invalidStatus := primitives.PAYLOAD_INVALID_STATUS
|
||||||
|
|
||||||
|
cache := &PayloadAttestationCache{
|
||||||
|
root: root,
|
||||||
|
attestations: [primitives.PAYLOAD_INVALID_STATUS]*eth.PayloadAttestation{
|
||||||
|
{
|
||||||
|
Signature: []byte{1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Signature: []byte{2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Signature: []byte{3},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("valid root and status", func(t *testing.T) {
|
||||||
|
result := cache.Get(root, status)
|
||||||
|
require.NotNil(t, result, "Expected a non-nil result")
|
||||||
|
require.DeepEqual(t, cache.attestations[status], result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("invalid root", func(t *testing.T) {
|
||||||
|
result := cache.Get(wrongRoot, status)
|
||||||
|
require.IsNil(t, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("status out of bound", func(t *testing.T) {
|
||||||
|
result := cache.Get(root, invalidStatus)
|
||||||
|
require.IsNil(t, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("no attestation", func(t *testing.T) {
|
||||||
|
emptyCache := &PayloadAttestationCache{
|
||||||
|
root: root,
|
||||||
|
attestations: [primitives.PAYLOAD_INVALID_STATUS]*eth.PayloadAttestation{},
|
||||||
|
}
|
||||||
|
|
||||||
|
result := emptyCache.Get(root, status)
|
||||||
|
require.IsNil(t, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
76
beacon-chain/cache/signed_execution_header.go
vendored
Normal file
76
beacon-chain/cache/signed_execution_header.go
vendored
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
|
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExecutionPayloadHeaders is used by the sync service to store signed execution payload headers after they pass validation,
|
||||||
|
// and filter out subsequent headers with lower value.
|
||||||
|
// The signed header from this cache could be used by the proposer when proposing the next slot.
|
||||||
|
type ExecutionPayloadHeaders struct {
|
||||||
|
headers map[primitives.Slot][]*enginev1.SignedExecutionPayloadHeader
|
||||||
|
sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewExecutionPayloadHeaders() *ExecutionPayloadHeaders {
|
||||||
|
return &ExecutionPayloadHeaders{
|
||||||
|
headers: make(map[primitives.Slot][]*enginev1.SignedExecutionPayloadHeader),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveSignedExecutionPayloadHeader saves the signed execution payload header to the cache.
|
||||||
|
// The cache stores headers for up to two slots. If the input slot is higher than the lowest slot
|
||||||
|
// currently in the cache, the lowest slot is removed to make space for the new header.
|
||||||
|
// Only the highest value header for a given parent block hash will be stored.
|
||||||
|
// This function assumes caller already checks header's slot is current or next slot, it doesn't account for slot validation.
|
||||||
|
func (c *ExecutionPayloadHeaders) SaveSignedExecutionPayloadHeader(header *enginev1.SignedExecutionPayloadHeader) {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
|
||||||
|
for s := range c.headers {
|
||||||
|
if s+1 < header.Message.Slot {
|
||||||
|
delete(c.headers, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add or update the header in the map
|
||||||
|
if _, ok := c.headers[header.Message.Slot]; !ok {
|
||||||
|
c.headers[header.Message.Slot] = []*enginev1.SignedExecutionPayloadHeader{header}
|
||||||
|
} else {
|
||||||
|
found := false
|
||||||
|
for i, h := range c.headers[header.Message.Slot] {
|
||||||
|
if bytes.Equal(h.Message.ParentBlockHash, header.Message.ParentBlockHash) && bytes.Equal(h.Message.ParentBlockRoot, header.Message.ParentBlockRoot) {
|
||||||
|
if header.Message.Value > h.Message.Value {
|
||||||
|
c.headers[header.Message.Slot][i] = header
|
||||||
|
}
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
c.headers[header.Message.Slot] = append(c.headers[header.Message.Slot], header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignedExecutionPayloadHeader returns the signed payload header for the given slot and parent block hash and block root.
|
||||||
|
// Returns nil if the header is not found.
|
||||||
|
// This should be used when the caller wants the header to match parent block hash and parent block root such as proposer choosing a header to propose.
|
||||||
|
func (c *ExecutionPayloadHeaders) SignedExecutionPayloadHeader(slot primitives.Slot, parentBlockHash []byte, parentBlockRoot []byte) *enginev1.SignedExecutionPayloadHeader {
|
||||||
|
c.RLock()
|
||||||
|
defer c.RUnlock()
|
||||||
|
|
||||||
|
if headers, ok := c.headers[slot]; ok {
|
||||||
|
for _, header := range headers {
|
||||||
|
if bytes.Equal(header.Message.ParentBlockHash, parentBlockHash) && bytes.Equal(header.Message.ParentBlockRoot, parentBlockRoot) {
|
||||||
|
return header
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
243
beacon-chain/cache/signed_execution_header_test.go
vendored
Normal file
243
beacon-chain/cache/signed_execution_header_test.go
vendored
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_SaveSignedExecutionPayloadHeader(t *testing.T) {
|
||||||
|
t.Run("First header should be added to cache", func(t *testing.T) {
|
||||||
|
c := NewExecutionPayloadHeaders()
|
||||||
|
header := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 1,
|
||||||
|
ParentBlockHash: []byte("parent1"),
|
||||||
|
Value: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header)
|
||||||
|
require.Equal(t, 1, len(c.headers))
|
||||||
|
require.Equal(t, header, c.headers[1][0])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Second header with higher slot should be added, and both slots should be in cache", func(t *testing.T) {
|
||||||
|
c := NewExecutionPayloadHeaders()
|
||||||
|
header1 := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 1,
|
||||||
|
ParentBlockHash: []byte("parent1"),
|
||||||
|
Value: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
header2 := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 2,
|
||||||
|
ParentBlockHash: []byte("parent2"),
|
||||||
|
Value: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header1)
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header2)
|
||||||
|
require.Equal(t, 2, len(c.headers))
|
||||||
|
require.Equal(t, header1, c.headers[1][0])
|
||||||
|
require.Equal(t, header2, c.headers[2][0])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Third header with higher slot should replace the oldest slot", func(t *testing.T) {
|
||||||
|
c := NewExecutionPayloadHeaders()
|
||||||
|
header1 := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 1,
|
||||||
|
ParentBlockHash: []byte("parent1"),
|
||||||
|
Value: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
header2 := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 2,
|
||||||
|
ParentBlockHash: []byte("parent2"),
|
||||||
|
Value: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
header3 := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 3,
|
||||||
|
ParentBlockHash: []byte("parent3"),
|
||||||
|
Value: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header1)
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header2)
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header3)
|
||||||
|
require.Equal(t, 2, len(c.headers))
|
||||||
|
require.Equal(t, header2, c.headers[2][0])
|
||||||
|
require.Equal(t, header3, c.headers[3][0])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Header with same slot but higher value should replace the existing one", func(t *testing.T) {
|
||||||
|
c := NewExecutionPayloadHeaders()
|
||||||
|
header1 := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 2,
|
||||||
|
ParentBlockHash: []byte("parent2"),
|
||||||
|
Value: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
header2 := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 2,
|
||||||
|
ParentBlockHash: []byte("parent2"),
|
||||||
|
Value: 200,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header1)
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header2)
|
||||||
|
require.Equal(t, 1, len(c.headers[2]))
|
||||||
|
require.Equal(t, header2, c.headers[2][0])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Header with different parent block hash should be appended to the same slot", func(t *testing.T) {
|
||||||
|
c := NewExecutionPayloadHeaders()
|
||||||
|
header1 := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 2,
|
||||||
|
ParentBlockHash: []byte("parent1"),
|
||||||
|
Value: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
header2 := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 2,
|
||||||
|
ParentBlockHash: []byte("parent2"),
|
||||||
|
Value: 200,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header1)
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header2)
|
||||||
|
require.Equal(t, 2, len(c.headers[2]))
|
||||||
|
require.Equal(t, header1, c.headers[2][0])
|
||||||
|
require.Equal(t, header2, c.headers[2][1])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignedExecutionPayloadHeader(t *testing.T) {
|
||||||
|
t.Run("Return header when slot and parentBlockHash match", func(t *testing.T) {
|
||||||
|
c := NewExecutionPayloadHeaders()
|
||||||
|
header := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 1,
|
||||||
|
ParentBlockHash: []byte("parent1"),
|
||||||
|
ParentBlockRoot: []byte("root1"),
|
||||||
|
Value: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header)
|
||||||
|
result := c.SignedExecutionPayloadHeader(1, []byte("parent1"), []byte("root1"))
|
||||||
|
require.NotNil(t, result)
|
||||||
|
require.Equal(t, header, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Return nil when no matching slot and parentBlockHash", func(t *testing.T) {
|
||||||
|
c := NewExecutionPayloadHeaders()
|
||||||
|
header := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 1,
|
||||||
|
ParentBlockHash: []byte("parent1"),
|
||||||
|
ParentBlockRoot: []byte("root1"),
|
||||||
|
Value: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header)
|
||||||
|
result := c.SignedExecutionPayloadHeader(2, []byte("parent2"), []byte("root1"))
|
||||||
|
require.IsNil(t, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Return nil when no matching slot and parentBlockRoot", func(t *testing.T) {
|
||||||
|
c := NewExecutionPayloadHeaders()
|
||||||
|
header := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 1,
|
||||||
|
ParentBlockHash: []byte("parent1"),
|
||||||
|
ParentBlockRoot: []byte("root1"),
|
||||||
|
Value: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header)
|
||||||
|
result := c.SignedExecutionPayloadHeader(2, []byte("parent1"), []byte("root2"))
|
||||||
|
require.IsNil(t, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Return header when there are two slots in the cache and a match is found", func(t *testing.T) {
|
||||||
|
c := NewExecutionPayloadHeaders()
|
||||||
|
header1 := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 1,
|
||||||
|
ParentBlockHash: []byte("parent1"),
|
||||||
|
Value: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
header2 := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 2,
|
||||||
|
ParentBlockHash: []byte("parent2"),
|
||||||
|
Value: 200,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header1)
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header2)
|
||||||
|
|
||||||
|
// Check for the first header
|
||||||
|
result1 := c.SignedExecutionPayloadHeader(1, []byte("parent1"), []byte{})
|
||||||
|
require.NotNil(t, result1)
|
||||||
|
require.Equal(t, header1, result1)
|
||||||
|
|
||||||
|
// Check for the second header
|
||||||
|
result2 := c.SignedExecutionPayloadHeader(2, []byte("parent2"), []byte{})
|
||||||
|
require.NotNil(t, result2)
|
||||||
|
require.Equal(t, header2, result2)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Return nil when slot is evicted from cache", func(t *testing.T) {
|
||||||
|
c := NewExecutionPayloadHeaders()
|
||||||
|
header1 := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 1,
|
||||||
|
ParentBlockHash: []byte("parent1"),
|
||||||
|
Value: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
header2 := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 2,
|
||||||
|
ParentBlockHash: []byte("parent2"),
|
||||||
|
Value: 200,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
header3 := &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
Slot: 3,
|
||||||
|
ParentBlockHash: []byte("parent3"),
|
||||||
|
Value: 300,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header1)
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header2)
|
||||||
|
c.SaveSignedExecutionPayloadHeader(header3)
|
||||||
|
|
||||||
|
// The first slot should be evicted, so result should be nil
|
||||||
|
result := c.SignedExecutionPayloadHeader(1, []byte("parent1"), []byte{})
|
||||||
|
require.IsNil(t, result)
|
||||||
|
|
||||||
|
// The second slot should still be present
|
||||||
|
result = c.SignedExecutionPayloadHeader(2, []byte("parent2"), []byte{})
|
||||||
|
require.NotNil(t, result)
|
||||||
|
require.Equal(t, header2, result)
|
||||||
|
|
||||||
|
// The third slot should be present
|
||||||
|
result = c.SignedExecutionPayloadHeader(3, []byte("parent3"), []byte{})
|
||||||
|
require.NotNil(t, result)
|
||||||
|
require.Equal(t, header3, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -107,6 +107,7 @@ go_test(
|
|||||||
"//testing/assert:go_default_library",
|
"//testing/assert:go_default_library",
|
||||||
"//testing/require:go_default_library",
|
"//testing/require:go_default_library",
|
||||||
"//testing/util:go_default_library",
|
"//testing/util:go_default_library",
|
||||||
|
"//testing/util/random:go_default_library",
|
||||||
"//time/slots:go_default_library",
|
"//time/slots:go_default_library",
|
||||||
"@com_github_google_gofuzz//:go_default_library",
|
"@com_github_google_gofuzz//:go_default_library",
|
||||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
||||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/encoding/ssz"
|
||||||
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||||
)
|
)
|
||||||
@@ -222,6 +223,42 @@ func NewGenesisBlockForState(ctx context.Context, st state.BeaconState) (interfa
|
|||||||
},
|
},
|
||||||
Signature: params.BeaconConfig().EmptySignature[:],
|
Signature: params.BeaconConfig().EmptySignature[:],
|
||||||
})
|
})
|
||||||
|
case *ethpb.BeaconStateEPBS:
|
||||||
|
kzgs := make([][]byte, 0)
|
||||||
|
kzgRoot, err := ssz.KzgCommitmentsRoot(kzgs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockEpbs{
|
||||||
|
Block: ðpb.BeaconBlockEpbs{
|
||||||
|
ParentRoot: params.BeaconConfig().ZeroHash[:],
|
||||||
|
StateRoot: root[:],
|
||||||
|
Body: ðpb.BeaconBlockBodyEpbs{
|
||||||
|
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),
|
||||||
|
},
|
||||||
|
SignedExecutionPayloadHeader: &enginev1.SignedExecutionPayloadHeader{
|
||||||
|
Message: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
ParentBlockHash: make([]byte, 32),
|
||||||
|
ParentBlockRoot: make([]byte, 32),
|
||||||
|
BlockHash: make([]byte, 32),
|
||||||
|
BlobKzgCommitmentsRoot: kzgRoot[:],
|
||||||
|
},
|
||||||
|
Signature: make([]byte, 96),
|
||||||
|
},
|
||||||
|
BlsToExecutionChanges: make([]*ethpb.SignedBLSToExecutionChange, 0),
|
||||||
|
PayloadAttestations: make([]*ethpb.PayloadAttestation, 0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Signature: params.BeaconConfig().EmptySignature[:],
|
||||||
|
})
|
||||||
default:
|
default:
|
||||||
return nil, ErrUnrecognizedState
|
return nil, ErrUnrecognizedState
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,9 @@ func IsMergeTransitionComplete(st state.BeaconState) (bool, error) {
|
|||||||
//
|
//
|
||||||
// return block.body.execution_payload != ExecutionPayload()
|
// return block.body.execution_payload != ExecutionPayload()
|
||||||
func IsExecutionBlock(body interfaces.ReadOnlyBeaconBlockBody) (bool, error) {
|
func IsExecutionBlock(body interfaces.ReadOnlyBeaconBlockBody) (bool, error) {
|
||||||
|
if body.Version() >= version.Capella {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
if body == nil {
|
if body == nil {
|
||||||
return false, errors.New("nil block body")
|
return false, errors.New("nil block body")
|
||||||
}
|
}
|
||||||
@@ -94,6 +97,9 @@ func IsExecutionEnabled(st state.BeaconState, body interfaces.ReadOnlyBeaconBloc
|
|||||||
if IsPreBellatrixVersion(st.Version()) {
|
if IsPreBellatrixVersion(st.Version()) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
if body.Version() >= version.Capella {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
header, err := st.LatestExecutionPayloadHeader()
|
header, err := st.LatestExecutionPayloadHeader()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@@ -244,12 +250,47 @@ func verifyBlobCommitmentCount(slot primitives.Slot, body interfaces.ReadOnlyBea
|
|||||||
// GetBlockPayloadHash returns the hash of the execution payload of the block
|
// GetBlockPayloadHash returns the hash of the execution payload of the block
|
||||||
func GetBlockPayloadHash(blk interfaces.ReadOnlyBeaconBlock) ([32]byte, error) {
|
func GetBlockPayloadHash(blk interfaces.ReadOnlyBeaconBlock) ([32]byte, error) {
|
||||||
var payloadHash [32]byte
|
var payloadHash [32]byte
|
||||||
if IsPreBellatrixVersion(blk.Version()) {
|
if blk.Version() >= version.EPBS {
|
||||||
return payloadHash, nil
|
header, err := blk.Body().SignedExecutionPayloadHeader()
|
||||||
|
if err != nil {
|
||||||
|
return payloadHash, err
|
||||||
|
}
|
||||||
|
payload, err := header.Header()
|
||||||
|
if err != nil {
|
||||||
|
return payloadHash, err
|
||||||
|
}
|
||||||
|
return payload.BlockHash(), nil
|
||||||
}
|
}
|
||||||
payload, err := blk.Body().Execution()
|
if blk.Version() >= version.Bellatrix {
|
||||||
if err != nil {
|
payload, err := blk.Body().Execution()
|
||||||
return payloadHash, err
|
if err != nil {
|
||||||
|
return payloadHash, err
|
||||||
|
}
|
||||||
|
return bytesutil.ToBytes32(payload.BlockHash()), nil
|
||||||
}
|
}
|
||||||
return bytesutil.ToBytes32(payload.BlockHash()), nil
|
return payloadHash, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockParentHash returns the hash of the parent execution payload
|
||||||
|
func GetBlockParentHash(blk interfaces.ReadOnlyBeaconBlock) ([32]byte, error) {
|
||||||
|
var parentHash [32]byte
|
||||||
|
if blk.Version() >= version.EPBS {
|
||||||
|
header, err := blk.Body().SignedExecutionPayloadHeader()
|
||||||
|
if err != nil {
|
||||||
|
return parentHash, err
|
||||||
|
}
|
||||||
|
payload, err := header.Header()
|
||||||
|
if err != nil {
|
||||||
|
return parentHash, err
|
||||||
|
}
|
||||||
|
return payload.ParentBlockHash(), nil
|
||||||
|
}
|
||||||
|
if blk.Version() >= version.Bellatrix {
|
||||||
|
payload, err := blk.Body().Execution()
|
||||||
|
if err != nil {
|
||||||
|
return parentHash, err
|
||||||
|
}
|
||||||
|
return bytesutil.ToBytes32(payload.ParentHash()), nil
|
||||||
|
}
|
||||||
|
return parentHash, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import (
|
|||||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
|
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
|
||||||
"github.com/prysmaticlabs/prysm/v5/crypto/hash"
|
"github.com/prysmaticlabs/prysm/v5/crypto/hash"
|
||||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
|
||||||
"github.com/prysmaticlabs/prysm/v5/encoding/ssz"
|
"github.com/prysmaticlabs/prysm/v5/encoding/ssz"
|
||||||
|
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||||
)
|
)
|
||||||
@@ -118,14 +118,97 @@ func ValidateBLSToExecutionChange(st state.ReadOnlyBeaconState, signed *ethpb.Si
|
|||||||
return val, nil
|
return val, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkWithdrawalsAgainstPayload(
|
||||||
|
executionData interfaces.ExecutionData,
|
||||||
|
numExpected int,
|
||||||
|
expectedRoot [32]byte,
|
||||||
|
) error {
|
||||||
|
var wdRoot [32]byte
|
||||||
|
if executionData.IsBlinded() {
|
||||||
|
r, err := executionData.WithdrawalsRoot()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not get withdrawals root")
|
||||||
|
}
|
||||||
|
copy(wdRoot[:], r)
|
||||||
|
} else {
|
||||||
|
wds, err := executionData.Withdrawals()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not get withdrawals")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(wds) != numExpected {
|
||||||
|
return fmt.Errorf("execution payload header has %d withdrawals when %d were expected", len(wds), numExpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
wdRoot, err = ssz.WithdrawalSliceRoot(wds, fieldparams.MaxWithdrawalsPerPayload)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not get withdrawals root")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if expectedRoot != wdRoot {
|
||||||
|
return fmt.Errorf("expected withdrawals root %#x, got %#x", expectedRoot, wdRoot)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func processWithdrawalStateTransition(
|
||||||
|
st state.BeaconState,
|
||||||
|
expectedWithdrawals []*enginev1.Withdrawal,
|
||||||
|
partialWithdrawalsCount uint64,
|
||||||
|
) (err error) {
|
||||||
|
for _, withdrawal := range expectedWithdrawals {
|
||||||
|
err := helpers.DecreaseBalance(st, withdrawal.ValidatorIndex, withdrawal.Amount)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not decrease balance")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if st.Version() >= version.Electra {
|
||||||
|
if err := st.DequeuePendingPartialWithdrawals(partialWithdrawalsCount); err != nil {
|
||||||
|
return fmt.Errorf("unable to dequeue partial withdrawals from state: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(expectedWithdrawals) > 0 {
|
||||||
|
if err := st.SetNextWithdrawalIndex(expectedWithdrawals[len(expectedWithdrawals)-1].Index + 1); err != nil {
|
||||||
|
return errors.Wrap(err, "could not set next withdrawal index")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var nextValidatorIndex primitives.ValidatorIndex
|
||||||
|
if uint64(len(expectedWithdrawals)) < params.BeaconConfig().MaxWithdrawalsPerPayload {
|
||||||
|
nextValidatorIndex, err = st.NextWithdrawalValidatorIndex()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not get next withdrawal validator index")
|
||||||
|
}
|
||||||
|
nextValidatorIndex += primitives.ValidatorIndex(params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep)
|
||||||
|
nextValidatorIndex = nextValidatorIndex % primitives.ValidatorIndex(st.NumValidators())
|
||||||
|
} else {
|
||||||
|
nextValidatorIndex = expectedWithdrawals[len(expectedWithdrawals)-1].ValidatorIndex + 1
|
||||||
|
if nextValidatorIndex == primitives.ValidatorIndex(st.NumValidators()) {
|
||||||
|
nextValidatorIndex = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := st.SetNextWithdrawalValidatorIndex(nextValidatorIndex); err != nil {
|
||||||
|
return errors.Wrap(err, "could not set next withdrawal validator index")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ProcessWithdrawals processes the validator withdrawals from the provided execution payload
|
// ProcessWithdrawals processes the validator withdrawals from the provided execution payload
|
||||||
// into the beacon state.
|
// into the beacon state.
|
||||||
//
|
//
|
||||||
// Spec pseudocode definition:
|
// Spec pseudocode definition:
|
||||||
//
|
//
|
||||||
// def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
|
// def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
|
||||||
|
// if state.fork.current_version >= EIP7732_FORK_VERSION :
|
||||||
|
// if not is_parent_block_full(state): # [New in EIP-7732]
|
||||||
|
// return
|
||||||
//
|
//
|
||||||
// expected_withdrawals, processed_partial_withdrawals_count = get_expected_withdrawals(state) # [Modified in Electra:EIP7251]
|
// expected_withdrawals, partial_withdrawals_count = get_expected_withdrawals(state) # [Modified in Electra:EIP7251]
|
||||||
|
//
|
||||||
|
// if state.fork.current_version >= EIP7732_FORK_VERSION :
|
||||||
|
// state.latest_withdrawals_root = hash_tree_root(expected_withdrawals) # [New in EIP-7732]
|
||||||
|
// else :
|
||||||
|
// assert len(payload.withdrawals) == len(expected_withdrawals)
|
||||||
//
|
//
|
||||||
// assert len(payload.withdrawals) == len(expected_withdrawals)
|
// assert len(payload.withdrawals) == len(expected_withdrawals)
|
||||||
//
|
//
|
||||||
@@ -152,76 +235,39 @@ func ValidateBLSToExecutionChange(st state.ReadOnlyBeaconState, signed *ethpb.Si
|
|||||||
// next_validator_index = ValidatorIndex(next_index % len(state.validators))
|
// next_validator_index = ValidatorIndex(next_index % len(state.validators))
|
||||||
// state.next_withdrawal_validator_index = next_validator_index
|
// state.next_withdrawal_validator_index = next_validator_index
|
||||||
func ProcessWithdrawals(st state.BeaconState, executionData interfaces.ExecutionData) (state.BeaconState, error) {
|
func ProcessWithdrawals(st state.BeaconState, executionData interfaces.ExecutionData) (state.BeaconState, error) {
|
||||||
expectedWithdrawals, processedPartialWithdrawalsCount, err := st.ExpectedWithdrawals()
|
if st.Version() >= version.EPBS {
|
||||||
if err != nil {
|
IsParentBlockFull, err := st.IsParentBlockFull()
|
||||||
return nil, errors.Wrap(err, "could not get expected withdrawals")
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "could not check if parent block is full")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !IsParentBlockFull {
|
||||||
|
return st, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var wdRoot [32]byte
|
expectedWithdrawals, partialWithdrawalsCount, err := st.ExpectedWithdrawals()
|
||||||
if executionData.IsBlinded() {
|
if err != nil {
|
||||||
r, err := executionData.WithdrawalsRoot()
|
return nil, errors.Wrap(err, "could not get expected withdrawals")
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "could not get withdrawals root")
|
|
||||||
}
|
|
||||||
wdRoot = bytesutil.ToBytes32(r)
|
|
||||||
} else {
|
|
||||||
wds, err := executionData.Withdrawals()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "could not get withdrawals")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(wds) != len(expectedWithdrawals) {
|
|
||||||
return nil, fmt.Errorf("execution payload header has %d withdrawals when %d were expected", len(wds), len(expectedWithdrawals))
|
|
||||||
}
|
|
||||||
|
|
||||||
wdRoot, err = ssz.WithdrawalSliceRoot(wds, fieldparams.MaxWithdrawalsPerPayload)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "could not get withdrawals root")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedRoot, err := ssz.WithdrawalSliceRoot(expectedWithdrawals, fieldparams.MaxWithdrawalsPerPayload)
|
expectedRoot, err := ssz.WithdrawalSliceRoot(expectedWithdrawals, fieldparams.MaxWithdrawalsPerPayload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "could not get expected withdrawals root")
|
return nil, errors.Wrap(err, "could not get expected withdrawals root")
|
||||||
}
|
}
|
||||||
if expectedRoot != wdRoot {
|
|
||||||
return nil, fmt.Errorf("expected withdrawals root %#x, got %#x", expectedRoot, wdRoot)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, withdrawal := range expectedWithdrawals {
|
if st.Version() >= version.EPBS {
|
||||||
err := helpers.DecreaseBalance(st, withdrawal.ValidatorIndex, withdrawal.Amount)
|
err = st.SetLastWithdrawalsRoot(expectedRoot[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "could not decrease balance")
|
return nil, errors.Wrap(err, "could not set withdrawals root")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if st.Version() >= version.Electra {
|
|
||||||
if err := st.DequeuePendingPartialWithdrawals(processedPartialWithdrawalsCount); err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to dequeue partial withdrawals from state: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(expectedWithdrawals) > 0 {
|
|
||||||
if err := st.SetNextWithdrawalIndex(expectedWithdrawals[len(expectedWithdrawals)-1].Index + 1); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "could not set next withdrawal index")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var nextValidatorIndex primitives.ValidatorIndex
|
|
||||||
if uint64(len(expectedWithdrawals)) < params.BeaconConfig().MaxWithdrawalsPerPayload {
|
|
||||||
nextValidatorIndex, err = st.NextWithdrawalValidatorIndex()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "could not get next withdrawal validator index")
|
|
||||||
}
|
|
||||||
nextValidatorIndex += primitives.ValidatorIndex(params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep)
|
|
||||||
nextValidatorIndex = nextValidatorIndex % primitives.ValidatorIndex(st.NumValidators())
|
|
||||||
} else {
|
} else {
|
||||||
nextValidatorIndex = expectedWithdrawals[len(expectedWithdrawals)-1].ValidatorIndex + 1
|
if err := checkWithdrawalsAgainstPayload(executionData, len(expectedWithdrawals), expectedRoot); err != nil {
|
||||||
if nextValidatorIndex == primitives.ValidatorIndex(st.NumValidators()) {
|
return nil, err
|
||||||
nextValidatorIndex = 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := st.SetNextWithdrawalValidatorIndex(nextValidatorIndex); err != nil {
|
if err := processWithdrawalStateTransition(st, expectedWithdrawals, partialWithdrawalsCount); err != nil {
|
||||||
return nil, errors.Wrap(err, "could not set next withdrawal validator index")
|
return nil, err
|
||||||
}
|
}
|
||||||
return st, nil
|
return st, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/util/random"
|
||||||
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1167,13 +1168,407 @@ func TestProcessWithdrawals(t *testing.T) {
|
|||||||
checkPostState(t, test.Control, post)
|
checkPostState(t, test.Control, post)
|
||||||
}
|
}
|
||||||
params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = saved
|
params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = saved
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProcessWithdrawalsEPBS(t *testing.T) {
|
||||||
|
const (
|
||||||
|
currentEpoch = primitives.Epoch(10)
|
||||||
|
epochInFuture = primitives.Epoch(12)
|
||||||
|
epochInPast = primitives.Epoch(8)
|
||||||
|
numValidators = 128
|
||||||
|
notWithdrawableIndex = 127
|
||||||
|
notPartiallyWithdrawable = 126
|
||||||
|
maxSweep = uint64(80)
|
||||||
|
)
|
||||||
|
maxEffectiveBalance := params.BeaconConfig().MaxEffectiveBalance
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
Name string
|
||||||
|
NextWithdrawalValidatorIndex primitives.ValidatorIndex
|
||||||
|
NextWithdrawalIndex uint64
|
||||||
|
FullWithdrawalIndices []primitives.ValidatorIndex
|
||||||
|
PendingPartialWithdrawalIndices []primitives.ValidatorIndex
|
||||||
|
Withdrawals []*enginev1.Withdrawal
|
||||||
|
PendingPartialWithdrawals []*ethpb.PendingPartialWithdrawal // Electra
|
||||||
|
LatestBlockHash []byte // EIP-7732
|
||||||
|
}
|
||||||
|
type control struct {
|
||||||
|
NextWithdrawalValidatorIndex primitives.ValidatorIndex
|
||||||
|
NextWithdrawalIndex uint64
|
||||||
|
Balances map[uint64]uint64
|
||||||
|
}
|
||||||
|
type Test struct {
|
||||||
|
Args args
|
||||||
|
Control control
|
||||||
|
}
|
||||||
|
executionAddress := func(i primitives.ValidatorIndex) []byte {
|
||||||
|
wc := make([]byte, 20)
|
||||||
|
wc[19] = byte(i)
|
||||||
|
return wc
|
||||||
|
}
|
||||||
|
withdrawalAmount := func(i primitives.ValidatorIndex) uint64 {
|
||||||
|
return maxEffectiveBalance + uint64(i)*100000
|
||||||
|
}
|
||||||
|
fullWithdrawal := func(i primitives.ValidatorIndex, idx uint64) *enginev1.Withdrawal {
|
||||||
|
return &enginev1.Withdrawal{
|
||||||
|
Index: idx,
|
||||||
|
ValidatorIndex: i,
|
||||||
|
Address: executionAddress(i),
|
||||||
|
Amount: withdrawalAmount(i),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PendingPartialWithdrawal := func(i primitives.ValidatorIndex, idx uint64) *enginev1.Withdrawal {
|
||||||
|
return &enginev1.Withdrawal{
|
||||||
|
Index: idx,
|
||||||
|
ValidatorIndex: i,
|
||||||
|
Address: executionAddress(i),
|
||||||
|
Amount: withdrawalAmount(i) - maxEffectiveBalance,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tests := []Test{
|
||||||
|
{
|
||||||
|
Args: args{
|
||||||
|
Name: "success no withdrawals",
|
||||||
|
NextWithdrawalValidatorIndex: 10,
|
||||||
|
NextWithdrawalIndex: 3,
|
||||||
|
},
|
||||||
|
Control: control{
|
||||||
|
NextWithdrawalValidatorIndex: 90,
|
||||||
|
NextWithdrawalIndex: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Args: args{
|
||||||
|
Name: "success one full withdrawal",
|
||||||
|
NextWithdrawalIndex: 3,
|
||||||
|
NextWithdrawalValidatorIndex: 5,
|
||||||
|
FullWithdrawalIndices: []primitives.ValidatorIndex{70},
|
||||||
|
Withdrawals: []*enginev1.Withdrawal{
|
||||||
|
fullWithdrawal(70, 3),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Control: control{
|
||||||
|
NextWithdrawalValidatorIndex: 85,
|
||||||
|
NextWithdrawalIndex: 4,
|
||||||
|
Balances: map[uint64]uint64{70: 0},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Args: args{
|
||||||
|
Name: "success one partial withdrawal",
|
||||||
|
NextWithdrawalIndex: 21,
|
||||||
|
NextWithdrawalValidatorIndex: 120,
|
||||||
|
PendingPartialWithdrawalIndices: []primitives.ValidatorIndex{7},
|
||||||
|
Withdrawals: []*enginev1.Withdrawal{
|
||||||
|
PendingPartialWithdrawal(7, 21),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Control: control{
|
||||||
|
NextWithdrawalValidatorIndex: 72,
|
||||||
|
NextWithdrawalIndex: 22,
|
||||||
|
Balances: map[uint64]uint64{7: maxEffectiveBalance},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Args: args{
|
||||||
|
Name: "success many full withdrawals",
|
||||||
|
NextWithdrawalIndex: 22,
|
||||||
|
NextWithdrawalValidatorIndex: 4,
|
||||||
|
FullWithdrawalIndices: []primitives.ValidatorIndex{7, 19, 28, 1},
|
||||||
|
Withdrawals: []*enginev1.Withdrawal{
|
||||||
|
fullWithdrawal(7, 22), fullWithdrawal(19, 23), fullWithdrawal(28, 24),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Control: control{
|
||||||
|
NextWithdrawalValidatorIndex: 84,
|
||||||
|
NextWithdrawalIndex: 25,
|
||||||
|
Balances: map[uint64]uint64{7: 0, 19: 0, 28: 0},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Args: args{
|
||||||
|
Name: "less than max sweep at end",
|
||||||
|
NextWithdrawalIndex: 22,
|
||||||
|
NextWithdrawalValidatorIndex: 4,
|
||||||
|
FullWithdrawalIndices: []primitives.ValidatorIndex{80, 81, 82, 83},
|
||||||
|
Withdrawals: []*enginev1.Withdrawal{
|
||||||
|
fullWithdrawal(80, 22), fullWithdrawal(81, 23), fullWithdrawal(82, 24),
|
||||||
|
fullWithdrawal(83, 25),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Control: control{
|
||||||
|
NextWithdrawalValidatorIndex: 84,
|
||||||
|
NextWithdrawalIndex: 26,
|
||||||
|
Balances: map[uint64]uint64{80: 0, 81: 0, 82: 0, 83: 0},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Args: args{
|
||||||
|
Name: "less than max sweep and beginning",
|
||||||
|
NextWithdrawalIndex: 22,
|
||||||
|
NextWithdrawalValidatorIndex: 4,
|
||||||
|
FullWithdrawalIndices: []primitives.ValidatorIndex{4, 5, 6},
|
||||||
|
Withdrawals: []*enginev1.Withdrawal{
|
||||||
|
fullWithdrawal(4, 22), fullWithdrawal(5, 23), fullWithdrawal(6, 24),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Control: control{
|
||||||
|
NextWithdrawalValidatorIndex: 84,
|
||||||
|
NextWithdrawalIndex: 25,
|
||||||
|
Balances: map[uint64]uint64{4: 0, 5: 0, 6: 0},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Args: args{
|
||||||
|
Name: "success many partial withdrawals",
|
||||||
|
NextWithdrawalIndex: 22,
|
||||||
|
NextWithdrawalValidatorIndex: 4,
|
||||||
|
PendingPartialWithdrawalIndices: []primitives.ValidatorIndex{7, 19, 28},
|
||||||
|
Withdrawals: []*enginev1.Withdrawal{
|
||||||
|
PendingPartialWithdrawal(7, 22), PendingPartialWithdrawal(19, 23), PendingPartialWithdrawal(28, 24),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Control: control{
|
||||||
|
NextWithdrawalValidatorIndex: 84,
|
||||||
|
NextWithdrawalIndex: 25,
|
||||||
|
Balances: map[uint64]uint64{
|
||||||
|
7: maxEffectiveBalance,
|
||||||
|
19: maxEffectiveBalance,
|
||||||
|
28: maxEffectiveBalance,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Args: args{
|
||||||
|
Name: "success many withdrawals",
|
||||||
|
NextWithdrawalIndex: 22,
|
||||||
|
NextWithdrawalValidatorIndex: 88,
|
||||||
|
FullWithdrawalIndices: []primitives.ValidatorIndex{7, 19, 28},
|
||||||
|
PendingPartialWithdrawalIndices: []primitives.ValidatorIndex{2, 1, 89, 15},
|
||||||
|
Withdrawals: []*enginev1.Withdrawal{
|
||||||
|
PendingPartialWithdrawal(89, 22), PendingPartialWithdrawal(1, 23), PendingPartialWithdrawal(2, 24),
|
||||||
|
fullWithdrawal(7, 25), PendingPartialWithdrawal(15, 26), fullWithdrawal(19, 27),
|
||||||
|
fullWithdrawal(28, 28),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Control: control{
|
||||||
|
NextWithdrawalValidatorIndex: 40,
|
||||||
|
NextWithdrawalIndex: 29,
|
||||||
|
Balances: map[uint64]uint64{
|
||||||
|
7: 0, 19: 0, 28: 0,
|
||||||
|
2: maxEffectiveBalance, 1: maxEffectiveBalance, 89: maxEffectiveBalance,
|
||||||
|
15: maxEffectiveBalance,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Args: args{
|
||||||
|
Name: "success many withdrawals with pending partial withdrawals in state",
|
||||||
|
NextWithdrawalIndex: 22,
|
||||||
|
NextWithdrawalValidatorIndex: 88,
|
||||||
|
FullWithdrawalIndices: []primitives.ValidatorIndex{7, 19, 28},
|
||||||
|
PendingPartialWithdrawalIndices: []primitives.ValidatorIndex{2, 1, 89, 15},
|
||||||
|
Withdrawals: []*enginev1.Withdrawal{
|
||||||
|
PendingPartialWithdrawal(89, 22), PendingPartialWithdrawal(1, 23), PendingPartialWithdrawal(2, 24),
|
||||||
|
fullWithdrawal(7, 25), PendingPartialWithdrawal(15, 26), fullWithdrawal(19, 27),
|
||||||
|
fullWithdrawal(28, 28),
|
||||||
|
},
|
||||||
|
PendingPartialWithdrawals: []*ethpb.PendingPartialWithdrawal{
|
||||||
|
{
|
||||||
|
Index: 11,
|
||||||
|
Amount: withdrawalAmount(11) - maxEffectiveBalance,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Control: control{
|
||||||
|
NextWithdrawalValidatorIndex: 40,
|
||||||
|
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: []primitives.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,
|
||||||
|
PendingPartialWithdrawalIndices: []primitives.ValidatorIndex{1, 2, 3, 4, 5, 6, 7, 8, 9, 21, 22, 23, 24, 25, 26, 27, 29, 35, 89},
|
||||||
|
Withdrawals: []*enginev1.Withdrawal{
|
||||||
|
PendingPartialWithdrawal(1, 22), PendingPartialWithdrawal(2, 23), PendingPartialWithdrawal(3, 24),
|
||||||
|
PendingPartialWithdrawal(4, 25), PendingPartialWithdrawal(5, 26), PendingPartialWithdrawal(6, 27),
|
||||||
|
PendingPartialWithdrawal(7, 28), PendingPartialWithdrawal(8, 29), PendingPartialWithdrawal(9, 30),
|
||||||
|
PendingPartialWithdrawal(21, 31), PendingPartialWithdrawal(22, 32), PendingPartialWithdrawal(23, 33),
|
||||||
|
PendingPartialWithdrawal(24, 34), PendingPartialWithdrawal(25, 35), PendingPartialWithdrawal(26, 36),
|
||||||
|
PendingPartialWithdrawal(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: "Parent Node is not full",
|
||||||
|
NextWithdrawalIndex: 22,
|
||||||
|
NextWithdrawalValidatorIndex: 4,
|
||||||
|
FullWithdrawalIndices: []primitives.ValidatorIndex{7, 19, 28, 1},
|
||||||
|
Withdrawals: []*enginev1.Withdrawal{
|
||||||
|
fullWithdrawal(7, 22), fullWithdrawal(19, 23), fullWithdrawal(28, 24),
|
||||||
|
},
|
||||||
|
LatestBlockHash: []byte{1, 2, 3},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
checkPostState := func(t *testing.T, expected control, st state.BeaconState) {
|
||||||
|
l, err := st.NextWithdrawalValidatorIndex()
|
||||||
|
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 state.BeaconState, arguments args) error {
|
||||||
|
validators := make([]*ethpb.Validator, numValidators)
|
||||||
|
if err := st.SetBalances(make([]uint64, numValidators)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := range validators {
|
||||||
|
v := ðpb.Validator{}
|
||||||
|
v.EffectiveBalance = maxEffectiveBalance
|
||||||
|
v.WithdrawableEpoch = epochInFuture
|
||||||
|
v.WithdrawalCredentials = make([]byte, 32)
|
||||||
|
v.WithdrawalCredentials[31] = byte(i)
|
||||||
|
if err := st.UpdateBalancesAtIndex(primitives.ValidatorIndex(i), v.EffectiveBalance-uint64(rand.Intn(1000))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
validators[i] = v
|
||||||
|
}
|
||||||
|
for _, idx := range arguments.FullWithdrawalIndices {
|
||||||
|
if idx != notWithdrawableIndex {
|
||||||
|
validators[idx].WithdrawableEpoch = epochInPast
|
||||||
|
}
|
||||||
|
if err := st.UpdateBalancesAtIndex(idx, withdrawalAmount(idx)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
validators[idx].WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
||||||
|
}
|
||||||
|
for _, idx := range arguments.PendingPartialWithdrawalIndices {
|
||||||
|
validators[idx].WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
||||||
|
if err := st.UpdateBalancesAtIndex(idx, withdrawalAmount(idx)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return st.SetValidators(validators)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.Args.Name, func(t *testing.T) {
|
||||||
|
saved := params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep
|
||||||
|
params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = maxSweep
|
||||||
|
if test.Args.Withdrawals == nil {
|
||||||
|
test.Args.Withdrawals = make([]*enginev1.Withdrawal, 0)
|
||||||
|
}
|
||||||
|
if test.Args.FullWithdrawalIndices == nil {
|
||||||
|
test.Args.FullWithdrawalIndices = make([]primitives.ValidatorIndex, 0)
|
||||||
|
}
|
||||||
|
if test.Args.PendingPartialWithdrawalIndices == nil {
|
||||||
|
test.Args.PendingPartialWithdrawalIndices = make([]primitives.ValidatorIndex, 0)
|
||||||
|
}
|
||||||
|
slot, err := slots.EpochStart(currentEpoch)
|
||||||
|
require.NoError(t, err)
|
||||||
|
var st state.BeaconState
|
||||||
|
var p interfaces.ExecutionData
|
||||||
|
spb := ðpb.BeaconStateEPBS{
|
||||||
|
Slot: slot,
|
||||||
|
NextWithdrawalValidatorIndex: test.Args.NextWithdrawalValidatorIndex,
|
||||||
|
NextWithdrawalIndex: test.Args.NextWithdrawalIndex,
|
||||||
|
PendingPartialWithdrawals: test.Args.PendingPartialWithdrawals,
|
||||||
|
LatestExecutionPayloadHeader: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
BlockHash: []byte{},
|
||||||
|
},
|
||||||
|
LatestBlockHash: test.Args.LatestBlockHash,
|
||||||
|
}
|
||||||
|
st, err = state_native.InitializeFromProtoUnsafeEpbs(spb)
|
||||||
|
require.NoError(t, err)
|
||||||
|
env := random.ExecutionPayloadEnvelope(t)
|
||||||
|
env.Payload.Withdrawals = test.Args.Withdrawals
|
||||||
|
wp, err := consensusblocks.WrappedROExecutionPayloadEnvelope(env)
|
||||||
|
require.NoError(t, err)
|
||||||
|
p, err = wp.Execution()
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = prepareValidators(st, test.Args)
|
||||||
|
require.NoError(t, err)
|
||||||
|
post, err := blocks.ProcessWithdrawals(st, p)
|
||||||
|
if test.Args.Name == "Parent Node is not full" {
|
||||||
|
require.DeepEqual(t, post, st)
|
||||||
|
require.IsNil(t, err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkPostState(t, test.Control, post)
|
||||||
|
}
|
||||||
|
params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = saved
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestProcessBLSToExecutionChanges(t *testing.T) {
|
func TestProcessBLSToExecutionChanges(t *testing.T) {
|
||||||
spb := ðpb.BeaconStateCapella{
|
spb := ðpb.BeaconStateCapella{
|
||||||
Fork: ðpb.Fork{
|
Fork: ðpb.Fork{
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ go_library(
|
|||||||
"effective_balance_updates.go",
|
"effective_balance_updates.go",
|
||||||
"registry_updates.go",
|
"registry_updates.go",
|
||||||
"transition.go",
|
"transition.go",
|
||||||
"transition_no_verify_sig.go",
|
|
||||||
"upgrade.go",
|
"upgrade.go",
|
||||||
"validator.go",
|
"validator.go",
|
||||||
"withdrawals.go",
|
"withdrawals.go",
|
||||||
|
|||||||
61
beacon-chain/core/epbs/BUILD.bazel
Normal file
61
beacon-chain/core/epbs/BUILD.bazel
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"attestation.go",
|
||||||
|
"execution_payload_envelope.go",
|
||||||
|
"execution_payload_header.go",
|
||||||
|
"operations.go",
|
||||||
|
"payload_attestation.go",
|
||||||
|
"upgrade.go",
|
||||||
|
],
|
||||||
|
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epbs",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//beacon-chain/core/altair:go_default_library",
|
||||||
|
"//beacon-chain/core/blocks:go_default_library",
|
||||||
|
"//beacon-chain/core/electra:go_default_library",
|
||||||
|
"//beacon-chain/core/helpers:go_default_library",
|
||||||
|
"//beacon-chain/core/signing:go_default_library",
|
||||||
|
"//beacon-chain/core/time:go_default_library",
|
||||||
|
"//beacon-chain/core/validators:go_default_library",
|
||||||
|
"//beacon-chain/state:go_default_library",
|
||||||
|
"//beacon-chain/state/state-native:go_default_library",
|
||||||
|
"//config/params:go_default_library",
|
||||||
|
"//consensus-types/interfaces:go_default_library",
|
||||||
|
"//consensus-types/primitives:go_default_library",
|
||||||
|
"//crypto/bls:go_default_library",
|
||||||
|
"//network/forks:go_default_library",
|
||||||
|
"//proto/engine/v1:go_default_library",
|
||||||
|
"//proto/prysm/v1alpha1:go_default_library",
|
||||||
|
"//runtime/version:go_default_library",
|
||||||
|
"//time/slots:go_default_library",
|
||||||
|
"@com_github_pkg_errors//:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = [
|
||||||
|
"attestation_test.go",
|
||||||
|
"execution_payload_envelope_test.go",
|
||||||
|
"upgrade_test.go",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
":go_default_library",
|
||||||
|
"//beacon-chain/core/altair:go_default_library",
|
||||||
|
"//beacon-chain/core/helpers:go_default_library",
|
||||||
|
"//beacon-chain/core/time:go_default_library",
|
||||||
|
"//beacon-chain/state/state-native:go_default_library",
|
||||||
|
"//config/params:go_default_library",
|
||||||
|
"//consensus-types/blocks: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",
|
||||||
|
"//testing/util/random:go_default_library",
|
||||||
|
"//time/slots:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
13
beacon-chain/core/epbs/attestation.go
Normal file
13
beacon-chain/core/epbs/attestation.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package epbs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RemoveValidatorFlag removes validator flag from existing one.
|
||||||
|
func RemoveValidatorFlag(flag, flagPosition uint8) (uint8, error) {
|
||||||
|
if flagPosition > 7 {
|
||||||
|
return flag, fmt.Errorf("flag position %d exceeds length", flagPosition)
|
||||||
|
}
|
||||||
|
return flag & ^(1 << flagPosition), nil
|
||||||
|
}
|
||||||
93
beacon-chain/core/epbs/attestation_test.go
Normal file
93
beacon-chain/core/epbs/attestation_test.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package epbs_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epbs"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidatorFlag_Remove(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
add []uint8
|
||||||
|
remove []uint8
|
||||||
|
expectedTrue []uint8
|
||||||
|
expectedFalse []uint8
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "none",
|
||||||
|
add: []uint8{},
|
||||||
|
remove: []uint8{},
|
||||||
|
expectedTrue: []uint8{},
|
||||||
|
expectedFalse: []uint8{params.BeaconConfig().TimelySourceFlagIndex, params.BeaconConfig().TimelyTargetFlagIndex, params.BeaconConfig().TimelyHeadFlagIndex},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "source",
|
||||||
|
add: []uint8{params.BeaconConfig().TimelySourceFlagIndex},
|
||||||
|
remove: []uint8{params.BeaconConfig().TimelySourceFlagIndex},
|
||||||
|
expectedTrue: []uint8{},
|
||||||
|
expectedFalse: []uint8{params.BeaconConfig().TimelySourceFlagIndex, params.BeaconConfig().TimelyTargetFlagIndex, params.BeaconConfig().TimelyHeadFlagIndex},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "source, target",
|
||||||
|
add: []uint8{params.BeaconConfig().TimelySourceFlagIndex, params.BeaconConfig().TimelyTargetFlagIndex},
|
||||||
|
remove: []uint8{params.BeaconConfig().TimelySourceFlagIndex},
|
||||||
|
expectedTrue: []uint8{params.BeaconConfig().TimelyTargetFlagIndex},
|
||||||
|
expectedFalse: []uint8{params.BeaconConfig().TimelySourceFlagIndex, params.BeaconConfig().TimelyHeadFlagIndex},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "source, target, head",
|
||||||
|
add: []uint8{params.BeaconConfig().TimelySourceFlagIndex, params.BeaconConfig().TimelyTargetFlagIndex, params.BeaconConfig().TimelyHeadFlagIndex},
|
||||||
|
remove: []uint8{params.BeaconConfig().TimelyTargetFlagIndex, params.BeaconConfig().TimelyHeadFlagIndex},
|
||||||
|
expectedTrue: []uint8{params.BeaconConfig().TimelySourceFlagIndex},
|
||||||
|
expectedFalse: []uint8{params.BeaconConfig().TimelyTargetFlagIndex, params.BeaconConfig().TimelyHeadFlagIndex},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
flag := uint8(0)
|
||||||
|
|
||||||
|
// Add flags.
|
||||||
|
for _, flagPosition := range test.add {
|
||||||
|
flag, err = altair.AddValidatorFlag(flag, flagPosition)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
has, err := altair.HasValidatorFlag(flag, flagPosition)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, true, has)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove flags.
|
||||||
|
for _, flagPosition := range test.remove {
|
||||||
|
flag, err = epbs.RemoveValidatorFlag(flag, flagPosition)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if flags are set correctly.
|
||||||
|
for _, flagPosition := range test.expectedTrue {
|
||||||
|
has, err := altair.HasValidatorFlag(flag, flagPosition)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, true, has)
|
||||||
|
}
|
||||||
|
for _, flagPosition := range test.expectedFalse {
|
||||||
|
has, err := altair.HasValidatorFlag(flag, flagPosition)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, false, has)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidatorFlag_Remove_ExceedsLength(t *testing.T) {
|
||||||
|
_, err := epbs.RemoveValidatorFlag(0, 8)
|
||||||
|
require.ErrorContains(t, "flag position 8 exceeds length", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidatorFlag_Remove_NotSet(t *testing.T) {
|
||||||
|
_, err := epbs.RemoveValidatorFlag(0, 1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
126
beacon-chain/core/epbs/execution_payload_envelope.go
Normal file
126
beacon-chain/core/epbs/execution_payload_envelope.go
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
package epbs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/electra"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||||
|
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidatePayloadStateTransition performs the process_execution_payload
|
||||||
|
// function.
|
||||||
|
func ValidatePayloadStateTransition(
|
||||||
|
ctx context.Context,
|
||||||
|
preState state.BeaconState,
|
||||||
|
envelope interfaces.ROExecutionPayloadEnvelope,
|
||||||
|
) error {
|
||||||
|
if err := UpdateHeaderAndVerify(ctx, preState, envelope); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
committedHeader, err := preState.LatestExecutionPayloadHeaderEPBS()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ValidateAgainstCommittedBid(committedHeader, envelope); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ProcessPayloadStateTransition(ctx, preState, envelope); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return CheckPostStateRoot(ctx, preState, envelope)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProcessPayloadStateTransition(
|
||||||
|
ctx context.Context,
|
||||||
|
preState state.BeaconState,
|
||||||
|
envelope interfaces.ROExecutionPayloadEnvelope,
|
||||||
|
) error {
|
||||||
|
er := envelope.ExecutionRequests()
|
||||||
|
preState, err := electra.ProcessDepositRequests(ctx, preState, er.Deposits)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not process deposit receipts")
|
||||||
|
}
|
||||||
|
preState, err = electra.ProcessWithdrawalRequests(ctx, preState, er.Withdrawals)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not process ercution layer withdrawal requests")
|
||||||
|
}
|
||||||
|
if err := electra.ProcessConsolidationRequests(ctx, preState, er.Consolidations); err != nil {
|
||||||
|
return errors.Wrap(err, "could not process consolidation requests")
|
||||||
|
}
|
||||||
|
payload, err := envelope.Execution()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not get execution payload")
|
||||||
|
}
|
||||||
|
if err := preState.SetLatestBlockHash(payload.BlockHash()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return preState.SetLatestFullSlot(preState.Slot())
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateHeaderAndVerify(
|
||||||
|
ctx context.Context,
|
||||||
|
preState state.BeaconState,
|
||||||
|
envelope interfaces.ROExecutionPayloadEnvelope,
|
||||||
|
) error {
|
||||||
|
blockHeader := preState.LatestBlockHeader()
|
||||||
|
if blockHeader == nil {
|
||||||
|
return errors.New("invalid nil latest block header")
|
||||||
|
}
|
||||||
|
if len(blockHeader.StateRoot) == 0 || [32]byte(blockHeader.StateRoot) == [32]byte{} {
|
||||||
|
prevStateRoot, err := preState.HashTreeRoot(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not compute previous state root")
|
||||||
|
}
|
||||||
|
blockHeader.StateRoot = prevStateRoot[:]
|
||||||
|
if err := preState.SetLatestBlockHeader(blockHeader); err != nil {
|
||||||
|
return errors.Wrap(err, "could not set latest block header")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blockHeaderRoot, err := blockHeader.HashTreeRoot()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
beaconBlockRoot := envelope.BeaconBlockRoot()
|
||||||
|
if blockHeaderRoot != beaconBlockRoot {
|
||||||
|
return fmt.Errorf("beacon block root does not match previous header, got: %#x wanted: %#x", beaconBlockRoot, blockHeaderRoot)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateAgainstCommittedBid(
|
||||||
|
committedHeader *enginev1.ExecutionPayloadHeaderEPBS,
|
||||||
|
envelope interfaces.ROExecutionPayloadEnvelope,
|
||||||
|
) error {
|
||||||
|
builderIndex := envelope.BuilderIndex()
|
||||||
|
if committedHeader.BuilderIndex != builderIndex {
|
||||||
|
return errors.New("builder index does not match committed header")
|
||||||
|
}
|
||||||
|
kzgRoot, err := envelope.BlobKzgCommitmentsRoot()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if [32]byte(committedHeader.BlobKzgCommitmentsRoot) != kzgRoot {
|
||||||
|
return errors.New("blob KZG commitments root does not match committed header")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckPostStateRoot(
|
||||||
|
ctx context.Context,
|
||||||
|
preState state.BeaconState,
|
||||||
|
envelope interfaces.ROExecutionPayloadEnvelope,
|
||||||
|
) error {
|
||||||
|
stateRoot, err := preState.HashTreeRoot(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
envelopeStateRoot := envelope.StateRoot()
|
||||||
|
if stateRoot != envelopeStateRoot {
|
||||||
|
return errors.New("state root mismatch")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
110
beacon-chain/core/epbs/execution_payload_envelope_test.go
Normal file
110
beacon-chain/core/epbs/execution_payload_envelope_test.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package epbs_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epbs"
|
||||||
|
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
||||||
|
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||||
|
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/util"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/util/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProcessPayloadStateTransition(t *testing.T) {
|
||||||
|
bh := [32]byte{'h'}
|
||||||
|
p := random.ExecutionPayloadEnvelope(t)
|
||||||
|
p.Payload.BlockHash = bh[:]
|
||||||
|
p.ExecutionRequests = &enginev1.ExecutionRequests{}
|
||||||
|
e, err := blocks.WrappedROExecutionPayloadEnvelope(p)
|
||||||
|
require.NoError(t, err)
|
||||||
|
validators := make([]*ethpb.Validator, 0)
|
||||||
|
stpb := ðpb.BeaconStateEPBS{Slot: 3, Validators: validators}
|
||||||
|
st, err := state_native.InitializeFromProtoUnsafeEpbs(stpb)
|
||||||
|
require.NoError(t, err)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
lbh, err := st.LatestBlockHash()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, [32]byte{}, [32]byte(lbh))
|
||||||
|
|
||||||
|
require.NoError(t, epbs.ProcessPayloadStateTransition(ctx, st, e))
|
||||||
|
|
||||||
|
lbh, err = st.LatestBlockHash()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, bh, [32]byte(lbh))
|
||||||
|
|
||||||
|
lfs, err := st.LatestFullSlot()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, lfs, st.Slot())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_validateAgainstHeader(t *testing.T) {
|
||||||
|
bh := [32]byte{'h'}
|
||||||
|
payload := &enginev1.ExecutionPayloadDeneb{BlockHash: bh[:]}
|
||||||
|
p := random.ExecutionPayloadEnvelope(t)
|
||||||
|
p.Payload = payload
|
||||||
|
e, err := blocks.WrappedROExecutionPayloadEnvelope(p)
|
||||||
|
require.NoError(t, err)
|
||||||
|
stpb := ðpb.BeaconStateEPBS{Slot: 3}
|
||||||
|
st, err := state_native.InitializeFromProtoUnsafeEpbs(stpb)
|
||||||
|
require.NoError(t, err)
|
||||||
|
ctx := context.Background()
|
||||||
|
require.ErrorContains(t, "invalid nil latest block header", epbs.UpdateHeaderAndVerify(ctx, st, e))
|
||||||
|
|
||||||
|
prest, _ := util.DeterministicGenesisStateEpbs(t, 64)
|
||||||
|
br := [32]byte{'r'}
|
||||||
|
p.BeaconBlockRoot = br[:]
|
||||||
|
require.ErrorContains(t, "beacon block root does not match previous header", epbs.UpdateHeaderAndVerify(ctx, prest, e))
|
||||||
|
|
||||||
|
header := prest.LatestBlockHeader()
|
||||||
|
require.NoError(t, err)
|
||||||
|
headerRoot, err := header.HashTreeRoot()
|
||||||
|
require.NoError(t, err)
|
||||||
|
p.BeaconBlockRoot = headerRoot[:]
|
||||||
|
require.NoError(t, epbs.UpdateHeaderAndVerify(ctx, prest, e))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_validateAgainstCommittedBid(t *testing.T) {
|
||||||
|
payload := &enginev1.ExecutionPayloadDeneb{}
|
||||||
|
p := random.ExecutionPayloadEnvelope(t)
|
||||||
|
p.Payload = payload
|
||||||
|
p.BuilderIndex = 1
|
||||||
|
e, err := blocks.WrappedROExecutionPayloadEnvelope(p)
|
||||||
|
require.NoError(t, err)
|
||||||
|
h := &enginev1.ExecutionPayloadHeaderEPBS{}
|
||||||
|
require.ErrorContains(t, "builder index does not match committed header", epbs.ValidateAgainstCommittedBid(h, e))
|
||||||
|
|
||||||
|
h.BuilderIndex = 1
|
||||||
|
p.BlobKzgCommitments = make([][]byte, 6)
|
||||||
|
for i := range p.BlobKzgCommitments {
|
||||||
|
p.BlobKzgCommitments[i] = make([]byte, 48)
|
||||||
|
}
|
||||||
|
h.BlobKzgCommitmentsRoot = make([]byte, 32)
|
||||||
|
require.ErrorContains(t, "blob KZG commitments root does not match committed header", epbs.ValidateAgainstCommittedBid(h, e))
|
||||||
|
|
||||||
|
root, err := e.BlobKzgCommitmentsRoot()
|
||||||
|
require.NoError(t, err)
|
||||||
|
h.BlobKzgCommitmentsRoot = root[:]
|
||||||
|
require.NoError(t, epbs.ValidateAgainstCommittedBid(h, e))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckPostStateRoot(t *testing.T) {
|
||||||
|
payload := &enginev1.ExecutionPayloadDeneb{}
|
||||||
|
p := random.ExecutionPayloadEnvelope(t)
|
||||||
|
p.Payload = payload
|
||||||
|
p.BuilderIndex = 1
|
||||||
|
e, err := blocks.WrappedROExecutionPayloadEnvelope(p)
|
||||||
|
require.NoError(t, err)
|
||||||
|
ctx := context.Background()
|
||||||
|
st, _ := util.DeterministicGenesisStateEpbs(t, 64)
|
||||||
|
p.StateRoot = make([]byte, 32)
|
||||||
|
require.ErrorContains(t, "state root mismatch", epbs.CheckPostStateRoot(ctx, st, e))
|
||||||
|
root, err := st.HashTreeRoot(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
p.StateRoot = root[:]
|
||||||
|
require.NoError(t, epbs.CheckPostStateRoot(ctx, st, e))
|
||||||
|
}
|
||||||
50
beacon-chain/core/epbs/execution_payload_header.go
Normal file
50
beacon-chain/core/epbs/execution_payload_header.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package epbs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/network/forks"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidatePayloadHeaderSignature validates the signature of the execution payload header.
|
||||||
|
func ValidatePayloadHeaderSignature(st state.ReadOnlyBeaconState, sh interfaces.ROSignedExecutionPayloadHeader) error {
|
||||||
|
h, err := sh.Header()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pubkey := st.PubkeyAtIndex(h.BuilderIndex())
|
||||||
|
pub, err := bls.PublicKeyFromBytes(pubkey[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s := sh.Signature()
|
||||||
|
sig, err := bls.SignatureFromBytes(s[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
currentEpoch := slots.ToEpoch(h.Slot())
|
||||||
|
f, err := forks.Fork(currentEpoch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
domain, err := signing.Domain(f, currentEpoch, params.BeaconConfig().DomainBeaconBuilder, st.GenesisValidatorsRoot())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
root, err := sh.SigningRoot(domain)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !sig.Verify(pub, root[:]) {
|
||||||
|
return signing.ErrSigFailedToVerify
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package electra
|
package epbs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -6,9 +6,11 @@ import (
|
|||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/electra"
|
||||||
v "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators"
|
v "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators"
|
||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -62,11 +64,11 @@ func ProcessOperations(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "could not process altair attester slashing")
|
return nil, errors.Wrap(err, "could not process altair attester slashing")
|
||||||
}
|
}
|
||||||
st, err = ProcessAttestationsNoVerifySignature(ctx, st, block)
|
st, err = electra.ProcessAttestationsNoVerifySignature(ctx, st, block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "could not process altair attestation")
|
return nil, errors.Wrap(err, "could not process altair attestation")
|
||||||
}
|
}
|
||||||
if _, err := ProcessDeposits(ctx, st, bb.Deposits()); err != nil { // new in electra
|
if _, err := electra.ProcessDeposits(ctx, st, bb.Deposits()); err != nil { // new in electra
|
||||||
return nil, errors.Wrap(err, "could not process altair deposit")
|
return nil, errors.Wrap(err, "could not process altair deposit")
|
||||||
}
|
}
|
||||||
st, err = ProcessVoluntaryExits(ctx, st, bb.VoluntaryExits())
|
st, err = ProcessVoluntaryExits(ctx, st, bb.VoluntaryExits())
|
||||||
@@ -77,20 +79,27 @@ func ProcessOperations(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "could not process bls-to-execution changes")
|
return nil, errors.Wrap(err, "could not process bls-to-execution changes")
|
||||||
}
|
}
|
||||||
|
// new in ePBS
|
||||||
|
if block.Version() >= version.EPBS {
|
||||||
|
if err := ProcessPayloadAttestations(st, bb); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return st, nil
|
||||||
|
}
|
||||||
// new in electra
|
// new in electra
|
||||||
requests, err := bb.ExecutionRequests()
|
requests, err := bb.ExecutionRequests()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "could not get execution requests")
|
return nil, errors.Wrap(err, "could not get execution requests")
|
||||||
}
|
}
|
||||||
st, err = ProcessDepositRequests(ctx, st, requests.Deposits)
|
st, err = electra.ProcessDepositRequests(ctx, st, requests.Deposits)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "could not process deposit requests")
|
return nil, errors.Wrap(err, "could not process deposit requests")
|
||||||
}
|
}
|
||||||
st, err = ProcessWithdrawalRequests(ctx, st, requests.Withdrawals)
|
st, err = electra.ProcessWithdrawalRequests(ctx, st, requests.Withdrawals)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "could not process withdrawal requests")
|
return nil, errors.Wrap(err, "could not process withdrawal requests")
|
||||||
}
|
}
|
||||||
if err := ProcessConsolidationRequests(ctx, st, requests.Consolidations); err != nil {
|
if err := electra.ProcessConsolidationRequests(ctx, st, requests.Consolidations); err != nil {
|
||||||
return nil, fmt.Errorf("could not process consolidation requests: %w", err)
|
return nil, fmt.Errorf("could not process consolidation requests: %w", err)
|
||||||
}
|
}
|
||||||
return st, nil
|
return st, nil
|
||||||
125
beacon-chain/core/epbs/payload_attestation.go
Normal file
125
beacon-chain/core/epbs/payload_attestation.go
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
package epbs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ProcessPayloadAttestations(state state.BeaconState, body interfaces.ReadOnlyBeaconBlockBody) error {
|
||||||
|
atts, err := body.PayloadAttestations()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(atts) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
lbh := state.LatestBlockHeader()
|
||||||
|
proposerIndex := lbh.ProposerIndex
|
||||||
|
var participation []byte
|
||||||
|
if state.Slot()%32 == 0 {
|
||||||
|
participation, err = state.PreviousEpochParticipation()
|
||||||
|
} else {
|
||||||
|
participation, err = state.CurrentEpochParticipation()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
totalBalance, err := helpers.TotalActiveBalance(state)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
baseReward, err := altair.BaseRewardWithTotalBalance(state, proposerIndex, totalBalance)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lfs, err := state.LatestFullSlot()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := params.BeaconConfig()
|
||||||
|
sourceFlagIndex := cfg.TimelySourceFlagIndex
|
||||||
|
targetFlagIndex := cfg.TimelyTargetFlagIndex
|
||||||
|
headFlagIndex := cfg.TimelyHeadFlagIndex
|
||||||
|
penaltyNumerator := uint64(0)
|
||||||
|
rewardNumerator := uint64(0)
|
||||||
|
rewardDenominator := (cfg.WeightDenominator - cfg.ProposerWeight) * cfg.WeightDenominator / cfg.ProposerWeight
|
||||||
|
|
||||||
|
for _, att := range atts {
|
||||||
|
data := att.Data
|
||||||
|
if !bytes.Equal(data.BeaconBlockRoot, lbh.ParentRoot) {
|
||||||
|
return errors.New("invalid beacon block root in payload attestation data")
|
||||||
|
}
|
||||||
|
if data.Slot+1 != state.Slot() {
|
||||||
|
return errors.New("invalid data slot")
|
||||||
|
}
|
||||||
|
indexed, err := helpers.GetIndexedPayloadAttestation(ctx, state, data.Slot, att)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
valid, err := helpers.IsValidIndexedPayloadAttestation(state, indexed)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !valid {
|
||||||
|
return errors.New("invalid payload attestation")
|
||||||
|
}
|
||||||
|
payloadWasPreset := data.Slot == lfs
|
||||||
|
votedPresent := data.PayloadStatus == primitives.PAYLOAD_PRESENT
|
||||||
|
if votedPresent != payloadWasPreset {
|
||||||
|
for _, idx := range indexed.GetAttestingIndices() {
|
||||||
|
flags := participation[idx]
|
||||||
|
has, err := altair.HasValidatorFlag(flags, targetFlagIndex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if has {
|
||||||
|
penaltyNumerator += baseReward * cfg.TimelyTargetWeight
|
||||||
|
}
|
||||||
|
has, err = altair.HasValidatorFlag(flags, sourceFlagIndex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if has {
|
||||||
|
penaltyNumerator += baseReward * cfg.TimelySourceWeight
|
||||||
|
}
|
||||||
|
has, err = altair.HasValidatorFlag(flags, headFlagIndex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if has {
|
||||||
|
penaltyNumerator += baseReward * cfg.TimelyHeadWeight
|
||||||
|
}
|
||||||
|
participation[idx] = 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, idx := range indexed.GetAttestingIndices() {
|
||||||
|
participation[idx] = (1 << headFlagIndex) | (1 << sourceFlagIndex) | (1 << targetFlagIndex)
|
||||||
|
rewardNumerator += baseReward * (cfg.TimelyHeadWeight + cfg.TimelySourceWeight + cfg.TimelyTargetWeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if penaltyNumerator > 0 {
|
||||||
|
if err := helpers.DecreaseBalance(state, proposerIndex, penaltyNumerator/rewardDenominator); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if rewardNumerator > 0 {
|
||||||
|
if err := helpers.IncreaseBalance(state, proposerIndex, penaltyNumerator/rewardDenominator); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
150
beacon-chain/core/epbs/upgrade.go
Normal file
150
beacon-chain/core/epbs/upgrade.go
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
package epbs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||||
|
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||||
|
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||||
|
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UpgradeToEIP7732 updates inputs a generic state to return the version EIP-7732 state.
|
||||||
|
// https://github.com/ethereum/consensus-specs/blob/dev/specs/_features/eip7732/fork.md
|
||||||
|
func UpgradeToEIP7732(beaconState state.BeaconState) (state.BeaconState, error) {
|
||||||
|
currentSyncCommittee, err := beaconState.CurrentSyncCommittee()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
nextSyncCommittee, err := beaconState.NextSyncCommittee()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
prevEpochParticipation, err := beaconState.PreviousEpochParticipation()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
currentEpochParticipation, err := beaconState.CurrentEpochParticipation()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
inactivityScores, err := beaconState.InactivityScores()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payloadHeader, err := beaconState.LatestExecutionPayloadHeader()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
wi, err := beaconState.NextWithdrawalIndex()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
vi, err := beaconState.NextWithdrawalValidatorIndex()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
summaries, err := beaconState.HistoricalSummaries()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
historicalRoots, err := beaconState.HistoricalRoots()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
depositBalanceToConsume, err := beaconState.DepositBalanceToConsume()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
exitBalanceToConsume, err := beaconState.ExitBalanceToConsume()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
earliestExitEpoch, err := beaconState.EarliestExitEpoch()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
consolidationBalanceToConsume, err := beaconState.ConsolidationBalanceToConsume()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
earliestConsolidationEpoch, err := beaconState.EarliestConsolidationEpoch()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pendingDeposits, err := beaconState.PendingDeposits()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pendingPartialWithdrawals, err := beaconState.PendingPartialWithdrawals()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pendingConsolidations, err := beaconState.PendingConsolidations()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s := ðpb.BeaconStateEPBS{
|
||||||
|
GenesisTime: beaconState.GenesisTime(),
|
||||||
|
GenesisValidatorsRoot: beaconState.GenesisValidatorsRoot(),
|
||||||
|
Slot: beaconState.Slot(),
|
||||||
|
Fork: ðpb.Fork{
|
||||||
|
PreviousVersion: beaconState.Fork().CurrentVersion,
|
||||||
|
CurrentVersion: params.BeaconConfig().EPBSForkVersion,
|
||||||
|
Epoch: time.CurrentEpoch(beaconState),
|
||||||
|
},
|
||||||
|
LatestBlockHeader: beaconState.LatestBlockHeader(),
|
||||||
|
BlockRoots: beaconState.BlockRoots(),
|
||||||
|
StateRoots: beaconState.StateRoots(),
|
||||||
|
HistoricalRoots: historicalRoots,
|
||||||
|
Eth1Data: beaconState.Eth1Data(),
|
||||||
|
Eth1DataVotes: beaconState.Eth1DataVotes(),
|
||||||
|
Eth1DepositIndex: beaconState.Eth1DepositIndex(),
|
||||||
|
Validators: beaconState.Validators(),
|
||||||
|
Balances: beaconState.Balances(),
|
||||||
|
RandaoMixes: beaconState.RandaoMixes(),
|
||||||
|
Slashings: beaconState.Slashings(),
|
||||||
|
PreviousEpochParticipation: prevEpochParticipation,
|
||||||
|
CurrentEpochParticipation: currentEpochParticipation,
|
||||||
|
JustificationBits: beaconState.JustificationBits(),
|
||||||
|
PreviousJustifiedCheckpoint: beaconState.PreviousJustifiedCheckpoint(),
|
||||||
|
CurrentJustifiedCheckpoint: beaconState.CurrentJustifiedCheckpoint(),
|
||||||
|
FinalizedCheckpoint: beaconState.FinalizedCheckpoint(),
|
||||||
|
InactivityScores: inactivityScores,
|
||||||
|
CurrentSyncCommittee: currentSyncCommittee,
|
||||||
|
NextSyncCommittee: nextSyncCommittee,
|
||||||
|
NextWithdrawalIndex: wi,
|
||||||
|
NextWithdrawalValidatorIndex: vi,
|
||||||
|
HistoricalSummaries: summaries,
|
||||||
|
|
||||||
|
DepositRequestsStartIndex: params.BeaconConfig().UnsetDepositRequestsStartIndex,
|
||||||
|
DepositBalanceToConsume: depositBalanceToConsume,
|
||||||
|
ExitBalanceToConsume: exitBalanceToConsume,
|
||||||
|
EarliestExitEpoch: earliestExitEpoch,
|
||||||
|
ConsolidationBalanceToConsume: consolidationBalanceToConsume,
|
||||||
|
EarliestConsolidationEpoch: earliestConsolidationEpoch,
|
||||||
|
PendingDeposits: pendingDeposits,
|
||||||
|
PendingPartialWithdrawals: pendingPartialWithdrawals,
|
||||||
|
PendingConsolidations: pendingConsolidations,
|
||||||
|
|
||||||
|
// Newly added for EIP7732
|
||||||
|
LatestExecutionPayloadHeader: &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
ParentBlockHash: make([]byte, 32),
|
||||||
|
ParentBlockRoot: make([]byte, 32),
|
||||||
|
BlockHash: make([]byte, 32),
|
||||||
|
BlobKzgCommitmentsRoot: make([]byte, 32),
|
||||||
|
},
|
||||||
|
LatestBlockHash: payloadHeader.BlockHash(),
|
||||||
|
LatestFullSlot: beaconState.Slot(),
|
||||||
|
LastWithdrawalsRoot: make([]byte, 32),
|
||||||
|
}
|
||||||
|
|
||||||
|
post, err := state_native.InitializeFromProtoUnsafeEpbs(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to initialize post EIP-7732 beaconState")
|
||||||
|
}
|
||||||
|
|
||||||
|
return post, nil
|
||||||
|
}
|
||||||
135
beacon-chain/core/epbs/upgrade_test.go
Normal file
135
beacon-chain/core/epbs/upgrade_test.go
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
package epbs_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epbs"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
|
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||||
|
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/util"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUpgradeToEip7732(t *testing.T) {
|
||||||
|
st, _ := util.DeterministicGenesisStateElectra(t, params.BeaconConfig().MaxValidatorsPerCommittee)
|
||||||
|
require.NoError(t, st.SetHistoricalRoots([][]byte{{1}}))
|
||||||
|
|
||||||
|
preForkState := st.Copy()
|
||||||
|
mSt, err := epbs.UpgradeToEIP7732(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.Validators()[2:], mSt.Validators()[2:])
|
||||||
|
require.DeepSSZEqual(t, preForkState.Balances()[2:], mSt.Balances()[2:])
|
||||||
|
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.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())
|
||||||
|
|
||||||
|
require.Equal(t, len(preForkState.Validators()), len(mSt.Validators()))
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
hr1, err := preForkState.HistoricalRoots()
|
||||||
|
require.NoError(t, err)
|
||||||
|
hr2, err := mSt.HistoricalRoots()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.DeepEqual(t, hr1, hr2)
|
||||||
|
|
||||||
|
f := mSt.Fork()
|
||||||
|
require.DeepSSZEqual(t, ðpb.Fork{
|
||||||
|
PreviousVersion: st.Fork().CurrentVersion,
|
||||||
|
CurrentVersion: params.BeaconConfig().EPBSForkVersion,
|
||||||
|
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)
|
||||||
|
|
||||||
|
nwi, err := mSt.NextWithdrawalIndex()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, uint64(0), nwi)
|
||||||
|
|
||||||
|
lwvi, err := mSt.NextWithdrawalValidatorIndex()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, primitives.ValidatorIndex(0), lwvi)
|
||||||
|
|
||||||
|
summaries, err := mSt.HistoricalSummaries()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 0, len(summaries))
|
||||||
|
|
||||||
|
startIndex, err := mSt.DepositRequestsStartIndex()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, params.BeaconConfig().UnsetDepositRequestsStartIndex, startIndex)
|
||||||
|
|
||||||
|
balance, err := mSt.DepositBalanceToConsume()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, primitives.Gwei(0), balance)
|
||||||
|
|
||||||
|
tab, err := helpers.TotalActiveBalance(mSt)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
ebtc, err := mSt.ExitBalanceToConsume()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, helpers.ActivationExitChurnLimit(primitives.Gwei(tab)), ebtc)
|
||||||
|
|
||||||
|
cbtc, err := mSt.ConsolidationBalanceToConsume()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, helpers.ConsolidationChurnLimit(primitives.Gwei(tab)), cbtc)
|
||||||
|
|
||||||
|
earliestConsolidationEpoch, err := mSt.EarliestConsolidationEpoch()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, helpers.ActivationExitEpoch(slots.ToEpoch(preForkState.Slot())), earliestConsolidationEpoch)
|
||||||
|
|
||||||
|
// EIP-7732 checks.
|
||||||
|
h, err := mSt.LatestExecutionPayloadHeaderEPBS()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.DeepEqual(t, &enginev1.ExecutionPayloadHeaderEPBS{
|
||||||
|
ParentBlockHash: make([]byte, 32),
|
||||||
|
ParentBlockRoot: make([]byte, 32),
|
||||||
|
BlockHash: make([]byte, 32),
|
||||||
|
BlobKzgCommitmentsRoot: make([]byte, 32),
|
||||||
|
}, h)
|
||||||
|
lwr, err := mSt.LastWithdrawalsRoot()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.DeepEqual(t, lwr, make([]byte, 32))
|
||||||
|
lbh, err := mSt.LatestBlockHash()
|
||||||
|
require.NoError(t, err)
|
||||||
|
lh, err := preForkState.LatestExecutionPayloadHeader()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.DeepEqual(t, lbh, lh.BlockHash())
|
||||||
|
slot, err := mSt.LatestFullSlot()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, slot, preForkState.Slot())
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ go_library(
|
|||||||
"genesis.go",
|
"genesis.go",
|
||||||
"legacy.go",
|
"legacy.go",
|
||||||
"metrics.go",
|
"metrics.go",
|
||||||
|
"payload_attestation.go",
|
||||||
"randao.go",
|
"randao.go",
|
||||||
"rewards_penalties.go",
|
"rewards_penalties.go",
|
||||||
"shuffle.go",
|
"shuffle.go",
|
||||||
@@ -21,11 +22,13 @@ go_library(
|
|||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"//beacon-chain/cache:go_default_library",
|
"//beacon-chain/cache:go_default_library",
|
||||||
|
"//beacon-chain/core/signing:go_default_library",
|
||||||
"//beacon-chain/core/time:go_default_library",
|
"//beacon-chain/core/time:go_default_library",
|
||||||
"//beacon-chain/forkchoice/types:go_default_library",
|
"//beacon-chain/forkchoice/types:go_default_library",
|
||||||
"//beacon-chain/state:go_default_library",
|
"//beacon-chain/state:go_default_library",
|
||||||
"//config/fieldparams:go_default_library",
|
"//config/fieldparams:go_default_library",
|
||||||
"//config/params:go_default_library",
|
"//config/params:go_default_library",
|
||||||
|
"//consensus-types/epbs:go_default_library",
|
||||||
"//consensus-types/primitives:go_default_library",
|
"//consensus-types/primitives:go_default_library",
|
||||||
"//container/slice:go_default_library",
|
"//container/slice:go_default_library",
|
||||||
"//container/trie:go_default_library",
|
"//container/trie:go_default_library",
|
||||||
@@ -53,7 +56,9 @@ go_test(
|
|||||||
"attestation_test.go",
|
"attestation_test.go",
|
||||||
"beacon_committee_test.go",
|
"beacon_committee_test.go",
|
||||||
"block_test.go",
|
"block_test.go",
|
||||||
|
"exports_test.go",
|
||||||
"legacy_test.go",
|
"legacy_test.go",
|
||||||
|
"payload_attestation_test.go",
|
||||||
"private_access_fuzz_noop_test.go", # keep
|
"private_access_fuzz_noop_test.go", # keep
|
||||||
"private_access_test.go",
|
"private_access_test.go",
|
||||||
"randao_test.go",
|
"randao_test.go",
|
||||||
@@ -70,21 +75,27 @@ go_test(
|
|||||||
tags = ["CI_race_detection"],
|
tags = ["CI_race_detection"],
|
||||||
deps = [
|
deps = [
|
||||||
"//beacon-chain/cache:go_default_library",
|
"//beacon-chain/cache:go_default_library",
|
||||||
|
"//beacon-chain/core/signing:go_default_library",
|
||||||
"//beacon-chain/core/time:go_default_library",
|
"//beacon-chain/core/time:go_default_library",
|
||||||
"//beacon-chain/forkchoice/types:go_default_library",
|
"//beacon-chain/forkchoice/types:go_default_library",
|
||||||
"//beacon-chain/state:go_default_library",
|
"//beacon-chain/state:go_default_library",
|
||||||
"//beacon-chain/state/state-native:go_default_library",
|
"//beacon-chain/state/state-native:go_default_library",
|
||||||
"//config/fieldparams:go_default_library",
|
"//config/fieldparams:go_default_library",
|
||||||
"//config/params:go_default_library",
|
"//config/params:go_default_library",
|
||||||
|
"//consensus-types/epbs:go_default_library",
|
||||||
"//consensus-types/primitives:go_default_library",
|
"//consensus-types/primitives:go_default_library",
|
||||||
"//container/slice:go_default_library",
|
"//container/slice:go_default_library",
|
||||||
|
"//crypto/bls:go_default_library",
|
||||||
"//crypto/hash:go_default_library",
|
"//crypto/hash:go_default_library",
|
||||||
|
"//crypto/rand:go_default_library",
|
||||||
"//encoding/bytesutil:go_default_library",
|
"//encoding/bytesutil:go_default_library",
|
||||||
|
"//math:go_default_library",
|
||||||
"//proto/prysm/v1alpha1:go_default_library",
|
"//proto/prysm/v1alpha1:go_default_library",
|
||||||
"//runtime/version:go_default_library",
|
"//runtime/version:go_default_library",
|
||||||
"//testing/assert:go_default_library",
|
"//testing/assert:go_default_library",
|
||||||
"//testing/require:go_default_library",
|
"//testing/require:go_default_library",
|
||||||
"//testing/util:go_default_library",
|
"//testing/util:go_default_library",
|
||||||
|
"//testing/util/random:go_default_library",
|
||||||
"//time:go_default_library",
|
"//time:go_default_library",
|
||||||
"//time/slots:go_default_library",
|
"//time/slots:go_default_library",
|
||||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||||
|
|||||||
@@ -213,6 +213,7 @@ type CommitteeAssignment struct {
|
|||||||
Committee []primitives.ValidatorIndex
|
Committee []primitives.ValidatorIndex
|
||||||
AttesterSlot primitives.Slot
|
AttesterSlot primitives.Slot
|
||||||
CommitteeIndex primitives.CommitteeIndex
|
CommitteeIndex primitives.CommitteeIndex
|
||||||
|
PtcSlot primitives.Slot
|
||||||
}
|
}
|
||||||
|
|
||||||
// verifyAssignmentEpoch verifies if the given epoch is valid for assignment based on the provided state.
|
// verifyAssignmentEpoch verifies if the given epoch is valid for assignment based on the provided state.
|
||||||
@@ -294,7 +295,7 @@ func CommitteeAssignments(ctx context.Context, state state.BeaconState, epoch pr
|
|||||||
if err := verifyAssignmentEpoch(epoch, state); err != nil {
|
if err := verifyAssignmentEpoch(epoch, state); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
startSlot, err := slots.EpochStart(epoch)
|
slot, err := slots.EpochStart(epoch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -303,14 +304,17 @@ func CommitteeAssignments(ctx context.Context, state state.BeaconState, epoch pr
|
|||||||
vals[v] = struct{}{}
|
vals[v] = struct{}{}
|
||||||
}
|
}
|
||||||
assignments := make(map[primitives.ValidatorIndex]*CommitteeAssignment)
|
assignments := make(map[primitives.ValidatorIndex]*CommitteeAssignment)
|
||||||
|
|
||||||
|
committees, err := BeaconCommittees(ctx, state, slot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "could not compute beacon committees")
|
||||||
|
}
|
||||||
|
ptcPerSlot, ptcMembersPerCommittee := PtcAllocation(len(committees))
|
||||||
// Compute committee assignments for each slot in the epoch.
|
// Compute committee assignments for each slot in the epoch.
|
||||||
for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ {
|
endSlot := slot + params.BeaconConfig().SlotsPerEpoch
|
||||||
committees, err := BeaconCommittees(ctx, state, slot)
|
for {
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "could not compute beacon committees")
|
|
||||||
}
|
|
||||||
for j, committee := range committees {
|
for j, committee := range committees {
|
||||||
for _, vIndex := range committee {
|
for i, vIndex := range committee {
|
||||||
if _, ok := vals[vIndex]; !ok { // Skip if the validator is not in the provided validators slice.
|
if _, ok := vals[vIndex]; !ok { // Skip if the validator is not in the provided validators slice.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -320,8 +324,19 @@ func CommitteeAssignments(ctx context.Context, state state.BeaconState, epoch pr
|
|||||||
assignments[vIndex].Committee = committee
|
assignments[vIndex].Committee = committee
|
||||||
assignments[vIndex].AttesterSlot = slot
|
assignments[vIndex].AttesterSlot = slot
|
||||||
assignments[vIndex].CommitteeIndex = primitives.CommitteeIndex(j)
|
assignments[vIndex].CommitteeIndex = primitives.CommitteeIndex(j)
|
||||||
|
if uint64(j) < ptcPerSlot && uint64(i) < ptcMembersPerCommittee {
|
||||||
|
assignments[vIndex].PtcSlot = slot
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
slot++
|
||||||
|
if slot == endSlot {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
committees, err = BeaconCommittees(ctx, state, slot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "could not compute beacon committees")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return assignments, nil
|
return assignments, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package helpers_test
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -10,6 +11,7 @@ import (
|
|||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
|
||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
|
||||||
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
|
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
|
||||||
|
field_params "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
"github.com/prysmaticlabs/prysm/v5/container/slice"
|
"github.com/prysmaticlabs/prysm/v5/container/slice"
|
||||||
@@ -729,15 +731,26 @@ func TestCommitteeIndices(t *testing.T) {
|
|||||||
assert.DeepEqual(t, []primitives.CommitteeIndex{0, 1, 3}, indices)
|
assert.DeepEqual(t, []primitives.CommitteeIndex{0, 1, 3}, indices)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAttestationCommittees(t *testing.T) {
|
func TestCommitteeAssignments_PTC(t *testing.T) {
|
||||||
validators := make([]*ethpb.Validator, params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().TargetCommitteeSize))
|
helpers.ClearCache()
|
||||||
|
// Create 10 committees. Total 40960 validators.
|
||||||
|
committeeCount := uint64(10)
|
||||||
|
validatorCount := committeeCount * params.BeaconConfig().TargetCommitteeSize * uint64(params.BeaconConfig().SlotsPerEpoch)
|
||||||
|
validators := make([]*ethpb.Validator, validatorCount)
|
||||||
|
validatorIndices := make([]primitives.ValidatorIndex, validatorCount)
|
||||||
|
|
||||||
for i := 0; i < len(validators); i++ {
|
for i := 0; i < len(validators); i++ {
|
||||||
|
k := make([]byte, 48)
|
||||||
|
copy(k, strconv.Itoa(i))
|
||||||
validators[i] = ðpb.Validator{
|
validators[i] = ðpb.Validator{
|
||||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
PublicKey: k,
|
||||||
|
WithdrawalCredentials: make([]byte, 32),
|
||||||
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||||
}
|
}
|
||||||
|
validatorIndices[i] = primitives.ValidatorIndex(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
state, err := state_native.InitializeFromProtoPhase0(ðpb.BeaconState{
|
state, err := state_native.InitializeFromProtoEpbs(ðpb.BeaconStateEPBS{
|
||||||
Validators: validators,
|
Validators: validators,
|
||||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||||
})
|
})
|
||||||
@@ -761,6 +774,31 @@ func TestAttestationCommittees(t *testing.T) {
|
|||||||
assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[0])))
|
assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[0])))
|
||||||
assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[1])))
|
assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[1])))
|
||||||
})
|
})
|
||||||
|
as, err := helpers.CommitteeAssignments(context.Background(), state, 1, validatorIndices)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Capture all the slots and all the validator index that belonged in a PTC using a map for verification later.
|
||||||
|
slotValidatorMap := make(map[primitives.Slot][]primitives.ValidatorIndex)
|
||||||
|
for i, a := range as {
|
||||||
|
slotValidatorMap[a.PtcSlot] = append(slotValidatorMap[a.PtcSlot], i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that all the slots have the correct number of PTC.
|
||||||
|
for s, v := range slotValidatorMap {
|
||||||
|
if s == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Make sure all the PTC are the correct size from the map.
|
||||||
|
require.Equal(t, len(v), field_params.PTCSize)
|
||||||
|
|
||||||
|
// Get the actual PTC from the beacon state using the helper function
|
||||||
|
ptc, err := helpers.GetPayloadTimelinessCommittee(context.Background(), state, s)
|
||||||
|
require.NoError(t, err)
|
||||||
|
for _, index := range ptc {
|
||||||
|
i := slices.Index(v, index)
|
||||||
|
require.NotEqual(t, -1, i) // PTC not found from the assignment map
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBeaconCommittees(t *testing.T) {
|
func TestBeaconCommittees(t *testing.T) {
|
||||||
|
|||||||
12
beacon-chain/core/helpers/exports_test.go
Normal file
12
beacon-chain/core/helpers/exports_test.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNilMessage = errNilMessage
|
||||||
|
ErrNilData = errNilData
|
||||||
|
ErrNilBeaconBlockRoot = errNilBeaconBlockRoot
|
||||||
|
ErrNilPayloadAttestation = errNilPayloadAttestation
|
||||||
|
ErrNilSignature = errNilSignature
|
||||||
|
ErrNilAggregationBits = errNilAggregationBits
|
||||||
|
ErrPreEPBSState = errPreEPBSState
|
||||||
|
ErrCommitteeOverflow = errCommitteeOverflow
|
||||||
|
)
|
||||||
296
beacon-chain/core/helpers/payload_attestation.go
Normal file
296
beacon-chain/core/helpers/payload_attestation.go
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||||
|
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/epbs"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/math"
|
||||||
|
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNilMessage = errors.New("nil PayloadAttestationMessage")
|
||||||
|
errNilData = errors.New("nil PayloadAttestationData")
|
||||||
|
errNilBeaconBlockRoot = errors.New("nil BeaconBlockRoot")
|
||||||
|
errNilPayloadAttestation = errors.New("nil PayloadAttestation")
|
||||||
|
errNilSignature = errors.New("nil Signature")
|
||||||
|
errNilAggregationBits = errors.New("nil AggregationBits")
|
||||||
|
errPreEPBSState = errors.New("beacon state pre ePBS fork")
|
||||||
|
errCommitteeOverflow = errors.New("beacon committee of insufficient size")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidateNilPayloadAttestationData checks if any composite field of the
|
||||||
|
// payload attestation data is nil
|
||||||
|
func ValidateNilPayloadAttestationData(data *eth.PayloadAttestationData) error {
|
||||||
|
if data == nil {
|
||||||
|
return errNilData
|
||||||
|
}
|
||||||
|
if data.BeaconBlockRoot == nil {
|
||||||
|
return errNilBeaconBlockRoot
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateNilPayloadAttestationMessage checks if any composite field of the
|
||||||
|
// payload attestation message is nil
|
||||||
|
func ValidateNilPayloadAttestationMessage(att *eth.PayloadAttestationMessage) error {
|
||||||
|
if att == nil {
|
||||||
|
return errNilMessage
|
||||||
|
}
|
||||||
|
if att.Signature == nil {
|
||||||
|
return errNilSignature
|
||||||
|
}
|
||||||
|
return ValidateNilPayloadAttestationData(att.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateNilPayloadAttestation checks if any composite field of the
|
||||||
|
// payload attestation is nil
|
||||||
|
func ValidateNilPayloadAttestation(att *eth.PayloadAttestation) error {
|
||||||
|
if att == nil {
|
||||||
|
return errNilPayloadAttestation
|
||||||
|
}
|
||||||
|
if att.AggregationBits == nil {
|
||||||
|
return errNilAggregationBits
|
||||||
|
}
|
||||||
|
if att.Signature == nil {
|
||||||
|
return errNilSignature
|
||||||
|
}
|
||||||
|
return ValidateNilPayloadAttestationData(att.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InPayloadTimelinessCommittee returns whether the given index belongs to the
|
||||||
|
// PTC computed from the passed state.
|
||||||
|
func InPayloadTimelinessCommittee(ctx context.Context, state state.ReadOnlyBeaconState, slot primitives.Slot, idx primitives.ValidatorIndex) (bool, error) {
|
||||||
|
ptc, err := GetPayloadTimelinessCommittee(ctx, state, slot)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
for _, i := range ptc {
|
||||||
|
if i == idx {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPayloadTimelinessCommittee returns the PTC for the given slot, computed from the passed state as in the
|
||||||
|
// spec function `get_ptc`.
|
||||||
|
func GetPayloadTimelinessCommittee(ctx context.Context, state state.ReadOnlyBeaconState, slot primitives.Slot) (indices []primitives.ValidatorIndex, err error) {
|
||||||
|
if state.Version() < version.EPBS {
|
||||||
|
return nil, errPreEPBSState
|
||||||
|
}
|
||||||
|
committees, err := BeaconCommittees(ctx, state, slot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "could not get beacon committees")
|
||||||
|
}
|
||||||
|
committeesPerSlot, membersPerCommittee := PtcAllocation(len(committees))
|
||||||
|
for i, committee := range committees {
|
||||||
|
if uint64(i) >= committeesPerSlot {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if uint64(len(committee)) < membersPerCommittee {
|
||||||
|
return nil, errCommitteeOverflow
|
||||||
|
}
|
||||||
|
indices = append(indices, committee[:membersPerCommittee]...)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PtcAllocation returns:
|
||||||
|
// 1. The number of beacon committees that PTC will borrow from in a slot.
|
||||||
|
// 2. The number of validators that PTC will borrow from in a beacon committee.
|
||||||
|
func PtcAllocation(slotCommittees int) (committeesPerSlot, membersPerCommittee uint64) {
|
||||||
|
committeesPerSlot = math.LargestPowerOfTwo(math.Min(uint64(slotCommittees), fieldparams.PTCSize))
|
||||||
|
membersPerCommittee = fieldparams.PTCSize / committeesPerSlot
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPayloadAttestingIndices returns the set of attester indices corresponding to the given PayloadAttestation.
|
||||||
|
//
|
||||||
|
// Spec pseudocode definition:
|
||||||
|
//
|
||||||
|
// def get_payload_attesting_indices(state: BeaconState, slot: Slot,
|
||||||
|
// payload_attestation: PayloadAttestation) -> Set[ValidatorIndex]:
|
||||||
|
// """
|
||||||
|
// Return the set of attesting indices corresponding to ``payload_attestation``.
|
||||||
|
// """
|
||||||
|
// ptc = get_ptc(state, slot)
|
||||||
|
// return set(index for i, index in enumerate(ptc) if payload_attestation.aggregation_bits[i])
|
||||||
|
func GetPayloadAttestingIndices(ctx context.Context, state state.ReadOnlyBeaconState, slot primitives.Slot, att *eth.PayloadAttestation) (indices []primitives.ValidatorIndex, err error) {
|
||||||
|
if state.Version() < version.EPBS {
|
||||||
|
return nil, errPreEPBSState
|
||||||
|
}
|
||||||
|
|
||||||
|
ptc, err := GetPayloadTimelinessCommittee(ctx, state, slot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, validatorIndex := range ptc {
|
||||||
|
if att.AggregationBits.BitAt(uint64(i)) {
|
||||||
|
indices = append(indices, validatorIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIndexedPayloadAttestation replaces a PayloadAttestation's AggregationBits with sorted AttestingIndices and returns an IndexedPayloadAttestation.
|
||||||
|
//
|
||||||
|
// Spec pseudocode definition:
|
||||||
|
//
|
||||||
|
// def get_indexed_payload_attestation(state: BeaconState, slot: Slot,
|
||||||
|
// payload_attestation: PayloadAttestation) -> IndexedPayloadAttestation:
|
||||||
|
// """
|
||||||
|
// Return the indexed payload attestation corresponding to ``payload_attestation``.
|
||||||
|
// """
|
||||||
|
// attesting_indices = get_payload_attesting_indices(state, slot, payload_attestation)
|
||||||
|
//
|
||||||
|
// return IndexedPayloadAttestation(
|
||||||
|
// attesting_indices=sorted(attesting_indices),
|
||||||
|
// data=payload_attestation.data,
|
||||||
|
// signature=payload_attestation.signature,
|
||||||
|
// )
|
||||||
|
func GetIndexedPayloadAttestation(ctx context.Context, state state.ReadOnlyBeaconState, slot primitives.Slot, att *eth.PayloadAttestation) (*epbs.IndexedPayloadAttestation, error) {
|
||||||
|
if state.Version() < version.EPBS {
|
||||||
|
return nil, errPreEPBSState
|
||||||
|
}
|
||||||
|
|
||||||
|
attestingIndices, err := GetPayloadAttestingIndices(ctx, state, slot, att)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.Sort(attestingIndices)
|
||||||
|
|
||||||
|
return &epbs.IndexedPayloadAttestation{
|
||||||
|
AttestingIndices: attestingIndices,
|
||||||
|
Data: att.Data,
|
||||||
|
Signature: att.Signature,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidIndexedPayloadAttestation validates the given IndexedPayloadAttestation.
|
||||||
|
//
|
||||||
|
// Spec pseudocode definition:
|
||||||
|
//
|
||||||
|
// def is_valid_indexed_payload_attestation(
|
||||||
|
// state: BeaconState,
|
||||||
|
// indexed_payload_attestation: IndexedPayloadAttestation) -> bool:
|
||||||
|
// """
|
||||||
|
// Check if ``indexed_payload_attestation`` is not empty, has sorted and unique indices and has
|
||||||
|
// a valid aggregate signature.
|
||||||
|
// """
|
||||||
|
// # Verify the data is valid
|
||||||
|
// if indexed_payload_attestation.data.payload_status >= PAYLOAD_INVALID_STATUS:
|
||||||
|
// return False
|
||||||
|
//
|
||||||
|
// # Verify indices are sorted and unique
|
||||||
|
// indices = indexed_payload_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_PTC_ATTESTER, None)
|
||||||
|
// signing_root = compute_signing_root(indexed_payload_attestation.data, domain)
|
||||||
|
// return bls.FastAggregateVerify(pubkeys, signing_root, indexed_payload_attestation.signature)
|
||||||
|
func IsValidIndexedPayloadAttestation(state state.ReadOnlyBeaconState, att *epbs.IndexedPayloadAttestation) (bool, error) {
|
||||||
|
if state.Version() < version.EPBS {
|
||||||
|
return false, errPreEPBSState
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the data is valid.
|
||||||
|
if att.Data.PayloadStatus >= primitives.PAYLOAD_INVALID_STATUS {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify indices are sorted and unique.
|
||||||
|
indices := att.AttestingIndices
|
||||||
|
slices.Sort(indices)
|
||||||
|
if len(indices) == 0 || !slices.Equal(att.AttestingIndices, indices) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify aggregate signature.
|
||||||
|
publicKeys := make([]bls.PublicKey, len(indices))
|
||||||
|
for i, index := range indices {
|
||||||
|
validator, err := state.ValidatorAtIndexReadOnly(index)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKeyBytes := validator.PublicKey()
|
||||||
|
publicKey, err := bls.PublicKeyFromBytes(publicKeyBytes[:])
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKeys[i] = publicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
domain, err := signing.Domain(
|
||||||
|
state.Fork(),
|
||||||
|
slots.ToEpoch(state.Slot()),
|
||||||
|
params.BeaconConfig().DomainPTCAttester,
|
||||||
|
state.GenesisValidatorsRoot(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
signingRoot, err := signing.ComputeSigningRoot(att.Data, domain)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
signature, err := bls.SignatureFromBytes(att.Signature)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return signature.FastAggregateVerify(publicKeys, signingRoot), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidatePayloadAttestationMessageSignature verifies the signature of a
|
||||||
|
// payload attestation message.
|
||||||
|
func ValidatePayloadAttestationMessageSignature(ctx context.Context, st state.ReadOnlyBeaconState, msg *eth.PayloadAttestationMessage) error {
|
||||||
|
if err := ValidateNilPayloadAttestationMessage(msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
val, err := st.ValidatorAtIndex(msg.ValidatorIndex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pub, err := bls.PublicKeyFromBytes(val.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sig, err := bls.SignatureFromBytes(msg.Signature)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
currentEpoch := slots.ToEpoch(st.Slot())
|
||||||
|
domain, err := signing.Domain(st.Fork(), currentEpoch, params.BeaconConfig().DomainPTCAttester, st.GenesisValidatorsRoot())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
root, err := signing.ComputeSigningRoot(msg.Data, domain)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !sig.Verify(pub, root[:]) {
|
||||||
|
return signing.ErrSigFailedToVerify
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
363
beacon-chain/core/helpers/payload_attestation_test.go
Normal file
363
beacon-chain/core/helpers/payload_attestation_test.go
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
package helpers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prysmaticlabs/go-bitfield"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing"
|
||||||
|
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
|
||||||
|
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/epbs"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/crypto/rand"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/math"
|
||||||
|
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||||
|
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/util"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/util/random"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidateNilPayloadAttestation(t *testing.T) {
|
||||||
|
require.ErrorIs(t, helpers.ErrNilData, helpers.ValidateNilPayloadAttestationData(nil))
|
||||||
|
data := ð.PayloadAttestationData{}
|
||||||
|
require.ErrorIs(t, helpers.ErrNilBeaconBlockRoot, helpers.ValidateNilPayloadAttestationData(data))
|
||||||
|
data.BeaconBlockRoot = make([]byte, 32)
|
||||||
|
require.NoError(t, helpers.ValidateNilPayloadAttestationData(data))
|
||||||
|
|
||||||
|
require.ErrorIs(t, helpers.ErrNilMessage, helpers.ValidateNilPayloadAttestationMessage(nil))
|
||||||
|
message := ð.PayloadAttestationMessage{}
|
||||||
|
require.ErrorIs(t, helpers.ErrNilSignature, helpers.ValidateNilPayloadAttestationMessage(message))
|
||||||
|
message.Signature = make([]byte, 96)
|
||||||
|
require.ErrorIs(t, helpers.ErrNilData, helpers.ValidateNilPayloadAttestationMessage(message))
|
||||||
|
message.Data = data
|
||||||
|
require.NoError(t, helpers.ValidateNilPayloadAttestationMessage(message))
|
||||||
|
|
||||||
|
require.ErrorIs(t, helpers.ErrNilPayloadAttestation, helpers.ValidateNilPayloadAttestation(nil))
|
||||||
|
att := ð.PayloadAttestation{}
|
||||||
|
require.ErrorIs(t, helpers.ErrNilAggregationBits, helpers.ValidateNilPayloadAttestation(att))
|
||||||
|
att.AggregationBits = bitfield.NewBitvector512()
|
||||||
|
require.ErrorIs(t, helpers.ErrNilSignature, helpers.ValidateNilPayloadAttestation(att))
|
||||||
|
att.Signature = message.Signature
|
||||||
|
require.ErrorIs(t, helpers.ErrNilData, helpers.ValidateNilPayloadAttestation(att))
|
||||||
|
att.Data = data
|
||||||
|
require.NoError(t, helpers.ValidateNilPayloadAttestation(att))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetPayloadTimelinessCommittee(t *testing.T) {
|
||||||
|
helpers.ClearCache()
|
||||||
|
|
||||||
|
// Create 10 committees
|
||||||
|
committeeCount := uint64(10)
|
||||||
|
validatorCount := committeeCount * params.BeaconConfig().TargetCommitteeSize * uint64(params.BeaconConfig().SlotsPerEpoch)
|
||||||
|
validators := make([]*ethpb.Validator, validatorCount)
|
||||||
|
|
||||||
|
for i := 0; i < len(validators); i++ {
|
||||||
|
k := make([]byte, 48)
|
||||||
|
copy(k, strconv.Itoa(i))
|
||||||
|
validators[i] = ðpb.Validator{
|
||||||
|
PublicKey: k,
|
||||||
|
WithdrawalCredentials: make([]byte, 32),
|
||||||
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state, err := state_native.InitializeFromProtoEpbs(random.BeaconState(t))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, state.SetValidators(validators))
|
||||||
|
require.NoError(t, state.SetSlot(200))
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
indices, err := helpers.BeaconCommitteeFromState(ctx, state, state.Slot(), 1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 128, len(indices))
|
||||||
|
|
||||||
|
epoch := slots.ToEpoch(state.Slot())
|
||||||
|
activeCount, err := helpers.ActiveValidatorCount(ctx, state, epoch)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, uint64(40960), activeCount)
|
||||||
|
|
||||||
|
computedCommitteeCount := helpers.SlotCommitteeCount(activeCount)
|
||||||
|
require.Equal(t, committeeCount, computedCommitteeCount)
|
||||||
|
committeesPerSlot := math.LargestPowerOfTwo(math.Min(committeeCount, fieldparams.PTCSize))
|
||||||
|
require.Equal(t, uint64(8), committeesPerSlot)
|
||||||
|
|
||||||
|
ptc, err := helpers.GetPayloadTimelinessCommittee(ctx, state, state.Slot())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, fieldparams.PTCSize, len(ptc))
|
||||||
|
|
||||||
|
committee1, err := helpers.BeaconCommitteeFromState(ctx, state, state.Slot(), 0)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.DeepEqual(t, committee1[:64], ptc[:64])
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_PtcAllocation(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
committeeCount int
|
||||||
|
memberPerCommittee uint64
|
||||||
|
committeesPerSlot uint64
|
||||||
|
}{
|
||||||
|
{1, 512, 1},
|
||||||
|
{4, 128, 4},
|
||||||
|
{128, 4, 128},
|
||||||
|
{512, 1, 512},
|
||||||
|
{1024, 1, 512},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
committeesPerSlot, memberPerCommittee := helpers.PtcAllocation(test.committeeCount)
|
||||||
|
if memberPerCommittee != test.memberPerCommittee {
|
||||||
|
t.Errorf("memberPerCommittee(%d) = %d; expected %d", test.committeeCount, memberPerCommittee, test.memberPerCommittee)
|
||||||
|
}
|
||||||
|
if committeesPerSlot != test.committeesPerSlot {
|
||||||
|
t.Errorf("committeesPerSlot(%d) = %d; expected %d", test.committeeCount, committeesPerSlot, test.committeesPerSlot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetPayloadAttestingIndices(t *testing.T) {
|
||||||
|
helpers.ClearCache()
|
||||||
|
|
||||||
|
// Create 10 committees. Total 40960 validators.
|
||||||
|
committeeCount := uint64(10)
|
||||||
|
validatorCount := committeeCount * params.BeaconConfig().TargetCommitteeSize * uint64(params.BeaconConfig().SlotsPerEpoch)
|
||||||
|
validators := make([]*ethpb.Validator, validatorCount)
|
||||||
|
|
||||||
|
for i := 0; i < len(validators); i++ {
|
||||||
|
pubkey := make([]byte, 48)
|
||||||
|
copy(pubkey, strconv.Itoa(i))
|
||||||
|
validators[i] = ðpb.Validator{
|
||||||
|
PublicKey: pubkey,
|
||||||
|
WithdrawalCredentials: make([]byte, 32),
|
||||||
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a beacon state.
|
||||||
|
state, err := state_native.InitializeFromProtoEpbs(ðpb.BeaconStateEPBS{
|
||||||
|
Validators: validators,
|
||||||
|
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Get PTC.
|
||||||
|
ptc, err := helpers.GetPayloadTimelinessCommittee(context.Background(), state, state.Slot())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, fieldparams.PTCSize, len(ptc))
|
||||||
|
|
||||||
|
// Generate random indices. PTC members at the corresponding indices are considered attested.
|
||||||
|
randGen := rand.NewDeterministicGenerator()
|
||||||
|
attesterCount := randGen.Intn(fieldparams.PTCSize) + 1
|
||||||
|
indices := randGen.Perm(fieldparams.PTCSize)[:attesterCount]
|
||||||
|
slices.Sort(indices)
|
||||||
|
require.Equal(t, attesterCount, len(indices))
|
||||||
|
|
||||||
|
// Create a PayloadAttestation with AggregationBits set true at the indices.
|
||||||
|
aggregationBits := bitfield.NewBitvector512()
|
||||||
|
for _, index := range indices {
|
||||||
|
aggregationBits.SetBitAt(uint64(index), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadAttestation := ð.PayloadAttestation{
|
||||||
|
AggregationBits: aggregationBits,
|
||||||
|
Data: ð.PayloadAttestationData{
|
||||||
|
BeaconBlockRoot: make([]byte, 32),
|
||||||
|
},
|
||||||
|
Signature: make([]byte, 96),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get attesting indices.
|
||||||
|
attesters, err := helpers.GetPayloadAttestingIndices(context.Background(), state, state.Slot(), payloadAttestation)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, len(indices), len(attesters))
|
||||||
|
|
||||||
|
// Check if each attester equals to the PTC member at the corresponding index.
|
||||||
|
for i, index := range indices {
|
||||||
|
require.Equal(t, attesters[i], ptc[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetIndexedPayloadAttestation(t *testing.T) {
|
||||||
|
helpers.ClearCache()
|
||||||
|
|
||||||
|
// Create 10 committees. Total 40960 validators.
|
||||||
|
committeeCount := uint64(10)
|
||||||
|
validatorCount := committeeCount * params.BeaconConfig().TargetCommitteeSize * uint64(params.BeaconConfig().SlotsPerEpoch)
|
||||||
|
validators := make([]*ethpb.Validator, validatorCount)
|
||||||
|
|
||||||
|
for i := 0; i < len(validators); i++ {
|
||||||
|
publicKey := make([]byte, 48)
|
||||||
|
copy(publicKey, strconv.Itoa(i))
|
||||||
|
validators[i] = ðpb.Validator{
|
||||||
|
PublicKey: publicKey,
|
||||||
|
WithdrawalCredentials: make([]byte, 32),
|
||||||
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a beacon state.
|
||||||
|
state, err := state_native.InitializeFromProtoEpbs(ðpb.BeaconStateEPBS{
|
||||||
|
Validators: validators,
|
||||||
|
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Get PTC.
|
||||||
|
ptc, err := helpers.GetPayloadTimelinessCommittee(context.Background(), state, state.Slot())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, fieldparams.PTCSize, len(ptc))
|
||||||
|
|
||||||
|
// Generate random indices. PTC members at the corresponding indices are considered attested.
|
||||||
|
randGen := rand.NewDeterministicGenerator()
|
||||||
|
attesterCount := randGen.Intn(fieldparams.PTCSize) + 1
|
||||||
|
indices := randGen.Perm(fieldparams.PTCSize)[:attesterCount]
|
||||||
|
slices.Sort(indices)
|
||||||
|
require.Equal(t, attesterCount, len(indices))
|
||||||
|
|
||||||
|
// Create a PayloadAttestation with AggregationBits set true at the indices.
|
||||||
|
aggregationBits := bitfield.NewBitvector512()
|
||||||
|
for _, index := range indices {
|
||||||
|
aggregationBits.SetBitAt(uint64(index), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadAttestation := ð.PayloadAttestation{
|
||||||
|
AggregationBits: aggregationBits,
|
||||||
|
Data: ð.PayloadAttestationData{
|
||||||
|
BeaconBlockRoot: make([]byte, 32),
|
||||||
|
},
|
||||||
|
Signature: make([]byte, 96),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get attesting indices.
|
||||||
|
ctx := context.Background()
|
||||||
|
attesters, err := helpers.GetPayloadAttestingIndices(ctx, state, state.Slot(), payloadAttestation)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, len(indices), len(attesters))
|
||||||
|
|
||||||
|
// Get an IndexedPayloadAttestation.
|
||||||
|
indexedPayloadAttestation, err := helpers.GetIndexedPayloadAttestation(ctx, state, state.Slot(), payloadAttestation)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, len(indices), len(indexedPayloadAttestation.AttestingIndices))
|
||||||
|
require.DeepEqual(t, payloadAttestation.Data, indexedPayloadAttestation.Data)
|
||||||
|
require.DeepEqual(t, payloadAttestation.Signature, indexedPayloadAttestation.Signature)
|
||||||
|
|
||||||
|
// Check if the attesting indices are the same.
|
||||||
|
slices.Sort(attesters) // GetIndexedPayloadAttestation sorts attesting indices.
|
||||||
|
require.DeepEqual(t, attesters, indexedPayloadAttestation.AttestingIndices)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsValidIndexedPayloadAttestation(t *testing.T) {
|
||||||
|
helpers.ClearCache()
|
||||||
|
|
||||||
|
// Create validators.
|
||||||
|
validatorCount := uint64(350)
|
||||||
|
validators := make([]*ethpb.Validator, validatorCount)
|
||||||
|
_, secretKeys, err := util.DeterministicDepositsAndKeys(validatorCount)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for i := 0; i < len(validators); i++ {
|
||||||
|
validators[i] = ðpb.Validator{
|
||||||
|
PublicKey: secretKeys[i].PublicKey().Marshal(),
|
||||||
|
WithdrawalCredentials: make([]byte, 32),
|
||||||
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a beacon state.
|
||||||
|
state, err := state_native.InitializeFromProtoEpbs(ðpb.BeaconStateEPBS{
|
||||||
|
Validators: validators,
|
||||||
|
Fork: ðpb.Fork{
|
||||||
|
Epoch: 0,
|
||||||
|
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
|
||||||
|
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
|
||||||
|
},
|
||||||
|
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Define test cases.
|
||||||
|
tests := []struct {
|
||||||
|
attestation *epbs.IndexedPayloadAttestation
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
attestation: &epbs.IndexedPayloadAttestation{
|
||||||
|
AttestingIndices: []primitives.ValidatorIndex{1},
|
||||||
|
Data: ð.PayloadAttestationData{
|
||||||
|
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
|
||||||
|
},
|
||||||
|
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attestation: &epbs.IndexedPayloadAttestation{
|
||||||
|
AttestingIndices: []primitives.ValidatorIndex{13, 19},
|
||||||
|
Data: ð.PayloadAttestationData{
|
||||||
|
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
|
||||||
|
},
|
||||||
|
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attestation: &epbs.IndexedPayloadAttestation{
|
||||||
|
AttestingIndices: []primitives.ValidatorIndex{123, 234, 345},
|
||||||
|
Data: ð.PayloadAttestationData{
|
||||||
|
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
|
||||||
|
},
|
||||||
|
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attestation: &epbs.IndexedPayloadAttestation{
|
||||||
|
AttestingIndices: []primitives.ValidatorIndex{38, 46, 54, 62, 70, 78, 86, 194},
|
||||||
|
Data: ð.PayloadAttestationData{
|
||||||
|
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
|
||||||
|
},
|
||||||
|
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attestation: &epbs.IndexedPayloadAttestation{
|
||||||
|
AttestingIndices: []primitives.ValidatorIndex{5},
|
||||||
|
Data: ð.PayloadAttestationData{
|
||||||
|
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
|
||||||
|
},
|
||||||
|
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run test cases.
|
||||||
|
for _, test := range tests {
|
||||||
|
signatures := make([]bls.Signature, len(test.attestation.AttestingIndices))
|
||||||
|
for i, index := range test.attestation.AttestingIndices {
|
||||||
|
signedBytes, err := signing.ComputeDomainAndSign(
|
||||||
|
state,
|
||||||
|
slots.ToEpoch(test.attestation.Data.Slot),
|
||||||
|
test.attestation.Data,
|
||||||
|
params.BeaconConfig().DomainPTCAttester,
|
||||||
|
secretKeys[index],
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
signature, err := bls.SignatureFromBytes(signedBytes)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
signatures[i] = signature
|
||||||
|
}
|
||||||
|
|
||||||
|
aggregatedSignature := bls.AggregateSignatures(signatures)
|
||||||
|
test.attestation.Signature = aggregatedSignature.Marshal()
|
||||||
|
|
||||||
|
isValid, err := helpers.IsValidIndexedPayloadAttestation(state, test.attestation)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, true, isValid)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,10 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
|||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = ["slot_epoch.go"],
|
srcs = [
|
||||||
|
"slot_epoch.go",
|
||||||
|
"slot_epoch_epbs.go",
|
||||||
|
],
|
||||||
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time",
|
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
|
|||||||
15
beacon-chain/core/time/slot_epoch_epbs.go
Normal file
15
beacon-chain/core/time/slot_epoch_epbs.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package time
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CanUpgradeToEip7732 returns true if the input `slot` can upgrade to EIP-7732(epbs).
|
||||||
|
// Spec code:
|
||||||
|
// If state.slot % SLOTS_PER_EPOCH == 0 and compute_epoch_at_slot(state.slot) == EIP7732_FORK_EPOCH
|
||||||
|
func CanUpgradeToEip7732(slot primitives.Slot) bool {
|
||||||
|
epochStart := slots.IsEpochStart(slot)
|
||||||
|
return epochStart && slots.ToEpoch(slot) == params.BeaconConfig().EPBSForkEpoch
|
||||||
|
}
|
||||||
@@ -233,6 +233,11 @@ func TestCanUpgradeTo(t *testing.T) {
|
|||||||
forkEpoch: &beaconConfig.FuluForkEpoch,
|
forkEpoch: &beaconConfig.FuluForkEpoch,
|
||||||
upgradeFunc: time.CanUpgradeToFulu,
|
upgradeFunc: time.CanUpgradeToFulu,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Eip7732",
|
||||||
|
forkEpoch: &beaconConfig.EPBSForkEpoch,
|
||||||
|
upgradeFunc: time.CanUpgradeToEip7732,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, otc := range outerTestCases {
|
for _, otc := range outerTestCases {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ go_library(
|
|||||||
"state-bellatrix.go",
|
"state-bellatrix.go",
|
||||||
"trailing_slot_state_cache.go",
|
"trailing_slot_state_cache.go",
|
||||||
"transition.go",
|
"transition.go",
|
||||||
|
"transition_epbs.go",
|
||||||
"transition_no_verify_sig.go",
|
"transition_no_verify_sig.go",
|
||||||
],
|
],
|
||||||
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition",
|
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition",
|
||||||
@@ -20,6 +21,7 @@ go_library(
|
|||||||
"//beacon-chain/core/capella:go_default_library",
|
"//beacon-chain/core/capella:go_default_library",
|
||||||
"//beacon-chain/core/deneb:go_default_library",
|
"//beacon-chain/core/deneb:go_default_library",
|
||||||
"//beacon-chain/core/electra:go_default_library",
|
"//beacon-chain/core/electra:go_default_library",
|
||||||
|
"//beacon-chain/core/epbs:go_default_library",
|
||||||
"//beacon-chain/core/epoch:go_default_library",
|
"//beacon-chain/core/epoch:go_default_library",
|
||||||
"//beacon-chain/core/epoch/precompute:go_default_library",
|
"//beacon-chain/core/epoch/precompute:go_default_library",
|
||||||
"//beacon-chain/core/execution:go_default_library",
|
"//beacon-chain/core/execution:go_default_library",
|
||||||
@@ -46,6 +48,7 @@ go_library(
|
|||||||
"//proto/engine/v1:go_default_library",
|
"//proto/engine/v1:go_default_library",
|
||||||
"//proto/prysm/v1alpha1:go_default_library",
|
"//proto/prysm/v1alpha1:go_default_library",
|
||||||
"//runtime/version:go_default_library",
|
"//runtime/version:go_default_library",
|
||||||
|
"//time/slots:go_default_library",
|
||||||
"@com_github_pkg_errors//:go_default_library",
|
"@com_github_pkg_errors//:go_default_library",
|
||||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/capella"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/capella"
|
||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/deneb"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/deneb"
|
||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/electra"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/electra"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epbs"
|
||||||
e "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epoch"
|
e "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epoch"
|
||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epoch/precompute"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epoch/precompute"
|
||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/execution"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/execution"
|
||||||
@@ -381,6 +382,15 @@ func UpgradeState(ctx context.Context, state state.BeaconState) (state.BeaconSta
|
|||||||
upgraded = true
|
upgraded = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if time.CanUpgradeToEip7732(slot) {
|
||||||
|
state, err = epbs.UpgradeToEIP7732(state)
|
||||||
|
if err != nil {
|
||||||
|
tracing.AnnotateError(span, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
upgraded = true
|
||||||
|
}
|
||||||
|
|
||||||
if upgraded {
|
if upgraded {
|
||||||
log.WithField("version", version.String(state.Version())).Info("Upgraded state to")
|
log.WithField("version", version.String(state.Version())).Info("Upgraded state to")
|
||||||
}
|
}
|
||||||
|
|||||||
111
beacon-chain/core/transition/transition_epbs.go
Normal file
111
beacon-chain/core/transition/transition_epbs.go
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
package transition
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epbs"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
|
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||||
|
)
|
||||||
|
|
||||||
|
func processExecution(state state.BeaconState, body interfaces.ReadOnlyBeaconBlockBody) (err error) {
|
||||||
|
if body.Version() >= version.EPBS {
|
||||||
|
state, err = blocks.ProcessWithdrawals(state, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not process withdrawals")
|
||||||
|
}
|
||||||
|
return processExecutionPayloadHeader(state, body)
|
||||||
|
}
|
||||||
|
enabled, err := blocks.IsExecutionEnabled(state, body)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not check if execution is enabled")
|
||||||
|
}
|
||||||
|
if !enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
executionData, err := body.Execution()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if state.Version() >= version.Capella {
|
||||||
|
state, err = blocks.ProcessWithdrawals(state, executionData)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not process withdrawals")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := blocks.ProcessPayload(state, body); err != nil {
|
||||||
|
return errors.Wrap(err, "could not process execution data")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function verifies the signature as it is not necessarily signed by the
|
||||||
|
// proposer
|
||||||
|
func processExecutionPayloadHeader(state state.BeaconState, body interfaces.ReadOnlyBeaconBlockBody) (err error) {
|
||||||
|
sh, err := body.SignedExecutionPayloadHeader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
header, err := sh.Header()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := epbs.ValidatePayloadHeaderSignature(state, sh); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
builderIndex := header.BuilderIndex()
|
||||||
|
builder, err := state.ValidatorAtIndex(builderIndex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
epoch := slots.ToEpoch(state.Slot())
|
||||||
|
if builder.ActivationEpoch > epoch || epoch >= builder.ExitEpoch {
|
||||||
|
return errors.New("builder is not active")
|
||||||
|
}
|
||||||
|
if builder.Slashed {
|
||||||
|
return errors.New("builder is slashed")
|
||||||
|
}
|
||||||
|
amount := header.Value()
|
||||||
|
builderBalance, err := state.BalanceAtIndex(builderIndex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if amount > primitives.Gwei(builderBalance) {
|
||||||
|
return errors.New("builder has insufficient balance")
|
||||||
|
}
|
||||||
|
// sate.Slot == block.Slot because of process_slot
|
||||||
|
if header.Slot() != state.Slot() {
|
||||||
|
return errors.New("incorrect header slot")
|
||||||
|
}
|
||||||
|
// the state latest block header has the parent root because of
|
||||||
|
// process_block_header
|
||||||
|
blockHeader := state.LatestBlockHeader()
|
||||||
|
if header.ParentBlockRoot() != [32]byte(blockHeader.ParentRoot) {
|
||||||
|
return errors.New("incorrect parent block root")
|
||||||
|
}
|
||||||
|
lbh, err := state.LatestBlockHash()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if header.ParentBlockHash() != [32]byte(lbh) {
|
||||||
|
return fmt.Errorf("incorrect parent block hash, got: %x, wanted: %x", header.ParentBlockHash(), lbh)
|
||||||
|
}
|
||||||
|
if err := state.UpdateBalancesAtIndex(builderIndex, builderBalance-uint64(amount)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := helpers.IncreaseBalance(state, blockHeader.ProposerIndex, uint64(amount)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
headerEPBS, ok := header.Proto().(*enginev1.ExecutionPayloadHeaderEPBS)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("not an ePBS execution payload header")
|
||||||
|
}
|
||||||
|
return state.SetLatestExecutionPayloadHeaderEPBS(headerEPBS)
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
|
||||||
b "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
|
b "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
|
||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/electra"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epbs"
|
||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition/interop"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition/interop"
|
||||||
v "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators"
|
v "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators"
|
||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||||
@@ -132,7 +132,26 @@ func CalculateStateRoot(
|
|||||||
|
|
||||||
// Execute per slots transition.
|
// Execute per slots transition.
|
||||||
var err error
|
var err error
|
||||||
parentRoot := signed.Block().ParentRoot()
|
var parentRoot [32]byte
|
||||||
|
if state.Version() >= version.EPBS {
|
||||||
|
signedHeader, err := signed.Block().Body().SignedExecutionPayloadHeader()
|
||||||
|
if err != nil {
|
||||||
|
return [32]byte{}, errors.Wrap(err, "could not retrieve signed execution payload header")
|
||||||
|
}
|
||||||
|
header, err := signedHeader.Header()
|
||||||
|
if err != nil {
|
||||||
|
return [32]byte{}, errors.Wrap(err, "could not retrieve execution payload header")
|
||||||
|
}
|
||||||
|
lbh, err := state.LatestBlockHash()
|
||||||
|
if err != nil {
|
||||||
|
return [32]byte{}, errors.Wrap(err, "could not get state latest block hash")
|
||||||
|
}
|
||||||
|
if header.ParentBlockHash() == [32]byte(lbh) {
|
||||||
|
parentRoot = [32]byte(lbh)
|
||||||
|
} else {
|
||||||
|
parentRoot = signed.Block().ParentRoot()
|
||||||
|
}
|
||||||
|
}
|
||||||
state, err = ProcessSlotsUsingNextSlotCache(ctx, state, parentRoot[:], signed.Block().Slot())
|
state, err = ProcessSlotsUsingNextSlotCache(ctx, state, parentRoot[:], signed.Block().Slot())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return [32]byte{}, errors.Wrap(err, "could not process slots")
|
return [32]byte{}, errors.Wrap(err, "could not process slots")
|
||||||
@@ -272,7 +291,7 @@ func ProcessOperationsNoVerifyAttsSigs(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state, err = electra.ProcessOperations(ctx, state, beaconBlock)
|
state, err = epbs.ProcessOperations(ctx, state, beaconBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -318,26 +337,9 @@ func ProcessBlockForStateRoot(
|
|||||||
return nil, errors.Wrap(err, "could not process block header")
|
return nil, errors.Wrap(err, "could not process block header")
|
||||||
}
|
}
|
||||||
|
|
||||||
enabled, err := b.IsExecutionEnabled(state, blk.Body())
|
if err := processExecution(state, blk.Body()); err != nil {
|
||||||
if err != nil {
|
return nil, errors.Wrap(err, "could not process execution")
|
||||||
return nil, errors.Wrap(err, "could not check if execution is enabled")
|
|
||||||
}
|
}
|
||||||
if enabled {
|
|
||||||
executionData, err := blk.Body().Execution()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if state.Version() >= version.Capella {
|
|
||||||
state, err = b.ProcessWithdrawals(state, executionData)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "could not process withdrawals")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err = b.ProcessPayload(state, blk.Body()); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "could not process execution data")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
randaoReveal := signed.Block().Body().RandaoReveal()
|
randaoReveal := signed.Block().Body().RandaoReveal()
|
||||||
state, err = b.ProcessRandaoNoVerify(state, randaoReveal[:])
|
state, err = b.ProcessRandaoNoVerify(state, randaoReveal[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ go_library(
|
|||||||
"//consensus-types/primitives:go_default_library",
|
"//consensus-types/primitives:go_default_library",
|
||||||
"//monitoring/backup:go_default_library",
|
"//monitoring/backup:go_default_library",
|
||||||
"//proto/dbval:go_default_library",
|
"//proto/dbval:go_default_library",
|
||||||
|
"//proto/engine/v1:go_default_library",
|
||||||
"//proto/prysm/v1alpha1:go_default_library",
|
"//proto/prysm/v1alpha1:go_default_library",
|
||||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
"github.com/prysmaticlabs/prysm/v5/monitoring/backup"
|
"github.com/prysmaticlabs/prysm/v5/monitoring/backup"
|
||||||
"github.com/prysmaticlabs/prysm/v5/proto/dbval"
|
"github.com/prysmaticlabs/prysm/v5/proto/dbval"
|
||||||
|
engine "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,11 +24,13 @@ import (
|
|||||||
type ReadOnlyDatabase interface {
|
type ReadOnlyDatabase interface {
|
||||||
// Block related methods.
|
// Block related methods.
|
||||||
Block(ctx context.Context, blockRoot [32]byte) (interfaces.ReadOnlySignedBeaconBlock, error)
|
Block(ctx context.Context, blockRoot [32]byte) (interfaces.ReadOnlySignedBeaconBlock, error)
|
||||||
|
SignedExecutionPayloadHeader(ctx context.Context, blockRoot [32]byte) (interfaces.ROSignedExecutionPayloadHeader, error)
|
||||||
Blocks(ctx context.Context, f *filters.QueryFilter) ([]interfaces.ReadOnlySignedBeaconBlock, [][32]byte, error)
|
Blocks(ctx context.Context, f *filters.QueryFilter) ([]interfaces.ReadOnlySignedBeaconBlock, [][32]byte, error)
|
||||||
BlockRoots(ctx context.Context, f *filters.QueryFilter) ([][32]byte, error)
|
BlockRoots(ctx context.Context, f *filters.QueryFilter) ([][32]byte, error)
|
||||||
BlocksBySlot(ctx context.Context, slot primitives.Slot) ([]interfaces.ReadOnlySignedBeaconBlock, error)
|
BlocksBySlot(ctx context.Context, slot primitives.Slot) ([]interfaces.ReadOnlySignedBeaconBlock, error)
|
||||||
BlockRootsBySlot(ctx context.Context, slot primitives.Slot) (bool, [][32]byte, error)
|
BlockRootsBySlot(ctx context.Context, slot primitives.Slot) (bool, [][32]byte, error)
|
||||||
HasBlock(ctx context.Context, blockRoot [32]byte) bool
|
HasBlock(ctx context.Context, blockRoot [32]byte) bool
|
||||||
|
HasBlindPayloadEnvelope(ctx context.Context, hash [32]byte) bool
|
||||||
GenesisBlock(ctx context.Context) (interfaces.ReadOnlySignedBeaconBlock, error)
|
GenesisBlock(ctx context.Context) (interfaces.ReadOnlySignedBeaconBlock, error)
|
||||||
GenesisBlockRoot(ctx context.Context) ([32]byte, error)
|
GenesisBlockRoot(ctx context.Context) ([32]byte, error)
|
||||||
IsFinalizedBlock(ctx context.Context, blockRoot [32]byte) bool
|
IsFinalizedBlock(ctx context.Context, blockRoot [32]byte) bool
|
||||||
@@ -53,6 +56,7 @@ type ReadOnlyDatabase interface {
|
|||||||
DepositContractAddress(ctx context.Context) ([]byte, error)
|
DepositContractAddress(ctx context.Context) ([]byte, error)
|
||||||
// ExecutionChainData operations.
|
// ExecutionChainData operations.
|
||||||
ExecutionChainData(ctx context.Context) (*ethpb.ETH1ChainData, error)
|
ExecutionChainData(ctx context.Context) (*ethpb.ETH1ChainData, error)
|
||||||
|
SignedBlindPayloadEnvelope(ctx context.Context, blockHash []byte) (*engine.SignedBlindPayloadEnvelope, error)
|
||||||
// Fee recipients operations.
|
// Fee recipients operations.
|
||||||
FeeRecipientByValidatorID(ctx context.Context, id primitives.ValidatorIndex) (common.Address, error)
|
FeeRecipientByValidatorID(ctx context.Context, id primitives.ValidatorIndex) (common.Address, error)
|
||||||
RegistrationByValidatorID(ctx context.Context, id primitives.ValidatorIndex) (*ethpb.ValidatorRegistrationV1, error)
|
RegistrationByValidatorID(ctx context.Context, id primitives.ValidatorIndex) (*ethpb.ValidatorRegistrationV1, error)
|
||||||
@@ -91,6 +95,7 @@ type NoHeadAccessDatabase interface {
|
|||||||
SaveDepositContractAddress(ctx context.Context, addr common.Address) error
|
SaveDepositContractAddress(ctx context.Context, addr common.Address) error
|
||||||
// SaveExecutionChainData operations.
|
// SaveExecutionChainData operations.
|
||||||
SaveExecutionChainData(ctx context.Context, data *ethpb.ETH1ChainData) error
|
SaveExecutionChainData(ctx context.Context, data *ethpb.ETH1ChainData) error
|
||||||
|
SaveBlindPayloadEnvelope(ctx context.Context, envelope interfaces.ROSignedExecutionPayloadEnvelope) error
|
||||||
// Run any required database migrations.
|
// Run any required database migrations.
|
||||||
RunMigrations(ctx context.Context) error
|
RunMigrations(ctx context.Context) error
|
||||||
// Fee recipients operations.
|
// Fee recipients operations.
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ go_library(
|
|||||||
"archived_point.go",
|
"archived_point.go",
|
||||||
"backfill.go",
|
"backfill.go",
|
||||||
"backup.go",
|
"backup.go",
|
||||||
|
"blind_payload_envelope.go",
|
||||||
"blocks.go",
|
"blocks.go",
|
||||||
"checkpoint.go",
|
"checkpoint.go",
|
||||||
"deposit_contract.go",
|
"deposit_contract.go",
|
||||||
"encoding.go",
|
"encoding.go",
|
||||||
|
"epbs.go",
|
||||||
"error.go",
|
"error.go",
|
||||||
"execution_chain.go",
|
"execution_chain.go",
|
||||||
"finalized_block_roots.go",
|
"finalized_block_roots.go",
|
||||||
@@ -55,6 +57,7 @@ go_library(
|
|||||||
"//monitoring/tracing:go_default_library",
|
"//monitoring/tracing:go_default_library",
|
||||||
"//monitoring/tracing/trace:go_default_library",
|
"//monitoring/tracing/trace:go_default_library",
|
||||||
"//proto/dbval:go_default_library",
|
"//proto/dbval:go_default_library",
|
||||||
|
"//proto/engine/v1:go_default_library",
|
||||||
"//proto/prysm/v1alpha1:go_default_library",
|
"//proto/prysm/v1alpha1:go_default_library",
|
||||||
"//runtime/version:go_default_library",
|
"//runtime/version:go_default_library",
|
||||||
"//time:go_default_library",
|
"//time:go_default_library",
|
||||||
@@ -71,6 +74,7 @@ go_library(
|
|||||||
"@com_github_schollz_progressbar_v3//:go_default_library",
|
"@com_github_schollz_progressbar_v3//:go_default_library",
|
||||||
"@com_github_sirupsen_logrus//:go_default_library",
|
"@com_github_sirupsen_logrus//:go_default_library",
|
||||||
"@io_etcd_go_bbolt//:go_default_library",
|
"@io_etcd_go_bbolt//:go_default_library",
|
||||||
|
"@io_opencensus_go//trace:go_default_library",
|
||||||
"@org_golang_google_protobuf//proto:go_default_library",
|
"@org_golang_google_protobuf//proto:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -81,10 +85,12 @@ go_test(
|
|||||||
"archived_point_test.go",
|
"archived_point_test.go",
|
||||||
"backfill_test.go",
|
"backfill_test.go",
|
||||||
"backup_test.go",
|
"backup_test.go",
|
||||||
|
"blind_payload_envelope_test.go",
|
||||||
"blocks_test.go",
|
"blocks_test.go",
|
||||||
"checkpoint_test.go",
|
"checkpoint_test.go",
|
||||||
"deposit_contract_test.go",
|
"deposit_contract_test.go",
|
||||||
"encoding_test.go",
|
"encoding_test.go",
|
||||||
|
"epbs_test.go",
|
||||||
"execution_chain_test.go",
|
"execution_chain_test.go",
|
||||||
"finalized_block_roots_test.go",
|
"finalized_block_roots_test.go",
|
||||||
"genesis_test.go",
|
"genesis_test.go",
|
||||||
@@ -124,6 +130,7 @@ go_test(
|
|||||||
"//testing/assert:go_default_library",
|
"//testing/assert:go_default_library",
|
||||||
"//testing/require:go_default_library",
|
"//testing/require:go_default_library",
|
||||||
"//testing/util:go_default_library",
|
"//testing/util:go_default_library",
|
||||||
|
"//testing/util/random:go_default_library",
|
||||||
"//time/slots:go_default_library",
|
"//time/slots:go_default_library",
|
||||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||||
"@com_github_golang_snappy//:go_default_library",
|
"@com_github_golang_snappy//:go_default_library",
|
||||||
|
|||||||
76
beacon-chain/db/kv/blind_payload_envelope.go
Normal file
76
beacon-chain/db/kv/blind_payload_envelope.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package kv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||||
|
engine "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||||
|
bolt "go.etcd.io/bbolt"
|
||||||
|
"go.opencensus.io/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SaveBlindPayloadEnvelope saves a signed execution payload envelope blind in the database.
|
||||||
|
func (s *Store) SaveBlindPayloadEnvelope(ctx context.Context, signed interfaces.ROSignedExecutionPayloadEnvelope) error {
|
||||||
|
ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveBlindPayloadEnvelope")
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
pb := signed.Proto()
|
||||||
|
if pb == nil {
|
||||||
|
return errors.New("nil payload envelope")
|
||||||
|
}
|
||||||
|
env, ok := pb.(*engine.SignedExecutionPayloadEnvelope)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid payload envelope")
|
||||||
|
}
|
||||||
|
|
||||||
|
r := env.Message.Payload.BlockHash
|
||||||
|
blind := env.Blind()
|
||||||
|
if blind == nil {
|
||||||
|
return errors.New("nil blind payload envelope")
|
||||||
|
}
|
||||||
|
enc, err := encode(ctx, blind)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = s.db.Update(func(tx *bolt.Tx) error {
|
||||||
|
bucket := tx.Bucket(executionPayloadEnvelopeBucket)
|
||||||
|
return bucket.Put(r, enc)
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignedBlindPayloadEnvelope retrieves a signed execution payload envelope blind from the database.
|
||||||
|
func (s *Store) SignedBlindPayloadEnvelope(ctx context.Context, blockHash []byte) (*engine.SignedBlindPayloadEnvelope, error) {
|
||||||
|
ctx, span := trace.StartSpan(ctx, "BeaconDB.SignedBlindPayloadEnvelope")
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
env := &engine.SignedBlindPayloadEnvelope{}
|
||||||
|
err := s.db.View(func(tx *bolt.Tx) error {
|
||||||
|
bkt := tx.Bucket(executionPayloadEnvelopeBucket)
|
||||||
|
enc := bkt.Get(blockHash)
|
||||||
|
if enc == nil {
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
return decode(ctx, enc, env)
|
||||||
|
})
|
||||||
|
return env, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) HasBlindPayloadEnvelope(ctx context.Context, hash [32]byte) bool {
|
||||||
|
_, span := trace.StartSpan(ctx, "BeaconDB.HasBlock")
|
||||||
|
defer span.End()
|
||||||
|
if v, ok := s.blockCache.Get(string(hash[:])); v != nil && ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
exists := false
|
||||||
|
if err := s.db.View(func(tx *bolt.Tx) error {
|
||||||
|
bkt := tx.Bucket(executionPayloadEnvelopeBucket)
|
||||||
|
exists = bkt.Get(hash[:]) != nil
|
||||||
|
return nil
|
||||||
|
}); err != nil { // This view never returns an error, but we'll handle anyway for sanity.
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return exists
|
||||||
|
}
|
||||||
26
beacon-chain/db/kv/blind_payload_envelope_test.go
Normal file
26
beacon-chain/db/kv/blind_payload_envelope_test.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package kv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/util/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStore_SignedBlindPayloadEnvelope(t *testing.T) {
|
||||||
|
db := setupDB(t)
|
||||||
|
ctx := context.Background()
|
||||||
|
_, err := db.SignedBlindPayloadEnvelope(ctx, []byte("test"))
|
||||||
|
require.ErrorIs(t, err, ErrNotFound)
|
||||||
|
|
||||||
|
env := random.SignedExecutionPayloadEnvelope(t)
|
||||||
|
e, err := blocks.WrappedROSignedExecutionPayloadEnvelope(env)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = db.SaveBlindPayloadEnvelope(ctx, e)
|
||||||
|
require.NoError(t, err)
|
||||||
|
got, err := db.SignedBlindPayloadEnvelope(ctx, env.Message.Payload.BlockHash)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.DeepEqual(t, got, env.Blind())
|
||||||
|
}
|
||||||
@@ -916,6 +916,11 @@ func unmarshalBlock(_ context.Context, enc []byte) (interfaces.ReadOnlySignedBea
|
|||||||
if err := rawBlock.UnmarshalSSZ(enc[len(fuluBlindKey):]); err != nil {
|
if err := rawBlock.UnmarshalSSZ(enc[len(fuluBlindKey):]); err != nil {
|
||||||
return nil, errors.Wrap(err, "could not unmarshal blinded Fulu block")
|
return nil, errors.Wrap(err, "could not unmarshal blinded Fulu block")
|
||||||
}
|
}
|
||||||
|
case hasEpbsKey(enc):
|
||||||
|
rawBlock = ðpb.SignedBeaconBlockEpbs{}
|
||||||
|
if err := rawBlock.UnmarshalSSZ(enc[len(epbsKey):]); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "could not unmarshal EPBS block")
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
// Marshal block bytes to phase 0 beacon block.
|
// Marshal block bytes to phase 0 beacon block.
|
||||||
rawBlock = ðpb.SignedBeaconBlock{}
|
rawBlock = ðpb.SignedBeaconBlock{}
|
||||||
@@ -946,6 +951,10 @@ func encodeBlock(blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) {
|
|||||||
func keyForBlock(blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) {
|
func keyForBlock(blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) {
|
||||||
v := blk.Version()
|
v := blk.Version()
|
||||||
|
|
||||||
|
if v >= version.EPBS {
|
||||||
|
return epbsKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
if v >= version.Fulu {
|
if v >= version.Fulu {
|
||||||
if blk.IsBlinded() {
|
if blk.IsBlinded() {
|
||||||
return fuluBlindKey, nil
|
return fuluBlindKey, nil
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/prysmaticlabs/prysm/v5/testing/assert"
|
"github.com/prysmaticlabs/prysm/v5/testing/assert"
|
||||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||||
"github.com/prysmaticlabs/prysm/v5/testing/util"
|
"github.com/prysmaticlabs/prysm/v5/testing/util"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/util/random"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -150,6 +151,17 @@ var blockTests = []struct {
|
|||||||
}
|
}
|
||||||
return blocks.NewSignedBeaconBlock(b)
|
return blocks.NewSignedBeaconBlock(b)
|
||||||
}},
|
}},
|
||||||
|
{
|
||||||
|
name: "epbs",
|
||||||
|
newBlock: func(slot primitives.Slot, root []byte) (interfaces.ReadOnlySignedBeaconBlock, error) {
|
||||||
|
b := random.SignedBeaconBlock(&testing.T{})
|
||||||
|
b.Block.Slot = slot
|
||||||
|
if root != nil {
|
||||||
|
b.Block.ParentRoot = root
|
||||||
|
}
|
||||||
|
return blocks.NewSignedBeaconBlock(b)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStore_SaveBlock_NoDuplicates(t *testing.T) {
|
func TestStore_SaveBlock_NoDuplicates(t *testing.T) {
|
||||||
@@ -206,7 +218,7 @@ func TestStore_BlocksCRUD(t *testing.T) {
|
|||||||
retrievedBlock, err = db.Block(ctx, blockRoot)
|
retrievedBlock, err = db.Block(ctx, blockRoot)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
wanted := retrievedBlock
|
wanted := retrievedBlock
|
||||||
if retrievedBlock.Version() >= version.Bellatrix {
|
if retrievedBlock.Version() >= version.Bellatrix && retrievedBlock.Version() < version.EPBS {
|
||||||
wanted, err = retrievedBlock.ToBlinded()
|
wanted, err = retrievedBlock.ToBlinded()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
@@ -577,7 +589,7 @@ func TestStore_BlocksCRUD_NoCache(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
wanted := blk
|
wanted := blk
|
||||||
if blk.Version() >= version.Bellatrix {
|
if blk.Version() >= version.Bellatrix && blk.Version() < version.EPBS {
|
||||||
wanted, err = blk.ToBlinded()
|
wanted, err = blk.ToBlinded()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
@@ -796,7 +808,7 @@ func TestStore_SaveBlock_CanGetHighestAt(t *testing.T) {
|
|||||||
b, err := db.Block(ctx, root)
|
b, err := db.Block(ctx, root)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
wanted := block1
|
wanted := block1
|
||||||
if block1.Version() >= version.Bellatrix {
|
if block1.Version() >= version.Bellatrix && block1.Version() < version.EPBS {
|
||||||
wanted, err = wanted.ToBlinded()
|
wanted, err = wanted.ToBlinded()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
@@ -814,7 +826,7 @@ func TestStore_SaveBlock_CanGetHighestAt(t *testing.T) {
|
|||||||
b, err = db.Block(ctx, root)
|
b, err = db.Block(ctx, root)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
wanted2 := block2
|
wanted2 := block2
|
||||||
if block2.Version() >= version.Bellatrix {
|
if block2.Version() >= version.Bellatrix && block2.Version() < version.EPBS {
|
||||||
wanted2, err = block2.ToBlinded()
|
wanted2, err = block2.ToBlinded()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
@@ -832,7 +844,7 @@ func TestStore_SaveBlock_CanGetHighestAt(t *testing.T) {
|
|||||||
b, err = db.Block(ctx, root)
|
b, err = db.Block(ctx, root)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
wanted = block3
|
wanted = block3
|
||||||
if block3.Version() >= version.Bellatrix {
|
if block3.Version() >= version.Bellatrix && block3.Version() < version.EPBS {
|
||||||
wanted, err = wanted.ToBlinded()
|
wanted, err = wanted.ToBlinded()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
@@ -868,7 +880,7 @@ func TestStore_GenesisBlock_CanGetHighestAt(t *testing.T) {
|
|||||||
b, err := db.Block(ctx, root)
|
b, err := db.Block(ctx, root)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
wanted := block1
|
wanted := block1
|
||||||
if block1.Version() >= version.Bellatrix {
|
if block1.Version() >= version.Bellatrix && block1.Version() < version.EPBS {
|
||||||
wanted, err = block1.ToBlinded()
|
wanted, err = block1.ToBlinded()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
@@ -885,7 +897,7 @@ func TestStore_GenesisBlock_CanGetHighestAt(t *testing.T) {
|
|||||||
b, err = db.Block(ctx, root)
|
b, err = db.Block(ctx, root)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
wanted = genesisBlock
|
wanted = genesisBlock
|
||||||
if genesisBlock.Version() >= version.Bellatrix {
|
if genesisBlock.Version() >= version.Bellatrix && genesisBlock.Version() < version.EPBS {
|
||||||
wanted, err = genesisBlock.ToBlinded()
|
wanted, err = genesisBlock.ToBlinded()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
@@ -902,7 +914,7 @@ func TestStore_GenesisBlock_CanGetHighestAt(t *testing.T) {
|
|||||||
b, err = db.Block(ctx, root)
|
b, err = db.Block(ctx, root)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
wanted = genesisBlock
|
wanted = genesisBlock
|
||||||
if genesisBlock.Version() >= version.Bellatrix {
|
if genesisBlock.Version() >= version.Bellatrix && genesisBlock.Version() < version.EPBS {
|
||||||
wanted, err = genesisBlock.ToBlinded()
|
wanted, err = genesisBlock.ToBlinded()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
@@ -998,7 +1010,7 @@ func TestStore_BlocksBySlot_BlockRootsBySlot(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
wanted := b1
|
wanted := b1
|
||||||
if b1.Version() >= version.Bellatrix {
|
if b1.Version() >= version.Bellatrix && b1.Version() < version.EPBS {
|
||||||
wanted, err = b1.ToBlinded()
|
wanted, err = b1.ToBlinded()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
@@ -1014,7 +1026,7 @@ func TestStore_BlocksBySlot_BlockRootsBySlot(t *testing.T) {
|
|||||||
t.Fatalf("Expected 2 blocks, received %d blocks", len(retrievedBlocks))
|
t.Fatalf("Expected 2 blocks, received %d blocks", len(retrievedBlocks))
|
||||||
}
|
}
|
||||||
wanted = b2
|
wanted = b2
|
||||||
if b2.Version() >= version.Bellatrix {
|
if b2.Version() >= version.Bellatrix && b2.Version() < version.EPBS {
|
||||||
wanted, err = b2.ToBlinded()
|
wanted, err = b2.ToBlinded()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
@@ -1024,7 +1036,7 @@ func TestStore_BlocksBySlot_BlockRootsBySlot(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, true, proto.Equal(wantedPb, retrieved0Pb), "Wanted: %v, received: %v", retrievedBlocks[0], wanted)
|
assert.Equal(t, true, proto.Equal(wantedPb, retrieved0Pb), "Wanted: %v, received: %v", retrievedBlocks[0], wanted)
|
||||||
wanted = b3
|
wanted = b3
|
||||||
if b3.Version() >= version.Bellatrix {
|
if b3.Version() >= version.Bellatrix && b3.Version() < version.EPBS {
|
||||||
wanted, err = b3.ToBlinded()
|
wanted, err = b3.ToBlinded()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/golang/snappy"
|
"github.com/golang/snappy"
|
||||||
fastssz "github.com/prysmaticlabs/fastssz"
|
fastssz "github.com/prysmaticlabs/fastssz"
|
||||||
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace"
|
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace"
|
||||||
|
engine "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
@@ -78,6 +79,8 @@ func isSSZStorageFormat(obj interface{}) bool {
|
|||||||
return true
|
return true
|
||||||
case *ethpb.VoluntaryExit:
|
case *ethpb.VoluntaryExit:
|
||||||
return true
|
return true
|
||||||
|
case *engine.SignedBlindPayloadEnvelope:
|
||||||
|
return true
|
||||||
case *ethpb.ValidatorRegistrationV1:
|
case *ethpb.ValidatorRegistrationV1:
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
|
|||||||
18
beacon-chain/db/kv/epbs.go
Normal file
18
beacon-chain/db/kv/epbs.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package kv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Store) SignedExecutionPayloadHeader(ctx context.Context, blockRoot [32]byte) (interfaces.ROSignedExecutionPayloadHeader, error) {
|
||||||
|
b, err := s.Block(ctx, blockRoot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if b.IsNil() {
|
||||||
|
return nil, ErrNotFound
|
||||||
|
}
|
||||||
|
return b.Block().Body().SignedExecutionPayloadHeader()
|
||||||
|
}
|
||||||
28
beacon-chain/db/kv/epbs_test.go
Normal file
28
beacon-chain/db/kv/epbs_test.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package kv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/util/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_SignedExecutionPayloadHeader(t *testing.T) {
|
||||||
|
db := setupDB(t)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
b := random.SignedBeaconBlock(t)
|
||||||
|
blk, err := blocks.NewSignedBeaconBlock(b)
|
||||||
|
require.NoError(t, err)
|
||||||
|
blockRoot, err := blk.Block().HashTreeRoot()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NoError(t, db.SaveBlock(ctx, blk))
|
||||||
|
retrievedHeader, err := db.SignedExecutionPayloadHeader(ctx, blockRoot)
|
||||||
|
require.NoError(t, err)
|
||||||
|
wantedHeader, err := blk.Block().Body().SignedExecutionPayloadHeader()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.DeepEqual(t, wantedHeader, retrievedHeader)
|
||||||
|
}
|
||||||
@@ -80,3 +80,10 @@ func hasFuluBlindKey(enc []byte) bool {
|
|||||||
}
|
}
|
||||||
return bytes.Equal(enc[:len(fuluBlindKey)], fuluBlindKey)
|
return bytes.Equal(enc[:len(fuluBlindKey)], fuluBlindKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hasEpbsKey(enc []byte) bool {
|
||||||
|
if len(epbsKey) >= len(enc) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return bytes.Equal(enc[:len(epbsKey)], epbsKey)
|
||||||
|
}
|
||||||
|
|||||||
@@ -121,6 +121,9 @@ var Buckets = [][]byte{
|
|||||||
|
|
||||||
feeRecipientBucket,
|
feeRecipientBucket,
|
||||||
registrationBucket,
|
registrationBucket,
|
||||||
|
|
||||||
|
// ePBS
|
||||||
|
executionPayloadEnvelopeBucket,
|
||||||
}
|
}
|
||||||
|
|
||||||
// KVStoreOption is a functional option that modifies a kv.Store.
|
// KVStoreOption is a functional option that modifies a kv.Store.
|
||||||
|
|||||||
@@ -7,15 +7,16 @@ package kv
|
|||||||
// it easy to scan for keys that have a certain shard number as a prefix and return those
|
// it easy to scan for keys that have a certain shard number as a prefix and return those
|
||||||
// corresponding attestations.
|
// corresponding attestations.
|
||||||
var (
|
var (
|
||||||
blocksBucket = []byte("blocks")
|
blocksBucket = []byte("blocks")
|
||||||
stateBucket = []byte("state")
|
stateBucket = []byte("state")
|
||||||
stateSummaryBucket = []byte("state-summary")
|
stateSummaryBucket = []byte("state-summary")
|
||||||
chainMetadataBucket = []byte("chain-metadata")
|
chainMetadataBucket = []byte("chain-metadata")
|
||||||
checkpointBucket = []byte("check-point")
|
checkpointBucket = []byte("check-point")
|
||||||
powchainBucket = []byte("powchain")
|
powchainBucket = []byte("powchain")
|
||||||
stateValidatorsBucket = []byte("state-validators")
|
stateValidatorsBucket = []byte("state-validators")
|
||||||
feeRecipientBucket = []byte("fee-recipient")
|
feeRecipientBucket = []byte("fee-recipient")
|
||||||
registrationBucket = []byte("registration")
|
registrationBucket = []byte("registration")
|
||||||
|
executionPayloadEnvelopeBucket = []byte("execution-payload-envelope")
|
||||||
|
|
||||||
// Light Client Updates Bucket
|
// Light Client Updates Bucket
|
||||||
lightClientUpdatesBucket = []byte("light-client-updates")
|
lightClientUpdatesBucket = []byte("light-client-updates")
|
||||||
@@ -57,6 +58,7 @@ var (
|
|||||||
electraBlindKey = []byte("blind-electra")
|
electraBlindKey = []byte("blind-electra")
|
||||||
fuluKey = []byte("fulu")
|
fuluKey = []byte("fulu")
|
||||||
fuluBlindKey = []byte("blind-fulu")
|
fuluBlindKey = []byte("blind-fulu")
|
||||||
|
epbsKey = []byte("epbs")
|
||||||
|
|
||||||
// block root included in the beacon state used by weak subjectivity initial sync
|
// block root included in the beacon state used by weak subjectivity initial sync
|
||||||
originCheckpointBlockRootKey = []byte("origin-checkpoint-block-root")
|
originCheckpointBlockRootKey = []byte("origin-checkpoint-block-root")
|
||||||
|
|||||||
@@ -253,6 +253,10 @@ func (s *Store) saveStatesEfficientInternal(ctx context.Context, tx *bolt.Tx, bl
|
|||||||
if err := s.processElectra(ctx, rawType, rt[:], bucket, valIdxBkt, validatorKeys[i]); err != nil {
|
if err := s.processElectra(ctx, rawType, rt[:], bucket, valIdxBkt, validatorKeys[i]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case *ethpb.BeaconStateEPBS:
|
||||||
|
if err := s.processEPBS(ctx, rawType, rt[:], bucket, valIdxBkt, validatorKeys[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return errors.New("invalid state type")
|
return errors.New("invalid state type")
|
||||||
}
|
}
|
||||||
@@ -368,6 +372,24 @@ func (s *Store) processElectra(ctx context.Context, pbState *ethpb.BeaconStateEl
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Store) processEPBS(ctx context.Context, pbState *ethpb.BeaconStateEPBS, rootHash []byte, bucket, valIdxBkt *bolt.Bucket, validatorKey []byte) error {
|
||||||
|
valEntries := pbState.Validators
|
||||||
|
pbState.Validators = make([]*ethpb.Validator, 0)
|
||||||
|
rawObj, err := pbState.MarshalSSZ()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
encodedState := snappy.Encode(nil, append(epbsKey, rawObj...))
|
||||||
|
if err := bucket.Put(rootHash, encodedState); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pbState.Validators = valEntries
|
||||||
|
if err := valIdxBkt.Put(rootHash, validatorKey); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Store) storeValidatorEntriesSeparately(ctx context.Context, tx *bolt.Tx, validatorsEntries map[string]*ethpb.Validator) error {
|
func (s *Store) storeValidatorEntriesSeparately(ctx context.Context, tx *bolt.Tx, validatorsEntries map[string]*ethpb.Validator) error {
|
||||||
valBkt := tx.Bucket(stateValidatorsBucket)
|
valBkt := tx.Bucket(stateValidatorsBucket)
|
||||||
for hashStr, validatorEntry := range validatorsEntries {
|
for hashStr, validatorEntry := range validatorsEntries {
|
||||||
@@ -517,18 +539,17 @@ func (s *Store) unmarshalState(_ context.Context, enc []byte, validatorEntries [
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
case hasEpbsKey(enc):
|
||||||
|
protoState := ðpb.BeaconStateEPBS{}
|
||||||
|
if err := protoState.UnmarshalSSZ(enc[len(epbsKey):]); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to unmarshal encoding for EPBS")
|
||||||
|
}
|
||||||
|
return statenative.InitializeFromProtoEpbs(protoState)
|
||||||
case hasFuluKey(enc):
|
case hasFuluKey(enc):
|
||||||
protoState := ðpb.BeaconStateFulu{}
|
protoState := ðpb.BeaconStateFulu{}
|
||||||
if err := protoState.UnmarshalSSZ(enc[len(fuluKey):]); err != nil {
|
if err := protoState.UnmarshalSSZ(enc[len(fuluKey):]); err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to unmarshal encoding for Electra")
|
return nil, errors.Wrap(err, "failed to unmarshal encoding for Electra")
|
||||||
}
|
}
|
||||||
ok, err := s.isStateValidatorMigrationOver()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
protoState.Validators = validatorEntries
|
|
||||||
}
|
|
||||||
return statenative.InitializeFromProtoUnsafeFulu(protoState)
|
return statenative.InitializeFromProtoUnsafeFulu(protoState)
|
||||||
case HasElectraKey(enc):
|
case HasElectraKey(enc):
|
||||||
protoState := ðpb.BeaconStateElectra{}
|
protoState := ðpb.BeaconStateElectra{}
|
||||||
@@ -690,7 +711,7 @@ func marshalState(ctx context.Context, st state.ReadOnlyBeaconState) ([]byte, er
|
|||||||
}
|
}
|
||||||
return snappy.Encode(nil, append(ElectraKey, rawObj...)), nil
|
return snappy.Encode(nil, append(ElectraKey, rawObj...)), nil
|
||||||
case version.Fulu:
|
case version.Fulu:
|
||||||
rState, ok := st.ToProtoUnsafe().(*ethpb.BeaconStateFulu)
|
rState, ok := st.ToProtoUnsafe().(*ethpb.BeaconStateElectra)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("non valid inner state")
|
return nil, errors.New("non valid inner state")
|
||||||
}
|
}
|
||||||
@@ -701,7 +722,20 @@ func marshalState(ctx context.Context, st state.ReadOnlyBeaconState) ([]byte, er
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return snappy.Encode(nil, append(fuluKey, rawObj...)), nil
|
return snappy.Encode(nil, append(ElectraKey, rawObj...)), nil
|
||||||
|
case version.EPBS:
|
||||||
|
rState, ok := st.ToProtoUnsafe().(*ethpb.BeaconStateEPBS)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("non valid inner state")
|
||||||
|
}
|
||||||
|
if rState == nil {
|
||||||
|
return nil, errors.New("nil state")
|
||||||
|
}
|
||||||
|
rawObj, err := rState.MarshalSSZ()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return snappy.Encode(nil, append(epbsKey, rawObj...)), nil
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("invalid inner state")
|
return nil, errors.New("invalid inner state")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
mathRand "math/rand"
|
mathRand "math/rand"
|
||||||
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||||
|
statenative "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
|
||||||
"github.com/prysmaticlabs/prysm/v5/config/features"
|
"github.com/prysmaticlabs/prysm/v5/config/features"
|
||||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/prysmaticlabs/prysm/v5/testing/assert"
|
"github.com/prysmaticlabs/prysm/v5/testing/assert"
|
||||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||||
"github.com/prysmaticlabs/prysm/v5/testing/util"
|
"github.com/prysmaticlabs/prysm/v5/testing/util"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/util/random"
|
||||||
bolt "go.etcd.io/bbolt"
|
bolt "go.etcd.io/bbolt"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -158,6 +159,16 @@ func TestState_CanSaveRetrieve(t *testing.T) {
|
|||||||
},
|
},
|
||||||
rootSeed: 'E',
|
rootSeed: 'E',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "epbs",
|
||||||
|
s: func() state.BeaconState {
|
||||||
|
stPb := random.BeaconState(t)
|
||||||
|
st, err := statenative.InitializeFromProtoUnsafeEpbs(stPb)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return st
|
||||||
|
},
|
||||||
|
rootSeed: 'F',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
db := setupDB(t)
|
db := setupDB(t)
|
||||||
@@ -166,6 +177,9 @@ func TestState_CanSaveRetrieve(t *testing.T) {
|
|||||||
reset := features.InitWithReset(&features.Flags{EnableHistoricalSpaceRepresentation: enableFlag})
|
reset := features.InitWithReset(&features.Flags{EnableHistoricalSpaceRepresentation: enableFlag})
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
|
if tc.name == "epbs" && enableFlag {
|
||||||
|
t.Skip("Skipping epbs test as EnableHistoricalSpaceRepresentation is true")
|
||||||
|
}
|
||||||
t.Run(tc.name+" - EnableHistoricalSpaceRepresentation is "+strconv.FormatBool(enableFlag), func(t *testing.T) {
|
t.Run(tc.name+" - EnableHistoricalSpaceRepresentation is "+strconv.FormatBool(enableFlag), func(t *testing.T) {
|
||||||
rootNonce := byte('0')
|
rootNonce := byte('0')
|
||||||
if enableFlag {
|
if enableFlag {
|
||||||
@@ -1112,6 +1126,26 @@ func TestDenebState_CanDelete(t *testing.T) {
|
|||||||
require.Equal(t, state.ReadOnlyBeaconState(nil), savedS, "Unsaved state should've been nil")
|
require.Equal(t, state.ReadOnlyBeaconState(nil), savedS, "Unsaved state should've been nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEpbsState_CanDelete(t *testing.T) {
|
||||||
|
db := setupDB(t)
|
||||||
|
|
||||||
|
r := [32]byte{'A'}
|
||||||
|
|
||||||
|
require.Equal(t, false, db.HasState(context.Background(), r))
|
||||||
|
|
||||||
|
s := random.BeaconState(t)
|
||||||
|
st, err := statenative.InitializeFromProtoUnsafeEpbs(s)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NoError(t, db.SaveState(context.Background(), st, r))
|
||||||
|
require.Equal(t, true, db.HasState(context.Background(), r))
|
||||||
|
|
||||||
|
require.NoError(t, db.DeleteState(context.Background(), r))
|
||||||
|
savedS, err := db.State(context.Background(), r)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, state.ReadOnlyBeaconState(nil), savedS, "Unsaved state should've been nil")
|
||||||
|
}
|
||||||
|
|
||||||
func TestStateDeneb_CanSaveRetrieveValidatorEntries(t *testing.T) {
|
func TestStateDeneb_CanSaveRetrieveValidatorEntries(t *testing.T) {
|
||||||
db := setupDB(t)
|
db := setupDB(t)
|
||||||
|
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ func (s *Service) ForkchoiceUpdated(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, handleRPCError(err)
|
return nil, nil, handleRPCError(err)
|
||||||
}
|
}
|
||||||
case version.Deneb, version.Electra, version.Fulu:
|
case version.Deneb, version.Electra, version.Fulu, version.EPBS:
|
||||||
a, err := attrs.PbV3()
|
a, err := attrs.PbV3()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ go_library(
|
|||||||
"//config/fieldparams:go_default_library",
|
"//config/fieldparams:go_default_library",
|
||||||
"//consensus-types/blocks:go_default_library",
|
"//consensus-types/blocks:go_default_library",
|
||||||
"//consensus-types/forkchoice:go_default_library",
|
"//consensus-types/forkchoice:go_default_library",
|
||||||
|
"//consensus-types/interfaces:go_default_library",
|
||||||
"//consensus-types/primitives:go_default_library",
|
"//consensus-types/primitives:go_default_library",
|
||||||
"@com_github_pkg_errors//:go_default_library",
|
"@com_github_pkg_errors//:go_default_library",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ go_library(
|
|||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = [
|
srcs = [
|
||||||
"doc.go",
|
"doc.go",
|
||||||
|
"epbs.go",
|
||||||
"errors.go",
|
"errors.go",
|
||||||
"forkchoice.go",
|
"forkchoice.go",
|
||||||
"last_root.go",
|
"last_root.go",
|
||||||
@@ -31,6 +32,7 @@ go_library(
|
|||||||
"//config/params:go_default_library",
|
"//config/params:go_default_library",
|
||||||
"//consensus-types/blocks:go_default_library",
|
"//consensus-types/blocks:go_default_library",
|
||||||
"//consensus-types/forkchoice:go_default_library",
|
"//consensus-types/forkchoice:go_default_library",
|
||||||
|
"//consensus-types/interfaces:go_default_library",
|
||||||
"//consensus-types/primitives:go_default_library",
|
"//consensus-types/primitives:go_default_library",
|
||||||
"//encoding/bytesutil:go_default_library",
|
"//encoding/bytesutil:go_default_library",
|
||||||
"//monitoring/tracing/trace:go_default_library",
|
"//monitoring/tracing/trace:go_default_library",
|
||||||
@@ -47,6 +49,7 @@ go_library(
|
|||||||
go_test(
|
go_test(
|
||||||
name = "go_default_test",
|
name = "go_default_test",
|
||||||
srcs = [
|
srcs = [
|
||||||
|
"epbs_test.go",
|
||||||
"ffg_update_test.go",
|
"ffg_update_test.go",
|
||||||
"forkchoice_test.go",
|
"forkchoice_test.go",
|
||||||
"last_root_test.go",
|
"last_root_test.go",
|
||||||
@@ -71,12 +74,12 @@ go_test(
|
|||||||
"//consensus-types/forkchoice:go_default_library",
|
"//consensus-types/forkchoice:go_default_library",
|
||||||
"//consensus-types/primitives:go_default_library",
|
"//consensus-types/primitives:go_default_library",
|
||||||
"//crypto/hash:go_default_library",
|
"//crypto/hash:go_default_library",
|
||||||
"//encoding/bytesutil:go_default_library",
|
|
||||||
"//proto/engine/v1:go_default_library",
|
"//proto/engine/v1:go_default_library",
|
||||||
"//proto/prysm/v1alpha1:go_default_library",
|
"//proto/prysm/v1alpha1:go_default_library",
|
||||||
"//testing/assert:go_default_library",
|
"//testing/assert:go_default_library",
|
||||||
"//testing/require:go_default_library",
|
"//testing/require:go_default_library",
|
||||||
"//testing/util:go_default_library",
|
"//testing/util:go_default_library",
|
||||||
"//time/slots:go_default_library",
|
"//time/slots:go_default_library",
|
||||||
|
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
91
beacon-chain/forkchoice/doubly-linked-tree/epbs.go
Normal file
91
beacon-chain/forkchoice/doubly-linked-tree/epbs.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package doublylinkedtree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *ForkChoice) GetPTCVote() primitives.PTCStatus {
|
||||||
|
highestNode := f.store.highestReceivedNode
|
||||||
|
if highestNode == nil {
|
||||||
|
return primitives.PAYLOAD_ABSENT
|
||||||
|
}
|
||||||
|
if slots.CurrentSlot(f.store.genesisTime) > highestNode.block.slot {
|
||||||
|
return primitives.PAYLOAD_ABSENT
|
||||||
|
}
|
||||||
|
if highestNode.full {
|
||||||
|
return primitives.PAYLOAD_PRESENT
|
||||||
|
}
|
||||||
|
return primitives.PAYLOAD_ABSENT
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ForkChoice) insertExecutionPayload(b *BlockNode, e interfaces.ExecutionData) error {
|
||||||
|
s := f.store
|
||||||
|
hash := [32]byte(e.BlockHash())
|
||||||
|
if _, ok := s.fullNodeByPayload[hash]; ok {
|
||||||
|
// We ignore nodes with the give payload hash already included
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
n := &Node{
|
||||||
|
block: b,
|
||||||
|
children: make([]*Node, 0),
|
||||||
|
full: true,
|
||||||
|
optimistic: true,
|
||||||
|
}
|
||||||
|
if n.block.parent != nil {
|
||||||
|
n.block.parent.children = append(n.block.parent.children, n)
|
||||||
|
} else {
|
||||||
|
// make this the tree node
|
||||||
|
f.store.treeRootNode = n
|
||||||
|
}
|
||||||
|
s.fullNodeByPayload[hash] = n
|
||||||
|
s.updateWithPayload(n)
|
||||||
|
processedPayloadCount.Inc()
|
||||||
|
payloadCount.Set(float64(len(s.fullNodeByPayload)))
|
||||||
|
|
||||||
|
// make this node head if the empty node was
|
||||||
|
if s.headNode.block == n.block {
|
||||||
|
s.headNode = n
|
||||||
|
}
|
||||||
|
if b.slot == s.highestReceivedNode.block.slot {
|
||||||
|
s.highestReceivedNode = n
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertPayloadEnvelope adds a full node to forkchoice from the given payload
|
||||||
|
// envelope.
|
||||||
|
func (f *ForkChoice) InsertPayloadEnvelope(envelope interfaces.ROExecutionPayloadEnvelope) error {
|
||||||
|
s := f.store
|
||||||
|
b, ok := s.emptyNodeByRoot[envelope.BeaconBlockRoot()]
|
||||||
|
if !ok {
|
||||||
|
return ErrNilNode
|
||||||
|
}
|
||||||
|
e, err := envelope.Execution()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return f.insertExecutionPayload(b.block, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) updateWithPayload(n *Node) {
|
||||||
|
for _, node := range s.emptyNodeByRoot {
|
||||||
|
if node.bestDescendant != nil && node.bestDescendant.block == n.block {
|
||||||
|
node.bestDescendant = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, node := range s.fullNodeByPayload {
|
||||||
|
if node.bestDescendant != nil && node.bestDescendant.block == n.block {
|
||||||
|
node.bestDescendant = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ForkChoice) HashForBlockRoot(root [32]byte) [32]byte {
|
||||||
|
node, ok := f.store.emptyNodeByRoot[root]
|
||||||
|
if !ok {
|
||||||
|
return [32]byte{}
|
||||||
|
}
|
||||||
|
return node.block.payloadHash
|
||||||
|
}
|
||||||
27
beacon-chain/forkchoice/doubly-linked-tree/epbs_test.go
Normal file
27
beacon-chain/forkchoice/doubly-linked-tree/epbs_test.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package doublylinkedtree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFullInsertionPreEPBS(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
f := setup(1, 1)
|
||||||
|
require.Equal(t, 1, f.NodeCount())
|
||||||
|
|
||||||
|
payloadHash := [32]byte{'A'}
|
||||||
|
state, blk, err := prepareForkchoiceState(ctx, f, 1, [32]byte{'a'}, params.BeaconConfig().ZeroHash, payloadHash, 1, 1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
|
node, ok := f.store.emptyNodeByRoot[blk.Root()]
|
||||||
|
require.Equal(t, true, ok)
|
||||||
|
require.Equal(t, blk.Root(), node.block.root)
|
||||||
|
|
||||||
|
fullNode, ok := f.store.fullNodeByPayload[payloadHash]
|
||||||
|
require.Equal(t, true, ok)
|
||||||
|
require.Equal(t, payloadHash, fullNode.block.payloadHash)
|
||||||
|
}
|
||||||
@@ -20,6 +20,8 @@ func TestFFGUpdates_OneBranch(t *testing.T) {
|
|||||||
r, err := f.Head(context.Background())
|
r, err := f.Head(context.Background())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, params.BeaconConfig().ZeroHash, r, "Incorrect head with genesis")
|
assert.Equal(t, params.BeaconConfig().ZeroHash, r, "Incorrect head with genesis")
|
||||||
|
// check that the head is the full block
|
||||||
|
require.Equal(t, true, f.store.headNode.full)
|
||||||
|
|
||||||
// Define the following tree:
|
// Define the following tree:
|
||||||
// 0 <- justified: 0, finalized: 0
|
// 0 <- justified: 0, finalized: 0
|
||||||
@@ -29,13 +31,13 @@ func TestFFGUpdates_OneBranch(t *testing.T) {
|
|||||||
// 2 <- justified: 1, finalized: 0
|
// 2 <- justified: 1, finalized: 0
|
||||||
// |
|
// |
|
||||||
// 3 <- justified: 2, finalized: 1
|
// 3 <- justified: 2, finalized: 1
|
||||||
state, blkRoot, err := prepareForkchoiceState(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
state, blkRoot, err := prepareForkchoiceState(context.Background(), f, 1, indexToHash(1), params.BeaconConfig().ZeroHash, indexToHash(101), 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 0)
|
state, blkRoot, err = prepareForkchoiceState(context.Background(), f, 2, indexToHash(2), indexToHash(1), indexToHash(102), 1, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 2, 1)
|
state, blkRoot, err = prepareForkchoiceState(context.Background(), f, 3, indexToHash(3), indexToHash(2), indexToHash(103), 2, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
|
|
||||||
@@ -101,35 +103,35 @@ func TestFFGUpdates_TwoBranches(t *testing.T) {
|
|||||||
// | |
|
// | |
|
||||||
// justified: 2, finalized: 0 -> 9 10 <- justified: 2, finalized: 0
|
// justified: 2, finalized: 0 -> 9 10 <- justified: 2, finalized: 0
|
||||||
// Left branch.
|
// Left branch.
|
||||||
state, blkRoot, err := prepareForkchoiceState(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
state, blkRoot, err := prepareForkchoiceState(context.Background(), f, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 2, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 0)
|
state, blkRoot, err = prepareForkchoiceState(context.Background(), f, 2, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 3, indexToHash(5), indexToHash(3), params.BeaconConfig().ZeroHash, 1, 0)
|
state, blkRoot, err = prepareForkchoiceState(context.Background(), f, 3, indexToHash(5), indexToHash(3), params.BeaconConfig().ZeroHash, 1, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 4, indexToHash(7), indexToHash(5), params.BeaconConfig().ZeroHash, 1, 0)
|
state, blkRoot, err = prepareForkchoiceState(context.Background(), f, 4, indexToHash(7), indexToHash(5), params.BeaconConfig().ZeroHash, 1, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 4, indexToHash(9), indexToHash(7), params.BeaconConfig().ZeroHash, 2, 0)
|
state, blkRoot, err = prepareForkchoiceState(context.Background(), f, 4, indexToHash(9), indexToHash(7), params.BeaconConfig().ZeroHash, 2, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
// Right branch.
|
// Right branch.
|
||||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 1, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
state, blkRoot, err = prepareForkchoiceState(context.Background(), f, 1, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 2, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0)
|
state, blkRoot, err = prepareForkchoiceState(context.Background(), f, 2, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 3, indexToHash(6), indexToHash(4), params.BeaconConfig().ZeroHash, 0, 0)
|
state, blkRoot, err = prepareForkchoiceState(context.Background(), f, 3, indexToHash(6), indexToHash(4), params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 4, indexToHash(8), indexToHash(6), params.BeaconConfig().ZeroHash, 1, 0)
|
state, blkRoot, err = prepareForkchoiceState(context.Background(), f, 4, indexToHash(8), indexToHash(6), params.BeaconConfig().ZeroHash, 1, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 4, indexToHash(10), indexToHash(8), params.BeaconConfig().ZeroHash, 2, 0)
|
state, blkRoot, err = prepareForkchoiceState(context.Background(), f, 4, indexToHash(10), indexToHash(8), params.BeaconConfig().ZeroHash, 2, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
|
|
||||||
@@ -220,12 +222,12 @@ func setup(justifiedEpoch, finalizedEpoch primitives.Epoch) *ForkChoice {
|
|||||||
f := New()
|
f := New()
|
||||||
f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: justifiedEpoch, Root: params.BeaconConfig().ZeroHash}
|
f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: justifiedEpoch, Root: params.BeaconConfig().ZeroHash}
|
||||||
f.store.finalizedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: finalizedEpoch, Root: params.BeaconConfig().ZeroHash}
|
f.store.finalizedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: finalizedEpoch, Root: params.BeaconConfig().ZeroHash}
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, 0, params.BeaconConfig().ZeroHash, [32]byte{}, params.BeaconConfig().ZeroHash, justifiedEpoch, finalizedEpoch)
|
state, blk, err := prepareForkchoiceState(ctx, f, 0, params.BeaconConfig().ZeroHash, [32]byte{}, params.BeaconConfig().ZeroHash, justifiedEpoch, finalizedEpoch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
f.SetBalancesByRooter(func(_ context.Context, _ [32]byte) ([]uint64, error) { return f.justifiedBalances, nil })
|
f.SetBalancesByRooter(func(_ context.Context, _ [32]byte) ([]uint64, error) { return f.justifiedBalances, nil })
|
||||||
err = f.InsertNode(ctx, state, blkRoot)
|
err = f.InsertNode(ctx, state, blk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||||
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace"
|
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace"
|
||||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||||
|
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||||
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@@ -30,8 +31,8 @@ func New() *ForkChoice {
|
|||||||
prevJustifiedCheckpoint: &forkchoicetypes.Checkpoint{},
|
prevJustifiedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||||
finalizedCheckpoint: &forkchoicetypes.Checkpoint{},
|
finalizedCheckpoint: &forkchoicetypes.Checkpoint{},
|
||||||
proposerBoostRoot: [32]byte{},
|
proposerBoostRoot: [32]byte{},
|
||||||
nodeByRoot: make(map[[fieldparams.RootLength]byte]*Node),
|
emptyNodeByRoot: make(map[[fieldparams.RootLength]byte]*Node),
|
||||||
nodeByPayload: make(map[[fieldparams.RootLength]byte]*Node),
|
fullNodeByPayload: make(map[[fieldparams.RootLength]byte]*Node),
|
||||||
slashedIndices: make(map[primitives.ValidatorIndex]bool),
|
slashedIndices: make(map[primitives.ValidatorIndex]bool),
|
||||||
receivedBlocksLastEpoch: [fieldparams.SlotsPerEpoch]primitives.Slot{},
|
receivedBlocksLastEpoch: [fieldparams.SlotsPerEpoch]primitives.Slot{},
|
||||||
}
|
}
|
||||||
@@ -43,7 +44,7 @@ func New() *ForkChoice {
|
|||||||
|
|
||||||
// NodeCount returns the current number of nodes in the Store.
|
// NodeCount returns the current number of nodes in the Store.
|
||||||
func (f *ForkChoice) NodeCount() int {
|
func (f *ForkChoice) NodeCount() int {
|
||||||
return len(f.store.nodeByRoot)
|
return len(f.store.emptyNodeByRoot)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Head returns the head root from fork choice store.
|
// Head returns the head root from fork choice store.
|
||||||
@@ -79,7 +80,7 @@ func (f *ForkChoice) Head(
|
|||||||
|
|
||||||
// ProcessAttestation processes attestation for vote accounting, it iterates around validator indices
|
// ProcessAttestation processes attestation for vote accounting, it iterates around validator indices
|
||||||
// and update their votes accordingly.
|
// and update their votes accordingly.
|
||||||
func (f *ForkChoice) ProcessAttestation(ctx context.Context, validatorIndices []uint64, blockRoot [32]byte, targetEpoch primitives.Epoch) {
|
func (f *ForkChoice) ProcessAttestation(ctx context.Context, validatorIndices []uint64, blockRoot [32]byte, attSlot primitives.Slot) {
|
||||||
_, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.ProcessAttestation")
|
_, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.ProcessAttestation")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
@@ -93,9 +94,9 @@ func (f *ForkChoice) ProcessAttestation(ctx context.Context, validatorIndices []
|
|||||||
newVote := f.votes[index].nextRoot == params.BeaconConfig().ZeroHash &&
|
newVote := f.votes[index].nextRoot == params.BeaconConfig().ZeroHash &&
|
||||||
f.votes[index].currentRoot == params.BeaconConfig().ZeroHash
|
f.votes[index].currentRoot == params.BeaconConfig().ZeroHash
|
||||||
|
|
||||||
// Vote gets updated if it's newly allocated or high target epoch.
|
// Vote gets updated if it's newly allocated or higher attestation slot.
|
||||||
if newVote || targetEpoch > f.votes[index].nextEpoch {
|
if newVote || attSlot > f.votes[index].slot {
|
||||||
f.votes[index].nextEpoch = targetEpoch
|
f.votes[index].slot = attSlot
|
||||||
f.votes[index].nextRoot = blockRoot
|
f.votes[index].nextRoot = blockRoot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,12 +126,29 @@ func (f *ForkChoice) InsertNode(ctx context.Context, state state.BeaconState, ro
|
|||||||
|
|
||||||
jc, fc = f.store.pullTips(state, node, jc, fc)
|
jc, fc = f.store.pullTips(state, node, jc, fc)
|
||||||
if err := f.updateCheckpoints(ctx, jc, fc); err != nil {
|
if err := f.updateCheckpoints(ctx, jc, fc); err != nil {
|
||||||
_, remErr := f.store.removeNode(ctx, node)
|
emptyNode := f.store.emptyNodeByRoot[node.root]
|
||||||
|
_, remErr := f.store.removeNode(ctx, emptyNode)
|
||||||
if remErr != nil {
|
if remErr != nil {
|
||||||
log.WithError(remErr).Error("could not remove node")
|
log.WithError(remErr).Error("could not remove node")
|
||||||
}
|
}
|
||||||
return errors.Wrap(err, "could not update checkpoints")
|
return errors.Wrap(err, "could not update checkpoints")
|
||||||
}
|
}
|
||||||
|
if roblock.Version() >= version.EPBS {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if roblock.Version() >= version.Bellatrix {
|
||||||
|
e, err := roblock.Block().Body().Execution()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not get execution data")
|
||||||
|
}
|
||||||
|
currentEpoch := slots.EpochsSinceGenesis(time.Unix(int64(f.store.genesisTime), 0))
|
||||||
|
if err := f.store.treeRootNode.updateBestDescendant(ctx, jc.Epoch, fc.Epoch, currentEpoch); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := f.insertExecutionPayload(node, e); err != nil {
|
||||||
|
return errors.Wrap(err, "could not insert execution payload to forkchoice")
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,16 +174,28 @@ func (f *ForkChoice) updateCheckpoints(ctx context.Context, jc, fc *ethpb.Checkp
|
|||||||
// HasNode returns true if the node exists in fork choice store,
|
// HasNode returns true if the node exists in fork choice store,
|
||||||
// false else wise.
|
// false else wise.
|
||||||
func (f *ForkChoice) HasNode(root [32]byte) bool {
|
func (f *ForkChoice) HasNode(root [32]byte) bool {
|
||||||
_, ok := f.store.nodeByRoot[root]
|
_, ok := f.store.emptyNodeByRoot[root]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasHash returns true if the node with the given payload hash exists in fork choice store,
|
||||||
|
// false else wise.
|
||||||
|
func (f *ForkChoice) HasHash(hash [32]byte) bool {
|
||||||
|
_, ok := f.store.fullNodeByPayload[hash]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsCanonical returns true if the given root is part of the canonical chain.
|
// IsCanonical returns true if the given root is part of the canonical chain.
|
||||||
|
// TODO: change the API to pass also full/empty
|
||||||
func (f *ForkChoice) IsCanonical(root [32]byte) bool {
|
func (f *ForkChoice) IsCanonical(root [32]byte) bool {
|
||||||
node, ok := f.store.nodeByRoot[root]
|
node, ok := f.store.emptyNodeByRoot[root]
|
||||||
if !ok || node == nil {
|
if !ok || node == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
fullNode, ok := f.store.fullNodeByPayload[node.block.payloadHash]
|
||||||
|
if ok {
|
||||||
|
node = fullNode
|
||||||
|
}
|
||||||
|
|
||||||
if node.bestDescendant == nil {
|
if node.bestDescendant == nil {
|
||||||
if f.store.headNode.bestDescendant == nil {
|
if f.store.headNode.bestDescendant == nil {
|
||||||
@@ -185,7 +215,7 @@ func (f *ForkChoice) IsOptimistic(root [32]byte) (bool, error) {
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
node, ok := f.store.nodeByRoot[root]
|
node, ok := f.store.emptyNodeByRoot[root]
|
||||||
if !ok || node == nil {
|
if !ok || node == nil {
|
||||||
return true, ErrNilNode
|
return true, ErrNilNode
|
||||||
}
|
}
|
||||||
@@ -197,31 +227,37 @@ func (f *ForkChoice) IsOptimistic(root [32]byte) (bool, error) {
|
|||||||
func (f *ForkChoice) AncestorRoot(ctx context.Context, root [32]byte, slot primitives.Slot) ([32]byte, error) {
|
func (f *ForkChoice) AncestorRoot(ctx context.Context, root [32]byte, slot primitives.Slot) ([32]byte, error) {
|
||||||
ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.AncestorRoot")
|
ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.AncestorRoot")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
return f.store.ancestorRoot(ctx, root, slot)
|
||||||
|
}
|
||||||
|
|
||||||
node, ok := f.store.nodeByRoot[root]
|
func (s *Store) ancestorRoot(ctx context.Context, root [32]byte, slot primitives.Slot) ([32]byte, error) {
|
||||||
|
ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.AncestorRoot")
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
node, ok := s.emptyNodeByRoot[root]
|
||||||
if !ok || node == nil {
|
if !ok || node == nil {
|
||||||
return [32]byte{}, errors.Wrap(ErrNilNode, "could not determine ancestor root")
|
return [32]byte{}, errors.Wrap(ErrNilNode, "could not determine ancestor root")
|
||||||
}
|
}
|
||||||
|
|
||||||
n := node
|
n := node
|
||||||
for n != nil && n.slot > slot {
|
for n != nil && n.block.slot > slot {
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
return [32]byte{}, ctx.Err()
|
return [32]byte{}, ctx.Err()
|
||||||
}
|
}
|
||||||
n = n.parent
|
n = n.block.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
if n == nil {
|
if n == nil {
|
||||||
return [32]byte{}, errors.Wrap(ErrNilNode, "could not determine ancestor root")
|
return [32]byte{}, errors.Wrap(ErrNilNode, "could not determine ancestor root")
|
||||||
}
|
}
|
||||||
|
|
||||||
return n.root, nil
|
return n.block.root, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsViableForCheckpoint returns whether the root passed is a checkpoint root for any
|
// IsViableForCheckpoint returns whether the root passed is a checkpoint root for any
|
||||||
// known chain in forkchoice.
|
// known chain in forkchoice.
|
||||||
func (f *ForkChoice) IsViableForCheckpoint(cp *forkchoicetypes.Checkpoint) (bool, error) {
|
func (f *ForkChoice) IsViableForCheckpoint(cp *forkchoicetypes.Checkpoint) (bool, error) {
|
||||||
node, ok := f.store.nodeByRoot[cp.Root]
|
node, ok := f.store.emptyNodeByRoot[cp.Root]
|
||||||
if !ok || node == nil {
|
if !ok || node == nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@@ -229,22 +265,30 @@ func (f *ForkChoice) IsViableForCheckpoint(cp *forkchoicetypes.Checkpoint) (bool
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if node.slot > epochStart {
|
if node.block.slot > epochStart {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fullNode := f.store.fullNodeByPayload[node.block.payloadHash]
|
||||||
|
|
||||||
if len(node.children) == 0 {
|
if len(node.children) == 0 {
|
||||||
return true, nil
|
if fullNode != nil && len(fullNode.children) == 0 {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if node.slot == epochStart {
|
if node.block.slot == epochStart {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
nodeEpoch := slots.ToEpoch(node.slot)
|
|
||||||
if nodeEpoch >= cp.Epoch {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
for _, child := range node.children {
|
for _, child := range node.children {
|
||||||
if child.slot > epochStart {
|
if child.block.slot > epochStart {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if fullNode == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
for _, child := range fullNode.children {
|
||||||
|
if child.block.slot > epochStart {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -284,34 +328,35 @@ func (f *ForkChoice) updateBalances() error {
|
|||||||
if vote.currentRoot != vote.nextRoot || oldBalance != newBalance {
|
if vote.currentRoot != vote.nextRoot || oldBalance != newBalance {
|
||||||
// Ignore the vote if the root is not in fork choice
|
// Ignore the vote if the root is not in fork choice
|
||||||
// store, that means we have not seen the block before.
|
// store, that means we have not seen the block before.
|
||||||
nextNode, ok := f.store.nodeByRoot[vote.nextRoot]
|
nextNode, ok := f.store.emptyNodeByRoot[vote.nextRoot]
|
||||||
if ok && vote.nextRoot != zHash {
|
if ok && vote.nextRoot != zHash {
|
||||||
// Protection against nil node
|
// Protection against nil node
|
||||||
if nextNode == nil {
|
if nextNode == nil {
|
||||||
return errors.Wrap(ErrNilNode, "could not update balances")
|
return errors.Wrap(ErrNilNode, "could not update balances")
|
||||||
}
|
}
|
||||||
nextNode.balance += newBalance
|
// New votes count both for full and empty nodes
|
||||||
|
nextNode.block.balance += newBalance
|
||||||
}
|
}
|
||||||
|
|
||||||
currentNode, ok := f.store.nodeByRoot[vote.currentRoot]
|
currentNode, ok := f.store.emptyNodeByRoot[vote.currentRoot]
|
||||||
if ok && vote.currentRoot != zHash {
|
if ok && vote.currentRoot != zHash {
|
||||||
// Protection against nil node
|
// Protection against nil node
|
||||||
if currentNode == nil {
|
if currentNode == nil {
|
||||||
return errors.Wrap(ErrNilNode, "could not update balances")
|
return errors.Wrap(ErrNilNode, "could not update balances")
|
||||||
}
|
}
|
||||||
if currentNode.balance < oldBalance {
|
if currentNode.block.balance < oldBalance {
|
||||||
log.WithFields(logrus.Fields{
|
log.WithFields(logrus.Fields{
|
||||||
"nodeRoot": fmt.Sprintf("%#x", bytesutil.Trunc(vote.currentRoot[:])),
|
"nodeRoot": fmt.Sprintf("%#x", bytesutil.Trunc(vote.currentRoot[:])),
|
||||||
"oldBalance": oldBalance,
|
"oldBalance": oldBalance,
|
||||||
"nodeBalance": currentNode.balance,
|
"nodeBalance": currentNode.block.balance,
|
||||||
"nodeWeight": currentNode.weight,
|
"emptyNodeWeight": currentNode.weight,
|
||||||
"proposerBoostRoot": fmt.Sprintf("%#x", bytesutil.Trunc(f.store.proposerBoostRoot[:])),
|
"proposerBoostRoot": fmt.Sprintf("%#x", bytesutil.Trunc(f.store.proposerBoostRoot[:])),
|
||||||
"previousProposerBoostRoot": fmt.Sprintf("%#x", bytesutil.Trunc(f.store.previousProposerBoostRoot[:])),
|
"previousProposerBoostRoot": fmt.Sprintf("%#x", bytesutil.Trunc(f.store.previousProposerBoostRoot[:])),
|
||||||
"previousProposerBoostScore": f.store.previousProposerBoostScore,
|
"previousProposerBoostScore": f.store.previousProposerBoostScore,
|
||||||
}).Warning("node with invalid balance, setting it to zero")
|
}).Warning("node with invalid balance, setting it to zero")
|
||||||
currentNode.balance = 0
|
currentNode.block.balance = 0
|
||||||
} else {
|
} else {
|
||||||
currentNode.balance -= oldBalance
|
currentNode.block.balance -= oldBalance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,7 +381,7 @@ func (f *ForkChoice) ProposerBoost() [fieldparams.RootLength]byte {
|
|||||||
|
|
||||||
// SetOptimisticToValid sets the node with the given root as a fully validated node
|
// SetOptimisticToValid sets the node with the given root as a fully validated node
|
||||||
func (f *ForkChoice) SetOptimisticToValid(ctx context.Context, root [fieldparams.RootLength]byte) error {
|
func (f *ForkChoice) SetOptimisticToValid(ctx context.Context, root [fieldparams.RootLength]byte) error {
|
||||||
node, ok := f.store.nodeByRoot[root]
|
node, ok := f.store.emptyNodeByRoot[root]
|
||||||
if !ok || node == nil {
|
if !ok || node == nil {
|
||||||
return errors.Wrap(ErrNilNode, "could not set node to valid")
|
return errors.Wrap(ErrNilNode, "could not set node to valid")
|
||||||
}
|
}
|
||||||
@@ -383,15 +428,15 @@ func (f *ForkChoice) InsertSlashedIndex(_ context.Context, index primitives.Vali
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
node, ok := f.store.nodeByRoot[f.votes[index].currentRoot]
|
node, ok := f.store.emptyNodeByRoot[f.votes[index].currentRoot]
|
||||||
if !ok || node == nil {
|
if !ok || node == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.balance < f.balances[index] {
|
if node.block.balance < f.balances[index] {
|
||||||
node.balance = 0
|
node.block.balance = 0
|
||||||
} else {
|
} else {
|
||||||
node.balance -= f.balances[index]
|
node.block.balance -= f.balances[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,21 +463,22 @@ func (f *ForkChoice) UpdateFinalizedCheckpoint(fc *forkchoicetypes.Checkpoint) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CommonAncestor returns the common ancestor root and slot between the two block roots r1 and r2.
|
// CommonAncestor returns the common ancestor root and slot between the two block roots r1 and r2.
|
||||||
|
// TODO: change the API to pass full/empty not just roots.
|
||||||
func (f *ForkChoice) CommonAncestor(ctx context.Context, r1 [32]byte, r2 [32]byte) ([32]byte, primitives.Slot, error) {
|
func (f *ForkChoice) CommonAncestor(ctx context.Context, r1 [32]byte, r2 [32]byte) ([32]byte, primitives.Slot, error) {
|
||||||
ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.CommonAncestorRoot")
|
ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.CommonAncestorRoot")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
n1, ok := f.store.nodeByRoot[r1]
|
n1, ok := f.store.emptyNodeByRoot[r1]
|
||||||
if !ok || n1 == nil {
|
if !ok || n1 == nil {
|
||||||
return [32]byte{}, 0, forkchoice.ErrUnknownCommonAncestor
|
return [32]byte{}, 0, forkchoice.ErrUnknownCommonAncestor
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do nothing if the input roots are the same.
|
// Do nothing if the input roots are the same.
|
||||||
if r1 == r2 {
|
if r1 == r2 {
|
||||||
return r1, n1.slot, nil
|
return r1, n1.block.slot, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
n2, ok := f.store.nodeByRoot[r2]
|
n2, ok := f.store.emptyNodeByRoot[r2]
|
||||||
if !ok || n2 == nil {
|
if !ok || n2 == nil {
|
||||||
return [32]byte{}, 0, forkchoice.ErrUnknownCommonAncestor
|
return [32]byte{}, 0, forkchoice.ErrUnknownCommonAncestor
|
||||||
}
|
}
|
||||||
@@ -441,23 +487,28 @@ func (f *ForkChoice) CommonAncestor(ctx context.Context, r1 [32]byte, r2 [32]byt
|
|||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
return [32]byte{}, 0, ctx.Err()
|
return [32]byte{}, 0, ctx.Err()
|
||||||
}
|
}
|
||||||
if n1.slot > n2.slot {
|
if n1.block.slot > n2.block.slot {
|
||||||
n1 = n1.parent
|
n1 = n1.block.parent
|
||||||
// Reaches the end of the tree and unable to find common ancestor.
|
// Reaches the end of the tree and unable to find common ancestor.
|
||||||
// This should not happen at runtime as the finalized
|
// This should not happen at runtime as the finalized
|
||||||
// node has to be a common ancestor
|
// node has to be a common ancestor
|
||||||
if n1 == nil {
|
if n1 == nil {
|
||||||
return [32]byte{}, 0, forkchoice.ErrUnknownCommonAncestor
|
return [32]byte{}, 0, forkchoice.ErrUnknownCommonAncestor
|
||||||
}
|
}
|
||||||
} else {
|
} else if n1.block.slot < n2.block.slot {
|
||||||
n2 = n2.parent
|
n2 = n2.block.parent
|
||||||
// Reaches the end of the tree and unable to find common ancestor.
|
// Reaches the end of the tree and unable to find common ancestor.
|
||||||
if n2 == nil {
|
if n2 == nil {
|
||||||
return [32]byte{}, 0, forkchoice.ErrUnknownCommonAncestor
|
return [32]byte{}, 0, forkchoice.ErrUnknownCommonAncestor
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
if n1 == n2 {
|
if n1.block.root == n2.block.root {
|
||||||
return n1.root, n1.slot, nil
|
return n1.block.root, n1.block.slot, nil
|
||||||
|
}
|
||||||
|
n1 = n1.block.parent
|
||||||
|
if n1 == nil {
|
||||||
|
return [32]byte{}, 0, forkchoice.ErrUnknownCommonAncestor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -501,40 +552,36 @@ func (f *ForkChoice) CachedHeadRoot() [32]byte {
|
|||||||
if node == nil {
|
if node == nil {
|
||||||
return [32]byte{}
|
return [32]byte{}
|
||||||
}
|
}
|
||||||
return f.store.headNode.root
|
return f.store.headNode.block.root
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ForkChoice) presentHashForBlockRoot(root [32]byte) [32]byte {
|
||||||
|
node, ok := f.store.emptyNodeByRoot[root]
|
||||||
|
if !ok || node == nil {
|
||||||
|
return [32]byte{}
|
||||||
|
}
|
||||||
|
if node.full {
|
||||||
|
return node.block.payloadHash
|
||||||
|
}
|
||||||
|
if node.block.fullParent == nil {
|
||||||
|
return [32]byte{}
|
||||||
|
}
|
||||||
|
return node.block.fullParent.block.payloadHash
|
||||||
}
|
}
|
||||||
|
|
||||||
// FinalizedPayloadBlockHash returns the hash of the payload at the finalized checkpoint
|
// FinalizedPayloadBlockHash returns the hash of the payload at the finalized checkpoint
|
||||||
func (f *ForkChoice) FinalizedPayloadBlockHash() [32]byte {
|
func (f *ForkChoice) FinalizedPayloadBlockHash() [32]byte {
|
||||||
root := f.FinalizedCheckpoint().Root
|
return f.presentHashForBlockRoot(f.FinalizedCheckpoint().Root)
|
||||||
node, ok := f.store.nodeByRoot[root]
|
|
||||||
if !ok || node == nil {
|
|
||||||
// This should not happen
|
|
||||||
return [32]byte{}
|
|
||||||
}
|
|
||||||
return node.payloadHash
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// JustifiedPayloadBlockHash returns the hash of the payload at the justified checkpoint
|
// JustifiedPayloadBlockHash returns the hash of the payload at the justified checkpoint
|
||||||
func (f *ForkChoice) JustifiedPayloadBlockHash() [32]byte {
|
func (f *ForkChoice) JustifiedPayloadBlockHash() [32]byte {
|
||||||
root := f.JustifiedCheckpoint().Root
|
return f.presentHashForBlockRoot(f.JustifiedCheckpoint().Root)
|
||||||
node, ok := f.store.nodeByRoot[root]
|
|
||||||
if !ok || node == nil {
|
|
||||||
// This should not happen
|
|
||||||
return [32]byte{}
|
|
||||||
}
|
|
||||||
return node.payloadHash
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnrealizedJustifiedPayloadBlockHash returns the hash of the payload at the unrealized justified checkpoint
|
// UnrealizedJustifiedPayloadBlockHash returns the hash of the payload at the unrealized justified checkpoint
|
||||||
func (f *ForkChoice) UnrealizedJustifiedPayloadBlockHash() [32]byte {
|
func (f *ForkChoice) UnrealizedJustifiedPayloadBlockHash() [32]byte {
|
||||||
root := f.store.unrealizedJustifiedCheckpoint.Root
|
return f.presentHashForBlockRoot(f.store.unrealizedJustifiedCheckpoint.Root)
|
||||||
node, ok := f.store.nodeByRoot[root]
|
|
||||||
if !ok || node == nil {
|
|
||||||
// This should not happen
|
|
||||||
return [32]byte{}
|
|
||||||
}
|
|
||||||
return node.payloadHash
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForkChoiceDump returns a full dump of forkchoice.
|
// ForkChoiceDump returns a full dump of forkchoice.
|
||||||
@@ -565,7 +612,7 @@ func (f *ForkChoice) ForkChoiceDump(ctx context.Context) (*forkchoice2.Dump, err
|
|||||||
}
|
}
|
||||||
var headRoot [32]byte
|
var headRoot [32]byte
|
||||||
if f.store.headNode != nil {
|
if f.store.headNode != nil {
|
||||||
headRoot = f.store.headNode.root
|
headRoot = f.store.headNode.block.root
|
||||||
}
|
}
|
||||||
resp := &forkchoice2.Dump{
|
resp := &forkchoice2.Dump{
|
||||||
JustifiedCheckpoint: jc,
|
JustifiedCheckpoint: jc,
|
||||||
@@ -587,7 +634,7 @@ func (f *ForkChoice) SetBalancesByRooter(handler forkchoice.BalancesByRooter) {
|
|||||||
|
|
||||||
// Weight returns the weight of the given root if found on the store
|
// Weight returns the weight of the given root if found on the store
|
||||||
func (f *ForkChoice) Weight(root [32]byte) (uint64, error) {
|
func (f *ForkChoice) Weight(root [32]byte) (uint64, error) {
|
||||||
n, ok := f.store.nodeByRoot[root]
|
n, ok := f.store.emptyNodeByRoot[root]
|
||||||
if !ok || n == nil {
|
if !ok || n == nil {
|
||||||
return 0, ErrNilNode
|
return 0, ErrNilNode
|
||||||
}
|
}
|
||||||
@@ -615,11 +662,11 @@ func (f *ForkChoice) updateJustifiedBalances(ctx context.Context, root [32]byte)
|
|||||||
|
|
||||||
// Slot returns the slot of the given root if it's known to forkchoice
|
// Slot returns the slot of the given root if it's known to forkchoice
|
||||||
func (f *ForkChoice) Slot(root [32]byte) (primitives.Slot, error) {
|
func (f *ForkChoice) Slot(root [32]byte) (primitives.Slot, error) {
|
||||||
n, ok := f.store.nodeByRoot[root]
|
n, ok := f.store.emptyNodeByRoot[root]
|
||||||
if !ok || n == nil {
|
if !ok || n == nil {
|
||||||
return 0, ErrNilNode
|
return 0, ErrNilNode
|
||||||
}
|
}
|
||||||
return n.slot, nil
|
return n.block.slot, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TargetRootForEpoch returns the root of the target block for a given epoch.
|
// TargetRootForEpoch returns the root of the target block for a given epoch.
|
||||||
@@ -631,28 +678,25 @@ func (f *ForkChoice) Slot(root [32]byte) (primitives.Slot, error) {
|
|||||||
// which case we return the root of the checkpoint of the chain containing the
|
// which case we return the root of the checkpoint of the chain containing the
|
||||||
// passed root, at the given epoch
|
// passed root, at the given epoch
|
||||||
func (f *ForkChoice) TargetRootForEpoch(root [32]byte, epoch primitives.Epoch) ([32]byte, error) {
|
func (f *ForkChoice) TargetRootForEpoch(root [32]byte, epoch primitives.Epoch) ([32]byte, error) {
|
||||||
n, ok := f.store.nodeByRoot[root]
|
n, ok := f.store.emptyNodeByRoot[root]
|
||||||
if !ok || n == nil {
|
if !ok || n == nil {
|
||||||
return [32]byte{}, ErrNilNode
|
return [32]byte{}, ErrNilNode
|
||||||
}
|
}
|
||||||
nodeEpoch := slots.ToEpoch(n.slot)
|
nodeEpoch := slots.ToEpoch(n.block.slot)
|
||||||
if epoch > nodeEpoch {
|
if epoch > nodeEpoch {
|
||||||
return n.root, nil
|
return n.block.root, nil
|
||||||
}
|
}
|
||||||
if n.target == nil {
|
targetNode := n.block.target
|
||||||
|
if targetNode == nil {
|
||||||
return [32]byte{}, nil
|
return [32]byte{}, nil
|
||||||
}
|
}
|
||||||
targetRoot := n.target.root
|
targetRoot := targetNode.root
|
||||||
if epoch == nodeEpoch {
|
if epoch == nodeEpoch {
|
||||||
return targetRoot, nil
|
return targetRoot, nil
|
||||||
}
|
}
|
||||||
targetNode, ok := f.store.nodeByRoot[targetRoot]
|
|
||||||
if !ok || targetNode == nil {
|
|
||||||
return [32]byte{}, ErrNilNode
|
|
||||||
}
|
|
||||||
// If slot 0 was not missed we consider a previous block to go back at least one epoch
|
// If slot 0 was not missed we consider a previous block to go back at least one epoch
|
||||||
if nodeEpoch == slots.ToEpoch(targetNode.slot) {
|
if nodeEpoch == slots.ToEpoch(targetNode.slot) {
|
||||||
targetNode = targetNode.parent
|
targetNode = targetNode.parent.block
|
||||||
if targetNode == nil {
|
if targetNode == nil {
|
||||||
return [32]byte{}, ErrNilNode
|
return [32]byte{}, ErrNilNode
|
||||||
}
|
}
|
||||||
@@ -664,13 +708,13 @@ func (f *ForkChoice) TargetRootForEpoch(root [32]byte, epoch primitives.Epoch) (
|
|||||||
// The exception is for the finalized checkpoint root which we return the zero
|
// The exception is for the finalized checkpoint root which we return the zero
|
||||||
// hash.
|
// hash.
|
||||||
func (f *ForkChoice) ParentRoot(root [32]byte) ([32]byte, error) {
|
func (f *ForkChoice) ParentRoot(root [32]byte) ([32]byte, error) {
|
||||||
n, ok := f.store.nodeByRoot[root]
|
n, ok := f.store.emptyNodeByRoot[root]
|
||||||
if !ok || n == nil {
|
if !ok || n == nil {
|
||||||
return [32]byte{}, ErrNilNode
|
return [32]byte{}, ErrNilNode
|
||||||
}
|
}
|
||||||
// Return the zero hash for the tree root
|
// Return the zero hash for the tree root
|
||||||
if n.parent == nil {
|
if n.block.parent == nil {
|
||||||
return [32]byte{}, nil
|
return [32]byte{}, nil
|
||||||
}
|
}
|
||||||
return n.parent.root, nil
|
return n.block.parent.block.root, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/prysmaticlabs/go-bitfield"
|
||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice"
|
||||||
forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types"
|
forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types"
|
||||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||||
@@ -23,9 +24,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// prepareForkchoiceState prepares a beacon State with the given data to mock
|
// prepareForkchoiceState prepares a beacon State with the given data to mock
|
||||||
// insert into forkchoice
|
// insert into forkchoice. This method prepares full states and blocks for
|
||||||
|
// bellatrix, it cannot be used for ePBS tests.
|
||||||
func prepareForkchoiceState(
|
func prepareForkchoiceState(
|
||||||
_ context.Context,
|
_ context.Context,
|
||||||
|
f *ForkChoice,
|
||||||
slot primitives.Slot,
|
slot primitives.Slot,
|
||||||
blockRoot [32]byte,
|
blockRoot [32]byte,
|
||||||
parentRoot [32]byte,
|
parentRoot [32]byte,
|
||||||
@@ -48,7 +51,11 @@ func prepareForkchoiceState(
|
|||||||
finalizedCheckpoint := ðpb.Checkpoint{
|
finalizedCheckpoint := ðpb.Checkpoint{
|
||||||
Epoch: finalizedEpoch,
|
Epoch: finalizedEpoch,
|
||||||
}
|
}
|
||||||
|
var parentHash [32]byte
|
||||||
|
parentNode, ok := f.store.emptyNodeByRoot[parentRoot]
|
||||||
|
if ok {
|
||||||
|
parentHash = parentNode.block.payloadHash
|
||||||
|
}
|
||||||
base := ðpb.BeaconStateBellatrix{
|
base := ðpb.BeaconStateBellatrix{
|
||||||
Slot: slot,
|
Slot: slot,
|
||||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||||
@@ -68,7 +75,8 @@ func prepareForkchoiceState(
|
|||||||
ParentRoot: parentRoot[:],
|
ParentRoot: parentRoot[:],
|
||||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||||
ExecutionPayload: &enginev1.ExecutionPayload{
|
ExecutionPayload: &enginev1.ExecutionPayload{
|
||||||
BlockHash: payloadHash[:],
|
BlockHash: payloadHash[:],
|
||||||
|
ParentHash: parentHash[:],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -81,16 +89,24 @@ func prepareForkchoiceState(
|
|||||||
return st, roblock, err
|
return st, roblock, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to set all bits in a Bitvector512
|
||||||
|
func setAllBits(bv bitfield.Bitvector512) bitfield.Bitvector512 {
|
||||||
|
for i := 0; i < len(bv); i++ {
|
||||||
|
bv[i] = 0xFF
|
||||||
|
}
|
||||||
|
return bv
|
||||||
|
}
|
||||||
|
|
||||||
func TestForkChoice_UpdateBalancesPositiveChange(t *testing.T) {
|
func TestForkChoice_UpdateBalancesPositiveChange(t *testing.T) {
|
||||||
f := setup(0, 0)
|
f := setup(0, 0)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
st, roblock, err := prepareForkchoiceState(ctx, f, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
|
|
||||||
@@ -105,27 +121,27 @@ func TestForkChoice_UpdateBalancesPositiveChange(t *testing.T) {
|
|||||||
f.justifiedBalances = []uint64{10, 20, 30}
|
f.justifiedBalances = []uint64{10, 20, 30}
|
||||||
require.NoError(t, f.updateBalances())
|
require.NoError(t, f.updateBalances())
|
||||||
s := f.store
|
s := f.store
|
||||||
assert.Equal(t, uint64(10), s.nodeByRoot[indexToHash(1)].balance)
|
assert.Equal(t, uint64(10), s.emptyNodeByRoot[indexToHash(1)].block.balance)
|
||||||
assert.Equal(t, uint64(20), s.nodeByRoot[indexToHash(2)].balance)
|
assert.Equal(t, uint64(20), s.emptyNodeByRoot[indexToHash(2)].block.balance)
|
||||||
assert.Equal(t, uint64(30), s.nodeByRoot[indexToHash(3)].balance)
|
assert.Equal(t, uint64(30), s.emptyNodeByRoot[indexToHash(3)].block.balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestForkChoice_UpdateBalancesNegativeChange(t *testing.T) {
|
func TestForkChoice_UpdateBalancesNegativeChange(t *testing.T) {
|
||||||
f := setup(0, 0)
|
f := setup(0, 0)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
st, roblock, err := prepareForkchoiceState(ctx, f, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
s := f.store
|
s := f.store
|
||||||
s.nodeByRoot[indexToHash(1)].balance = 100
|
s.emptyNodeByRoot[indexToHash(1)].block.balance = 100
|
||||||
s.nodeByRoot[indexToHash(2)].balance = 100
|
s.emptyNodeByRoot[indexToHash(2)].block.balance = 100
|
||||||
s.nodeByRoot[indexToHash(3)].balance = 100
|
s.emptyNodeByRoot[indexToHash(3)].block.balance = 100
|
||||||
|
|
||||||
f.balances = []uint64{100, 100, 100}
|
f.balances = []uint64{100, 100, 100}
|
||||||
f.votes = []Vote{
|
f.votes = []Vote{
|
||||||
@@ -136,27 +152,27 @@ func TestForkChoice_UpdateBalancesNegativeChange(t *testing.T) {
|
|||||||
|
|
||||||
f.justifiedBalances = []uint64{10, 20, 30}
|
f.justifiedBalances = []uint64{10, 20, 30}
|
||||||
require.NoError(t, f.updateBalances())
|
require.NoError(t, f.updateBalances())
|
||||||
assert.Equal(t, uint64(10), s.nodeByRoot[indexToHash(1)].balance)
|
assert.Equal(t, uint64(10), s.emptyNodeByRoot[indexToHash(1)].block.balance)
|
||||||
assert.Equal(t, uint64(20), s.nodeByRoot[indexToHash(2)].balance)
|
assert.Equal(t, uint64(20), s.emptyNodeByRoot[indexToHash(2)].block.balance)
|
||||||
assert.Equal(t, uint64(30), s.nodeByRoot[indexToHash(3)].balance)
|
assert.Equal(t, uint64(30), s.emptyNodeByRoot[indexToHash(3)].block.balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestForkChoice_UpdateBalancesUnderflow(t *testing.T) {
|
func TestForkChoice_UpdateBalancesUnderflow(t *testing.T) {
|
||||||
f := setup(0, 0)
|
f := setup(0, 0)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
st, roblock, err := prepareForkchoiceState(ctx, f, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
s := f.store
|
s := f.store
|
||||||
s.nodeByRoot[indexToHash(1)].balance = 100
|
s.emptyNodeByRoot[indexToHash(1)].block.balance = 100
|
||||||
s.nodeByRoot[indexToHash(2)].balance = 100
|
s.emptyNodeByRoot[indexToHash(2)].block.balance = 100
|
||||||
s.nodeByRoot[indexToHash(3)].balance = 100
|
s.emptyNodeByRoot[indexToHash(3)].block.balance = 100
|
||||||
|
|
||||||
f.balances = []uint64{125, 125, 125}
|
f.balances = []uint64{125, 125, 125}
|
||||||
f.votes = []Vote{
|
f.votes = []Vote{
|
||||||
@@ -167,30 +183,53 @@ func TestForkChoice_UpdateBalancesUnderflow(t *testing.T) {
|
|||||||
|
|
||||||
f.justifiedBalances = []uint64{10, 20, 30}
|
f.justifiedBalances = []uint64{10, 20, 30}
|
||||||
require.NoError(t, f.updateBalances())
|
require.NoError(t, f.updateBalances())
|
||||||
assert.Equal(t, uint64(0), s.nodeByRoot[indexToHash(1)].balance)
|
assert.Equal(t, uint64(0), s.emptyNodeByRoot[indexToHash(1)].block.balance)
|
||||||
assert.Equal(t, uint64(0), s.nodeByRoot[indexToHash(2)].balance)
|
assert.Equal(t, uint64(0), s.emptyNodeByRoot[indexToHash(2)].block.balance)
|
||||||
assert.Equal(t, uint64(5), s.nodeByRoot[indexToHash(3)].balance)
|
assert.Equal(t, uint64(5), s.emptyNodeByRoot[indexToHash(3)].block.balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestForkChoice_IsCanonical(t *testing.T) {
|
func TestForkChoice_IsCanonical(t *testing.T) {
|
||||||
f := setup(1, 1)
|
f := setup(1, 1)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
// there are two nodes in f, the full and empty nodes
|
||||||
|
require.Equal(t, 1, len(f.store.emptyNodeByRoot))
|
||||||
|
require.Equal(t, 1, len(f.store.fullNodeByPayload))
|
||||||
|
// The full node is the tree root node
|
||||||
|
require.Equal(t, true, f.store.treeRootNode.full)
|
||||||
|
fullRoot := f.store.treeRootNode
|
||||||
|
// Both blocks have nil best descendants
|
||||||
|
emptyRoot := f.store.emptyNodeByRoot[[32]byte{}]
|
||||||
|
require.Equal(t, emptyRoot.block, f.store.treeRootNode.block)
|
||||||
|
require.Equal(t, false, emptyRoot.full)
|
||||||
|
require.IsNil(t, emptyRoot.bestDescendant)
|
||||||
|
require.IsNil(t, fullRoot.bestDescendant)
|
||||||
|
require.Equal(t, fullRoot, f.store.fullNodeByPayload[[32]byte{}])
|
||||||
|
require.Equal(t, fullRoot, f.store.fullNodeByPayload[emptyRoot.block.payloadHash])
|
||||||
|
|
||||||
|
st, roblock, err := prepareForkchoiceState(ctx, f, 1, indexToHash(1), params.BeaconConfig().ZeroHash, indexToHash(101), 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
// This block descends from the full node
|
||||||
|
require.Equal(t, 2, len(fullRoot.children))
|
||||||
|
require.Equal(t, 0, len(emptyRoot.children))
|
||||||
|
// The full block is the head of chain
|
||||||
|
fullOne := f.store.fullNodeByPayload[indexToHash(101)]
|
||||||
|
require.Equal(t, fullRoot.bestDescendant, fullOne)
|
||||||
|
require.Equal(t, true, f.IsCanonical([32]byte{}))
|
||||||
|
|
||||||
|
st, roblock, err = prepareForkchoiceState(ctx, f, 2, indexToHash(2), params.BeaconConfig().ZeroHash, indexToHash(102), 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 3, indexToHash(3), indexToHash(1), indexToHash(103), 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 4, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 4, indexToHash(4), indexToHash(2), indexToHash(104), 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 5, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 5, indexToHash(5), indexToHash(4), indexToHash(105), 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 6, indexToHash(6), indexToHash(5), params.BeaconConfig().ZeroHash, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 6, indexToHash(6), indexToHash(5), indexToHash(106), 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
|
|
||||||
@@ -205,40 +244,56 @@ func TestForkChoice_IsCanonical(t *testing.T) {
|
|||||||
|
|
||||||
func TestForkChoice_IsCanonicalReorg(t *testing.T) {
|
func TestForkChoice_IsCanonicalReorg(t *testing.T) {
|
||||||
f := setup(1, 1)
|
f := setup(1, 1)
|
||||||
|
rootNode := f.store.treeRootNode
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 1, [32]byte{'1'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
st, roblock, err := prepareForkchoiceState(ctx, f, 1, [32]byte{'1'}, params.BeaconConfig().ZeroHash, indexToHash(101), 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 2, [32]byte{'2'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
// We have two children for the full root node
|
||||||
|
require.Equal(t, rootNode, f.store.treeRootNode)
|
||||||
|
require.Equal(t, true, rootNode.full)
|
||||||
|
require.Equal(t, 2, len(rootNode.children))
|
||||||
|
|
||||||
|
st, roblock, err = prepareForkchoiceState(ctx, f, 2, [32]byte{'2'}, params.BeaconConfig().ZeroHash, indexToHash(102), 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 3, [32]byte{'3'}, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1)
|
// We have now 4 children on the rootnode:
|
||||||
|
require.Equal(t, rootNode, f.store.treeRootNode)
|
||||||
|
require.Equal(t, true, rootNode.full)
|
||||||
|
require.Equal(t, 4, len(rootNode.children))
|
||||||
|
|
||||||
|
fullOne := f.store.fullNodeByPayload[indexToHash(101)]
|
||||||
|
require.Equal(t, 0, len(fullOne.children))
|
||||||
|
st, roblock, err = prepareForkchoiceState(ctx, f, 3, [32]byte{'3'}, [32]byte{'1'}, indexToHash(103), 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 4, [32]byte{'4'}, [32]byte{'2'}, params.BeaconConfig().ZeroHash, 1, 1)
|
require.Equal(t, 2, len(fullOne.children))
|
||||||
|
|
||||||
|
st, roblock, err = prepareForkchoiceState(ctx, f, 4, [32]byte{'4'}, [32]byte{'2'}, indexToHash(104), 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 5, [32]byte{'5'}, [32]byte{'4'}, params.BeaconConfig().ZeroHash, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 5, [32]byte{'5'}, [32]byte{'4'}, indexToHash(105), 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 6, [32]byte{'6'}, [32]byte{'5'}, params.BeaconConfig().ZeroHash, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 6, [32]byte{'6'}, [32]byte{'5'}, indexToHash(106), 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
|
|
||||||
f.store.nodeByRoot[[32]byte{'3'}].balance = 10
|
f.store.fullNodeByPayload[indexToHash(103)].block.balance = 10
|
||||||
require.NoError(t, f.store.treeRootNode.applyWeightChanges(ctx))
|
require.NoError(t, f.store.treeRootNode.applyWeightChanges(ctx))
|
||||||
require.Equal(t, uint64(10), f.store.nodeByRoot[[32]byte{'1'}].weight)
|
// We get 20 for 101 because both empty and full nodes contribute to it.
|
||||||
require.Equal(t, uint64(0), f.store.nodeByRoot[[32]byte{'2'}].weight)
|
require.Equal(t, uint64(10), f.store.fullNodeByPayload[indexToHash(101)].weight)
|
||||||
|
require.Equal(t, uint64(0), f.store.emptyNodeByRoot[[32]byte{'2'}].weight)
|
||||||
|
|
||||||
require.NoError(t, f.store.treeRootNode.updateBestDescendant(ctx, 1, 1, 1))
|
require.NoError(t, f.store.treeRootNode.updateBestDescendant(ctx, 1, 1, 1))
|
||||||
require.DeepEqual(t, [32]byte{'3'}, f.store.treeRootNode.bestDescendant.root)
|
require.DeepEqual(t, [32]byte{'3'}, f.store.treeRootNode.bestDescendant.block.root)
|
||||||
|
|
||||||
r1 := [32]byte{'1'}
|
r1 := [32]byte{'1'}
|
||||||
f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 1, Root: r1}
|
f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 1, Root: r1}
|
||||||
h, err := f.store.head(ctx)
|
h, err := f.store.head(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.DeepEqual(t, [32]byte{'3'}, h)
|
require.DeepEqual(t, [32]byte{'3'}, h)
|
||||||
require.DeepEqual(t, h, f.store.headNode.root)
|
require.DeepEqual(t, h, f.store.headNode.block.root)
|
||||||
|
|
||||||
require.Equal(t, true, f.IsCanonical(params.BeaconConfig().ZeroHash))
|
require.Equal(t, true, f.IsCanonical(params.BeaconConfig().ZeroHash))
|
||||||
require.Equal(t, true, f.IsCanonical([32]byte{'1'}))
|
require.Equal(t, true, f.IsCanonical([32]byte{'1'}))
|
||||||
@@ -252,17 +307,17 @@ func TestForkChoice_IsCanonicalReorg(t *testing.T) {
|
|||||||
func TestForkChoice_AncestorRoot(t *testing.T) {
|
func TestForkChoice_AncestorRoot(t *testing.T) {
|
||||||
f := setup(1, 1)
|
f := setup(1, 1)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
st, roblock, err := prepareForkchoiceState(ctx, f, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 5, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 5, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
f.store.treeRootNode = f.store.nodeByRoot[indexToHash(1)]
|
f.store.treeRootNode = f.store.emptyNodeByRoot[indexToHash(1)]
|
||||||
f.store.treeRootNode.parent = nil
|
f.store.treeRootNode.block.parent = nil
|
||||||
|
|
||||||
r, err := f.AncestorRoot(ctx, indexToHash(3), 6)
|
r, err := f.AncestorRoot(ctx, indexToHash(3), 6)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -284,10 +339,10 @@ func TestForkChoice_AncestorRoot(t *testing.T) {
|
|||||||
func TestForkChoice_AncestorEqualSlot(t *testing.T) {
|
func TestForkChoice_AncestorEqualSlot(t *testing.T) {
|
||||||
f := setup(1, 1)
|
f := setup(1, 1)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
st, roblock, err := prepareForkchoiceState(ctx, f, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 101, [32]byte{'3'}, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 101, [32]byte{'3'}, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
|
|
||||||
@@ -299,10 +354,10 @@ func TestForkChoice_AncestorEqualSlot(t *testing.T) {
|
|||||||
func TestForkChoice_AncestorLowerSlot(t *testing.T) {
|
func TestForkChoice_AncestorLowerSlot(t *testing.T) {
|
||||||
f := setup(1, 1)
|
f := setup(1, 1)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
st, roblock, err := prepareForkchoiceState(ctx, f, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 200, [32]byte{'3'}, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 200, [32]byte{'3'}, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
|
|
||||||
@@ -315,7 +370,7 @@ func TestForkChoice_RemoveEquivocating(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
f := setup(1, 1)
|
f := setup(1, 1)
|
||||||
// Insert a block it will be head
|
// Insert a block it will be head
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 1, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1)
|
st, roblock, err := prepareForkchoiceState(ctx, f, 1, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
head, err := f.Head(ctx)
|
head, err := f.Head(ctx)
|
||||||
@@ -323,10 +378,10 @@ func TestForkChoice_RemoveEquivocating(t *testing.T) {
|
|||||||
require.Equal(t, [32]byte{'a'}, head)
|
require.Equal(t, [32]byte{'a'}, head)
|
||||||
|
|
||||||
// Insert two extra blocks
|
// Insert two extra blocks
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 2, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 2, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 3, [32]byte{'c'}, [32]byte{'a'}, [32]byte{'C'}, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 3, [32]byte{'c'}, [32]byte{'a'}, [32]byte{'C'}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
head, err = f.Head(ctx)
|
head, err = f.Head(ctx)
|
||||||
@@ -343,21 +398,21 @@ func TestForkChoice_RemoveEquivocating(t *testing.T) {
|
|||||||
|
|
||||||
// Process b's slashing, c is now head
|
// Process b's slashing, c is now head
|
||||||
f.InsertSlashedIndex(ctx, 1)
|
f.InsertSlashedIndex(ctx, 1)
|
||||||
require.Equal(t, uint64(200), f.store.nodeByRoot[[32]byte{'b'}].balance)
|
require.Equal(t, uint64(200), f.store.emptyNodeByRoot[[32]byte{'b'}].block.balance)
|
||||||
f.justifiedBalances = []uint64{100, 200, 200, 300}
|
f.justifiedBalances = []uint64{100, 200, 200, 300}
|
||||||
head, err = f.Head(ctx)
|
head, err = f.Head(ctx)
|
||||||
require.Equal(t, uint64(200), f.store.nodeByRoot[[32]byte{'b'}].weight)
|
require.Equal(t, uint64(200), f.store.emptyNodeByRoot[[32]byte{'b'}].weight)
|
||||||
require.Equal(t, uint64(300), f.store.nodeByRoot[[32]byte{'c'}].weight)
|
require.Equal(t, uint64(300), f.store.emptyNodeByRoot[[32]byte{'c'}].weight)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, [32]byte{'c'}, head)
|
require.Equal(t, [32]byte{'c'}, head)
|
||||||
|
|
||||||
// Process b's slashing again, should be a noop
|
// Process b's slashing again, should be a noop
|
||||||
f.InsertSlashedIndex(ctx, 1)
|
f.InsertSlashedIndex(ctx, 1)
|
||||||
require.Equal(t, uint64(200), f.store.nodeByRoot[[32]byte{'b'}].balance)
|
require.Equal(t, uint64(200), f.store.emptyNodeByRoot[[32]byte{'b'}].block.balance)
|
||||||
f.justifiedBalances = []uint64{100, 200, 200, 300}
|
f.justifiedBalances = []uint64{100, 200, 200, 300}
|
||||||
head, err = f.Head(ctx)
|
head, err = f.Head(ctx)
|
||||||
require.Equal(t, uint64(200), f.store.nodeByRoot[[32]byte{'b'}].weight)
|
require.Equal(t, uint64(200), f.store.emptyNodeByRoot[[32]byte{'b'}].weight)
|
||||||
require.Equal(t, uint64(300), f.store.nodeByRoot[[32]byte{'c'}].weight)
|
require.Equal(t, uint64(300), f.store.emptyNodeByRoot[[32]byte{'c'}].weight)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, [32]byte{'c'}, head)
|
require.Equal(t, [32]byte{'c'}, head)
|
||||||
|
|
||||||
@@ -397,34 +452,34 @@ func TestStore_CommonAncestor(t *testing.T) {
|
|||||||
// \-- c -- f
|
// \-- c -- f
|
||||||
// \-- g
|
// \-- g
|
||||||
// \ -- h -- i -- j
|
// \ -- h -- i -- j
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 0, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1)
|
st, roblock, err := prepareForkchoiceState(ctx, f, 0, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 1, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 1, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 2, [32]byte{'c'}, [32]byte{'a'}, [32]byte{'C'}, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 2, [32]byte{'c'}, [32]byte{'a'}, [32]byte{'C'}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 3, [32]byte{'d'}, [32]byte{'b'}, [32]byte{}, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 3, [32]byte{'d'}, [32]byte{'b'}, [32]byte{}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 4, [32]byte{'e'}, [32]byte{'d'}, [32]byte{}, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 4, [32]byte{'e'}, [32]byte{'d'}, [32]byte{}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 5, [32]byte{'f'}, [32]byte{'c'}, [32]byte{}, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 5, [32]byte{'f'}, [32]byte{'c'}, [32]byte{}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 6, [32]byte{'g'}, [32]byte{'c'}, [32]byte{}, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 6, [32]byte{'g'}, [32]byte{'c'}, [32]byte{}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 7, [32]byte{'h'}, [32]byte{'c'}, [32]byte{}, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 7, [32]byte{'h'}, [32]byte{'c'}, [32]byte{}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 8, [32]byte{'i'}, [32]byte{'h'}, [32]byte{}, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 8, [32]byte{'i'}, [32]byte{'h'}, [32]byte{}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 9, [32]byte{'j'}, [32]byte{'i'}, [32]byte{}, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 9, [32]byte{'j'}, [32]byte{'i'}, [32]byte{}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
|
|
||||||
@@ -517,16 +572,16 @@ func TestStore_CommonAncestor(t *testing.T) {
|
|||||||
|
|
||||||
// a -- b -- c -- d
|
// a -- b -- c -- d
|
||||||
f = setup(0, 0)
|
f = setup(0, 0)
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 0, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 0, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 1, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 1, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 2, [32]byte{'c'}, [32]byte{'b'}, [32]byte{'C'}, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 2, [32]byte{'c'}, [32]byte{'b'}, [32]byte{'C'}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 3, [32]byte{'d'}, [32]byte{'c'}, [32]byte{}, 1, 1)
|
st, roblock, err = prepareForkchoiceState(ctx, f, 3, [32]byte{'d'}, [32]byte{'c'}, [32]byte{'D'}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
tests = []struct {
|
tests = []struct {
|
||||||
@@ -583,16 +638,18 @@ func TestStore_CommonAncestor(t *testing.T) {
|
|||||||
_, _, err = f.CommonAncestor(ctx, [32]byte{'z'}, [32]byte{'a'})
|
_, _, err = f.CommonAncestor(ctx, [32]byte{'z'}, [32]byte{'a'})
|
||||||
require.ErrorIs(t, err, forkchoice.ErrUnknownCommonAncestor)
|
require.ErrorIs(t, err, forkchoice.ErrUnknownCommonAncestor)
|
||||||
n := &Node{
|
n := &Node{
|
||||||
slot: 100,
|
block: &BlockNode{
|
||||||
root: [32]byte{'y'},
|
slot: 100,
|
||||||
justifiedEpoch: 1,
|
root: [32]byte{'y'},
|
||||||
unrealizedJustifiedEpoch: 1,
|
justifiedEpoch: 1,
|
||||||
finalizedEpoch: 1,
|
unrealizedJustifiedEpoch: 1,
|
||||||
unrealizedFinalizedEpoch: 1,
|
finalizedEpoch: 1,
|
||||||
optimistic: true,
|
unrealizedFinalizedEpoch: 1,
|
||||||
|
},
|
||||||
|
optimistic: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
f.store.nodeByRoot[[32]byte{'y'}] = n
|
f.store.emptyNodeByRoot[[32]byte{'y'}] = n
|
||||||
// broken link
|
// broken link
|
||||||
_, _, err = f.CommonAncestor(ctx, [32]byte{'y'}, [32]byte{'a'})
|
_, _, err = f.CommonAncestor(ctx, [32]byte{'y'}, [32]byte{'a'})
|
||||||
require.ErrorIs(t, err, forkchoice.ErrUnknownCommonAncestor)
|
require.ErrorIs(t, err, forkchoice.ErrUnknownCommonAncestor)
|
||||||
@@ -693,23 +750,23 @@ func TestForkChoice_UpdateCheckpoints(t *testing.T) {
|
|||||||
fcs.store.finalizedCheckpoint = tt.finalized
|
fcs.store.finalizedCheckpoint = tt.finalized
|
||||||
fcs.store.genesisTime = uint64(time.Now().Unix()) - uint64(tt.currentSlot)*params.BeaconConfig().SecondsPerSlot
|
fcs.store.genesisTime = uint64(time.Now().Unix()) - uint64(tt.currentSlot)*params.BeaconConfig().SecondsPerSlot
|
||||||
|
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 32, [32]byte{'f'},
|
st, roblock, err := prepareForkchoiceState(ctx, fcs, 32, [32]byte{'f'},
|
||||||
[32]byte{}, [32]byte{}, tt.finalized.Epoch, tt.finalized.Epoch)
|
[32]byte{}, [32]byte{}, tt.finalized.Epoch, tt.finalized.Epoch)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, st, roblock))
|
require.NoError(t, fcs.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 64, [32]byte{'j'},
|
st, roblock, err = prepareForkchoiceState(ctx, fcs, 64, [32]byte{'j'},
|
||||||
[32]byte{'f'}, [32]byte{}, tt.justified.Epoch, tt.finalized.Epoch)
|
[32]byte{'f'}, [32]byte{}, tt.justified.Epoch, tt.finalized.Epoch)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, st, roblock))
|
require.NoError(t, fcs.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 96, [32]byte{'b'},
|
st, roblock, err = prepareForkchoiceState(ctx, fcs, 96, [32]byte{'b'},
|
||||||
[32]byte{'j'}, [32]byte{}, tt.newJustified.Epoch, tt.newFinalized.Epoch)
|
[32]byte{'j'}, [32]byte{}, tt.newJustified.Epoch, tt.newFinalized.Epoch)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, st, roblock))
|
require.NoError(t, fcs.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 96, [32]byte{'c'},
|
st, roblock, err = prepareForkchoiceState(ctx, fcs, 96, [32]byte{'c'},
|
||||||
[32]byte{'f'}, [32]byte{}, tt.newJustified.Epoch, tt.newFinalized.Epoch)
|
[32]byte{'f'}, [32]byte{}, tt.newJustified.Epoch, tt.newFinalized.Epoch)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, st, roblock))
|
require.NoError(t, fcs.InsertNode(ctx, st, roblock))
|
||||||
st, roblock, err = prepareForkchoiceState(ctx, 65, [32]byte{'h'},
|
st, roblock, err = prepareForkchoiceState(ctx, fcs, 65, [32]byte{'h'},
|
||||||
[32]byte{'f'}, [32]byte{}, tt.newFinalized.Epoch, tt.newFinalized.Epoch)
|
[32]byte{'f'}, [32]byte{}, tt.newFinalized.Epoch, tt.newFinalized.Epoch)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, fcs.InsertNode(ctx, st, roblock))
|
require.NoError(t, fcs.InsertNode(ctx, st, roblock))
|
||||||
@@ -738,11 +795,11 @@ func TestWeight(t *testing.T) {
|
|||||||
f := setup(0, 0)
|
f := setup(0, 0)
|
||||||
|
|
||||||
root := [32]byte{'a'}
|
root := [32]byte{'a'}
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 0, root, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1)
|
st, roblock, err := prepareForkchoiceState(ctx, f, 0, root, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
|
|
||||||
n, ok := f.store.nodeByRoot[root]
|
n, ok := f.store.emptyNodeByRoot[root]
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
n.weight = 10
|
n.weight = 10
|
||||||
w, err := f.Weight(root)
|
w, err := f.Weight(root)
|
||||||
@@ -770,7 +827,7 @@ func TestForkChoice_UnrealizedJustifiedPayloadBlockHash(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
f := setup(0, 0)
|
f := setup(0, 0)
|
||||||
|
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 0, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1)
|
st, roblock, err := prepareForkchoiceState(ctx, f, 0, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
require.NoError(t, f.InsertNode(ctx, st, roblock))
|
||||||
|
|
||||||
@@ -783,7 +840,7 @@ func TestForkChoiceIsViableForCheckpoint(t *testing.T) {
|
|||||||
f := setup(0, 0)
|
f := setup(0, 0)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
st, blk, err := prepareForkchoiceState(ctx, 0, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0)
|
st, blk, err := prepareForkchoiceState(ctx, f, 0, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// No Node
|
// No Node
|
||||||
viable, err := f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk.Root()})
|
viable, err := f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk.Root()})
|
||||||
@@ -804,7 +861,7 @@ func TestForkChoiceIsViableForCheckpoint(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, true, viable)
|
require.Equal(t, true, viable)
|
||||||
|
|
||||||
st, blk2, err := prepareForkchoiceState(ctx, 1, [32]byte{'b'}, blk.Root(), [32]byte{'B'}, 0, 0)
|
st, blk2, err := prepareForkchoiceState(ctx, f, 1, [32]byte{'b'}, blk.Root(), [32]byte{'B'}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, blk2))
|
require.NoError(t, f.InsertNode(ctx, st, blk2))
|
||||||
|
|
||||||
@@ -826,7 +883,7 @@ func TestForkChoiceIsViableForCheckpoint(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, true, viable)
|
require.Equal(t, true, viable)
|
||||||
|
|
||||||
st, blk3, err := prepareForkchoiceState(ctx, 2, [32]byte{'c'}, blk2.Root(), [32]byte{'C'}, 0, 0)
|
st, blk3, err := prepareForkchoiceState(ctx, f, 2, [32]byte{'c'}, blk2.Root(), [32]byte{'C'}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, blk3))
|
require.NoError(t, f.InsertNode(ctx, st, blk3))
|
||||||
|
|
||||||
@@ -839,7 +896,7 @@ func TestForkChoiceIsViableForCheckpoint(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, false, viable)
|
require.Equal(t, false, viable)
|
||||||
|
|
||||||
st, blk4, err := prepareForkchoiceState(ctx, params.BeaconConfig().SlotsPerEpoch, [32]byte{'d'}, blk2.Root(), [32]byte{'D'}, 0, 0)
|
st, blk4, err := prepareForkchoiceState(ctx, f, params.BeaconConfig().SlotsPerEpoch, [32]byte{'d'}, blk2.Root(), [32]byte{'D'}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, blk4))
|
require.NoError(t, f.InsertNode(ctx, st, blk4))
|
||||||
|
|
||||||
@@ -862,7 +919,7 @@ func TestForkChoiceIsViableForCheckpoint(t *testing.T) {
|
|||||||
require.Equal(t, false, viable)
|
require.Equal(t, false, viable)
|
||||||
|
|
||||||
// Children in next epoch
|
// Children in next epoch
|
||||||
st, blk5, err := prepareForkchoiceState(ctx, params.BeaconConfig().SlotsPerEpoch+1, [32]byte{'e'}, blk2.Root(), [32]byte{'E'}, 0, 0)
|
st, blk5, err := prepareForkchoiceState(ctx, f, params.BeaconConfig().SlotsPerEpoch+1, [32]byte{'e'}, blk2.Root(), [32]byte{'E'}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, blk5))
|
require.NoError(t, f.InsertNode(ctx, st, blk5))
|
||||||
|
|
||||||
@@ -874,7 +931,7 @@ func TestForkChoiceIsViableForCheckpoint(t *testing.T) {
|
|||||||
func TestForkChoiceSlot(t *testing.T) {
|
func TestForkChoiceSlot(t *testing.T) {
|
||||||
f := setup(0, 0)
|
f := setup(0, 0)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
st, blk, err := prepareForkchoiceState(ctx, 3, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0)
|
st, blk, err := prepareForkchoiceState(ctx, f, 3, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// No Node
|
// No Node
|
||||||
_, err = f.Slot(blk.Root())
|
_, err = f.Slot(blk.Root())
|
||||||
@@ -890,12 +947,12 @@ func TestForkchoiceParentRoot(t *testing.T) {
|
|||||||
f := setup(0, 0)
|
f := setup(0, 0)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
root1 := [32]byte{'a'}
|
root1 := [32]byte{'a'}
|
||||||
st, blk, err := prepareForkchoiceState(ctx, 3, root1, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0)
|
st, blk, err := prepareForkchoiceState(ctx, f, 3, root1, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, blk))
|
require.NoError(t, f.InsertNode(ctx, st, blk))
|
||||||
|
|
||||||
root2 := [32]byte{'b'}
|
root2 := [32]byte{'b'}
|
||||||
st, blk, err = prepareForkchoiceState(ctx, 3, root2, root1, [32]byte{'A'}, 0, 0)
|
st, blk, err = prepareForkchoiceState(ctx, f, 3, root2, root1, [32]byte{'A'}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, blk))
|
require.NoError(t, f.InsertNode(ctx, st, blk))
|
||||||
|
|
||||||
@@ -915,7 +972,7 @@ func TestForkchoiceParentRoot(t *testing.T) {
|
|||||||
func TestForkChoice_CleanupInserting(t *testing.T) {
|
func TestForkChoice_CleanupInserting(t *testing.T) {
|
||||||
f := setup(0, 0)
|
f := setup(0, 0)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
st, roblock, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 2, 2)
|
st, roblock, err := prepareForkchoiceState(ctx, f, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 2, 2)
|
||||||
f.SetBalancesByRooter(func(_ context.Context, _ [32]byte) ([]uint64, error) {
|
f.SetBalancesByRooter(func(_ context.Context, _ [32]byte) ([]uint64, error) {
|
||||||
return f.justifiedBalances, errors.New("mock err")
|
return f.justifiedBalances, errors.New("mock err")
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,19 +8,19 @@ import (
|
|||||||
// LastRoot returns the last canonical block root in the given epoch
|
// LastRoot returns the last canonical block root in the given epoch
|
||||||
func (f *ForkChoice) LastRoot(epoch primitives.Epoch) [32]byte {
|
func (f *ForkChoice) LastRoot(epoch primitives.Epoch) [32]byte {
|
||||||
head := f.store.headNode
|
head := f.store.headNode
|
||||||
headEpoch := slots.ToEpoch(head.slot)
|
headEpoch := slots.ToEpoch(head.block.slot)
|
||||||
epochEnd, err := slots.EpochEnd(epoch)
|
epochEnd, err := slots.EpochEnd(epoch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return [32]byte{}
|
return [32]byte{}
|
||||||
}
|
}
|
||||||
if headEpoch <= epoch {
|
if headEpoch <= epoch {
|
||||||
return head.root
|
return head.block.root
|
||||||
}
|
}
|
||||||
for head != nil && head.slot > epochEnd {
|
for head != nil && head.block.slot > epochEnd {
|
||||||
head = head.parent
|
head = head.block.parent
|
||||||
}
|
}
|
||||||
if head == nil {
|
if head == nil {
|
||||||
return [32]byte{}
|
return [32]byte{}
|
||||||
}
|
}
|
||||||
return head.root
|
return head.block.root
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,27 +12,27 @@ func TestLastRoot(t *testing.T) {
|
|||||||
f := setup(0, 0)
|
f := setup(0, 0)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
st, root, err := prepareForkchoiceState(ctx, 1, [32]byte{'1'}, params.BeaconConfig().ZeroHash, [32]byte{'1'}, 0, 0)
|
st, root, err := prepareForkchoiceState(ctx, f, 1, [32]byte{'1'}, params.BeaconConfig().ZeroHash, [32]byte{'1'}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, root))
|
require.NoError(t, f.InsertNode(ctx, st, root))
|
||||||
st, root, err = prepareForkchoiceState(ctx, 2, [32]byte{'2'}, [32]byte{'1'}, [32]byte{'2'}, 0, 0)
|
st, root, err = prepareForkchoiceState(ctx, f, 2, [32]byte{'2'}, [32]byte{'1'}, [32]byte{'2'}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, root))
|
require.NoError(t, f.InsertNode(ctx, st, root))
|
||||||
st, root, err = prepareForkchoiceState(ctx, 3, [32]byte{'3'}, [32]byte{'1'}, [32]byte{'3'}, 0, 0)
|
st, root, err = prepareForkchoiceState(ctx, f, 3, [32]byte{'3'}, [32]byte{'1'}, [32]byte{'3'}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, root))
|
require.NoError(t, f.InsertNode(ctx, st, root))
|
||||||
st, root, err = prepareForkchoiceState(ctx, 32, [32]byte{'4'}, [32]byte{'3'}, [32]byte{'4'}, 0, 0)
|
st, root, err = prepareForkchoiceState(ctx, f, 32, [32]byte{'4'}, [32]byte{'3'}, [32]byte{'4'}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, root))
|
require.NoError(t, f.InsertNode(ctx, st, root))
|
||||||
st, root, err = prepareForkchoiceState(ctx, 33, [32]byte{'5'}, [32]byte{'2'}, [32]byte{'5'}, 0, 0)
|
st, root, err = prepareForkchoiceState(ctx, f, 33, [32]byte{'5'}, [32]byte{'2'}, [32]byte{'5'}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, root))
|
require.NoError(t, f.InsertNode(ctx, st, root))
|
||||||
st, root, err = prepareForkchoiceState(ctx, 34, [32]byte{'6'}, [32]byte{'5'}, [32]byte{'6'}, 0, 0)
|
st, root, err = prepareForkchoiceState(ctx, f, 34, [32]byte{'6'}, [32]byte{'5'}, [32]byte{'6'}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, st, root))
|
require.NoError(t, f.InsertNode(ctx, st, root))
|
||||||
headNode, _ := f.store.nodeByRoot[[32]byte{'6'}]
|
headNode, _ := f.store.emptyNodeByRoot[[32]byte{'6'}]
|
||||||
f.store.headNode = headNode
|
f.store.headNode = headNode
|
||||||
require.Equal(t, [32]byte{'6'}, f.store.headNode.root)
|
require.Equal(t, [32]byte{'6'}, f.store.headNode.block.root)
|
||||||
require.Equal(t, [32]byte{'2'}, f.LastRoot(0))
|
require.Equal(t, [32]byte{'2'}, f.LastRoot(0))
|
||||||
require.Equal(t, [32]byte{'6'}, f.LastRoot(1))
|
require.Equal(t, [32]byte{'6'}, f.LastRoot(1))
|
||||||
require.Equal(t, [32]byte{'6'}, f.LastRoot(2))
|
require.Equal(t, [32]byte{'6'}, f.LastRoot(2))
|
||||||
|
|||||||
@@ -18,7 +18,13 @@ var (
|
|||||||
nodeCount = promauto.NewGauge(
|
nodeCount = promauto.NewGauge(
|
||||||
prometheus.GaugeOpts{
|
prometheus.GaugeOpts{
|
||||||
Name: "doublylinkedtree_node_count",
|
Name: "doublylinkedtree_node_count",
|
||||||
Help: "The number of nodes in the doubly linked tree based store structure.",
|
Help: "The number of nodes for blocks in the doubly linked tree based store structure.",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
payloadCount = promauto.NewGauge(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "doublylinkedtree_payload_count",
|
||||||
|
Help: "The number of nodes for execution payloads in the doubly linked tree based store structure.",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
headChangesCount = promauto.NewCounter(
|
headChangesCount = promauto.NewCounter(
|
||||||
@@ -39,6 +45,12 @@ var (
|
|||||||
Help: "The number of times a block is processed for fork choice.",
|
Help: "The number of times a block is processed for fork choice.",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
processedPayloadCount = promauto.NewCounter(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Name: "doublylinkedtree_payload_processed_count",
|
||||||
|
Help: "The number of times an execution payload is processed for fork choice.",
|
||||||
|
},
|
||||||
|
)
|
||||||
processedAttestationCount = promauto.NewCounter(
|
processedAttestationCount = promauto.NewCounter(
|
||||||
prometheus.CounterOpts{
|
prometheus.CounterOpts{
|
||||||
Name: "doublylinkedtree_attestation_processed_count",
|
Name: "doublylinkedtree_attestation_processed_count",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
|||||||
// 0
|
// 0
|
||||||
// /
|
// /
|
||||||
// 2 <- head
|
// 2 <- head
|
||||||
state, blkRoot, err := prepareForkchoiceState(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
state, blkRoot, err := prepareForkchoiceState(context.Background(), f, 0, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
r, err = f.Head(context.Background())
|
r, err = f.Head(context.Background())
|
||||||
@@ -36,7 +36,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
|||||||
// 0
|
// 0
|
||||||
// / \
|
// / \
|
||||||
// head -> 2 1
|
// head -> 2 1
|
||||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
state, blkRoot, err = prepareForkchoiceState(context.Background(), f, 0, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
r, err = f.Head(context.Background())
|
r, err = f.Head(context.Background())
|
||||||
@@ -49,7 +49,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
|||||||
// head -> 2 1
|
// head -> 2 1
|
||||||
// |
|
// |
|
||||||
// 3
|
// 3
|
||||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 0, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1)
|
state, blkRoot, err = prepareForkchoiceState(context.Background(), f, 0, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
r, err = f.Head(context.Background())
|
r, err = f.Head(context.Background())
|
||||||
@@ -62,7 +62,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
|||||||
// 2 1
|
// 2 1
|
||||||
// | |
|
// | |
|
||||||
// head -> 4 3
|
// head -> 4 3
|
||||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 0, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1)
|
state, blkRoot, err = prepareForkchoiceState(context.Background(), f, 0, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
r, err = f.Head(context.Background())
|
r, err = f.Head(context.Background())
|
||||||
@@ -77,7 +77,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
|||||||
// head -> 4 3
|
// head -> 4 3
|
||||||
// |
|
// |
|
||||||
// 5 <- justified epoch = 2
|
// 5 <- justified epoch = 2
|
||||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 0, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 2, 1)
|
state, blkRoot, err = prepareForkchoiceState(context.Background(), f, 0, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 2, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
r, err = f.Head(context.Background())
|
r, err = f.Head(context.Background())
|
||||||
@@ -94,7 +94,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
|||||||
// 5
|
// 5
|
||||||
// |
|
// |
|
||||||
// 6 <- head
|
// 6 <- head
|
||||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 0, indexToHash(6), indexToHash(5), params.BeaconConfig().ZeroHash, 2, 1)
|
state, blkRoot, err = prepareForkchoiceState(context.Background(), f, 0, indexToHash(6), indexToHash(5), params.BeaconConfig().ZeroHash, 2, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||||
r, err = f.Head(context.Background())
|
r, err = f.Head(context.Background())
|
||||||
|
|||||||
@@ -28,14 +28,37 @@ func (n *Node) applyWeightChanges(ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
childrenWeight += child.weight
|
childrenWeight += child.weight
|
||||||
|
if child.full {
|
||||||
|
childrenWeight -= child.block.balance
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if n.root == params.BeaconConfig().ZeroHash {
|
if n.block.root == params.BeaconConfig().ZeroHash {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
n.weight = n.balance + childrenWeight
|
n.weight = n.block.balance + childrenWeight
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func compareChildren(child, bestChild *Node) *Node {
|
||||||
|
// If both are viable, compare their weights.
|
||||||
|
if child.weight == bestChild.weight {
|
||||||
|
// tie breaker of equal weights for full blocks
|
||||||
|
if child.full != bestChild.full {
|
||||||
|
if child.full {
|
||||||
|
return child
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Tie-breaker of equal weights by root.
|
||||||
|
if bytes.Compare(child.block.root[:], bestChild.block.root[:]) > 0 {
|
||||||
|
return child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if child.weight > bestChild.weight {
|
||||||
|
return child
|
||||||
|
}
|
||||||
|
return bestChild
|
||||||
|
}
|
||||||
|
|
||||||
// updateBestDescendant updates the best descendant of this node and its
|
// updateBestDescendant updates the best descendant of this node and its
|
||||||
// children.
|
// children.
|
||||||
func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finalizedEpoch, currentEpoch primitives.Epoch) error {
|
func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finalizedEpoch, currentEpoch primitives.Epoch) error {
|
||||||
@@ -48,7 +71,6 @@ func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finaliz
|
|||||||
}
|
}
|
||||||
|
|
||||||
var bestChild *Node
|
var bestChild *Node
|
||||||
bestWeight := uint64(0)
|
|
||||||
hasViableDescendant := false
|
hasViableDescendant := false
|
||||||
for _, child := range n.children {
|
for _, child := range n.children {
|
||||||
if child == nil {
|
if child == nil {
|
||||||
@@ -61,20 +83,10 @@ func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finaliz
|
|||||||
if childLeadsToViableHead && !hasViableDescendant {
|
if childLeadsToViableHead && !hasViableDescendant {
|
||||||
// The child leads to a viable head, but the current
|
// The child leads to a viable head, but the current
|
||||||
// parent's best child doesn't.
|
// parent's best child doesn't.
|
||||||
bestWeight = child.weight
|
|
||||||
bestChild = child
|
bestChild = child
|
||||||
hasViableDescendant = true
|
hasViableDescendant = true
|
||||||
} else if childLeadsToViableHead {
|
} else if childLeadsToViableHead {
|
||||||
// If both are viable, compare their weights.
|
bestChild = compareChildren(child, bestChild)
|
||||||
if child.weight == bestWeight {
|
|
||||||
// Tie-breaker of equal weights by root.
|
|
||||||
if bytes.Compare(child.root[:], bestChild.root[:]) > 0 {
|
|
||||||
bestChild = child
|
|
||||||
}
|
|
||||||
} else if child.weight > bestWeight {
|
|
||||||
bestChild = child
|
|
||||||
bestWeight = child.weight
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if hasViableDescendant {
|
if hasViableDescendant {
|
||||||
@@ -99,7 +111,7 @@ func (n *Node) viableForHead(justifiedEpoch, currentEpoch primitives.Epoch) bool
|
|||||||
// We use n.justifiedEpoch as the voting source because:
|
// We use n.justifiedEpoch as the voting source because:
|
||||||
// 1. if this node is from current epoch, n.justifiedEpoch is the realized justification epoch.
|
// 1. if this node is from current epoch, n.justifiedEpoch is the realized justification epoch.
|
||||||
// 2. if this node is from a previous epoch, n.justifiedEpoch has already been updated to the unrealized justification epoch.
|
// 2. if this node is from a previous epoch, n.justifiedEpoch has already been updated to the unrealized justification epoch.
|
||||||
return n.justifiedEpoch == justifiedEpoch || n.justifiedEpoch+2 >= currentEpoch
|
return n.block.justifiedEpoch == justifiedEpoch || n.block.justifiedEpoch+2 >= currentEpoch
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) leadsToViableHead(justifiedEpoch, currentEpoch primitives.Epoch) bool {
|
func (n *Node) leadsToViableHead(justifiedEpoch, currentEpoch primitives.Epoch) bool {
|
||||||
@@ -120,10 +132,10 @@ func (n *Node) setNodeAndParentValidated(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
n.optimistic = false
|
n.optimistic = false
|
||||||
|
|
||||||
if n.parent == nil {
|
if n.block.parent == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return n.parent.setNodeAndParentValidated(ctx)
|
return n.block.parent.setNodeAndParentValidated(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// arrivedEarly returns whether this node was inserted before the first
|
// arrivedEarly returns whether this node was inserted before the first
|
||||||
@@ -131,7 +143,7 @@ func (n *Node) setNodeAndParentValidated(ctx context.Context) error {
|
|||||||
// Note that genesisTime has seconds granularity, therefore we use a strict
|
// Note that genesisTime has seconds granularity, therefore we use a strict
|
||||||
// inequality < here. For example a block that arrives 3.9999 seconds into the
|
// inequality < here. For example a block that arrives 3.9999 seconds into the
|
||||||
// slot will have secs = 3 below.
|
// slot will have secs = 3 below.
|
||||||
func (n *Node) arrivedEarly(genesisTime uint64) (bool, error) {
|
func (n *BlockNode) arrivedEarly(genesisTime uint64) (bool, error) {
|
||||||
secs, err := slots.SecondsSinceSlotStart(n.slot, genesisTime, n.timestamp)
|
secs, err := slots.SecondsSinceSlotStart(n.slot, genesisTime, n.timestamp)
|
||||||
votingWindow := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot
|
votingWindow := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot
|
||||||
return secs < votingWindow, err
|
return secs < votingWindow, err
|
||||||
@@ -142,7 +154,7 @@ func (n *Node) arrivedEarly(genesisTime uint64) (bool, error) {
|
|||||||
// Note that genesisTime has seconds granularity, therefore we use an
|
// Note that genesisTime has seconds granularity, therefore we use an
|
||||||
// inequality >= here. For example a block that arrives 10.00001 seconds into the
|
// inequality >= here. For example a block that arrives 10.00001 seconds into the
|
||||||
// slot will have secs = 10 below.
|
// slot will have secs = 10 below.
|
||||||
func (n *Node) arrivedAfterOrphanCheck(genesisTime uint64) (bool, error) {
|
func (n *BlockNode) arrivedAfterOrphanCheck(genesisTime uint64) (bool, error) {
|
||||||
secs, err := slots.SecondsSinceSlotStart(n.slot, genesisTime, n.timestamp)
|
secs, err := slots.SecondsSinceSlotStart(n.slot, genesisTime, n.timestamp)
|
||||||
return secs >= ProcessAttestationsThreshold, err
|
return secs >= ProcessAttestationsThreshold, err
|
||||||
}
|
}
|
||||||
@@ -153,22 +165,22 @@ func (n *Node) nodeTreeDump(ctx context.Context, nodes []*forkchoice2.Node) ([]*
|
|||||||
return nil, ctx.Err()
|
return nil, ctx.Err()
|
||||||
}
|
}
|
||||||
var parentRoot [32]byte
|
var parentRoot [32]byte
|
||||||
if n.parent != nil {
|
if n.block.parent != nil {
|
||||||
parentRoot = n.parent.root
|
parentRoot = n.block.parent.block.root
|
||||||
}
|
}
|
||||||
thisNode := &forkchoice2.Node{
|
thisNode := &forkchoice2.Node{
|
||||||
Slot: n.slot,
|
Slot: n.block.slot,
|
||||||
BlockRoot: n.root[:],
|
BlockRoot: n.block.root[:],
|
||||||
ParentRoot: parentRoot[:],
|
ParentRoot: parentRoot[:],
|
||||||
JustifiedEpoch: n.justifiedEpoch,
|
JustifiedEpoch: n.block.justifiedEpoch,
|
||||||
FinalizedEpoch: n.finalizedEpoch,
|
FinalizedEpoch: n.block.finalizedEpoch,
|
||||||
UnrealizedJustifiedEpoch: n.unrealizedJustifiedEpoch,
|
UnrealizedJustifiedEpoch: n.block.unrealizedJustifiedEpoch,
|
||||||
UnrealizedFinalizedEpoch: n.unrealizedFinalizedEpoch,
|
UnrealizedFinalizedEpoch: n.block.unrealizedFinalizedEpoch,
|
||||||
Balance: n.balance,
|
Balance: n.block.balance,
|
||||||
Weight: n.weight,
|
Weight: n.weight,
|
||||||
ExecutionOptimistic: n.optimistic,
|
ExecutionOptimistic: n.optimistic,
|
||||||
ExecutionBlockHash: n.payloadHash[:],
|
ExecutionBlockHash: n.block.payloadHash[:],
|
||||||
Timestamp: n.timestamp,
|
Timestamp: n.block.timestamp,
|
||||||
}
|
}
|
||||||
if n.optimistic {
|
if n.optimistic {
|
||||||
thisNode.Validity = forkchoice2.Optimistic
|
thisNode.Validity = forkchoice2.Optimistic
|
||||||
|
|||||||
@@ -14,80 +14,65 @@ import (
|
|||||||
func TestNode_ApplyWeightChanges_PositiveChange(t *testing.T) {
|
func TestNode_ApplyWeightChanges_PositiveChange(t *testing.T) {
|
||||||
f := setup(0, 0)
|
f := setup(0, 0)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
state, blk, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
state, blk, err := prepareForkchoiceState(ctx, f, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
state, blk, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0)
|
state, blk, err = prepareForkchoiceState(ctx, f, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
state, blk, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0)
|
state, blk, err = prepareForkchoiceState(ctx, f, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
|
|
||||||
// The updated balances of each node is 100
|
// The updated balances of each node is 100
|
||||||
s := f.store
|
s := f.store
|
||||||
|
|
||||||
s.nodeByRoot[indexToHash(1)].balance = 100
|
s.emptyNodeByRoot[indexToHash(1)].block.balance = 100
|
||||||
s.nodeByRoot[indexToHash(2)].balance = 100
|
s.emptyNodeByRoot[indexToHash(2)].block.balance = 100
|
||||||
s.nodeByRoot[indexToHash(3)].balance = 100
|
s.emptyNodeByRoot[indexToHash(3)].block.balance = 100
|
||||||
|
|
||||||
assert.NoError(t, s.treeRootNode.applyWeightChanges(ctx))
|
assert.NoError(t, s.treeRootNode.applyWeightChanges(ctx))
|
||||||
|
|
||||||
assert.Equal(t, uint64(300), s.nodeByRoot[indexToHash(1)].weight)
|
assert.Equal(t, uint64(300), s.emptyNodeByRoot[indexToHash(1)].weight)
|
||||||
assert.Equal(t, uint64(200), s.nodeByRoot[indexToHash(2)].weight)
|
assert.Equal(t, uint64(200), s.emptyNodeByRoot[indexToHash(2)].weight)
|
||||||
assert.Equal(t, uint64(100), s.nodeByRoot[indexToHash(3)].weight)
|
assert.Equal(t, uint64(100), s.emptyNodeByRoot[indexToHash(3)].weight)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNode_ApplyWeightChanges_NegativeChange(t *testing.T) {
|
func TestNode_ApplyWeightChanges_NegativeChange(t *testing.T) {
|
||||||
f := setup(0, 0)
|
f := setup(0, 0)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
state, blk, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
state, blk, err := prepareForkchoiceState(ctx, f, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
state, blk, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0)
|
state, blk, err = prepareForkchoiceState(ctx, f, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
state, blk, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0)
|
state, blk, err = prepareForkchoiceState(ctx, f, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
|
|
||||||
// The updated balances of each node is 100
|
// The updated balances of each node is 100
|
||||||
s := f.store
|
s := f.store
|
||||||
s.nodeByRoot[indexToHash(1)].weight = 400
|
s.emptyNodeByRoot[indexToHash(1)].weight = 400
|
||||||
s.nodeByRoot[indexToHash(2)].weight = 400
|
s.emptyNodeByRoot[indexToHash(2)].weight = 400
|
||||||
s.nodeByRoot[indexToHash(3)].weight = 400
|
s.emptyNodeByRoot[indexToHash(3)].weight = 400
|
||||||
|
|
||||||
s.nodeByRoot[indexToHash(1)].balance = 100
|
s.emptyNodeByRoot[indexToHash(1)].block.balance = 100
|
||||||
s.nodeByRoot[indexToHash(2)].balance = 100
|
s.emptyNodeByRoot[indexToHash(2)].block.balance = 100
|
||||||
s.nodeByRoot[indexToHash(3)].balance = 100
|
s.emptyNodeByRoot[indexToHash(3)].block.balance = 100
|
||||||
|
|
||||||
assert.NoError(t, s.treeRootNode.applyWeightChanges(ctx))
|
assert.NoError(t, s.treeRootNode.applyWeightChanges(ctx))
|
||||||
|
|
||||||
assert.Equal(t, uint64(300), s.nodeByRoot[indexToHash(1)].weight)
|
assert.Equal(t, uint64(300), s.emptyNodeByRoot[indexToHash(1)].weight)
|
||||||
assert.Equal(t, uint64(200), s.nodeByRoot[indexToHash(2)].weight)
|
assert.Equal(t, uint64(200), s.emptyNodeByRoot[indexToHash(2)].weight)
|
||||||
assert.Equal(t, uint64(100), s.nodeByRoot[indexToHash(3)].weight)
|
assert.Equal(t, uint64(100), s.emptyNodeByRoot[indexToHash(3)].weight)
|
||||||
}
|
|
||||||
|
|
||||||
func TestNode_UpdateBestDescendant_NonViableChild(t *testing.T) {
|
|
||||||
f := setup(1, 1)
|
|
||||||
ctx := context.Background()
|
|
||||||
// Input child is not viable.
|
|
||||||
state, blk, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 2, 3)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
|
||||||
|
|
||||||
// Verify parent's best child and best descendant are `none`.
|
|
||||||
s := f.store
|
|
||||||
assert.Equal(t, 1, len(s.treeRootNode.children))
|
|
||||||
nilBestDescendant := s.treeRootNode.bestDescendant == nil
|
|
||||||
assert.Equal(t, true, nilBestDescendant)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNode_UpdateBestDescendant_ViableChild(t *testing.T) {
|
func TestNode_UpdateBestDescendant_ViableChild(t *testing.T) {
|
||||||
f := setup(1, 1)
|
f := setup(1, 1)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
// Input child is the best descendant
|
// Input child is the best descendant
|
||||||
state, blk, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
state, blk, err := prepareForkchoiceState(ctx, f, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
|
|
||||||
@@ -100,16 +85,16 @@ func TestNode_UpdateBestDescendant_HigherWeightChild(t *testing.T) {
|
|||||||
f := setup(1, 1)
|
f := setup(1, 1)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
// Input child is the best descendant
|
// Input child is the best descendant
|
||||||
state, blk, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
state, blk, err := prepareForkchoiceState(ctx, f, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
state, blk, err = prepareForkchoiceState(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
state, blk, err = prepareForkchoiceState(ctx, f, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
|
|
||||||
s := f.store
|
s := f.store
|
||||||
s.nodeByRoot[indexToHash(1)].weight = 100
|
s.emptyNodeByRoot[indexToHash(1)].weight = 100
|
||||||
s.nodeByRoot[indexToHash(2)].weight = 200
|
s.emptyNodeByRoot[indexToHash(2)].weight = 200
|
||||||
assert.NoError(t, s.treeRootNode.updateBestDescendant(ctx, 1, 1, 1))
|
assert.NoError(t, s.treeRootNode.updateBestDescendant(ctx, 1, 1, 1))
|
||||||
|
|
||||||
assert.Equal(t, 2, len(s.treeRootNode.children))
|
assert.Equal(t, 2, len(s.treeRootNode.children))
|
||||||
@@ -120,16 +105,16 @@ func TestNode_UpdateBestDescendant_LowerWeightChild(t *testing.T) {
|
|||||||
f := setup(1, 1)
|
f := setup(1, 1)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
// Input child is the best descendant
|
// Input child is the best descendant
|
||||||
state, blk, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
state, blk, err := prepareForkchoiceState(ctx, f, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
state, blk, err = prepareForkchoiceState(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
state, blk, err = prepareForkchoiceState(ctx, f, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
|
|
||||||
s := f.store
|
s := f.store
|
||||||
s.nodeByRoot[indexToHash(1)].weight = 200
|
s.emptyNodeByRoot[indexToHash(1)].weight = 200
|
||||||
s.nodeByRoot[indexToHash(2)].weight = 100
|
s.emptyNodeByRoot[indexToHash(2)].weight = 100
|
||||||
assert.NoError(t, s.treeRootNode.updateBestDescendant(ctx, 1, 1, 1))
|
assert.NoError(t, s.treeRootNode.updateBestDescendant(ctx, 1, 1, 1))
|
||||||
|
|
||||||
assert.Equal(t, 2, len(s.treeRootNode.children))
|
assert.Equal(t, 2, len(s.treeRootNode.children))
|
||||||
@@ -142,13 +127,13 @@ func TestNode_ViableForHead(t *testing.T) {
|
|||||||
justifiedEpoch primitives.Epoch
|
justifiedEpoch primitives.Epoch
|
||||||
want bool
|
want bool
|
||||||
}{
|
}{
|
||||||
{&Node{}, 0, true},
|
{&Node{block: &BlockNode{}}, 0, true},
|
||||||
{&Node{}, 1, false},
|
{&Node{block: &BlockNode{}}, 1, false},
|
||||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 1, true},
|
{&Node{block: &BlockNode{finalizedEpoch: 1, justifiedEpoch: 1}}, 1, true},
|
||||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 2, false},
|
{&Node{block: &BlockNode{finalizedEpoch: 1, justifiedEpoch: 1}}, 2, false},
|
||||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 2}, 3, false},
|
{&Node{block: &BlockNode{finalizedEpoch: 1, justifiedEpoch: 2}}, 3, false},
|
||||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 2}, 4, false},
|
{&Node{block: &BlockNode{finalizedEpoch: 1, justifiedEpoch: 2}}, 4, false},
|
||||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 3}, 4, true},
|
{&Node{block: &BlockNode{finalizedEpoch: 1, justifiedEpoch: 3}}, 4, true},
|
||||||
}
|
}
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
got := tc.n.viableForHead(tc.justifiedEpoch, 5)
|
got := tc.n.viableForHead(tc.justifiedEpoch, 5)
|
||||||
@@ -159,26 +144,26 @@ func TestNode_ViableForHead(t *testing.T) {
|
|||||||
func TestNode_LeadsToViableHead(t *testing.T) {
|
func TestNode_LeadsToViableHead(t *testing.T) {
|
||||||
f := setup(4, 3)
|
f := setup(4, 3)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
state, blk, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
state, blk, err := prepareForkchoiceState(ctx, f, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
state, blk, err = prepareForkchoiceState(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
state, blk, err = prepareForkchoiceState(ctx, f, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
state, blk, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1)
|
state, blk, err = prepareForkchoiceState(ctx, f, 3, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
state, blk, err = prepareForkchoiceState(ctx, 4, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1)
|
state, blk, err = prepareForkchoiceState(ctx, f, 4, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
state, blk, err = prepareForkchoiceState(ctx, 5, indexToHash(5), indexToHash(3), params.BeaconConfig().ZeroHash, 4, 3)
|
state, blk, err = prepareForkchoiceState(ctx, f, 5, indexToHash(5), indexToHash(3), params.BeaconConfig().ZeroHash, 4, 3)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
|
|
||||||
require.Equal(t, true, f.store.treeRootNode.leadsToViableHead(4, 5))
|
require.Equal(t, true, f.store.treeRootNode.leadsToViableHead(4, 5))
|
||||||
require.Equal(t, true, f.store.nodeByRoot[indexToHash(5)].leadsToViableHead(4, 5))
|
require.Equal(t, true, f.store.emptyNodeByRoot[indexToHash(5)].leadsToViableHead(4, 5))
|
||||||
require.Equal(t, false, f.store.nodeByRoot[indexToHash(2)].leadsToViableHead(4, 5))
|
require.Equal(t, false, f.store.emptyNodeByRoot[indexToHash(2)].leadsToViableHead(4, 5))
|
||||||
require.Equal(t, false, f.store.nodeByRoot[indexToHash(4)].leadsToViableHead(4, 5))
|
require.Equal(t, false, f.store.emptyNodeByRoot[indexToHash(4)].leadsToViableHead(4, 5))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNode_SetFullyValidated(t *testing.T) {
|
func TestNode_SetFullyValidated(t *testing.T) {
|
||||||
@@ -192,29 +177,28 @@ func TestNode_SetFullyValidated(t *testing.T) {
|
|||||||
// \
|
// \
|
||||||
// -- 5 (true)
|
// -- 5 (true)
|
||||||
//
|
//
|
||||||
state, blk, err := prepareForkchoiceState(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
state, blk, err := prepareForkchoiceState(ctx, f, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
storeNodes[1] = f.store.nodeByRoot[blk.Root()]
|
storeNodes[1] = f.store.emptyNodeByRoot[blk.Root()]
|
||||||
require.NoError(t, f.SetOptimisticToValid(ctx, params.BeaconConfig().ZeroHash))
|
require.NoError(t, f.SetOptimisticToValid(ctx, params.BeaconConfig().ZeroHash))
|
||||||
state, blk, err = prepareForkchoiceState(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1)
|
state, blk, err = prepareForkchoiceState(ctx, f, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
storeNodes[2] = f.store.nodeByRoot[blk.Root()]
|
storeNodes[2] = f.store.emptyNodeByRoot[blk.Root()]
|
||||||
require.NoError(t, f.SetOptimisticToValid(ctx, indexToHash(1)))
|
require.NoError(t, f.SetOptimisticToValid(ctx, indexToHash(1)))
|
||||||
state, blk, err = prepareForkchoiceState(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1)
|
state, blk, err = prepareForkchoiceState(ctx, f, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
storeNodes[3] = f.store.nodeByRoot[blk.Root()]
|
storeNodes[3] = f.store.emptyNodeByRoot[blk.Root()]
|
||||||
state, blk, err = prepareForkchoiceState(ctx, 4, indexToHash(4), indexToHash(3), params.BeaconConfig().ZeroHash, 1, 1)
|
state, blk, err = prepareForkchoiceState(ctx, f, 4, indexToHash(4), indexToHash(3), params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
storeNodes[4] = f.store.nodeByRoot[blk.Root()]
|
storeNodes[4] = f.store.emptyNodeByRoot[blk.Root()]
|
||||||
state, blk, err = prepareForkchoiceState(ctx, 5, indexToHash(5), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1)
|
state, blk, err = prepareForkchoiceState(ctx, f, 5, indexToHash(5), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
storeNodes[5] = f.store.nodeByRoot[blk.Root()]
|
storeNodes[5] = f.store.emptyNodeByRoot[blk.Root()]
|
||||||
|
|
||||||
opt, err := f.IsOptimistic(indexToHash(5))
|
opt, err := f.IsOptimistic(indexToHash(5))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, true, opt)
|
require.Equal(t, true, opt)
|
||||||
@@ -223,7 +207,7 @@ func TestNode_SetFullyValidated(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, true, opt)
|
require.Equal(t, true, opt)
|
||||||
|
|
||||||
require.NoError(t, f.store.nodeByRoot[indexToHash(4)].setNodeAndParentValidated(ctx))
|
require.NoError(t, f.store.emptyNodeByRoot[indexToHash(4)].setNodeAndParentValidated(ctx))
|
||||||
|
|
||||||
// block 5 should still be optimistic
|
// block 5 should still be optimistic
|
||||||
opt, err = f.IsOptimistic(indexToHash(5))
|
opt, err = f.IsOptimistic(indexToHash(5))
|
||||||
@@ -245,16 +229,16 @@ func TestNode_SetFullyValidated(t *testing.T) {
|
|||||||
require.Equal(t, len(respNodes), f.NodeCount())
|
require.Equal(t, len(respNodes), f.NodeCount())
|
||||||
|
|
||||||
for i, respNode := range respNodes {
|
for i, respNode := range respNodes {
|
||||||
require.Equal(t, storeNodes[i].slot, respNode.Slot)
|
require.Equal(t, storeNodes[i].block.slot, respNode.Slot)
|
||||||
require.DeepEqual(t, storeNodes[i].root[:], respNode.BlockRoot)
|
require.DeepEqual(t, storeNodes[i].block.root[:], respNode.BlockRoot)
|
||||||
require.Equal(t, storeNodes[i].balance, respNode.Balance)
|
require.Equal(t, storeNodes[i].block.balance, respNode.Balance)
|
||||||
require.Equal(t, storeNodes[i].weight, respNode.Weight)
|
require.Equal(t, storeNodes[i].weight, respNode.Weight)
|
||||||
require.Equal(t, storeNodes[i].optimistic, respNode.ExecutionOptimistic)
|
require.Equal(t, storeNodes[i].optimistic, respNode.ExecutionOptimistic)
|
||||||
require.Equal(t, storeNodes[i].justifiedEpoch, respNode.JustifiedEpoch)
|
require.Equal(t, storeNodes[i].block.justifiedEpoch, respNode.JustifiedEpoch)
|
||||||
require.Equal(t, storeNodes[i].unrealizedJustifiedEpoch, respNode.UnrealizedJustifiedEpoch)
|
require.Equal(t, storeNodes[i].block.unrealizedJustifiedEpoch, respNode.UnrealizedJustifiedEpoch)
|
||||||
require.Equal(t, storeNodes[i].finalizedEpoch, respNode.FinalizedEpoch)
|
require.Equal(t, storeNodes[i].block.finalizedEpoch, respNode.FinalizedEpoch)
|
||||||
require.Equal(t, storeNodes[i].unrealizedFinalizedEpoch, respNode.UnrealizedFinalizedEpoch)
|
require.Equal(t, storeNodes[i].block.unrealizedFinalizedEpoch, respNode.UnrealizedFinalizedEpoch)
|
||||||
require.Equal(t, storeNodes[i].timestamp, respNode.Timestamp)
|
require.Equal(t, storeNodes[i].block.timestamp, respNode.Timestamp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,16 +250,16 @@ func TestNode_TimeStampsChecks(t *testing.T) {
|
|||||||
driftGenesisTime(f, 1, 1)
|
driftGenesisTime(f, 1, 1)
|
||||||
root := [32]byte{'a'}
|
root := [32]byte{'a'}
|
||||||
f.justifiedBalances = []uint64{10}
|
f.justifiedBalances = []uint64{10}
|
||||||
state, blk, err := prepareForkchoiceState(ctx, 1, root, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0)
|
state, blk, err := prepareForkchoiceState(ctx, f, 1, root, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
headRoot, err := f.Head(ctx)
|
headRoot, err := f.Head(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, root, headRoot)
|
require.Equal(t, root, headRoot)
|
||||||
early, err := f.store.headNode.arrivedEarly(f.store.genesisTime)
|
early, err := f.store.headNode.block.arrivedEarly(f.store.genesisTime)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, true, early)
|
require.Equal(t, true, early)
|
||||||
late, err := f.store.headNode.arrivedAfterOrphanCheck(f.store.genesisTime)
|
late, err := f.store.headNode.block.arrivedAfterOrphanCheck(f.store.genesisTime)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, false, late)
|
require.Equal(t, false, late)
|
||||||
|
|
||||||
@@ -283,47 +267,47 @@ func TestNode_TimeStampsChecks(t *testing.T) {
|
|||||||
// late block
|
// late block
|
||||||
driftGenesisTime(f, 2, orphanLateBlockFirstThreshold+1)
|
driftGenesisTime(f, 2, orphanLateBlockFirstThreshold+1)
|
||||||
root = [32]byte{'b'}
|
root = [32]byte{'b'}
|
||||||
state, blk, err = prepareForkchoiceState(ctx, 2, root, [32]byte{'a'}, [32]byte{'B'}, 0, 0)
|
state, blk, err = prepareForkchoiceState(ctx, f, 2, root, [32]byte{'a'}, [32]byte{'B'}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
headRoot, err = f.Head(ctx)
|
headRoot, err = f.Head(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, root, headRoot)
|
require.Equal(t, root, headRoot)
|
||||||
early, err = f.store.headNode.arrivedEarly(f.store.genesisTime)
|
early, err = f.store.headNode.block.arrivedEarly(f.store.genesisTime)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, false, early)
|
require.Equal(t, false, early)
|
||||||
late, err = f.store.headNode.arrivedAfterOrphanCheck(f.store.genesisTime)
|
late, err = f.store.headNode.block.arrivedAfterOrphanCheck(f.store.genesisTime)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, false, late)
|
require.Equal(t, false, late)
|
||||||
|
|
||||||
// very late block
|
// very late block
|
||||||
driftGenesisTime(f, 3, ProcessAttestationsThreshold+1)
|
driftGenesisTime(f, 3, ProcessAttestationsThreshold+1)
|
||||||
root = [32]byte{'c'}
|
root = [32]byte{'c'}
|
||||||
state, blk, err = prepareForkchoiceState(ctx, 3, root, [32]byte{'b'}, [32]byte{'C'}, 0, 0)
|
state, blk, err = prepareForkchoiceState(ctx, f, 3, root, [32]byte{'b'}, [32]byte{'C'}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
headRoot, err = f.Head(ctx)
|
headRoot, err = f.Head(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, root, headRoot)
|
require.Equal(t, root, headRoot)
|
||||||
early, err = f.store.headNode.arrivedEarly(f.store.genesisTime)
|
early, err = f.store.headNode.block.arrivedEarly(f.store.genesisTime)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, false, early)
|
require.Equal(t, false, early)
|
||||||
late, err = f.store.headNode.arrivedAfterOrphanCheck(f.store.genesisTime)
|
late, err = f.store.headNode.block.arrivedAfterOrphanCheck(f.store.genesisTime)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, true, late)
|
require.Equal(t, true, late)
|
||||||
|
|
||||||
// block from the future
|
// block from the future
|
||||||
root = [32]byte{'d'}
|
root = [32]byte{'d'}
|
||||||
state, blk, err = prepareForkchoiceState(ctx, 5, root, [32]byte{'c'}, [32]byte{'D'}, 1, 1)
|
state, blk, err = prepareForkchoiceState(ctx, f, 5, root, [32]byte{'c'}, [32]byte{'D'}, 1, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blk))
|
require.NoError(t, f.InsertNode(ctx, state, blk))
|
||||||
headRoot, err = f.Head(ctx)
|
headRoot, err = f.Head(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, root, headRoot)
|
require.Equal(t, root, headRoot)
|
||||||
early, err = f.store.headNode.arrivedEarly(f.store.genesisTime)
|
early, err = f.store.headNode.block.arrivedEarly(f.store.genesisTime)
|
||||||
require.ErrorContains(t, "invalid timestamp", err)
|
require.ErrorContains(t, "invalid timestamp", err)
|
||||||
require.Equal(t, true, early)
|
require.Equal(t, true, early)
|
||||||
late, err = f.store.headNode.arrivedAfterOrphanCheck(f.store.genesisTime)
|
late, err = f.store.headNode.block.arrivedAfterOrphanCheck(f.store.genesisTime)
|
||||||
require.ErrorContains(t, "invalid timestamp", err)
|
require.ErrorContains(t, "invalid timestamp", err)
|
||||||
require.Equal(t, false, late)
|
require.Equal(t, false, late)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,19 +59,19 @@ func TestStore_NewSlot(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
f := setup(test.args.justified.Epoch, test.args.finalized.Epoch)
|
f := setup(test.args.justified.Epoch, test.args.finalized.Epoch)
|
||||||
state, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, [32]byte{}, 0, 0)
|
state, blkRoot, err := prepareForkchoiceState(ctx, f, 0, [32]byte{}, [32]byte{}, [32]byte{}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // genesis
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // genesis
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 32, [32]byte{'a'}, [32]byte{}, [32]byte{}, 0, 0)
|
state, blkRoot, err = prepareForkchoiceState(ctx, f, 32, [32]byte{'a'}, [32]byte{}, [32]byte{}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // finalized
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // finalized
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 64, [32]byte{'b'}, [32]byte{'a'}, [32]byte{}, 0, 0)
|
state, blkRoot, err = prepareForkchoiceState(ctx, f, 64, [32]byte{'b'}, [32]byte{'a'}, [32]byte{}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // justified
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // justified
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 96, bj, [32]byte{'a'}, [32]byte{}, 0, 0)
|
state, blkRoot, err = prepareForkchoiceState(ctx, f, 96, bj, [32]byte{'a'}, [32]byte{}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // best justified
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // best justified
|
||||||
state, blkRoot, err = prepareForkchoiceState(ctx, 97, [32]byte{'d'}, [32]byte{}, [32]byte{}, 0, 0)
|
state, blkRoot, err = prepareForkchoiceState(ctx, f, 97, [32]byte{'d'}, [32]byte{}, [32]byte{}, 0, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // bad
|
require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // bad
|
||||||
|
|
||||||
|
|||||||
@@ -7,42 +7,79 @@ import (
|
|||||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (s *Store) removeNodeIfSynced(ctx context.Context, node *Node) ([][32]byte, error) {
|
||||||
|
invalidRoots := make([][32]byte, 0)
|
||||||
|
if node == nil {
|
||||||
|
return invalidRoots, nil
|
||||||
|
}
|
||||||
|
fullNode := s.fullNodeByPayload[node.block.payloadHash]
|
||||||
|
if fullNode == nil {
|
||||||
|
return invalidRoots, nil
|
||||||
|
}
|
||||||
|
return s.removeNode(ctx, fullNode)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Store) setOptimisticToInvalid(ctx context.Context, root, parentRoot, lastValidHash [32]byte) ([][32]byte, error) {
|
func (s *Store) setOptimisticToInvalid(ctx context.Context, root, parentRoot, lastValidHash [32]byte) ([][32]byte, error) {
|
||||||
invalidRoots := make([][32]byte, 0)
|
invalidRoots := make([][32]byte, 0)
|
||||||
node, ok := s.nodeByRoot[root]
|
node, ok := s.emptyNodeByRoot[root]
|
||||||
if !ok {
|
// Check if we have a full node for this root
|
||||||
node, ok = s.nodeByRoot[parentRoot]
|
if ok {
|
||||||
if !ok || node == nil {
|
fullNode, ok := s.fullNodeByPayload[node.block.payloadHash]
|
||||||
return invalidRoots, errors.Wrap(ErrNilNode, "could not set node to invalid")
|
if ok {
|
||||||
|
node = fullNode
|
||||||
}
|
}
|
||||||
// return early if the parent is LVH
|
// check consistency of the input
|
||||||
if node.payloadHash == lastValidHash {
|
parent := node.block.parent
|
||||||
return invalidRoots, nil
|
if parent.block.root != parentRoot {
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if node == nil {
|
|
||||||
return invalidRoots, errors.Wrap(ErrNilNode, "could not set node to invalid")
|
|
||||||
}
|
|
||||||
if node.parent.root != parentRoot {
|
|
||||||
return invalidRoots, errInvalidParentRoot
|
return invalidRoots, errInvalidParentRoot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
firstInvalid := node
|
// if the last valid hash is not known or null, prune only the incoming
|
||||||
for ; firstInvalid.parent != nil && firstInvalid.parent.payloadHash != lastValidHash; firstInvalid = firstInvalid.parent {
|
// block.
|
||||||
if ctx.Err() != nil {
|
lastValid, ok := s.fullNodeByPayload[lastValidHash]
|
||||||
return invalidRoots, ctx.Err()
|
if !ok || lastValidHash == [32]byte{} {
|
||||||
|
return s.removeNodeIfSynced(ctx, node)
|
||||||
|
}
|
||||||
|
// We have a valid hash, find if it's in the same fork as the last valid
|
||||||
|
// root.
|
||||||
|
ancestor, err := s.ancestorRoot(ctx, parentRoot, lastValid.block.slot)
|
||||||
|
if err != nil {
|
||||||
|
return invalidRoots, errors.Wrap(err, "could not set block as invalid")
|
||||||
|
}
|
||||||
|
if ancestor != lastValid.block.root {
|
||||||
|
return s.removeNodeIfSynced(ctx, node)
|
||||||
|
}
|
||||||
|
// we go up we find a child of the last valid that is full. We find
|
||||||
|
// first the starting node for the loop
|
||||||
|
if node == nil {
|
||||||
|
node = s.emptyNodeByRoot[parentRoot]
|
||||||
|
fullParent, ok := s.fullNodeByPayload[node.block.payloadHash]
|
||||||
|
if ok {
|
||||||
|
// return early if the parent is the LVH
|
||||||
|
if fullParent.block.payloadHash == lastValidHash {
|
||||||
|
return invalidRoots, nil
|
||||||
|
}
|
||||||
|
node = fullParent
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fullNode, ok := s.fullNodeByPayload[node.block.payloadHash]
|
||||||
|
if ok {
|
||||||
|
node = fullNode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Deal with the case that the last valid payload is in a different fork
|
var lastFullNode *Node
|
||||||
// This means we are dealing with an EE that does not follow the spec
|
for ; node.block.fullParent != nil; node = node.block.fullParent {
|
||||||
if firstInvalid.parent == nil {
|
if node.full {
|
||||||
// return early if the invalid node was not imported
|
lastFullNode = node
|
||||||
if node.root == parentRoot {
|
}
|
||||||
return invalidRoots, nil
|
if node.block.fullParent.block.payloadHash == lastValidHash {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
firstInvalid = node
|
|
||||||
}
|
}
|
||||||
return s.removeNode(ctx, firstInvalid)
|
if lastFullNode == nil {
|
||||||
|
return invalidRoots, nil
|
||||||
|
}
|
||||||
|
return s.removeNode(ctx, lastFullNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeNode removes the node with the given root and all of its children
|
// removeNode removes the node with the given root and all of its children
|
||||||
@@ -53,20 +90,20 @@ func (s *Store) removeNode(ctx context.Context, node *Node) ([][32]byte, error)
|
|||||||
if node == nil {
|
if node == nil {
|
||||||
return invalidRoots, errors.Wrap(ErrNilNode, "could not remove node")
|
return invalidRoots, errors.Wrap(ErrNilNode, "could not remove node")
|
||||||
}
|
}
|
||||||
if !node.optimistic || node.parent == nil {
|
if !node.optimistic || node.block.parent == nil {
|
||||||
return invalidRoots, errInvalidOptimisticStatus
|
return invalidRoots, errInvalidOptimisticStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
children := node.parent.children
|
children := node.block.parent.children
|
||||||
if len(children) == 1 {
|
if len(children) == 1 {
|
||||||
node.parent.children = []*Node{}
|
node.block.parent.children = []*Node{}
|
||||||
} else {
|
} else {
|
||||||
for i, n := range children {
|
for i, n := range children {
|
||||||
if n == node {
|
if n == node {
|
||||||
if i != len(children)-1 {
|
if i != len(children)-1 {
|
||||||
children[i] = children[len(children)-1]
|
children[i] = children[len(children)-1]
|
||||||
}
|
}
|
||||||
node.parent.children = children[:len(children)-1]
|
node.block.parent.children = children[:len(children)-1]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,15 +122,20 @@ func (s *Store) removeNodeAndChildren(ctx context.Context, node *Node, invalidRo
|
|||||||
return invalidRoots, err
|
return invalidRoots, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
invalidRoots = append(invalidRoots, node.root)
|
// Only remove the current node's root when removing the empty node as well
|
||||||
if node.root == s.proposerBoostRoot {
|
if node.full {
|
||||||
|
delete(s.fullNodeByPayload, node.block.payloadHash)
|
||||||
|
return invalidRoots, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidRoots = append(invalidRoots, node.block.root)
|
||||||
|
if node.block.root == s.proposerBoostRoot {
|
||||||
s.proposerBoostRoot = [32]byte{}
|
s.proposerBoostRoot = [32]byte{}
|
||||||
}
|
}
|
||||||
if node.root == s.previousProposerBoostRoot {
|
if node.block.root == s.previousProposerBoostRoot {
|
||||||
s.previousProposerBoostRoot = params.BeaconConfig().ZeroHash
|
s.previousProposerBoostRoot = params.BeaconConfig().ZeroHash
|
||||||
s.previousProposerBoostScore = 0
|
s.previousProposerBoostScore = 0
|
||||||
}
|
}
|
||||||
delete(s.nodeByRoot, node.root)
|
delete(s.emptyNodeByRoot, node.block.root)
|
||||||
delete(s.nodeByPayload, node.payloadHash)
|
|
||||||
return invalidRoots, nil
|
return invalidRoots, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user