mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-11 06:18:05 -05:00
Compare commits
49 Commits
poc-api-do
...
simplify_f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c6efd785f | ||
|
|
b4ecb90a67 | ||
|
|
d66d918fa6 | ||
|
|
449438b414 | ||
|
|
08dc279ea9 | ||
|
|
d43540a00f | ||
|
|
2b2fde4732 | ||
|
|
fad88dce57 | ||
|
|
03614bd42a | ||
|
|
a39f9cce1a | ||
|
|
d3d25e3ae5 | ||
|
|
929e9ddf4c | ||
|
|
7c0e79d432 | ||
|
|
3c1c0b3c00 | ||
|
|
d439e6da74 | ||
|
|
e68b2821c1 | ||
|
|
cfef8f4676 | ||
|
|
9709412511 | ||
|
|
7781eb60f4 | ||
|
|
396b8bf970 | ||
|
|
d5107942a1 | ||
|
|
bd4a520013 | ||
|
|
a0ff1351a0 | ||
|
|
7e6fd5fd8b | ||
|
|
d984210baa | ||
|
|
31c72672d7 | ||
|
|
8c1e180dd1 | ||
|
|
886d76fe7c | ||
|
|
a602acf492 | ||
|
|
1b6547de6a | ||
|
|
88685bb3bd | ||
|
|
2319b7d4bd | ||
|
|
82b2840d68 | ||
|
|
cf221d0f4c | ||
|
|
0956e3a657 | ||
|
|
351ed1c511 | ||
|
|
9809f5ac77 | ||
|
|
cff5e2b5fe | ||
|
|
dd15f9e0cc | ||
|
|
1c9ded4684 | ||
|
|
d4cc6fcf4a | ||
|
|
49c16f1a71 | ||
|
|
e70b606e78 | ||
|
|
0e8b37c317 | ||
|
|
e80db9554d | ||
|
|
d0bf03e863 | ||
|
|
b7e0819f00 | ||
|
|
7d64104003 | ||
|
|
b1e8a9ea3d |
16
WORKSPACE
16
WORKSPACE
@@ -322,6 +322,22 @@ filegroup(
|
||||
url = "https://github.com/eth-clients/eth2-networks/archive/7b4897888cebef23801540236f73123e21774954.tar.gz",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "goerli_testnet",
|
||||
build_file_content = """
|
||||
filegroup(
|
||||
name = "configs",
|
||||
srcs = [
|
||||
"prater/config.yaml",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "43fc0f55ddff7b511713e2de07aa22846a67432df997296fb4fc09cd8ed1dcdb",
|
||||
strip_prefix = "goerli-6522ac6684693740cd4ddcc2a0662e03702aa4a1",
|
||||
url = "https://github.com/eth-clients/goerli/archive/6522ac6684693740cd4ddcc2a0662e03702aa4a1.tar.gz",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "holesky_testnet",
|
||||
build_file_content = """
|
||||
|
||||
@@ -275,7 +275,24 @@ func TestClient_GetHeader(t *testing.T) {
|
||||
require.Equal(t, len(kcgCommitments[i]) == 48, true)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("deneb, too many kzg commitments", func(t *testing.T) {
|
||||
hc := &http.Client{
|
||||
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
|
||||
require.Equal(t, expectedPath, r.URL.Path)
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(bytes.NewBufferString(testExampleHeaderResponseDenebTooManyBlobs)),
|
||||
Request: r.Clone(ctx),
|
||||
}, nil
|
||||
}),
|
||||
}
|
||||
c := &Client{
|
||||
hc: hc,
|
||||
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
|
||||
}
|
||||
_, err := c.GetHeader(ctx, slot, bytesutil.ToBytes32(parentHash), bytesutil.ToBytes48(pubkey))
|
||||
require.ErrorContains(t, "could not extract proto message from header: too many blob commitments: 7", err)
|
||||
})
|
||||
t.Run("unsupported version", func(t *testing.T) {
|
||||
hc := &http.Client{
|
||||
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
|
||||
@@ -412,7 +429,7 @@ func TestSubmitBlindedBlock(t *testing.T) {
|
||||
require.ErrorContains(t, "not a bellatrix payload", err)
|
||||
})
|
||||
t.Run("not blinded", func(t *testing.T) {
|
||||
sbb, err := blocks.NewSignedBeaconBlock(ð.SignedBeaconBlockBellatrix{Block: ð.BeaconBlockBellatrix{Body: ð.BeaconBlockBodyBellatrix{}}})
|
||||
sbb, err := blocks.NewSignedBeaconBlock(ð.SignedBeaconBlockBellatrix{Block: ð.BeaconBlockBellatrix{Body: ð.BeaconBlockBodyBellatrix{ExecutionPayload: &v1.ExecutionPayload{}}}})
|
||||
require.NoError(t, err)
|
||||
_, _, err = (&Client{}).SubmitBlindedBlock(ctx, sbb)
|
||||
require.ErrorIs(t, err, errNotBlinded)
|
||||
|
||||
@@ -908,6 +908,9 @@ func (bb *BuilderBidDeneb) ToProto() (*eth.BuilderBidDeneb, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(bb.BlobKzgCommitments) > fieldparams.MaxBlobsPerBlock {
|
||||
return nil, fmt.Errorf("too many blob commitments: %d", len(bb.BlobKzgCommitments))
|
||||
}
|
||||
kzgCommitments := make([][]byte, len(bb.BlobKzgCommitments))
|
||||
for i, commit := range bb.BlobKzgCommitments {
|
||||
if len(commit) != fieldparams.BLSPubkeyLength {
|
||||
|
||||
@@ -209,6 +209,39 @@ var testExampleHeaderResponseUnknownVersion = `{
|
||||
}
|
||||
}`
|
||||
|
||||
var testExampleHeaderResponseDenebTooManyBlobs = `{
|
||||
"version": "deneb",
|
||||
"data": {
|
||||
"message": {
|
||||
"header": {
|
||||
"parent_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
||||
"fee_recipient": "0xabcf8e0d4e9587369b2301d0790347320302cc09",
|
||||
"state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
||||
"receipts_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
||||
"logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"prev_randao": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
||||
"block_number": "1",
|
||||
"gas_limit": "1",
|
||||
"gas_used": "1",
|
||||
"timestamp": "1",
|
||||
"extra_data": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
||||
"base_fee_per_gas": "452312848583266388373324160190187140051835877600158453279131187530910662656",
|
||||
"block_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
||||
"transactions_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
||||
"withdrawals_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
||||
"blob_gas_used": "1",
|
||||
"excess_blob_gas": "2"
|
||||
},
|
||||
"blob_kzg_commitments": [
|
||||
"","","","","","",""
|
||||
],
|
||||
"value": "652312848583266388373324160190187140051835877600158453279131187530910662656",
|
||||
"pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a"
|
||||
},
|
||||
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
|
||||
}
|
||||
}`
|
||||
|
||||
func TestExecutionHeaderResponseUnmarshal(t *testing.T) {
|
||||
hr := &ExecHeaderResponse{}
|
||||
require.NoError(t, json.Unmarshal([]byte(testExampleHeaderResponse), hr))
|
||||
|
||||
@@ -7,4 +7,5 @@ const (
|
||||
ConsensusBlockValueHeader = "Eth-Consensus-Block-Value"
|
||||
JsonMediaType = "application/json"
|
||||
OctetStreamMediaType = "application/octet-stream"
|
||||
EventStreamMediaType = "text/event-stream"
|
||||
)
|
||||
|
||||
@@ -26,6 +26,7 @@ go_library(
|
||||
"receive_blob.go",
|
||||
"receive_block.go",
|
||||
"service.go",
|
||||
"tracked_proposer.go",
|
||||
"weak_subjectivity_checks.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain",
|
||||
@@ -52,7 +53,6 @@ go_library(
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/db/filesystem:go_default_library",
|
||||
"//beacon-chain/db/filters:go_default_library",
|
||||
"//beacon-chain/db/kv:go_default_library",
|
||||
"//beacon-chain/execution:go_default_library",
|
||||
"//beacon-chain/forkchoice:go_default_library",
|
||||
"//beacon-chain/forkchoice/doubly-linked-tree:go_default_library",
|
||||
|
||||
@@ -7,11 +7,11 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/time"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db/kv"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/execution"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/features"
|
||||
@@ -32,21 +32,18 @@ const blobCommitmentVersionKZG uint8 = 0x01
|
||||
|
||||
var defaultLatestValidHash = bytesutil.PadTo([]byte{0xff}, 32)
|
||||
|
||||
// notifyForkchoiceUpdateArg is the argument for the forkchoice update notification `notifyForkchoiceUpdate`.
|
||||
type notifyForkchoiceUpdateArg struct {
|
||||
headState state.BeaconState
|
||||
headRoot [32]byte
|
||||
headBlock interfaces.ReadOnlyBeaconBlock
|
||||
}
|
||||
|
||||
// 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) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkchoiceUpdateArg) (*enginev1.PayloadIDBytes, error) {
|
||||
func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *fcuConfig) (*enginev1.PayloadIDBytes, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.notifyForkchoiceUpdate")
|
||||
defer span.End()
|
||||
|
||||
headBlk := arg.headBlock
|
||||
if arg.headBlock.IsNil() {
|
||||
log.Error("Head block is nil")
|
||||
return nil, nil
|
||||
}
|
||||
headBlk := arg.headBlock.Block()
|
||||
if headBlk == nil || headBlk.IsNil() || headBlk.Body().IsNil() {
|
||||
log.Error("Head block is nil")
|
||||
return nil, nil
|
||||
@@ -72,11 +69,10 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
|
||||
SafeBlockHash: justifiedHash[:],
|
||||
FinalizedBlockHash: finalizedHash[:],
|
||||
}
|
||||
|
||||
nextSlot := s.CurrentSlot() + 1 // Cache payload ID for next slot proposer.
|
||||
hasAttr, attr, proposerId := s.getPayloadAttribute(ctx, arg.headState, nextSlot, arg.headRoot[:])
|
||||
|
||||
payloadID, lastValidHash, err := s.cfg.ExecutionEngineCaller.ForkchoiceUpdated(ctx, fcs, attr)
|
||||
if arg.attributes == nil {
|
||||
arg.attributes = payloadattribute.EmptyWithVersion(headBlk.Version())
|
||||
}
|
||||
payloadID, lastValidHash, err := s.cfg.ExecutionEngineCaller.ForkchoiceUpdated(ctx, fcs, arg.attributes)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case execution.ErrAcceptedSyncingPayloadStatus:
|
||||
@@ -122,10 +118,11 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
|
||||
log.WithError(err).Error("Could not get head state")
|
||||
return nil, nil
|
||||
}
|
||||
pid, err := s.notifyForkchoiceUpdate(ctx, ¬ifyForkchoiceUpdateArg{
|
||||
headState: st,
|
||||
headRoot: r,
|
||||
headBlock: b.Block(),
|
||||
pid, err := s.notifyForkchoiceUpdate(ctx, &fcuConfig{
|
||||
headState: st,
|
||||
headRoot: r,
|
||||
headBlock: b,
|
||||
attributes: arg.attributes,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err // Returning err because it's recursive here.
|
||||
@@ -153,7 +150,9 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
|
||||
log.WithError(err).Error("Could not set head root to valid")
|
||||
return nil, nil
|
||||
}
|
||||
// If the forkchoice update call has an attribute, update the proposer payload ID cache.
|
||||
// If the forkchoice update call has an attribute, update the payload ID cache.
|
||||
hasAttr := arg.attributes != nil && !arg.attributes.IsEmpty()
|
||||
nextSlot := s.CurrentSlot() + 1
|
||||
if hasAttr && payloadID != nil {
|
||||
var pId [8]byte
|
||||
copy(pId[:], payloadID[:])
|
||||
@@ -162,7 +161,7 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
|
||||
"headSlot": headBlk.Slot(),
|
||||
"payloadID": fmt.Sprintf("%#x", bytesutil.Trunc(payloadID[:])),
|
||||
}).Info("Forkchoice updated with payload attributes for proposal")
|
||||
s.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(nextSlot, proposerId, pId, arg.headRoot)
|
||||
s.cfg.PayloadIDCache.Set(nextSlot, arg.headRoot, pId)
|
||||
} else if hasAttr && payloadID == nil && !features.Get().PrepareAllPayloads {
|
||||
log.WithFields(logrus.Fields{
|
||||
"blockHash": fmt.Sprintf("%#x", headPayload.BlockHash()),
|
||||
@@ -277,56 +276,50 @@ func (s *Service) pruneInvalidBlock(ctx context.Context, root, parentRoot, lvh [
|
||||
|
||||
// getPayloadAttributes returns the payload attributes for the given state and slot.
|
||||
// The attribute is required to initiate a payload build process in the context of an `engine_forkchoiceUpdated` call.
|
||||
func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState, slot primitives.Slot, headRoot []byte) (bool, payloadattribute.Attributer, primitives.ValidatorIndex) {
|
||||
func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState, slot primitives.Slot, headRoot []byte) payloadattribute.Attributer {
|
||||
emptyAttri := payloadattribute.EmptyWithVersion(st.Version())
|
||||
// Root is `[32]byte{}` since we are retrieving proposer ID of a given slot. During insertion at assignment the root was not known.
|
||||
proposerID, _, ok := s.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(slot, [32]byte{} /* root */)
|
||||
if !ok && !features.Get().PrepareAllPayloads { // There's no need to build attribute if there is no proposer for slot.
|
||||
return false, emptyAttri, 0
|
||||
}
|
||||
|
||||
// Get previous randao.
|
||||
// If it is an epoch boundary then process slots to get the right
|
||||
// shuffling before checking if the proposer is tracked. Otherwise
|
||||
// perform this check before. This is cheap as the NSC has already been updated.
|
||||
var val cache.TrackedValidator
|
||||
var ok bool
|
||||
e := slots.ToEpoch(slot)
|
||||
stateEpoch := slots.ToEpoch(st.Slot())
|
||||
if e == stateEpoch {
|
||||
val, ok = s.trackedProposer(st, slot)
|
||||
if !ok {
|
||||
return emptyAttri
|
||||
}
|
||||
}
|
||||
st = st.Copy()
|
||||
if slot > st.Slot() {
|
||||
var err error
|
||||
st, err = transition.ProcessSlotsUsingNextSlotCache(ctx, st, headRoot, slot)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not process slots to get payload attribute")
|
||||
return false, emptyAttri, 0
|
||||
return emptyAttri
|
||||
}
|
||||
}
|
||||
if e > stateEpoch {
|
||||
emptyAttri := payloadattribute.EmptyWithVersion(st.Version())
|
||||
val, ok = s.trackedProposer(st, slot)
|
||||
if !ok {
|
||||
return emptyAttri
|
||||
}
|
||||
}
|
||||
// Get previous randao.
|
||||
prevRando, err := helpers.RandaoMix(st, time.CurrentEpoch(st))
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get randao mix to get payload attribute")
|
||||
return false, emptyAttri, 0
|
||||
}
|
||||
|
||||
// Get fee recipient.
|
||||
feeRecipient := params.BeaconConfig().DefaultFeeRecipient
|
||||
recipient, err := s.cfg.BeaconDB.FeeRecipientByValidatorID(ctx, proposerID)
|
||||
switch {
|
||||
case errors.Is(err, kv.ErrNotFoundFeeRecipient):
|
||||
if feeRecipient.String() == params.BeaconConfig().EthBurnAddressHex {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"validatorIndex": proposerID,
|
||||
"burnAddress": params.BeaconConfig().EthBurnAddressHex,
|
||||
}).Warn("Fee recipient is currently using the burn address, " +
|
||||
"you will not be rewarded transaction fees on this setting. " +
|
||||
"Please set a different eth address as the fee recipient. " +
|
||||
"Please refer to our documentation for instructions")
|
||||
}
|
||||
case err != nil:
|
||||
log.WithError(err).Error("Could not get fee recipient to get payload attribute")
|
||||
return false, emptyAttri, 0
|
||||
default:
|
||||
feeRecipient = recipient
|
||||
return emptyAttri
|
||||
}
|
||||
|
||||
// Get timestamp.
|
||||
t, err := slots.ToTime(uint64(s.genesisTime.Unix()), slot)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get timestamp to get payload attribute")
|
||||
return false, emptyAttri, 0
|
||||
return emptyAttri
|
||||
}
|
||||
|
||||
var attr payloadattribute.Attributer
|
||||
@@ -335,51 +328,51 @@ func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState,
|
||||
withdrawals, err := st.ExpectedWithdrawals()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get expected withdrawals to get payload attribute")
|
||||
return false, emptyAttri, 0
|
||||
return emptyAttri
|
||||
}
|
||||
attr, err = payloadattribute.New(&enginev1.PayloadAttributesV3{
|
||||
Timestamp: uint64(t.Unix()),
|
||||
PrevRandao: prevRando,
|
||||
SuggestedFeeRecipient: feeRecipient.Bytes(),
|
||||
SuggestedFeeRecipient: val.FeeRecipient[:],
|
||||
Withdrawals: withdrawals,
|
||||
ParentBeaconBlockRoot: headRoot,
|
||||
})
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get payload attribute")
|
||||
return false, emptyAttri, 0
|
||||
return emptyAttri
|
||||
}
|
||||
case version.Capella:
|
||||
withdrawals, err := st.ExpectedWithdrawals()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get expected withdrawals to get payload attribute")
|
||||
return false, emptyAttri, 0
|
||||
return emptyAttri
|
||||
}
|
||||
attr, err = payloadattribute.New(&enginev1.PayloadAttributesV2{
|
||||
Timestamp: uint64(t.Unix()),
|
||||
PrevRandao: prevRando,
|
||||
SuggestedFeeRecipient: feeRecipient.Bytes(),
|
||||
SuggestedFeeRecipient: val.FeeRecipient[:],
|
||||
Withdrawals: withdrawals,
|
||||
})
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get payload attribute")
|
||||
return false, emptyAttri, 0
|
||||
return emptyAttri
|
||||
}
|
||||
case version.Bellatrix:
|
||||
attr, err = payloadattribute.New(&enginev1.PayloadAttributes{
|
||||
Timestamp: uint64(t.Unix()),
|
||||
PrevRandao: prevRando,
|
||||
SuggestedFeeRecipient: feeRecipient.Bytes(),
|
||||
SuggestedFeeRecipient: val.FeeRecipient[:],
|
||||
})
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get payload attribute")
|
||||
return false, emptyAttri, 0
|
||||
return emptyAttri
|
||||
}
|
||||
default:
|
||||
log.WithField("version", st.Version()).Error("Could not get payload attribute due to unknown state version")
|
||||
return false, emptyAttri, 0
|
||||
return emptyAttri
|
||||
}
|
||||
|
||||
return true, attr, proposerID
|
||||
return attr
|
||||
}
|
||||
|
||||
// removeInvalidBlockAndState removes the invalid block, blob and its corresponding state from the cache and DB.
|
||||
|
||||
@@ -26,11 +26,10 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/util"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
func Test_NotifyForkchoiceUpdate_GetPayloadAttrErrorCanContinue(t *testing.T) {
|
||||
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
|
||||
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
|
||||
ctx, beaconDB, fcs := tr.ctx, tr.db, tr.fcs
|
||||
|
||||
altairBlk := util.SaveBlock(t, ctx, beaconDB, util.NewBeaconBlockAltair())
|
||||
@@ -57,11 +56,14 @@ func Test_NotifyForkchoiceUpdate_GetPayloadAttrErrorCanContinue(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
|
||||
|
||||
b, err := consensusblocks.NewBeaconBlock(ðpb.BeaconBlockBellatrix{
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
ExecutionPayload: &v1.ExecutionPayload{},
|
||||
sb := ðpb.SignedBeaconBlockBellatrix{
|
||||
Block: ðpb.BeaconBlockBellatrix{
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
ExecutionPayload: &v1.ExecutionPayload{},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
b, err := consensusblocks.NewSignedBeaconBlock(sb)
|
||||
require.NoError(t, err)
|
||||
|
||||
pid := &v1.PayloadIDBytes{1}
|
||||
@@ -73,20 +75,20 @@ func Test_NotifyForkchoiceUpdate_GetPayloadAttrErrorCanContinue(t *testing.T) {
|
||||
// Intentionally generate a bad state such that `hash_tree_root` fails during `process_slot`
|
||||
s, err := state_native.InitializeFromProtoPhase0(ðpb.BeaconState{})
|
||||
require.NoError(t, err)
|
||||
arg := ¬ifyForkchoiceUpdateArg{
|
||||
arg := &fcuConfig{
|
||||
headState: s,
|
||||
headRoot: [32]byte{},
|
||||
headBlock: b,
|
||||
}
|
||||
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(1, 0, [8]byte{}, [32]byte{})
|
||||
service.cfg.PayloadIDCache.Set(1, [32]byte{}, [8]byte{})
|
||||
got, err := service.notifyForkchoiceUpdate(ctx, arg)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, got, pid) // We still get a payload ID even though the state is bad. This means it returns until the end.
|
||||
}
|
||||
|
||||
func Test_NotifyForkchoiceUpdate(t *testing.T) {
|
||||
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
|
||||
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
|
||||
ctx, beaconDB, fcs := tr.ctx, tr.db, tr.fcs
|
||||
|
||||
altairBlk := util.SaveBlock(t, ctx, beaconDB, util.NewBeaconBlockAltair())
|
||||
@@ -114,7 +116,7 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
blk interfaces.ReadOnlyBeaconBlock
|
||||
blk interfaces.ReadOnlySignedBeaconBlock
|
||||
headRoot [32]byte
|
||||
finalizedRoot [32]byte
|
||||
justifiedRoot [32]byte
|
||||
@@ -123,24 +125,24 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "phase0 block",
|
||||
blk: func() interfaces.ReadOnlyBeaconBlock {
|
||||
b, err := consensusblocks.NewBeaconBlock(ðpb.BeaconBlock{Body: ðpb.BeaconBlockBody{}})
|
||||
blk: func() interfaces.ReadOnlySignedBeaconBlock {
|
||||
b, err := consensusblocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Body: ðpb.BeaconBlockBody{}}})
|
||||
require.NoError(t, err)
|
||||
return b
|
||||
}(),
|
||||
},
|
||||
{
|
||||
name: "altair block",
|
||||
blk: func() interfaces.ReadOnlyBeaconBlock {
|
||||
b, err := consensusblocks.NewBeaconBlock(ðpb.BeaconBlockAltair{Body: ðpb.BeaconBlockBodyAltair{}})
|
||||
blk: func() interfaces.ReadOnlySignedBeaconBlock {
|
||||
b, err := consensusblocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockAltair{Block: ðpb.BeaconBlockAltair{Body: ðpb.BeaconBlockBodyAltair{}}})
|
||||
require.NoError(t, err)
|
||||
return b
|
||||
}(),
|
||||
},
|
||||
{
|
||||
name: "not execution block",
|
||||
blk: func() interfaces.ReadOnlyBeaconBlock {
|
||||
b, err := consensusblocks.NewBeaconBlock(ðpb.BeaconBlockBellatrix{
|
||||
blk: func() interfaces.ReadOnlySignedBeaconBlock {
|
||||
b, err := consensusblocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockBellatrix{Block: ðpb.BeaconBlockBellatrix{
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
ExecutionPayload: &v1.ExecutionPayload{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
@@ -153,19 +155,19 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
},
|
||||
})
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
return b
|
||||
}(),
|
||||
},
|
||||
{
|
||||
name: "happy case: finalized root is altair block",
|
||||
blk: func() interfaces.ReadOnlyBeaconBlock {
|
||||
b, err := consensusblocks.NewBeaconBlock(ðpb.BeaconBlockBellatrix{
|
||||
blk: func() interfaces.ReadOnlySignedBeaconBlock {
|
||||
b, err := consensusblocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockBellatrix{Block: ðpb.BeaconBlockBellatrix{
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
ExecutionPayload: &v1.ExecutionPayload{},
|
||||
},
|
||||
})
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
return b
|
||||
}(),
|
||||
@@ -174,12 +176,12 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "happy case: finalized root is bellatrix block",
|
||||
blk: func() interfaces.ReadOnlyBeaconBlock {
|
||||
b, err := consensusblocks.NewBeaconBlock(ðpb.BeaconBlockBellatrix{
|
||||
blk: func() interfaces.ReadOnlySignedBeaconBlock {
|
||||
b, err := consensusblocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockBellatrix{Block: ðpb.BeaconBlockBellatrix{
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
ExecutionPayload: &v1.ExecutionPayload{},
|
||||
},
|
||||
})
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
return b
|
||||
}(),
|
||||
@@ -188,12 +190,12 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "forkchoice updated with optimistic block",
|
||||
blk: func() interfaces.ReadOnlyBeaconBlock {
|
||||
b, err := consensusblocks.NewBeaconBlock(ðpb.BeaconBlockBellatrix{
|
||||
blk: func() interfaces.ReadOnlySignedBeaconBlock {
|
||||
b, err := consensusblocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockBellatrix{Block: ðpb.BeaconBlockBellatrix{
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
ExecutionPayload: &v1.ExecutionPayload{},
|
||||
},
|
||||
})
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
return b
|
||||
}(),
|
||||
@@ -203,12 +205,12 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "forkchoice updated with invalid block",
|
||||
blk: func() interfaces.ReadOnlyBeaconBlock {
|
||||
b, err := consensusblocks.NewBeaconBlock(ðpb.BeaconBlockBellatrix{
|
||||
blk: func() interfaces.ReadOnlySignedBeaconBlock {
|
||||
b, err := consensusblocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockBellatrix{Block: ðpb.BeaconBlockBellatrix{
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
ExecutionPayload: &v1.ExecutionPayload{},
|
||||
},
|
||||
})
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
return b
|
||||
}(),
|
||||
@@ -226,7 +228,7 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
|
||||
st, _ := util.DeterministicGenesisState(t, 1)
|
||||
require.NoError(t, beaconDB.SaveState(ctx, st, tt.finalizedRoot))
|
||||
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, tt.finalizedRoot))
|
||||
arg := ¬ifyForkchoiceUpdateArg{
|
||||
arg := &fcuConfig{
|
||||
headState: st,
|
||||
headRoot: tt.headRoot,
|
||||
headBlock: tt.blk,
|
||||
@@ -246,7 +248,7 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_NotifyForkchoiceUpdate_NIlLVH(t *testing.T) {
|
||||
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
|
||||
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
|
||||
ctx, beaconDB, fcs := tr.ctx, tr.db, tr.fcs
|
||||
|
||||
// Prepare blocks
|
||||
@@ -306,9 +308,9 @@ func Test_NotifyForkchoiceUpdate_NIlLVH(t *testing.T) {
|
||||
|
||||
require.NoError(t, beaconDB.SaveState(ctx, st, bra))
|
||||
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, bra))
|
||||
a := ¬ifyForkchoiceUpdateArg{
|
||||
a := &fcuConfig{
|
||||
headState: st,
|
||||
headBlock: wbd.Block(),
|
||||
headBlock: wbd,
|
||||
headRoot: brd,
|
||||
}
|
||||
_, err = service.notifyForkchoiceUpdate(ctx, a)
|
||||
@@ -334,7 +336,7 @@ func Test_NotifyForkchoiceUpdate_NIlLVH(t *testing.T) {
|
||||
// 3. the blockchain package calls fcu to obtain heads G -> F -> D.
|
||||
|
||||
func Test_NotifyForkchoiceUpdateRecursive_DoublyLinkedTree(t *testing.T) {
|
||||
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
|
||||
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
|
||||
ctx, beaconDB, fcs := tr.ctx, tr.db, tr.fcs
|
||||
|
||||
// Prepare blocks
|
||||
@@ -443,9 +445,9 @@ func Test_NotifyForkchoiceUpdateRecursive_DoublyLinkedTree(t *testing.T) {
|
||||
|
||||
require.NoError(t, beaconDB.SaveState(ctx, st, bra))
|
||||
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, bra))
|
||||
a := ¬ifyForkchoiceUpdateArg{
|
||||
a := &fcuConfig{
|
||||
headState: st,
|
||||
headBlock: wbg.Block(),
|
||||
headBlock: wbg,
|
||||
headRoot: brg,
|
||||
}
|
||||
_, err = service.notifyForkchoiceUpdate(ctx, a)
|
||||
@@ -467,7 +469,7 @@ func Test_NotifyNewPayload(t *testing.T) {
|
||||
cfg := params.BeaconConfig()
|
||||
cfg.TerminalTotalDifficulty = "2"
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
|
||||
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
|
||||
ctx, fcs := tr.ctx, tr.fcs
|
||||
|
||||
phase0State, _ := util.DeterministicGenesisState(t, 1)
|
||||
@@ -709,7 +711,7 @@ func Test_NotifyNewPayload_SetOptimisticToValid(t *testing.T) {
|
||||
cfg.TerminalTotalDifficulty = "2"
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
|
||||
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
|
||||
ctx := tr.ctx
|
||||
|
||||
bellatrixState, _ := util.DeterministicGenesisStateBellatrix(t, 2)
|
||||
@@ -777,83 +779,70 @@ func Test_reportInvalidBlock(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_GetPayloadAttribute(t *testing.T) {
|
||||
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
|
||||
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
|
||||
ctx := tr.ctx
|
||||
|
||||
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
|
||||
hasPayload, _, vId := service.getPayloadAttribute(ctx, st, 0, []byte{})
|
||||
require.Equal(t, false, hasPayload)
|
||||
require.Equal(t, primitives.ValidatorIndex(0), vId)
|
||||
attr := service.getPayloadAttribute(ctx, st, 0, []byte{})
|
||||
require.Equal(t, true, attr.IsEmpty())
|
||||
|
||||
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, Index: 0})
|
||||
// Cache hit, advance state, no fee recipient
|
||||
suggestedVid := primitives.ValidatorIndex(1)
|
||||
slot := primitives.Slot(1)
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, suggestedVid, [8]byte{}, [32]byte{})
|
||||
hook := logTest.NewGlobal()
|
||||
hasPayload, attr, vId := service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
|
||||
require.Equal(t, true, hasPayload)
|
||||
require.Equal(t, suggestedVid, vId)
|
||||
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
|
||||
attr = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
|
||||
require.Equal(t, false, attr.IsEmpty())
|
||||
require.Equal(t, params.BeaconConfig().EthBurnAddressHex, common.BytesToAddress(attr.SuggestedFeeRecipient()).String())
|
||||
require.LogsContain(t, hook, "Fee recipient is currently using the burn address")
|
||||
|
||||
// Cache hit, advance state, has fee recipient
|
||||
suggestedAddr := common.HexToAddress("123")
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, []primitives.ValidatorIndex{suggestedVid}, []common.Address{suggestedAddr}))
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, suggestedVid, [8]byte{}, [32]byte{})
|
||||
hasPayload, attr, vId = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
|
||||
require.Equal(t, true, hasPayload)
|
||||
require.Equal(t, suggestedVid, vId)
|
||||
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, FeeRecipient: primitives.ExecutionAddress(suggestedAddr), Index: 0})
|
||||
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
|
||||
attr = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
|
||||
require.Equal(t, false, attr.IsEmpty())
|
||||
require.Equal(t, suggestedAddr, common.BytesToAddress(attr.SuggestedFeeRecipient()))
|
||||
}
|
||||
|
||||
func Test_GetPayloadAttribute_PrepareAllPayloads(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
resetCfg := features.InitWithReset(&features.Flags{
|
||||
PrepareAllPayloads: true,
|
||||
})
|
||||
defer resetCfg()
|
||||
|
||||
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
|
||||
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
|
||||
ctx := tr.ctx
|
||||
|
||||
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
|
||||
hasPayload, attr, vId := service.getPayloadAttribute(ctx, st, 0, []byte{})
|
||||
require.Equal(t, true, hasPayload)
|
||||
require.Equal(t, primitives.ValidatorIndex(0), vId)
|
||||
attr := service.getPayloadAttribute(ctx, st, 0, []byte{})
|
||||
require.Equal(t, false, attr.IsEmpty())
|
||||
require.Equal(t, params.BeaconConfig().EthBurnAddressHex, common.BytesToAddress(attr.SuggestedFeeRecipient()).String())
|
||||
require.LogsContain(t, hook, "Fee recipient is currently using the burn address")
|
||||
}
|
||||
|
||||
func Test_GetPayloadAttributeV2(t *testing.T) {
|
||||
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
|
||||
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
|
||||
ctx := tr.ctx
|
||||
|
||||
st, _ := util.DeterministicGenesisStateCapella(t, 1)
|
||||
hasPayload, _, vId := service.getPayloadAttribute(ctx, st, 0, []byte{})
|
||||
require.Equal(t, false, hasPayload)
|
||||
require.Equal(t, primitives.ValidatorIndex(0), vId)
|
||||
attr := service.getPayloadAttribute(ctx, st, 0, []byte{})
|
||||
require.Equal(t, true, attr.IsEmpty())
|
||||
|
||||
// Cache hit, advance state, no fee recipient
|
||||
suggestedVid := primitives.ValidatorIndex(1)
|
||||
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, Index: 0})
|
||||
slot := primitives.Slot(1)
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, suggestedVid, [8]byte{}, [32]byte{})
|
||||
hook := logTest.NewGlobal()
|
||||
hasPayload, attr, vId := service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
|
||||
require.Equal(t, true, hasPayload)
|
||||
require.Equal(t, suggestedVid, vId)
|
||||
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
|
||||
attr = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
|
||||
require.Equal(t, false, attr.IsEmpty())
|
||||
require.Equal(t, params.BeaconConfig().EthBurnAddressHex, common.BytesToAddress(attr.SuggestedFeeRecipient()).String())
|
||||
require.LogsContain(t, hook, "Fee recipient is currently using the burn address")
|
||||
a, err := attr.Withdrawals()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, len(a))
|
||||
|
||||
// Cache hit, advance state, has fee recipient
|
||||
suggestedAddr := common.HexToAddress("123")
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, []primitives.ValidatorIndex{suggestedVid}, []common.Address{suggestedAddr}))
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, suggestedVid, [8]byte{}, [32]byte{})
|
||||
hasPayload, attr, vId = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
|
||||
require.Equal(t, true, hasPayload)
|
||||
require.Equal(t, suggestedVid, vId)
|
||||
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, FeeRecipient: primitives.ExecutionAddress(suggestedAddr), Index: 0})
|
||||
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
|
||||
attr = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
|
||||
require.Equal(t, false, attr.IsEmpty())
|
||||
require.Equal(t, suggestedAddr, common.BytesToAddress(attr.SuggestedFeeRecipient()))
|
||||
a, err = attr.Withdrawals()
|
||||
require.NoError(t, err)
|
||||
@@ -861,35 +850,30 @@ func Test_GetPayloadAttributeV2(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_GetPayloadAttributeDeneb(t *testing.T) {
|
||||
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
|
||||
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
|
||||
ctx := tr.ctx
|
||||
|
||||
st, _ := util.DeterministicGenesisStateDeneb(t, 1)
|
||||
hasPayload, _, vId := service.getPayloadAttribute(ctx, st, 0, []byte{})
|
||||
require.Equal(t, false, hasPayload)
|
||||
require.Equal(t, primitives.ValidatorIndex(0), vId)
|
||||
attr := service.getPayloadAttribute(ctx, st, 0, []byte{})
|
||||
require.Equal(t, true, attr.IsEmpty())
|
||||
|
||||
// Cache hit, advance state, no fee recipient
|
||||
suggestedVid := primitives.ValidatorIndex(1)
|
||||
slot := primitives.Slot(1)
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, suggestedVid, [8]byte{}, [32]byte{})
|
||||
hook := logTest.NewGlobal()
|
||||
hasPayload, attr, vId := service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
|
||||
require.Equal(t, true, hasPayload)
|
||||
require.Equal(t, suggestedVid, vId)
|
||||
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, Index: 0})
|
||||
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
|
||||
attr = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
|
||||
require.Equal(t, false, attr.IsEmpty())
|
||||
require.Equal(t, params.BeaconConfig().EthBurnAddressHex, common.BytesToAddress(attr.SuggestedFeeRecipient()).String())
|
||||
require.LogsContain(t, hook, "Fee recipient is currently using the burn address")
|
||||
a, err := attr.Withdrawals()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, len(a))
|
||||
|
||||
// Cache hit, advance state, has fee recipient
|
||||
suggestedAddr := common.HexToAddress("123")
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, []primitives.ValidatorIndex{suggestedVid}, []common.Address{suggestedAddr}))
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, suggestedVid, [8]byte{}, [32]byte{})
|
||||
hasPayload, attr, vId = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
|
||||
require.Equal(t, true, hasPayload)
|
||||
require.Equal(t, suggestedVid, vId)
|
||||
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, FeeRecipient: primitives.ExecutionAddress(suggestedAddr), Index: 0})
|
||||
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
|
||||
attr = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
|
||||
require.Equal(t, false, attr.IsEmpty())
|
||||
require.Equal(t, suggestedAddr, common.BytesToAddress(attr.SuggestedFeeRecipient()))
|
||||
a, err = attr.Withdrawals()
|
||||
require.NoError(t, err)
|
||||
@@ -1112,3 +1096,35 @@ func TestKZGCommitmentToVersionedHashes(t *testing.T) {
|
||||
require.Equal(t, vhs[0].String(), vh0)
|
||||
require.Equal(t, vhs[1].String(), vh1)
|
||||
}
|
||||
|
||||
func TestComputePayloadAttribute(t *testing.T) {
|
||||
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
|
||||
ctx := tr.ctx
|
||||
|
||||
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
|
||||
|
||||
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, Index: 0})
|
||||
// Cache hit, advance state, no fee recipient
|
||||
slot := primitives.Slot(1)
|
||||
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
|
||||
cfg := &postBlockProcessConfig{
|
||||
ctx: ctx,
|
||||
blockRoot: [32]byte{'a'},
|
||||
}
|
||||
fcu := &fcuConfig{
|
||||
headState: st,
|
||||
proposingSlot: slot,
|
||||
headRoot: [32]byte{},
|
||||
}
|
||||
require.NoError(t, service.computePayloadAttributes(cfg, fcu))
|
||||
require.Equal(t, false, fcu.attributes.IsEmpty())
|
||||
require.Equal(t, params.BeaconConfig().EthBurnAddressHex, common.BytesToAddress(fcu.attributes.SuggestedFeeRecipient()).String())
|
||||
|
||||
// Cache hit, advance state, has fee recipient
|
||||
suggestedAddr := common.HexToAddress("123")
|
||||
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, FeeRecipient: primitives.ExecutionAddress(suggestedAddr), Index: 0})
|
||||
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
|
||||
require.NoError(t, service.computePayloadAttributes(cfg, fcu))
|
||||
require.Equal(t, false, fcu.attributes.IsEmpty())
|
||||
require.Equal(t, suggestedAddr, common.BytesToAddress(fcu.attributes.SuggestedFeeRecipient()))
|
||||
}
|
||||
|
||||
@@ -8,20 +8,15 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
doublylinkedtree "github.com/prysmaticlabs/prysm/v4/beacon-chain/forkchoice/doubly-linked-tree"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
|
||||
payloadattribute "github.com/prysmaticlabs/prysm/v4/consensus-types/payload-attribute"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
func (s *Service) isNewProposer(slot primitives.Slot) bool {
|
||||
_, _, ok := s.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(slot, [32]byte{} /* root */)
|
||||
return ok || features.Get().PrepareAllPayloads
|
||||
}
|
||||
|
||||
func (s *Service) isNewHead(r [32]byte) bool {
|
||||
s.headLock.RLock()
|
||||
defer s.headLock.RUnlock()
|
||||
@@ -49,48 +44,69 @@ func (s *Service) getStateAndBlock(ctx context.Context, r [32]byte) (state.Beaco
|
||||
return headState, newHeadBlock, nil
|
||||
}
|
||||
|
||||
type fcuConfig struct {
|
||||
headState state.BeaconState
|
||||
headBlock interfaces.ReadOnlySignedBeaconBlock
|
||||
headRoot [32]byte
|
||||
proposingSlot primitives.Slot
|
||||
attributes payloadattribute.Attributer
|
||||
}
|
||||
|
||||
// sendFCU handles the logic to notify the engine of a forckhoice update
|
||||
// for the first time when processing an incoming block during regular sync. It
|
||||
// always updates the shuffling caches and handles epoch transitions when the
|
||||
// incoming block is late, preparing payload attributes in this case while it
|
||||
// only sends a message with empty attributes for early blocks.
|
||||
func (s *Service) sendFCU(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) error {
|
||||
if !s.isNewHead(cfg.headRoot) {
|
||||
return nil
|
||||
}
|
||||
if fcuArgs.attributes != nil && !fcuArgs.attributes.IsEmpty() && s.shouldOverrideFCU(cfg.headRoot, s.CurrentSlot()+1) {
|
||||
return nil
|
||||
}
|
||||
return s.forkchoiceUpdateWithExecution(cfg.ctx, fcuArgs)
|
||||
}
|
||||
|
||||
// sendFCUWithAttributes computes the payload attributes and sends an FCU message
|
||||
// to the engine if needed
|
||||
func (s *Service) sendFCUWithAttributes(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) {
|
||||
slotCtx, cancel := context.WithTimeout(context.Background(), slotDeadline)
|
||||
defer cancel()
|
||||
cfg.ctx = slotCtx
|
||||
if err := s.computePayloadAttributes(cfg, fcuArgs); err != nil {
|
||||
log.WithError(err).Error("could not compute payload attributes")
|
||||
return
|
||||
}
|
||||
if fcuArgs.attributes.IsEmpty() {
|
||||
return
|
||||
}
|
||||
s.cfg.ForkChoiceStore.RLock()
|
||||
defer s.cfg.ForkChoiceStore.RUnlock()
|
||||
if _, err := s.notifyForkchoiceUpdate(cfg.ctx, fcuArgs); err != nil {
|
||||
log.WithError(err).Error("could not update forkchoice with payload attributes for proposal")
|
||||
}
|
||||
}
|
||||
|
||||
// fockchoiceUpdateWithExecution is a wrapper around notifyForkchoiceUpdate. It decides whether a new call to FCU should be made.
|
||||
// it returns true if the new head is updated
|
||||
func (s *Service) forkchoiceUpdateWithExecution(ctx context.Context, newHeadRoot [32]byte, proposingSlot primitives.Slot) (bool, error) {
|
||||
func (s *Service) forkchoiceUpdateWithExecution(ctx context.Context, args *fcuConfig) error {
|
||||
_, span := trace.StartSpan(ctx, "beacon-chain.blockchain.forkchoiceUpdateWithExecution")
|
||||
defer span.End()
|
||||
// Note: Use the service context here to avoid the parent context being ended during a forkchoice update.
|
||||
ctx = trace.NewContext(s.ctx, span)
|
||||
|
||||
isNewHead := s.isNewHead(newHeadRoot)
|
||||
if !isNewHead {
|
||||
return false, nil
|
||||
}
|
||||
isNewProposer := s.isNewProposer(proposingSlot)
|
||||
if isNewProposer && !features.Get().DisableReorgLateBlocks {
|
||||
if s.shouldOverrideFCU(newHeadRoot, proposingSlot) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
headState, headBlock, err := s.getStateAndBlock(ctx, newHeadRoot)
|
||||
_, err := s.notifyForkchoiceUpdate(ctx, args)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get forkchoice update argument")
|
||||
return false, nil
|
||||
return errors.Wrap(err, "could not notify forkchoice update")
|
||||
}
|
||||
|
||||
_, err = s.notifyForkchoiceUpdate(ctx, ¬ifyForkchoiceUpdateArg{
|
||||
headState: headState,
|
||||
headRoot: newHeadRoot,
|
||||
headBlock: headBlock.Block(),
|
||||
})
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "could not notify forkchoice update")
|
||||
}
|
||||
|
||||
if err := s.saveHead(ctx, newHeadRoot, headBlock, headState); err != nil {
|
||||
if err := s.saveHead(ctx, args.headRoot, args.headBlock, args.headState); err != nil {
|
||||
log.WithError(err).Error("could not save head")
|
||||
}
|
||||
|
||||
// Only need to prune attestations from pool if the head has changed.
|
||||
if err := s.pruneAttsFromPool(headBlock); err != nil {
|
||||
if err := s.pruneAttsFromPool(args.headBlock); err != nil {
|
||||
log.WithError(err).Error("could not prune attestations from pool")
|
||||
}
|
||||
return true, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// shouldOverrideFCU checks whether the incoming block is still subject to being
|
||||
|
||||
@@ -17,15 +17,6 @@ import (
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
func TestService_isNewProposer(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
service := setupBeaconChain(t, beaconDB)
|
||||
require.Equal(t, false, service.isNewProposer(service.CurrentSlot()+1))
|
||||
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(service.CurrentSlot()+1, 0, [8]byte{}, [32]byte{} /* root */)
|
||||
require.Equal(t, true, service.isNewProposer(service.CurrentSlot()+1))
|
||||
}
|
||||
|
||||
func TestService_isNewHead(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
service := setupBeaconChain(t, beaconDB)
|
||||
@@ -67,33 +58,14 @@ func TestService_getHeadStateAndBlock(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestService_forkchoiceUpdateWithExecution_exceptionalCases(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
ctx := context.Background()
|
||||
opts := testServiceOptsWithDB(t)
|
||||
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
service.cfg.ProposerSlotIndexCache = cache.NewProposerPayloadIDsCache()
|
||||
_, err = service.forkchoiceUpdateWithExecution(ctx, service.headRoot(), service.CurrentSlot()+1)
|
||||
require.NoError(t, err)
|
||||
hookErr := "could not notify forkchoice update"
|
||||
invalidStateErr := "could not get state summary: could not find block in DB"
|
||||
require.LogsDoNotContain(t, hook, invalidStateErr)
|
||||
require.LogsDoNotContain(t, hook, hookErr)
|
||||
gb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.saveInitSyncBlock(ctx, [32]byte{'a'}, gb))
|
||||
_, err = service.forkchoiceUpdateWithExecution(ctx, [32]byte{'a'}, service.CurrentSlot()+1)
|
||||
require.NoError(t, err)
|
||||
require.LogsContain(t, hook, invalidStateErr)
|
||||
service.cfg.PayloadIDCache = cache.NewPayloadIDCache()
|
||||
service.cfg.TrackedValidatorsCache = cache.NewTrackedValidatorsCache()
|
||||
|
||||
hook.Reset()
|
||||
service.head = &head{
|
||||
root: [32]byte{'a'},
|
||||
block: nil, /* should not panic if notify head uses correct head */
|
||||
}
|
||||
|
||||
// Block in Cache
|
||||
b := util.NewBeaconBlock()
|
||||
b.Block.Slot = 2
|
||||
wsb, err := blocks.NewSignedBeaconBlock(b)
|
||||
@@ -107,13 +79,7 @@ func TestService_forkchoiceUpdateWithExecution_exceptionalCases(t *testing.T) {
|
||||
block: wsb,
|
||||
state: st,
|
||||
}
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(2, 1, [8]byte{1}, [32]byte{2})
|
||||
_, err = service.forkchoiceUpdateWithExecution(ctx, r1, service.CurrentSlot())
|
||||
require.NoError(t, err)
|
||||
require.LogsDoNotContain(t, hook, invalidStateErr)
|
||||
require.LogsDoNotContain(t, hook, hookErr)
|
||||
|
||||
// Block in DB
|
||||
service.cfg.PayloadIDCache.Set(2, [32]byte{2}, [8]byte{1})
|
||||
b = util.NewBeaconBlock()
|
||||
b.Block.Slot = 3
|
||||
util.SaveBlock(t, ctx, service.cfg.BeaconDB, b)
|
||||
@@ -125,25 +91,22 @@ func TestService_forkchoiceUpdateWithExecution_exceptionalCases(t *testing.T) {
|
||||
block: wsb,
|
||||
state: st,
|
||||
}
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(2, 1, [8]byte{1}, [32]byte{2})
|
||||
_, err = service.forkchoiceUpdateWithExecution(ctx, r1, service.CurrentSlot()+1)
|
||||
require.NoError(t, err)
|
||||
require.LogsDoNotContain(t, hook, invalidStateErr)
|
||||
require.LogsDoNotContain(t, hook, hookErr)
|
||||
vId, payloadID, has := service.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(2, [32]byte{2})
|
||||
require.Equal(t, true, has)
|
||||
require.Equal(t, primitives.ValidatorIndex(1), vId)
|
||||
require.Equal(t, [8]byte{1}, payloadID)
|
||||
service.cfg.PayloadIDCache.Set(2, [32]byte{2}, [8]byte{1})
|
||||
args := &fcuConfig{
|
||||
headState: st,
|
||||
headRoot: r1,
|
||||
headBlock: wsb,
|
||||
proposingSlot: service.CurrentSlot() + 1,
|
||||
}
|
||||
require.NoError(t, service.forkchoiceUpdateWithExecution(ctx, args))
|
||||
|
||||
// Test zero headRoot returns immediately.
|
||||
headRoot := service.headRoot()
|
||||
_, err = service.forkchoiceUpdateWithExecution(ctx, [32]byte{}, service.CurrentSlot()+1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, service.headRoot(), headRoot)
|
||||
payloadID, has := service.cfg.PayloadIDCache.PayloadID(2, [32]byte{2})
|
||||
require.Equal(t, true, has)
|
||||
require.Equal(t, primitives.PayloadID{1}, payloadID)
|
||||
}
|
||||
|
||||
func TestService_forkchoiceUpdateWithExecution_SameHeadRootNewProposer(t *testing.T) {
|
||||
service, tr := minimalTestService(t)
|
||||
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
|
||||
ctx, beaconDB, fcs := tr.ctx, tr.db, tr.fcs
|
||||
|
||||
altairBlk := util.SaveBlock(t, ctx, beaconDB, util.NewBeaconBlockAltair())
|
||||
@@ -182,10 +145,14 @@ func TestService_forkchoiceUpdateWithExecution_SameHeadRootNewProposer(t *testin
|
||||
service.head.root = r
|
||||
service.head.block = sb
|
||||
service.head.state = st
|
||||
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(service.CurrentSlot()+1, 0, [8]byte{}, [32]byte{} /* root */)
|
||||
_, err = service.forkchoiceUpdateWithExecution(ctx, r, service.CurrentSlot()+1)
|
||||
require.NoError(t, err)
|
||||
|
||||
service.cfg.PayloadIDCache.Set(service.CurrentSlot()+1, [32]byte{} /* root */, [8]byte{})
|
||||
args := &fcuConfig{
|
||||
headState: st,
|
||||
headBlock: sb,
|
||||
headRoot: r,
|
||||
proposingSlot: service.CurrentSlot() + 1,
|
||||
}
|
||||
require.NoError(t, service.forkchoiceUpdateWithExecution(ctx, args))
|
||||
}
|
||||
|
||||
func TestShouldOverrideFCU(t *testing.T) {
|
||||
|
||||
@@ -69,10 +69,18 @@ func WithDepositCache(c cache.DepositCache) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithProposerIdsCache for proposer id cache.
|
||||
func WithProposerIdsCache(c *cache.ProposerPayloadIDsCache) Option {
|
||||
// WithPayloadIDCache for payload ID cache.
|
||||
func WithPayloadIDCache(c *cache.PayloadIDCache) Option {
|
||||
return func(s *Service) error {
|
||||
s.cfg.ProposerSlotIndexCache = c
|
||||
s.cfg.PayloadIDCache = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithTrackedValidatorsCache for tracked validators cache.
|
||||
func WithTrackedValidatorsCache(c *cache.TrackedValidatorsCache) Option {
|
||||
return func(s *Service) error {
|
||||
s.cfg.TrackedValidatorsCache = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/attestation"
|
||||
"github.com/prysmaticlabs/prysm/v4/runtime/version"
|
||||
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
@@ -41,106 +40,67 @@ const depositDeadline = 20 * time.Second
|
||||
// This defines size of the upper bound for initial sync block cache.
|
||||
var initialSyncBlockCacheSize = uint64(2 * params.BeaconConfig().SlotsPerEpoch)
|
||||
|
||||
// postBlockProcessConfig is a structure that contains the data needed to
|
||||
// process the beacon block after validating the state transition function
|
||||
type postBlockProcessConfig struct {
|
||||
ctx context.Context
|
||||
signed interfaces.ReadOnlySignedBeaconBlock
|
||||
blockRoot [32]byte
|
||||
headRoot [32]byte
|
||||
postState state.BeaconState
|
||||
isValidPayload bool
|
||||
}
|
||||
|
||||
// postBlockProcess is called when a gossip block is received. This function performs
|
||||
// several duties most importantly informing the engine if head was updated,
|
||||
// saving the new head information to the blockchain package and
|
||||
// handling attestations, slashings and similar included in the block.
|
||||
func (s *Service) postBlockProcess(ctx context.Context, signed interfaces.ReadOnlySignedBeaconBlock, blockRoot [32]byte, postState state.BeaconState, isValidPayload bool) error {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.onBlock")
|
||||
func (s *Service) postBlockProcess(cfg *postBlockProcessConfig) error {
|
||||
ctx, span := trace.StartSpan(cfg.ctx, "blockChain.onBlock")
|
||||
defer span.End()
|
||||
if err := consensusblocks.BeaconBlockIsNil(signed); err != nil {
|
||||
cfg.ctx = ctx
|
||||
if err := consensusblocks.BeaconBlockIsNil(cfg.signed); err != nil {
|
||||
return invalidBlock{error: err}
|
||||
}
|
||||
startTime := time.Now()
|
||||
b := signed.Block()
|
||||
fcuArgs := &fcuConfig{}
|
||||
|
||||
if err := s.cfg.ForkChoiceStore.InsertNode(ctx, postState, blockRoot); err != nil {
|
||||
return errors.Wrapf(err, "could not insert block %d to fork choice store", signed.Block().Slot())
|
||||
defer s.handleSecondFCUCall(cfg, fcuArgs)
|
||||
defer s.sendStateFeedOnBlock(cfg)
|
||||
defer reportProcessingTime(startTime)
|
||||
defer reportAttestationInclusion(cfg.signed.Block())
|
||||
|
||||
err := s.cfg.ForkChoiceStore.InsertNode(ctx, cfg.postState, cfg.blockRoot)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not insert block %d to fork choice store", cfg.signed.Block().Slot())
|
||||
}
|
||||
if err := s.handleBlockAttestations(ctx, signed.Block(), postState); err != nil {
|
||||
if err := s.handleBlockAttestations(ctx, cfg.signed.Block(), cfg.postState); err != nil {
|
||||
return errors.Wrap(err, "could not handle block's attestations")
|
||||
}
|
||||
|
||||
s.InsertSlashingsToForkChoiceStore(ctx, signed.Block().Body().AttesterSlashings())
|
||||
if isValidPayload {
|
||||
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, blockRoot); err != nil {
|
||||
s.InsertSlashingsToForkChoiceStore(ctx, cfg.signed.Block().Body().AttesterSlashings())
|
||||
if cfg.isValidPayload {
|
||||
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, cfg.blockRoot); err != nil {
|
||||
return errors.Wrap(err, "could not set optimistic block to valid")
|
||||
}
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
headRoot, err := s.cfg.ForkChoiceStore.Head(ctx)
|
||||
cfg.headRoot, err = s.cfg.ForkChoiceStore.Head(ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("Could not update head")
|
||||
}
|
||||
if blockRoot != headRoot {
|
||||
receivedWeight, err := s.cfg.ForkChoiceStore.Weight(blockRoot)
|
||||
if err != nil {
|
||||
log.WithField("root", fmt.Sprintf("%#x", blockRoot)).Warn("could not determine node weight")
|
||||
}
|
||||
headWeight, err := s.cfg.ForkChoiceStore.Weight(headRoot)
|
||||
if err != nil {
|
||||
log.WithField("root", fmt.Sprintf("%#x", headRoot)).Warn("could not determine node weight")
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"receivedRoot": fmt.Sprintf("%#x", blockRoot),
|
||||
"receivedWeight": receivedWeight,
|
||||
"headRoot": fmt.Sprintf("%#x", headRoot),
|
||||
"headWeight": headWeight,
|
||||
}).Debug("Head block is not the received block")
|
||||
}
|
||||
newBlockHeadElapsedTime.Observe(float64(time.Since(start).Milliseconds()))
|
||||
|
||||
// verify conditions for FCU, notifies FCU, and saves the new head.
|
||||
// This function also prunes attestations, other similar operations happen in prunePostBlockOperationPools.
|
||||
if _, err := s.forkchoiceUpdateWithExecution(ctx, headRoot, s.CurrentSlot()+1); err != nil {
|
||||
return err
|
||||
if cfg.headRoot != cfg.blockRoot {
|
||||
s.logNonCanonicalBlockReceived(cfg.blockRoot, cfg.headRoot)
|
||||
return nil
|
||||
}
|
||||
|
||||
optimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(blockRoot)
|
||||
if err != nil {
|
||||
log.WithError(err).Debug("Could not check if block is optimistic")
|
||||
optimistic = true
|
||||
if err := s.getFCUArgs(cfg, fcuArgs); err != nil {
|
||||
log.WithError(err).Error("Could not get forkchoice update argument")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send notification of the processed block to the state feed.
|
||||
s.cfg.StateNotifier.StateFeed().Send(&feed.Event{
|
||||
Type: statefeed.BlockProcessed,
|
||||
Data: &statefeed.BlockProcessedData{
|
||||
Slot: signed.Block().Slot(),
|
||||
BlockRoot: blockRoot,
|
||||
SignedBlock: signed,
|
||||
Verified: true,
|
||||
Optimistic: optimistic,
|
||||
},
|
||||
})
|
||||
|
||||
defer reportAttestationInclusion(b)
|
||||
if headRoot == blockRoot {
|
||||
// Updating next slot state cache can happen in the background
|
||||
// except in the epoch boundary in which case we lock to handle
|
||||
// the shuffling and proposer caches updates.
|
||||
// We handle these caches only on canonical
|
||||
// blocks, otherwise this will be handled by lateBlockTasks
|
||||
slot := postState.Slot()
|
||||
if slots.IsEpochEnd(slot) {
|
||||
if err := transition.UpdateNextSlotCache(ctx, blockRoot[:], postState); err != nil {
|
||||
return errors.Wrap(err, "could not update next slot state cache")
|
||||
}
|
||||
if err := s.handleEpochBoundary(ctx, slot, postState, blockRoot[:]); err != nil {
|
||||
return errors.Wrap(err, "could not handle epoch boundary")
|
||||
}
|
||||
} else {
|
||||
go func() {
|
||||
slotCtx, cancel := context.WithTimeout(context.Background(), slotDeadline)
|
||||
defer cancel()
|
||||
if err := transition.UpdateNextSlotCache(slotCtx, blockRoot[:], postState); err != nil {
|
||||
log.WithError(err).Error("could not update next slot state cache")
|
||||
}
|
||||
}()
|
||||
}
|
||||
if err := s.sendFCU(cfg, fcuArgs); err != nil {
|
||||
return errors.Wrap(err, "could not send FCU to engine")
|
||||
}
|
||||
onBlockProcessingTime.Observe(float64(time.Since(startTime).Milliseconds()))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -322,10 +282,10 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []consensusblocks.ROBlo
|
||||
return errors.Wrap(err, "could not set optimistic block to valid")
|
||||
}
|
||||
}
|
||||
arg := ¬ifyForkchoiceUpdateArg{
|
||||
arg := &fcuConfig{
|
||||
headState: preState,
|
||||
headRoot: lastBR,
|
||||
headBlock: lastB.Block(),
|
||||
headBlock: lastB,
|
||||
}
|
||||
if _, err := s.notifyForkchoiceUpdate(ctx, arg); err != nil {
|
||||
return err
|
||||
@@ -381,9 +341,6 @@ func (s *Service) updateEpochBoundaryCaches(ctx context.Context, st state.Beacon
|
||||
if err := helpers.UpdateCommitteeCache(slotCtx, st, e+1); err != nil {
|
||||
log.WithError(err).Warn("Could not update committee cache")
|
||||
}
|
||||
if err := helpers.UpdateUnsafeProposerIndicesInCache(slotCtx, st, e+1); err != nil {
|
||||
log.WithError(err).Warn("Failed to cache next epoch proposers")
|
||||
}
|
||||
}()
|
||||
// The latest block header is from the previous epoch
|
||||
r, err := st.LatestBlockHeader().HashTreeRoot()
|
||||
@@ -674,13 +631,12 @@ func (s *Service) lateBlockTasks(ctx context.Context) {
|
||||
log.WithError(err).Error("lateBlockTasks: could not update epoch boundary caches")
|
||||
}
|
||||
s.cfg.ForkChoiceStore.RUnlock()
|
||||
// Head root should be empty when retrieving proposer index for the next slot.
|
||||
_, id, has := s.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(s.CurrentSlot()+1, [32]byte{} /* head root */)
|
||||
// There exists proposer for next slot, but we haven't called fcu w/ payload attribute yet.
|
||||
if (!has && !features.Get().PrepareAllPayloads) || id != [8]byte{} {
|
||||
// return early if we already started building a block for the current
|
||||
// head root
|
||||
_, has := s.cfg.PayloadIDCache.PayloadID(s.CurrentSlot()+1, headRoot)
|
||||
if has {
|
||||
return
|
||||
}
|
||||
|
||||
s.headLock.RLock()
|
||||
headBlock, err := s.headBlock()
|
||||
if err != nil {
|
||||
@@ -690,11 +646,18 @@ func (s *Service) lateBlockTasks(ctx context.Context) {
|
||||
}
|
||||
s.headLock.RUnlock()
|
||||
s.cfg.ForkChoiceStore.RLock()
|
||||
_, err = s.notifyForkchoiceUpdate(ctx, ¬ifyForkchoiceUpdateArg{
|
||||
fcuArgs := &fcuConfig{
|
||||
headState: headState,
|
||||
headRoot: headRoot,
|
||||
headBlock: headBlock.Block(),
|
||||
})
|
||||
headBlock: headBlock,
|
||||
}
|
||||
fcuArgs.attributes = s.getPayloadAttribute(ctx, headState, s.CurrentSlot()+1, headRoot[:])
|
||||
// return early if we are not proposing next slot
|
||||
if fcuArgs.attributes.IsEmpty() {
|
||||
s.cfg.ForkChoiceStore.RUnlock()
|
||||
return
|
||||
}
|
||||
_, err = s.notifyForkchoiceUpdate(ctx, fcuArgs)
|
||||
s.cfg.ForkChoiceStore.RUnlock()
|
||||
if err != nil {
|
||||
log.WithError(err).Debug("could not perform late block tasks: failed to update forkchoice with engine")
|
||||
|
||||
@@ -3,9 +3,13 @@ package blockchain
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
|
||||
statefeed "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/state"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
|
||||
doublylinkedtree "github.com/prysmaticlabs/prysm/v4/beacon-chain/forkchoice/doubly-linked-tree"
|
||||
forkchoicetypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/forkchoice/types"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
@@ -15,8 +19,8 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
mathutil "github.com/prysmaticlabs/prysm/v4/math"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/time"
|
||||
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
@@ -25,6 +29,127 @@ func (s *Service) CurrentSlot() primitives.Slot {
|
||||
return slots.CurrentSlot(uint64(s.genesisTime.Unix()))
|
||||
}
|
||||
|
||||
// getFCUArgs returns the arguments to call forkchoice update
|
||||
func (s *Service) getFCUArgs(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) error {
|
||||
if err := s.getFCUArgsEarlyBlock(cfg, fcuArgs); err != nil {
|
||||
return err
|
||||
}
|
||||
slot := cfg.signed.Block().Slot()
|
||||
if slots.WithinVotingWindow(uint64(s.genesisTime.Unix()), slot) {
|
||||
return nil
|
||||
}
|
||||
return s.computePayloadAttributes(cfg, fcuArgs)
|
||||
}
|
||||
|
||||
func (s *Service) getFCUArgsEarlyBlock(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) error {
|
||||
if cfg.blockRoot == cfg.headRoot {
|
||||
fcuArgs.headState = cfg.postState
|
||||
fcuArgs.headBlock = cfg.signed
|
||||
fcuArgs.headRoot = cfg.headRoot
|
||||
fcuArgs.proposingSlot = s.CurrentSlot() + 1
|
||||
return nil
|
||||
}
|
||||
return s.fcuArgsNonCanonicalBlock(cfg, fcuArgs)
|
||||
}
|
||||
|
||||
// logNonCanonicalBlockReceived prints a message informing that the received
|
||||
// block is not the head of the chain. It requires the caller holds a lock on
|
||||
// Foprkchoice.
|
||||
func (s *Service) logNonCanonicalBlockReceived(blockRoot [32]byte, headRoot [32]byte) {
|
||||
receivedWeight, err := s.cfg.ForkChoiceStore.Weight(blockRoot)
|
||||
if err != nil {
|
||||
log.WithField("root", fmt.Sprintf("%#x", blockRoot)).Warn("could not determine node weight")
|
||||
}
|
||||
headWeight, err := s.cfg.ForkChoiceStore.Weight(headRoot)
|
||||
if err != nil {
|
||||
log.WithField("root", fmt.Sprintf("%#x", headRoot)).Warn("could not determine node weight")
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"receivedRoot": fmt.Sprintf("%#x", blockRoot),
|
||||
"receivedWeight": receivedWeight,
|
||||
"headRoot": fmt.Sprintf("%#x", headRoot),
|
||||
"headWeight": headWeight,
|
||||
}).Debug("Head block is not the received block")
|
||||
}
|
||||
|
||||
// fcuArgsNonCanonicalBlock returns the arguments to the FCU call when the
|
||||
// incoming block is non-canonical, that is, based on the head root.
|
||||
func (s *Service) fcuArgsNonCanonicalBlock(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) error {
|
||||
headState, headBlock, err := s.getStateAndBlock(cfg.ctx, cfg.headRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fcuArgs.headState = headState
|
||||
fcuArgs.headBlock = headBlock
|
||||
fcuArgs.headRoot = cfg.headRoot
|
||||
fcuArgs.proposingSlot = s.CurrentSlot() + 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// sendStateFeedOnBlock sends an event that a new block has been synced
|
||||
func (s *Service) sendStateFeedOnBlock(cfg *postBlockProcessConfig) {
|
||||
optimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(cfg.blockRoot)
|
||||
if err != nil {
|
||||
log.WithError(err).Debug("Could not check if block is optimistic")
|
||||
optimistic = true
|
||||
}
|
||||
// Send notification of the processed block to the state feed.
|
||||
s.cfg.StateNotifier.StateFeed().Send(&feed.Event{
|
||||
Type: statefeed.BlockProcessed,
|
||||
Data: &statefeed.BlockProcessedData{
|
||||
Slot: cfg.signed.Block().Slot(),
|
||||
BlockRoot: cfg.blockRoot,
|
||||
SignedBlock: cfg.signed,
|
||||
Verified: true,
|
||||
Optimistic: optimistic,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// updateCachesPostBlockProcessing updates the next slot cache and handles the epoch
|
||||
// boundary in order to compute the right proposer indices after processing
|
||||
// state transition. This function is called on late blocks while still locked,
|
||||
// before sending FCU to the engine.
|
||||
func (s *Service) updateCachesPostBlockProcessing(cfg *postBlockProcessConfig) error {
|
||||
slot := cfg.postState.Slot()
|
||||
if err := transition.UpdateNextSlotCache(cfg.ctx, cfg.blockRoot[:], cfg.postState); err != nil {
|
||||
return errors.Wrap(err, "could not update next slot state cache")
|
||||
}
|
||||
if !slots.IsEpochEnd(slot) {
|
||||
return nil
|
||||
}
|
||||
return s.handleEpochBoundary(cfg.ctx, slot, cfg.postState, cfg.blockRoot[:])
|
||||
}
|
||||
|
||||
// handleSecondFCUCall handles a second call to FCU when syncing a new block.
|
||||
// This is useful when proposing in the next block and we want to defer the
|
||||
// computation of the next slot shuffling.
|
||||
func (s *Service) handleSecondFCUCall(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) {
|
||||
if (fcuArgs.attributes == nil || fcuArgs.attributes.IsEmpty()) && cfg.headRoot == cfg.blockRoot {
|
||||
go s.sendFCUWithAttributes(cfg, fcuArgs)
|
||||
}
|
||||
}
|
||||
|
||||
// reportProcessingTime reports the metric of how long it took to process the
|
||||
// current block
|
||||
func reportProcessingTime(startTime time.Time) {
|
||||
onBlockProcessingTime.Observe(float64(time.Since(startTime).Milliseconds()))
|
||||
}
|
||||
|
||||
// computePayloadAttributes modifies the passed FCU arguments to
|
||||
// contain the right payload attributes with the tracked proposer. It gets
|
||||
// called on blocks that arrive after the attestation voting window, or in a
|
||||
// background routine after syncing early blocks.
|
||||
func (s *Service) computePayloadAttributes(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) error {
|
||||
if cfg.blockRoot == cfg.headRoot {
|
||||
if err := s.updateCachesPostBlockProcessing(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
fcuArgs.attributes = s.getPayloadAttribute(cfg.ctx, fcuArgs.headState, fcuArgs.proposingSlot, cfg.headRoot[:])
|
||||
return nil
|
||||
}
|
||||
|
||||
// getBlockPreState returns the pre state of an incoming block. It uses the parent root of the block
|
||||
// to retrieve the state in DB. It verifies the pre state's validity and the incoming block
|
||||
// is in the correct time window.
|
||||
|
||||
@@ -567,7 +567,7 @@ func TestOnBlock_CanFinalize_WithOnTick(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, r, wsb, postState))
|
||||
require.NoError(t, service.postBlockProcess(ctx, wsb, r, postState, true))
|
||||
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, r, [32]byte{}, postState, true}))
|
||||
require.NoError(t, service.updateJustificationOnBlock(ctx, preState, postState, currStoreJustifiedEpoch))
|
||||
_, err = service.updateFinalizationOnBlock(ctx, preState, postState, currStoreFinalizedEpoch)
|
||||
require.NoError(t, err)
|
||||
@@ -615,7 +615,7 @@ func TestOnBlock_CanFinalize(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, r, wsb, postState))
|
||||
require.NoError(t, service.postBlockProcess(ctx, wsb, r, postState, true))
|
||||
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, r, [32]byte{}, postState, true}))
|
||||
require.NoError(t, service.updateJustificationOnBlock(ctx, preState, postState, currStoreJustifiedEpoch))
|
||||
_, err = service.updateFinalizationOnBlock(ctx, preState, postState, currStoreFinalizedEpoch)
|
||||
require.NoError(t, err)
|
||||
@@ -641,7 +641,7 @@ func TestOnBlock_CanFinalize(t *testing.T) {
|
||||
|
||||
func TestOnBlock_NilBlock(t *testing.T) {
|
||||
service, tr := minimalTestService(t)
|
||||
err := service.postBlockProcess(tr.ctx, nil, [32]byte{}, nil, true)
|
||||
err := service.postBlockProcess(&postBlockProcessConfig{tr.ctx, nil, [32]byte{}, [32]byte{}, nil, true})
|
||||
require.Equal(t, true, IsInvalidBlock(err))
|
||||
}
|
||||
|
||||
@@ -689,7 +689,7 @@ func TestOnBlock_CallNewPayloadAndForkchoiceUpdated(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, r, wsb, postState))
|
||||
require.NoError(t, service.postBlockProcess(ctx, wsb, r, postState, false))
|
||||
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, r, [32]byte{}, postState, false}))
|
||||
testState, err = service.cfg.StateGen.StateByRoot(ctx, r)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -895,7 +895,7 @@ func Test_validateMergeTransitionBlock(t *testing.T) {
|
||||
cfg.TerminalBlockHash = params.BeaconConfig().ZeroHash
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
|
||||
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
|
||||
ctx := tr.ctx
|
||||
|
||||
aHash := common.BytesToHash([]byte("a"))
|
||||
@@ -1111,7 +1111,7 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb1)
|
||||
require.NoError(t, err)
|
||||
lock.Lock()
|
||||
require.NoError(t, service.postBlockProcess(ctx, wsb1, r1, postState, true))
|
||||
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb1, r1, [32]byte{}, postState, true}))
|
||||
lock.Unlock()
|
||||
wg.Done()
|
||||
}()
|
||||
@@ -1121,7 +1121,7 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb2)
|
||||
require.NoError(t, err)
|
||||
lock.Lock()
|
||||
require.NoError(t, service.postBlockProcess(ctx, wsb2, r2, postState, true))
|
||||
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb2, r2, [32]byte{}, postState, true}))
|
||||
lock.Unlock()
|
||||
wg.Done()
|
||||
}()
|
||||
@@ -1131,7 +1131,7 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb3)
|
||||
require.NoError(t, err)
|
||||
lock.Lock()
|
||||
require.NoError(t, service.postBlockProcess(ctx, wsb3, r3, postState, true))
|
||||
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb3, r3, [32]byte{}, postState, true}))
|
||||
lock.Unlock()
|
||||
wg.Done()
|
||||
}()
|
||||
@@ -1141,7 +1141,7 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb4)
|
||||
require.NoError(t, err)
|
||||
lock.Lock()
|
||||
require.NoError(t, service.postBlockProcess(ctx, wsb4, r4, postState, true))
|
||||
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb4, r4, [32]byte{}, postState, true}))
|
||||
lock.Unlock()
|
||||
wg.Done()
|
||||
}()
|
||||
@@ -1216,7 +1216,7 @@ func TestStore_NoViableHead_FCU(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
require.NoError(t, service.postBlockProcess(ctx, wsb, root, postState, false))
|
||||
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}))
|
||||
}
|
||||
|
||||
for i := 6; i < 12; i++ {
|
||||
@@ -1234,7 +1234,7 @@ func TestStore_NoViableHead_FCU(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
err = service.postBlockProcess(ctx, wsb, root, postState, false)
|
||||
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -1253,7 +1253,7 @@ func TestStore_NoViableHead_FCU(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
err = service.postBlockProcess(ctx, wsb, root, postState, false)
|
||||
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
// Check that we haven't justified the second epoch yet
|
||||
@@ -1275,7 +1275,7 @@ func TestStore_NoViableHead_FCU(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, firstInvalidRoot, wsb, postState))
|
||||
err = service.postBlockProcess(ctx, wsb, firstInvalidRoot, postState, false)
|
||||
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, firstInvalidRoot, [32]byte{}, postState, false})
|
||||
require.NoError(t, err)
|
||||
jc = service.cfg.ForkChoiceStore.JustifiedCheckpoint()
|
||||
require.Equal(t, primitives.Epoch(2), jc.Epoch)
|
||||
@@ -1303,7 +1303,7 @@ func TestStore_NoViableHead_FCU(t *testing.T) {
|
||||
postState, err = service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
err = service.postBlockProcess(ctx, wsb, root, postState, false)
|
||||
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})
|
||||
require.ErrorContains(t, "received an INVALID payload from execution engine", err)
|
||||
// Check that forkchoice's head is the last invalid block imported. The
|
||||
// store's headroot is the previous head (since the invalid block did
|
||||
@@ -1332,7 +1332,7 @@ func TestStore_NoViableHead_FCU(t *testing.T) {
|
||||
postState, err = service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
err = service.postBlockProcess(ctx, wsb, root, postState, true)
|
||||
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, true})
|
||||
require.NoError(t, err)
|
||||
// Check the newly imported block is head, it justified the right
|
||||
// checkpoint and the node is no longer optimistic
|
||||
@@ -1394,7 +1394,7 @@ func TestStore_NoViableHead_NewPayload(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
require.NoError(t, service.postBlockProcess(ctx, wsb, root, postState, false))
|
||||
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}))
|
||||
}
|
||||
|
||||
for i := 6; i < 12; i++ {
|
||||
@@ -1412,7 +1412,7 @@ func TestStore_NoViableHead_NewPayload(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
err = service.postBlockProcess(ctx, wsb, root, postState, false)
|
||||
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -1432,7 +1432,7 @@ func TestStore_NoViableHead_NewPayload(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
err = service.postBlockProcess(ctx, wsb, root, postState, false)
|
||||
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
// Check that we haven't justified the second epoch yet
|
||||
@@ -1454,7 +1454,7 @@ func TestStore_NoViableHead_NewPayload(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, firstInvalidRoot, wsb, postState))
|
||||
err = service.postBlockProcess(ctx, wsb, firstInvalidRoot, postState, false)
|
||||
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, firstInvalidRoot, [32]byte{}, postState, false})
|
||||
require.NoError(t, err)
|
||||
jc = service.cfg.ForkChoiceStore.JustifiedCheckpoint()
|
||||
require.Equal(t, primitives.Epoch(2), jc.Epoch)
|
||||
@@ -1510,7 +1510,7 @@ func TestStore_NoViableHead_NewPayload(t *testing.T) {
|
||||
postState, err = service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
err = service.postBlockProcess(ctx, wsb, root, postState, true)
|
||||
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, true})
|
||||
require.NoError(t, err)
|
||||
// Check the newly imported block is head, it justified the right
|
||||
// checkpoint and the node is no longer optimistic
|
||||
@@ -1574,7 +1574,7 @@ func TestStore_NoViableHead_Liveness(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
require.NoError(t, service.postBlockProcess(ctx, wsb, root, postState, false))
|
||||
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}))
|
||||
}
|
||||
|
||||
for i := 6; i < 12; i++ {
|
||||
@@ -1593,7 +1593,7 @@ func TestStore_NoViableHead_Liveness(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
err = service.postBlockProcess(ctx, wsb, root, postState, false)
|
||||
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -1612,7 +1612,7 @@ func TestStore_NoViableHead_Liveness(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, lastValidRoot, wsb, postState))
|
||||
err = service.postBlockProcess(ctx, wsb, lastValidRoot, postState, false)
|
||||
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, lastValidRoot, [32]byte{}, postState, false})
|
||||
require.NoError(t, err)
|
||||
// save the post state and the payload Hash of this block since it will
|
||||
// be the LVH
|
||||
@@ -1639,7 +1639,7 @@ func TestStore_NoViableHead_Liveness(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, invalidRoots[i-13], wsb, postState))
|
||||
err = service.postBlockProcess(ctx, wsb, invalidRoots[i-13], postState, false)
|
||||
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, invalidRoots[i-13], [32]byte{}, postState, false})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
// Check that we have justified the second epoch
|
||||
@@ -1704,7 +1704,7 @@ func TestStore_NoViableHead_Liveness(t *testing.T) {
|
||||
postState, err = service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
require.NoError(t, service.postBlockProcess(ctx, wsb, root, postState, true))
|
||||
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, true}))
|
||||
// Check that the head is still INVALID and the node is still optimistic
|
||||
require.Equal(t, invalidHeadRoot, service.cfg.ForkChoiceStore.CachedHeadRoot())
|
||||
optimistic, err = service.IsOptimistic(ctx)
|
||||
@@ -1727,7 +1727,7 @@ func TestStore_NoViableHead_Liveness(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
err = service.postBlockProcess(ctx, wsb, root, postState, true)
|
||||
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, true})
|
||||
require.NoError(t, err)
|
||||
st, err = service.cfg.StateGen.StateByRoot(ctx, root)
|
||||
require.NoError(t, err)
|
||||
@@ -1753,7 +1753,7 @@ func TestStore_NoViableHead_Liveness(t *testing.T) {
|
||||
postState, err = service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
err = service.postBlockProcess(ctx, wsb, root, postState, true)
|
||||
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, true})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, root, service.cfg.ForkChoiceStore.CachedHeadRoot())
|
||||
sjc = service.CurrentJustifiedCheckpt()
|
||||
@@ -1809,7 +1809,7 @@ func TestNoViableHead_Reboot(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
require.NoError(t, service.postBlockProcess(ctx, wsb, root, postState, false))
|
||||
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}))
|
||||
}
|
||||
|
||||
for i := 6; i < 12; i++ {
|
||||
@@ -1827,7 +1827,7 @@ func TestNoViableHead_Reboot(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
err = service.postBlockProcess(ctx, wsb, root, postState, false)
|
||||
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -1846,7 +1846,7 @@ func TestNoViableHead_Reboot(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, lastValidRoot, wsb, postState))
|
||||
err = service.postBlockProcess(ctx, wsb, lastValidRoot, postState, false)
|
||||
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, lastValidRoot, [32]byte{}, postState, false})
|
||||
require.NoError(t, err)
|
||||
// save the post state and the payload Hash of this block since it will
|
||||
// be the LVH
|
||||
@@ -1875,7 +1875,7 @@ func TestNoViableHead_Reboot(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
require.NoError(t, service.postBlockProcess(ctx, wsb, root, postState, false))
|
||||
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}))
|
||||
require.NoError(t, service.updateJustificationOnBlock(ctx, preState, postState, currStoreJustifiedEpoch))
|
||||
_, err = service.updateFinalizationOnBlock(ctx, preState, postState, currStoreFinalizedEpoch)
|
||||
require.NoError(t, err)
|
||||
@@ -1990,7 +1990,7 @@ func TestOnBlock_HandleBlockAttestations(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
|
||||
require.NoError(t, service.postBlockProcess(ctx, wsb, root, postState, false))
|
||||
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}))
|
||||
|
||||
st, err = service.HeadState(ctx)
|
||||
require.NoError(t, err)
|
||||
@@ -2045,7 +2045,7 @@ func TestFillMissingBlockPayloadId_PrepareAllPayloads(t *testing.T) {
|
||||
// Helper function to simulate the block being on time or delayed for proposer
|
||||
// boost. It alters the genesisTime tracked by the store.
|
||||
func driftGenesisTime(s *Service, slot, delay int64) {
|
||||
offset := slot*int64(params.BeaconConfig().SecondsPerSlot) - delay
|
||||
offset := slot*int64(params.BeaconConfig().SecondsPerSlot) + delay
|
||||
s.SetGenesisTime(time.Unix(time.Now().Unix()-offset, 0))
|
||||
}
|
||||
|
||||
@@ -2197,6 +2197,35 @@ func TestMissingIndices(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getFCUArgs(t *testing.T) {
|
||||
s, tr := minimalTestService(t)
|
||||
ctx := tr.ctx
|
||||
st, keys := util.DeterministicGenesisState(t, 64)
|
||||
b, err := util.GenerateFullBlock(st, keys, util.DefaultBlockGenConfig(), 1)
|
||||
require.NoError(t, err)
|
||||
wsb, err := consensusblocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg := &postBlockProcessConfig{
|
||||
ctx: ctx,
|
||||
signed: wsb,
|
||||
blockRoot: [32]byte{'a'},
|
||||
postState: st,
|
||||
isValidPayload: true,
|
||||
}
|
||||
// error branch
|
||||
fcuArgs := &fcuConfig{}
|
||||
err = s.getFCUArgs(cfg, fcuArgs)
|
||||
require.ErrorContains(t, "block does not exist", err)
|
||||
|
||||
// canonical branch
|
||||
cfg.headRoot = cfg.blockRoot
|
||||
fcuArgs = &fcuConfig{}
|
||||
err = s.getFCUArgs(cfg, fcuArgs)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, cfg.blockRoot, fcuArgs.headRoot)
|
||||
}
|
||||
|
||||
func fakeCommitments(n int) [][]byte {
|
||||
f := make([][]byte, n)
|
||||
for i := range f {
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
@@ -122,35 +121,41 @@ func (s *Service) UpdateHead(ctx context.Context, proposingSlot primitives.Slot)
|
||||
defer s.cfg.ForkChoiceStore.Unlock()
|
||||
// This function is only called at 10 seconds or 0 seconds into the slot
|
||||
disparity := params.BeaconConfig().MaximumGossipClockDisparityDuration()
|
||||
if !features.Get().DisableReorgLateBlocks {
|
||||
disparity += reorgLateBlockCountAttestations
|
||||
}
|
||||
disparity += reorgLateBlockCountAttestations
|
||||
|
||||
s.processAttestations(ctx, disparity)
|
||||
|
||||
processAttsElapsedTime.Observe(float64(time.Since(start).Milliseconds()))
|
||||
|
||||
start = time.Now()
|
||||
// return early if we haven't changed head
|
||||
newHeadRoot, err := s.cfg.ForkChoiceStore.Head(ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not compute head from new attestations")
|
||||
// Fallback to our current head root in the event of a failure.
|
||||
s.headLock.RLock()
|
||||
newHeadRoot = s.headRoot()
|
||||
s.headLock.RUnlock()
|
||||
return
|
||||
}
|
||||
if !s.isNewHead(newHeadRoot) {
|
||||
return
|
||||
}
|
||||
log.WithField("newHeadRoot", fmt.Sprintf("%#x", newHeadRoot)).Debug("Head changed due to attestations")
|
||||
headState, headBlock, err := s.getStateAndBlock(ctx, newHeadRoot)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not get head block")
|
||||
return
|
||||
}
|
||||
newAttHeadElapsedTime.Observe(float64(time.Since(start).Milliseconds()))
|
||||
|
||||
changed, err := s.forkchoiceUpdateWithExecution(s.ctx, newHeadRoot, proposingSlot)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not update forkchoice")
|
||||
fcuArgs := &fcuConfig{
|
||||
headState: headState,
|
||||
headRoot: newHeadRoot,
|
||||
headBlock: headBlock,
|
||||
proposingSlot: proposingSlot,
|
||||
}
|
||||
if changed {
|
||||
s.headLock.RLock()
|
||||
log.WithFields(logrus.Fields{
|
||||
"oldHeadRoot": fmt.Sprintf("%#x", s.headRoot()),
|
||||
"newHeadRoot": fmt.Sprintf("%#x", newHeadRoot),
|
||||
}).Debug("Head changed due to attestations")
|
||||
s.headLock.RUnlock()
|
||||
fcuArgs.attributes = s.getPayloadAttribute(ctx, headState, proposingSlot, newHeadRoot[:])
|
||||
if fcuArgs.attributes != nil && s.shouldOverrideFCU(newHeadRoot, proposingSlot) {
|
||||
return
|
||||
}
|
||||
if err := s.forkchoiceUpdateWithExecution(s.ctx, fcuArgs); err != nil {
|
||||
log.WithError(err).Error("could not update forkchoice")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ func TestService_ProcessAttestationsAndUpdateHead(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, tRoot, wsb, postState))
|
||||
require.NoError(t, service.postBlockProcess(ctx, wsb, tRoot, postState, false))
|
||||
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, tRoot, [32]byte{}, postState, false}))
|
||||
copied, err = service.cfg.StateGen.StateByRoot(ctx, tRoot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, fcs.NodeCount())
|
||||
@@ -168,7 +168,7 @@ func TestService_UpdateHead_NoAtts(t *testing.T) {
|
||||
postState, err := service.validateStateTransition(ctx, preState, wsb)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.savePostStateInfo(ctx, tRoot, wsb, postState))
|
||||
require.NoError(t, service.postBlockProcess(ctx, wsb, tRoot, postState, false))
|
||||
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, tRoot, [32]byte{}, postState, false}))
|
||||
require.Equal(t, 2, fcs.NodeCount())
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wsb))
|
||||
require.Equal(t, tRoot, service.head.root)
|
||||
|
||||
@@ -119,7 +119,14 @@ func (s *Service) ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySig
|
||||
if err := s.savePostStateInfo(ctx, blockRoot, blockCopy, postState); err != nil {
|
||||
return errors.Wrap(err, "could not save post state info")
|
||||
}
|
||||
if err := s.postBlockProcess(ctx, blockCopy, blockRoot, postState, isValidPayload); err != nil {
|
||||
args := &postBlockProcessConfig{
|
||||
ctx: ctx,
|
||||
signed: blockCopy,
|
||||
blockRoot: blockRoot,
|
||||
postState: postState,
|
||||
isValidPayload: isValidPayload,
|
||||
}
|
||||
if err := s.postBlockProcess(args); err != nil {
|
||||
err := errors.Wrap(err, "could not process block")
|
||||
tracing.AnnotateError(span, err)
|
||||
return err
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
blockchainTesting "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/voluntaryexits"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
|
||||
@@ -130,7 +131,9 @@ func TestService_ReceiveBlock(t *testing.T) {
|
||||
s, tr := minimalTestService(t,
|
||||
WithFinalizedStateAtStartUp(genesis),
|
||||
WithExitPool(voluntaryexits.NewPool()),
|
||||
WithStateNotifier(&blockchainTesting.MockStateNotifier{RecordEvents: true}))
|
||||
WithStateNotifier(&blockchainTesting.MockStateNotifier{RecordEvents: true}),
|
||||
WithTrackedValidatorsCache(cache.NewTrackedValidatorsCache()),
|
||||
)
|
||||
|
||||
beaconDB := tr.db
|
||||
genesisBlockRoot := bytesutil.ToBytes32(nil)
|
||||
|
||||
@@ -73,7 +73,8 @@ type config struct {
|
||||
ChainStartFetcher execution.ChainStartFetcher
|
||||
BeaconDB db.HeadAccessDatabase
|
||||
DepositCache cache.DepositCache
|
||||
ProposerSlotIndexCache *cache.ProposerPayloadIDsCache
|
||||
PayloadIDCache *cache.PayloadIDCache
|
||||
TrackedValidatorsCache *cache.TrackedValidatorsCache
|
||||
AttPool attestations.Pool
|
||||
ExitPool voluntaryexits.PoolManager
|
||||
SlashingPool slashings.PoolManager
|
||||
@@ -167,7 +168,7 @@ func NewService(ctx context.Context, opts ...Option) (*Service, error) {
|
||||
checkpointStateCache: cache.NewCheckpointStateCache(),
|
||||
initSyncBlocks: make(map[[32]byte]interfaces.ReadOnlySignedBeaconBlock),
|
||||
blobNotifiers: bn,
|
||||
cfg: &config{ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache()},
|
||||
cfg: &config{},
|
||||
blockBeingSynced: ¤tlySyncingBlock{roots: make(map[[32]byte]struct{})},
|
||||
}
|
||||
for _, opt := range opts {
|
||||
|
||||
@@ -100,7 +100,7 @@ func setupBeaconChain(t *testing.T, beaconDB db.Database) *Service {
|
||||
WithForkChoiceStore(fc),
|
||||
WithAttestationService(attService),
|
||||
WithStateGen(stateGen),
|
||||
WithProposerIdsCache(cache.NewProposerPayloadIDsCache()),
|
||||
WithPayloadIDCache(cache.NewPayloadIDCache()),
|
||||
WithClockSynchronizer(startup.NewClockSynchronizer()),
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v4/async/event"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache/depositcache"
|
||||
statefeed "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/state"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
|
||||
@@ -114,6 +115,7 @@ func minimalTestService(t *testing.T, opts ...Option) (*Service, *testServiceReq
|
||||
WithAttestationService(req.attSrv),
|
||||
WithBLSToExecPool(req.blsPool),
|
||||
WithDepositCache(dc),
|
||||
WithTrackedValidatorsCache(cache.NewTrackedValidatorsCache()),
|
||||
}
|
||||
// append the variadic opts so they override the defaults by being processed afterwards
|
||||
opts = append(defOpts, opts...)
|
||||
|
||||
27
beacon-chain/blockchain/tracked_proposer.go
Normal file
27
beacon-chain/blockchain/tracked_proposer.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
)
|
||||
|
||||
// trackedProposer returns whether the beacon node was informed, via the
|
||||
// validators/prepare_proposer endpoint, of the proposer at the given slot.
|
||||
// It only returns true if the tracked proposer is present and active.
|
||||
func (s *Service) trackedProposer(st state.ReadOnlyBeaconState, slot primitives.Slot) (cache.TrackedValidator, bool) {
|
||||
if features.Get().PrepareAllPayloads {
|
||||
return cache.TrackedValidator{Active: true}, true
|
||||
}
|
||||
id, err := helpers.BeaconProposerIndexAtSlot(s.ctx, st, slot)
|
||||
if err != nil {
|
||||
return cache.TrackedValidator{}, false
|
||||
}
|
||||
val, ok := s.cfg.TrackedValidatorsCache.Validator(id)
|
||||
if !ok {
|
||||
return cache.TrackedValidator{}, false
|
||||
}
|
||||
return val, val.Active
|
||||
}
|
||||
1
beacon-chain/cache/BUILD.bazel
vendored
1
beacon-chain/cache/BUILD.bazel
vendored
@@ -25,6 +25,7 @@ go_library(
|
||||
"sync_committee_disabled.go", # keep
|
||||
"sync_committee_head_state.go",
|
||||
"sync_subnet_ids.go",
|
||||
"tracked_validators.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/cache",
|
||||
visibility = [
|
||||
|
||||
111
beacon-chain/cache/payload_id.go
vendored
111
beacon-chain/cache/payload_id.go
vendored
@@ -1,94 +1,63 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
)
|
||||
|
||||
const keyLength = 40
|
||||
const vIdLength = 8
|
||||
const pIdLength = 8
|
||||
const vpIdsLength = vIdLength + pIdLength
|
||||
// RootToPayloadIDMap is a map with keys the head root and values the
|
||||
// corresponding PayloadID
|
||||
type RootToPayloadIDMap map[[32]byte]primitives.PayloadID
|
||||
|
||||
// ProposerPayloadIDsCache is a cache of proposer payload IDs.
|
||||
// The key is the concatenation of the slot and the block root.
|
||||
// The value is the concatenation of the proposer and payload IDs, 8 bytes each.
|
||||
type ProposerPayloadIDsCache struct {
|
||||
slotToProposerAndPayloadIDs map[[keyLength]byte][vpIdsLength]byte
|
||||
sync.RWMutex
|
||||
// PayloadIDCache is a cache that keeps track of the prepared payload ID for the
|
||||
// given slot and with the given head root.
|
||||
type PayloadIDCache struct {
|
||||
slotToPayloadID map[primitives.Slot]RootToPayloadIDMap
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// NewProposerPayloadIDsCache creates a new proposer payload IDs cache.
|
||||
func NewProposerPayloadIDsCache() *ProposerPayloadIDsCache {
|
||||
return &ProposerPayloadIDsCache{
|
||||
slotToProposerAndPayloadIDs: make(map[[keyLength]byte][vpIdsLength]byte),
|
||||
}
|
||||
// NewPayloadIDCache returns a new payload ID cache
|
||||
func NewPayloadIDCache() *PayloadIDCache {
|
||||
return &PayloadIDCache{slotToPayloadID: make(map[primitives.Slot]RootToPayloadIDMap)}
|
||||
}
|
||||
|
||||
// GetProposerPayloadIDs returns the proposer and payload IDs for the given slot and head root to build the block.
|
||||
func (f *ProposerPayloadIDsCache) GetProposerPayloadIDs(
|
||||
slot primitives.Slot,
|
||||
r [fieldparams.RootLength]byte,
|
||||
) (primitives.ValidatorIndex, [pIdLength]byte, bool) {
|
||||
f.RLock()
|
||||
defer f.RUnlock()
|
||||
ids, ok := f.slotToProposerAndPayloadIDs[idKey(slot, r)]
|
||||
// PayloadID returns the payload ID for the given slot and parent block root
|
||||
func (p *PayloadIDCache) PayloadID(slot primitives.Slot, root [32]byte) (primitives.PayloadID, bool) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
inner, ok := p.slotToPayloadID[slot]
|
||||
if !ok {
|
||||
return 0, [pIdLength]byte{}, false
|
||||
return primitives.PayloadID{}, false
|
||||
}
|
||||
vId := ids[:vIdLength]
|
||||
|
||||
b := ids[vIdLength:]
|
||||
var pId [pIdLength]byte
|
||||
copy(pId[:], b)
|
||||
|
||||
return primitives.ValidatorIndex(bytesutil.BytesToUint64BigEndian(vId)), pId, true
|
||||
pid, ok := inner[root]
|
||||
if !ok {
|
||||
return primitives.PayloadID{}, false
|
||||
}
|
||||
return pid, true
|
||||
}
|
||||
|
||||
// SetProposerAndPayloadIDs sets the proposer and payload IDs for the given slot and head root to build block.
|
||||
func (f *ProposerPayloadIDsCache) SetProposerAndPayloadIDs(
|
||||
slot primitives.Slot,
|
||||
vId primitives.ValidatorIndex,
|
||||
pId [pIdLength]byte,
|
||||
r [fieldparams.RootLength]byte,
|
||||
) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
var vIdBytes [vIdLength]byte
|
||||
copy(vIdBytes[:], bytesutil.Uint64ToBytesBigEndian(uint64(vId)))
|
||||
|
||||
var bs [vpIdsLength]byte
|
||||
copy(bs[:], append(vIdBytes[:], pId[:]...))
|
||||
|
||||
k := idKey(slot, r)
|
||||
ids, ok := f.slotToProposerAndPayloadIDs[k]
|
||||
// Ok to overwrite if the slot is already set but the cached payload ID is not set.
|
||||
// This combats the re-org case where payload assignment could change at the start of the epoch.
|
||||
var byte8 [vIdLength]byte
|
||||
if !ok || (ok && bytes.Equal(ids[vIdLength:], byte8[:])) {
|
||||
f.slotToProposerAndPayloadIDs[k] = bs
|
||||
// SetPayloadID updates the payload ID for the given slot and head root
|
||||
// it also prunes older entries in the cache
|
||||
func (p *PayloadIDCache) Set(slot primitives.Slot, root [32]byte, pid primitives.PayloadID) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
if slot > 1 {
|
||||
p.prune(slot - 2)
|
||||
}
|
||||
inner, ok := p.slotToPayloadID[slot]
|
||||
if !ok {
|
||||
inner = make(RootToPayloadIDMap)
|
||||
p.slotToPayloadID[slot] = inner
|
||||
}
|
||||
inner[root] = pid
|
||||
}
|
||||
|
||||
// PrunePayloadIDs removes the payload ID entries older than input slot.
|
||||
func (f *ProposerPayloadIDsCache) PrunePayloadIDs(slot primitives.Slot) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
|
||||
for k := range f.slotToProposerAndPayloadIDs {
|
||||
s := primitives.Slot(bytesutil.BytesToUint64BigEndian(k[:8]))
|
||||
if slot > s {
|
||||
delete(f.slotToProposerAndPayloadIDs, k)
|
||||
// Prune prunes old payload IDs. Requires a Lock in the cache
|
||||
func (p *PayloadIDCache) prune(slot primitives.Slot) {
|
||||
for key := range p.slotToPayloadID {
|
||||
if key < slot {
|
||||
delete(p.slotToPayloadID, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func idKey(slot primitives.Slot, r [fieldparams.RootLength]byte) [keyLength]byte {
|
||||
var k [keyLength]byte
|
||||
copy(k[:], append(bytesutil.Uint64ToBytesBigEndian(uint64(slot)), r[:]...))
|
||||
return k
|
||||
}
|
||||
|
||||
53
beacon-chain/cache/payload_id_test.go
vendored
53
beacon-chain/cache/payload_id_test.go
vendored
@@ -8,65 +8,54 @@ import (
|
||||
)
|
||||
|
||||
func TestValidatorPayloadIDsCache_GetAndSaveValidatorPayloadIDs(t *testing.T) {
|
||||
cache := NewProposerPayloadIDsCache()
|
||||
cache := NewPayloadIDCache()
|
||||
var r [32]byte
|
||||
i, p, ok := cache.GetProposerPayloadIDs(0, r)
|
||||
p, ok := cache.PayloadID(0, r)
|
||||
require.Equal(t, false, ok)
|
||||
require.Equal(t, primitives.ValidatorIndex(0), i)
|
||||
require.Equal(t, [pIdLength]byte{}, p)
|
||||
require.Equal(t, primitives.PayloadID{}, p)
|
||||
|
||||
slot := primitives.Slot(1234)
|
||||
vid := primitives.ValidatorIndex(34234324)
|
||||
pid := [8]byte{1, 2, 3, 3, 7, 8, 7, 8}
|
||||
pid := primitives.PayloadID{1, 2, 3, 3, 7, 8, 7, 8}
|
||||
r = [32]byte{1, 2, 3}
|
||||
cache.SetProposerAndPayloadIDs(slot, vid, pid, r)
|
||||
i, p, ok = cache.GetProposerPayloadIDs(slot, r)
|
||||
cache.Set(slot, r, pid)
|
||||
p, ok = cache.PayloadID(slot, r)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, vid, i)
|
||||
require.Equal(t, pid, p)
|
||||
|
||||
slot = primitives.Slot(9456456)
|
||||
vid = primitives.ValidatorIndex(6786745)
|
||||
r = [32]byte{4, 5, 6}
|
||||
cache.SetProposerAndPayloadIDs(slot, vid, [pIdLength]byte{}, r)
|
||||
i, p, ok = cache.GetProposerPayloadIDs(slot, r)
|
||||
cache.Set(slot, r, primitives.PayloadID{})
|
||||
p, ok = cache.PayloadID(slot, r)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, vid, i)
|
||||
require.Equal(t, [pIdLength]byte{}, p)
|
||||
require.Equal(t, primitives.PayloadID{}, p)
|
||||
|
||||
// reset cache without pid
|
||||
slot = primitives.Slot(9456456)
|
||||
vid = primitives.ValidatorIndex(11111)
|
||||
r = [32]byte{7, 8, 9}
|
||||
pid = [8]byte{3, 2, 3, 33, 72, 8, 7, 8}
|
||||
cache.SetProposerAndPayloadIDs(slot, vid, pid, r)
|
||||
i, p, ok = cache.GetProposerPayloadIDs(slot, r)
|
||||
cache.Set(slot, r, pid)
|
||||
p, ok = cache.PayloadID(slot, r)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, vid, i)
|
||||
require.Equal(t, pid, p)
|
||||
|
||||
// Forked chain
|
||||
r = [32]byte{1, 2, 3}
|
||||
i, p, ok = cache.GetProposerPayloadIDs(slot, r)
|
||||
p, ok = cache.PayloadID(slot, r)
|
||||
require.Equal(t, false, ok)
|
||||
require.Equal(t, primitives.ValidatorIndex(0), i)
|
||||
require.Equal(t, [pIdLength]byte{}, p)
|
||||
require.Equal(t, primitives.PayloadID{}, p)
|
||||
|
||||
// existing pid - no change in cache
|
||||
// existing pid - change the cache
|
||||
slot = primitives.Slot(9456456)
|
||||
vid = primitives.ValidatorIndex(11111)
|
||||
r = [32]byte{7, 8, 9}
|
||||
newPid := [8]byte{1, 2, 3, 33, 72, 8, 7, 1}
|
||||
cache.SetProposerAndPayloadIDs(slot, vid, newPid, r)
|
||||
i, p, ok = cache.GetProposerPayloadIDs(slot, r)
|
||||
newPid := primitives.PayloadID{1, 2, 3, 33, 72, 8, 7, 1}
|
||||
cache.Set(slot, r, newPid)
|
||||
p, ok = cache.PayloadID(slot, r)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, vid, i)
|
||||
require.Equal(t, pid, p)
|
||||
require.Equal(t, newPid, p)
|
||||
|
||||
// remove cache entry
|
||||
cache.PrunePayloadIDs(slot + 1)
|
||||
i, p, ok = cache.GetProposerPayloadIDs(slot, r)
|
||||
cache.prune(slot + 1)
|
||||
p, ok = cache.PayloadID(slot, r)
|
||||
require.Equal(t, false, ok)
|
||||
require.Equal(t, primitives.ValidatorIndex(0), i)
|
||||
require.Equal(t, [pIdLength]byte{}, p)
|
||||
require.Equal(t, primitives.PayloadID{}, p)
|
||||
}
|
||||
|
||||
39
beacon-chain/cache/proposer_indices.go
vendored
39
beacon-chain/cache/proposer_indices.go
vendored
@@ -42,17 +42,15 @@ var (
|
||||
// root would be for slot 32 if present.
|
||||
type ProposerIndicesCache struct {
|
||||
sync.Mutex
|
||||
indices map[primitives.Epoch]map[[32]byte][fieldparams.SlotsPerEpoch]primitives.ValidatorIndex
|
||||
unsafeIndices map[primitives.Epoch]map[[32]byte][fieldparams.SlotsPerEpoch]primitives.ValidatorIndex
|
||||
rootMap map[forkchoicetypes.Checkpoint][32]byte // A map from checkpoint root to state root
|
||||
indices map[primitives.Epoch]map[[32]byte][fieldparams.SlotsPerEpoch]primitives.ValidatorIndex
|
||||
rootMap map[forkchoicetypes.Checkpoint][32]byte // A map from checkpoint root to state root
|
||||
}
|
||||
|
||||
// NewProposerIndicesCache returns a newly created cache
|
||||
func NewProposerIndicesCache() *ProposerIndicesCache {
|
||||
return &ProposerIndicesCache{
|
||||
indices: make(map[primitives.Epoch]map[[32]byte][fieldparams.SlotsPerEpoch]primitives.ValidatorIndex),
|
||||
unsafeIndices: make(map[primitives.Epoch]map[[32]byte][fieldparams.SlotsPerEpoch]primitives.ValidatorIndex),
|
||||
rootMap: make(map[forkchoicetypes.Checkpoint][32]byte),
|
||||
indices: make(map[primitives.Epoch]map[[32]byte][fieldparams.SlotsPerEpoch]primitives.ValidatorIndex),
|
||||
rootMap: make(map[forkchoicetypes.Checkpoint][32]byte),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,18 +72,6 @@ func (p *ProposerIndicesCache) ProposerIndices(epoch primitives.Epoch, root [32]
|
||||
return indices, exists
|
||||
}
|
||||
|
||||
// UnsafeProposerIndices returns the proposer indices (unsafe) for the given root
|
||||
func (p *ProposerIndicesCache) UnsafeProposerIndices(epoch primitives.Epoch, root [32]byte) ([fieldparams.SlotsPerEpoch]primitives.ValidatorIndex, bool) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
inner, ok := p.unsafeIndices[epoch]
|
||||
if !ok {
|
||||
return [fieldparams.SlotsPerEpoch]primitives.ValidatorIndex{}, false
|
||||
}
|
||||
indices, exists := inner[root]
|
||||
return indices, exists
|
||||
}
|
||||
|
||||
// Prune resets the ProposerIndicesCache to its initial state
|
||||
func (p *ProposerIndicesCache) Prune(epoch primitives.Epoch) {
|
||||
p.Lock()
|
||||
@@ -95,11 +81,6 @@ func (p *ProposerIndicesCache) Prune(epoch primitives.Epoch) {
|
||||
delete(p.indices, key)
|
||||
}
|
||||
}
|
||||
for key := range p.unsafeIndices {
|
||||
if key < epoch {
|
||||
delete(p.unsafeIndices, key)
|
||||
}
|
||||
}
|
||||
for key := range p.rootMap {
|
||||
if key.Epoch+1 < epoch {
|
||||
delete(p.rootMap, key)
|
||||
@@ -120,18 +101,6 @@ func (p *ProposerIndicesCache) Set(epoch primitives.Epoch, root [32]byte, indice
|
||||
inner[root] = indices
|
||||
}
|
||||
|
||||
// SetUnsafe sets the unsafe proposer indices for the given root as key
|
||||
func (p *ProposerIndicesCache) SetUnsafe(epoch primitives.Epoch, root [32]byte, indices [fieldparams.SlotsPerEpoch]primitives.ValidatorIndex) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
inner, ok := p.unsafeIndices[epoch]
|
||||
if !ok {
|
||||
inner = make(map[[32]byte][fieldparams.SlotsPerEpoch]primitives.ValidatorIndex)
|
||||
p.unsafeIndices[epoch] = inner
|
||||
}
|
||||
inner[root] = indices
|
||||
}
|
||||
|
||||
// SetCheckpoint updates the map from checkpoints to state roots
|
||||
func (p *ProposerIndicesCache) SetCheckpoint(c forkchoicetypes.Checkpoint, root [32]byte) {
|
||||
p.Lock()
|
||||
|
||||
15
beacon-chain/cache/proposer_indices_disabled.go
vendored
15
beacon-chain/cache/proposer_indices_disabled.go
vendored
@@ -4,11 +4,26 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
forkchoicetypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/forkchoice/types"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
)
|
||||
|
||||
var (
|
||||
// ProposerIndicesCacheMiss tracks the number of proposerIndices requests that aren't present in the cache.
|
||||
ProposerIndicesCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "proposer_indices_cache_miss",
|
||||
Help: "The number of proposer indices requests that aren't present in the cache.",
|
||||
})
|
||||
// ProposerIndicesCacheHit tracks the number of proposerIndices requests that are in the cache.
|
||||
ProposerIndicesCacheHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "proposer_indices_cache_hit",
|
||||
Help: "The number of proposer indices requests that are present in the cache.",
|
||||
})
|
||||
)
|
||||
|
||||
// FakeProposerIndicesCache is a struct with 1 queue for looking up proposer indices by root.
|
||||
type FakeProposerIndicesCache struct {
|
||||
}
|
||||
|
||||
48
beacon-chain/cache/proposer_indices_test.go
vendored
48
beacon-chain/cache/proposer_indices_test.go
vendored
@@ -34,29 +34,6 @@ func TestProposerCache_Set(t *testing.T) {
|
||||
require.Equal(t, emptyIndices, received)
|
||||
}
|
||||
|
||||
func TestProposerCache_SetUnsafe(t *testing.T) {
|
||||
cache := NewProposerIndicesCache()
|
||||
bRoot := [32]byte{'A'}
|
||||
indices, ok := cache.UnsafeProposerIndices(0, bRoot)
|
||||
require.Equal(t, false, ok)
|
||||
emptyIndices := [fieldparams.SlotsPerEpoch]primitives.ValidatorIndex{}
|
||||
require.Equal(t, indices, emptyIndices, "Expected committee count not to exist in empty cache")
|
||||
emptyIndices[0] = 1
|
||||
cache.SetUnsafe(0, bRoot, emptyIndices)
|
||||
|
||||
received, ok := cache.UnsafeProposerIndices(0, bRoot)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, received, emptyIndices)
|
||||
|
||||
newRoot := [32]byte{'B'}
|
||||
copy(emptyIndices[3:], []primitives.ValidatorIndex{1, 2, 3, 4, 5, 6})
|
||||
cache.SetUnsafe(0, newRoot, emptyIndices)
|
||||
|
||||
received, ok = cache.UnsafeProposerIndices(0, newRoot)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, emptyIndices, received)
|
||||
}
|
||||
|
||||
func TestProposerCache_CheckpointAndPrune(t *testing.T) {
|
||||
cache := NewProposerIndicesCache()
|
||||
indices := [fieldparams.SlotsPerEpoch]primitives.ValidatorIndex{}
|
||||
@@ -65,7 +42,6 @@ func TestProposerCache_CheckpointAndPrune(t *testing.T) {
|
||||
copy(indices[3:], []primitives.ValidatorIndex{1, 2, 3, 4, 5, 6})
|
||||
for i := 1; i < 10; i++ {
|
||||
cache.Set(primitives.Epoch(i), root, indices)
|
||||
cache.SetUnsafe(primitives.Epoch(i), root, indices)
|
||||
cache.SetCheckpoint(forkchoicetypes.Checkpoint{Epoch: primitives.Epoch(i - 1), Root: cpRoot}, root)
|
||||
}
|
||||
received, ok := cache.ProposerIndices(1, root)
|
||||
@@ -80,18 +56,6 @@ func TestProposerCache_CheckpointAndPrune(t *testing.T) {
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, indices, received)
|
||||
|
||||
received, ok = cache.UnsafeProposerIndices(1, root)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, indices, received)
|
||||
|
||||
received, ok = cache.UnsafeProposerIndices(4, root)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, indices, received)
|
||||
|
||||
received, ok = cache.UnsafeProposerIndices(9, root)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, indices, received)
|
||||
|
||||
received, ok = cache.IndicesFromCheckpoint(forkchoicetypes.Checkpoint{Epoch: 0, Root: cpRoot})
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, indices, received)
|
||||
@@ -123,18 +87,6 @@ func TestProposerCache_CheckpointAndPrune(t *testing.T) {
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, indices, received)
|
||||
|
||||
received, ok = cache.UnsafeProposerIndices(1, root)
|
||||
require.Equal(t, false, ok)
|
||||
require.Equal(t, emptyIndices, received)
|
||||
|
||||
received, ok = cache.UnsafeProposerIndices(4, root)
|
||||
require.Equal(t, false, ok)
|
||||
require.Equal(t, emptyIndices, received)
|
||||
|
||||
received, ok = cache.UnsafeProposerIndices(9, root)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, indices, received)
|
||||
|
||||
received, ok = cache.IndicesFromCheckpoint(forkchoicetypes.Checkpoint{Epoch: 0, Root: cpRoot})
|
||||
require.Equal(t, false, ok)
|
||||
require.Equal(t, emptyIndices, received)
|
||||
|
||||
43
beacon-chain/cache/tracked_validators.go
vendored
Normal file
43
beacon-chain/cache/tracked_validators.go
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
)
|
||||
|
||||
type TrackedValidator struct {
|
||||
Active bool
|
||||
FeeRecipient primitives.ExecutionAddress
|
||||
Index primitives.ValidatorIndex
|
||||
}
|
||||
|
||||
type TrackedValidatorsCache struct {
|
||||
sync.Mutex
|
||||
trackedValidators map[primitives.ValidatorIndex]TrackedValidator
|
||||
}
|
||||
|
||||
func NewTrackedValidatorsCache() *TrackedValidatorsCache {
|
||||
return &TrackedValidatorsCache{
|
||||
trackedValidators: make(map[primitives.ValidatorIndex]TrackedValidator),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TrackedValidatorsCache) Validator(index primitives.ValidatorIndex) (TrackedValidator, bool) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
val, ok := t.trackedValidators[index]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
func (t *TrackedValidatorsCache) Set(val TrackedValidator) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
t.trackedValidators[val.Index] = val
|
||||
}
|
||||
|
||||
func (t *TrackedValidatorsCache) Prune() {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
t.trackedValidators = make(map[primitives.ValidatorIndex]TrackedValidator)
|
||||
}
|
||||
@@ -26,6 +26,12 @@ const (
|
||||
|
||||
// BlobSidecarReceived is sent after a blob sidecar is received from gossip or rpc.
|
||||
BlobSidecarReceived = 6
|
||||
|
||||
// ProposerSlashingReceived is sent after a proposer slashing is received from gossip or rpc
|
||||
ProposerSlashingReceived = 7
|
||||
|
||||
// AttesterSlashingReceived is sent after an attester slashing is received from gossip or rpc
|
||||
AttesterSlashingReceived = 8
|
||||
)
|
||||
|
||||
// UnAggregatedAttReceivedData is the data sent with UnaggregatedAttReceived events.
|
||||
@@ -61,3 +67,13 @@ type BLSToExecutionChangeReceivedData struct {
|
||||
type BlobSidecarReceivedData struct {
|
||||
Blob *blocks.VerifiedROBlob
|
||||
}
|
||||
|
||||
// ProposerSlashingReceivedData is the data sent with ProposerSlashingReceived events.
|
||||
type ProposerSlashingReceivedData struct {
|
||||
ProposerSlashing *ethpb.ProposerSlashing
|
||||
}
|
||||
|
||||
// AttesterSlashingReceivedData is the data sent with AttesterSlashingReceived events.
|
||||
type AttesterSlashingReceivedData struct {
|
||||
AttesterSlashing *ethpb.AttesterSlashing
|
||||
}
|
||||
|
||||
@@ -346,7 +346,7 @@ func UpdateProposerIndicesInCache(ctx context.Context, state state.ReadOnlyBeaco
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
root, err := state.StateRootAtIndex(uint64(slot % params.BeaconConfig().SlotsPerHistoricalRoot))
|
||||
root, err := StateRootAtSlot(state, slot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -391,49 +391,6 @@ func UpdateCachedCheckpointToStateRoot(state state.ReadOnlyBeaconState, cp *fork
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateUnsafeProposerIndicesInCache updates proposer indices entry of the
|
||||
// cache one epoch in advance.
|
||||
// Input state is used to retrieve active validator indices.
|
||||
// Input root is to use as key in the cache.
|
||||
// Input epoch is the epoch to retrieve proposer indices for.
|
||||
func UpdateUnsafeProposerIndicesInCache(ctx context.Context, state state.ReadOnlyBeaconState, epoch primitives.Epoch) error {
|
||||
// The cache uses the state root at the end of (current epoch - 2) as key.
|
||||
// (e.g. for epoch 2, the key is root at slot 31)
|
||||
if epoch <= params.BeaconConfig().GenesisEpoch+2*params.BeaconConfig().MinSeedLookahead {
|
||||
return nil
|
||||
}
|
||||
slot, err := slots.EpochEnd(epoch - 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
root, err := state.StateRootAtIndex(uint64(slot % params.BeaconConfig().SlotsPerHistoricalRoot))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Skip cache update if the key already exists
|
||||
_, ok := proposerIndicesCache.UnsafeProposerIndices(epoch, [32]byte(root))
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
indices, err := ActiveValidatorIndices(ctx, state, epoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
proposerIndices, err := precomputeProposerIndices(state, indices, epoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(proposerIndices) != int(params.BeaconConfig().SlotsPerEpoch) {
|
||||
return errors.New("invalid proposer length returned from state")
|
||||
}
|
||||
// This is here to deal with tests only
|
||||
var indicesArray [fieldparams.SlotsPerEpoch]primitives.ValidatorIndex
|
||||
copy(indicesArray[:], proposerIndices)
|
||||
proposerIndicesCache.Prune(epoch - 2)
|
||||
proposerIndicesCache.SetUnsafe(epoch, [32]byte(root), indicesArray)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearCache clears the beacon committee cache and sync committee cache.
|
||||
func ClearCache() {
|
||||
committeeCache.Clear()
|
||||
|
||||
@@ -20,10 +20,14 @@ import (
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
var CommitteeCacheInProgressHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "committee_cache_in_progress_hit",
|
||||
Help: "The number of committee requests that are present in the cache.",
|
||||
})
|
||||
var (
|
||||
CommitteeCacheInProgressHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "committee_cache_in_progress_hit",
|
||||
Help: "The number of committee requests that are present in the cache.",
|
||||
})
|
||||
|
||||
errProposerIndexMiss = errors.New("propoposer index not found in cache")
|
||||
)
|
||||
|
||||
// IsActiveValidator returns the boolean value on whether the validator
|
||||
// is active or not.
|
||||
@@ -259,10 +263,32 @@ func ValidatorActivationChurnLimitDeneb(activeValidatorCount uint64) uint64 {
|
||||
// indices = get_active_validator_indices(state, epoch)
|
||||
// return compute_proposer_index(state, indices, seed)
|
||||
func BeaconProposerIndex(ctx context.Context, state state.ReadOnlyBeaconState) (primitives.ValidatorIndex, error) {
|
||||
e := time.CurrentEpoch(state)
|
||||
return BeaconProposerIndexAtSlot(ctx, state, state.Slot())
|
||||
}
|
||||
|
||||
// cachedProposerIndexAtSlot returns the proposer index at the given slot from
|
||||
// the cache at the given root key.
|
||||
func cachedProposerIndexAtSlot(slot primitives.Slot, root [32]byte) (primitives.ValidatorIndex, error) {
|
||||
proposerIndices, has := proposerIndicesCache.ProposerIndices(slots.ToEpoch(slot), root)
|
||||
if !has {
|
||||
cache.ProposerIndicesCacheMiss.Inc()
|
||||
return 0, errProposerIndexMiss
|
||||
}
|
||||
if len(proposerIndices) != int(params.BeaconConfig().SlotsPerEpoch) {
|
||||
cache.ProposerIndicesCacheMiss.Inc()
|
||||
return 0, errProposerIndexMiss
|
||||
}
|
||||
return proposerIndices[slot%params.BeaconConfig().SlotsPerEpoch], nil
|
||||
}
|
||||
|
||||
// BeaconProposerIndexAtSlot returns proposer index at the given slot from the
|
||||
// point of view of the given state as head state
|
||||
func BeaconProposerIndexAtSlot(ctx context.Context, state state.ReadOnlyBeaconState, slot primitives.Slot) (primitives.ValidatorIndex, error) {
|
||||
e := slots.ToEpoch(slot)
|
||||
// The cache uses the state root of the previous epoch - minimum_seed_lookahead last slot as key. (e.g. Starting epoch 1, slot 32, the key would be block root at slot 31)
|
||||
// For simplicity, the node will skip caching of genesis epoch.
|
||||
if e > params.BeaconConfig().GenesisEpoch+params.BeaconConfig().MinSeedLookahead {
|
||||
wantedEpoch := time.PrevEpoch(state)
|
||||
s, err := slots.EpochEnd(wantedEpoch)
|
||||
s, err := slots.EpochEnd(e - 1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -271,12 +297,16 @@ func BeaconProposerIndex(ctx context.Context, state state.ReadOnlyBeaconState) (
|
||||
return 0, err
|
||||
}
|
||||
if r != nil && !bytes.Equal(r, params.BeaconConfig().ZeroHash[:]) {
|
||||
proposerIndices, ok := proposerIndicesCache.ProposerIndices(wantedEpoch+1, bytesutil.ToBytes32(r))
|
||||
if ok {
|
||||
return proposerIndices[state.Slot()%params.BeaconConfig().SlotsPerEpoch], nil
|
||||
pid, err := cachedProposerIndexAtSlot(slot, [32]byte(r))
|
||||
if err == nil {
|
||||
return pid, nil
|
||||
}
|
||||
if err := UpdateProposerIndicesInCache(ctx, state, time.CurrentEpoch(state)); err != nil {
|
||||
return 0, errors.Wrap(err, "could not update committee cache")
|
||||
if err := UpdateProposerIndicesInCache(ctx, state, e); err != nil {
|
||||
return 0, errors.Wrap(err, "could not update proposer index cache")
|
||||
}
|
||||
pid, err = cachedProposerIndexAtSlot(slot, [32]byte(r))
|
||||
if err == nil {
|
||||
return pid, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -286,7 +316,7 @@ func BeaconProposerIndex(ctx context.Context, state state.ReadOnlyBeaconState) (
|
||||
return 0, errors.Wrap(err, "could not generate seed")
|
||||
}
|
||||
|
||||
seedWithSlot := append(seed[:], bytesutil.Bytes8(uint64(state.Slot()))...)
|
||||
seedWithSlot := append(seed[:], bytesutil.Bytes8(uint64(slot))...)
|
||||
seedWithSlotHash := hash.Hash(seedWithSlot)
|
||||
|
||||
indices, err := ActiveValidatorIndices(ctx, state, e)
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
@@ -34,18 +35,22 @@ const (
|
||||
sszExt = "ssz"
|
||||
partExt = "part"
|
||||
|
||||
firstPruneEpoch = 0
|
||||
bufferEpochs = 2
|
||||
directoryPermissions = 0700
|
||||
)
|
||||
|
||||
// BlobStorageOption is a functional option for configuring a BlobStorage.
|
||||
type BlobStorageOption func(*BlobStorage)
|
||||
type BlobStorageOption func(*BlobStorage) error
|
||||
|
||||
// WithBlobRetentionEpochs is an option that changes the number of epochs blobs will be persisted.
|
||||
func WithBlobRetentionEpochs(e primitives.Epoch) BlobStorageOption {
|
||||
return func(b *BlobStorage) {
|
||||
b.retentionEpochs = e
|
||||
return func(b *BlobStorage) error {
|
||||
s, err := slots.EpochStart(e + bufferEpochs)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not set retentionSlots")
|
||||
}
|
||||
b.retentionSlots = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,21 +64,21 @@ func NewBlobStorage(base string, opts ...BlobStorageOption) (*BlobStorage, error
|
||||
}
|
||||
fs := afero.NewBasePathFs(afero.NewOsFs(), base)
|
||||
b := &BlobStorage{
|
||||
fs: fs,
|
||||
retentionEpochs: params.BeaconConfig().MinEpochsForBlobsSidecarsRequest,
|
||||
lastPrunedEpoch: firstPruneEpoch,
|
||||
fs: fs,
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(b)
|
||||
if err := o(b); err != nil {
|
||||
return nil, fmt.Errorf("failed to create blob storage at %s: %w", base, err)
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// BlobStorage is the concrete implementation of the filesystem backend for saving and retrieving BlobSidecars.
|
||||
type BlobStorage struct {
|
||||
fs afero.Fs
|
||||
retentionEpochs primitives.Epoch
|
||||
lastPrunedEpoch primitives.Epoch
|
||||
fs afero.Fs
|
||||
retentionSlots primitives.Slot
|
||||
prunedBefore atomic.Uint64
|
||||
}
|
||||
|
||||
// Save saves blobs given a list of sidecars.
|
||||
@@ -89,14 +94,7 @@ func (bs *BlobStorage) Save(sidecar blocks.VerifiedROBlob) error {
|
||||
log.WithFields(logging.BlobFields(sidecar.ROBlob)).Debug("ignoring a duplicate blob sidecar Save attempt")
|
||||
return nil
|
||||
}
|
||||
if bs.shouldPrune(sidecar.Slot()) {
|
||||
go func() {
|
||||
err := bs.pruneOlderThan(sidecar.Slot())
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("failed to prune blobs from slot %d", sidecar.Slot())
|
||||
}
|
||||
}()
|
||||
}
|
||||
bs.tryPrune(sidecar.Slot())
|
||||
|
||||
// Serialize the ethpb.BlobSidecar to binary data using SSZ.
|
||||
sidecarData, err := sidecar.MarshalSSZ()
|
||||
@@ -238,49 +236,50 @@ func (p blobNamer) path() string {
|
||||
// Prune prunes blobs in the base directory based on the retention epoch.
|
||||
// It deletes blobs older than currentEpoch - (retentionEpochs+bufferEpochs).
|
||||
// This is so that we keep a slight buffer and blobs are deleted after n+2 epochs.
|
||||
func (bs *BlobStorage) Prune(currentSlot primitives.Slot) error {
|
||||
func (bs *BlobStorage) Prune(pruneBefore primitives.Slot) error {
|
||||
t := time.Now()
|
||||
retentionSlots, err := slots.EpochStart(bs.retentionEpochs + bufferEpochs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if currentSlot < retentionSlots {
|
||||
return nil // Overflow would occur
|
||||
}
|
||||
|
||||
log.Debug("Pruning old blobs")
|
||||
|
||||
folders, err := afero.ReadDir(bs.fs, ".")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var totalPruned int
|
||||
for _, folder := range folders {
|
||||
if folder.IsDir() {
|
||||
num, err := bs.processFolder(folder, currentSlot, retentionSlots)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
blobsPrunedCounter.Add(float64(num))
|
||||
blobsTotalGauge.Add(-float64(num))
|
||||
totalPruned += num
|
||||
var dirs []string
|
||||
err := afero.Walk(bs.fs, ".", func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to walk blob storage directory")
|
||||
}
|
||||
if info.IsDir() && path != "." {
|
||||
dirs = append(dirs, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to build directories list")
|
||||
}
|
||||
pruneTime := time.Since(t)
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"lastPrunedEpoch": slots.ToEpoch(currentSlot - retentionSlots),
|
||||
"pruneTime": pruneTime,
|
||||
"numberBlobsPruned": totalPruned,
|
||||
}).Debug("Pruned old blobs")
|
||||
var totalPruned int
|
||||
for _, dir := range dirs {
|
||||
num, err := bs.processFolder(dir, pruneBefore)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to process folder %s", dir)
|
||||
}
|
||||
blobsPrunedCounter.Add(float64(num))
|
||||
blobsTotalGauge.Add(-float64(num))
|
||||
totalPruned += num
|
||||
}
|
||||
|
||||
if totalPruned > 0 {
|
||||
pruneTime := time.Since(t)
|
||||
log.WithFields(log.Fields{
|
||||
"lastPrunedEpoch": slots.ToEpoch(pruneBefore),
|
||||
"pruneTime": pruneTime,
|
||||
"numberBlobsPruned": totalPruned,
|
||||
}).Debug("Pruned old blobs")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processFolder will delete the folder of blobs if the blob slot is outside the
|
||||
// retention period. We determine the slot by looking at the first blob in the folder.
|
||||
func (bs *BlobStorage) processFolder(folder os.FileInfo, currentSlot, retentionSlots primitives.Slot) (int, error) {
|
||||
f, err := bs.fs.Open(filepath.Join(folder.Name(), "0."+sszExt))
|
||||
func (bs *BlobStorage) processFolder(folder string, pruneBefore primitives.Slot) (int, error) {
|
||||
f, err := bs.fs.Open(filepath.Join(folder, "0."+sszExt))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -295,12 +294,12 @@ func (bs *BlobStorage) processFolder(folder os.FileInfo, currentSlot, retentionS
|
||||
return 0, err
|
||||
}
|
||||
var num int
|
||||
if slot < (currentSlot - retentionSlots) {
|
||||
num, err = bs.countFiles(folder.Name())
|
||||
if slot < pruneBefore {
|
||||
num, err = bs.countFiles(folder)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err = bs.fs.RemoveAll(folder.Name()); err != nil {
|
||||
if err = bs.fs.RemoveAll(folder); err != nil {
|
||||
return 0, errors.Wrapf(err, "failed to delete blob %s", f.Name())
|
||||
}
|
||||
}
|
||||
@@ -328,24 +327,24 @@ func (bs *BlobStorage) Delete(root [32]byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// shouldPrune checks whether pruning should be triggered based on the given slot.
|
||||
func (bs *BlobStorage) shouldPrune(slot primitives.Slot) bool {
|
||||
if slots.SinceEpochStarts(slot) < params.BeaconConfig().SlotsPerEpoch/2 {
|
||||
return false
|
||||
// tryPrune checks whether we should prune and then calls prune
|
||||
func (bs *BlobStorage) tryPrune(latest primitives.Slot) {
|
||||
pruned := uint64(pruneBefore(latest, bs.retentionSlots))
|
||||
if bs.prunedBefore.Swap(pruned) == pruned {
|
||||
return
|
||||
}
|
||||
if slots.ToEpoch(slot) == bs.lastPrunedEpoch {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
go func() {
|
||||
if err := bs.Prune(primitives.Slot(pruned)); err != nil {
|
||||
log.WithError(err).Errorf("failed to prune blobs from slot %d", latest)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// pruneOlderThan prunes blobs in the base directory based on the retention epoch and current slot.
|
||||
func (bs *BlobStorage) pruneOlderThan(slot primitives.Slot) error {
|
||||
err := bs.Prune(slot)
|
||||
if err != nil {
|
||||
return err
|
||||
func pruneBefore(latest primitives.Slot, offset primitives.Slot) primitives.Slot {
|
||||
// Safely compute the first slot in the epoch for the latest slot
|
||||
latest = latest - latest%params.BeaconConfig().SlotsPerEpoch
|
||||
if latest < offset {
|
||||
return 0
|
||||
}
|
||||
// Update lastPrunedEpoch to the current epoch.
|
||||
bs.lastPrunedEpoch = slots.ToEpoch(slot)
|
||||
return nil
|
||||
return latest - offset
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ func TestBlobStorage_SaveBlobData(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Slot in first half of epoch therefore should not prune
|
||||
require.Equal(t, false, bs.shouldPrune(testSidecars[0].Slot()))
|
||||
bs.tryPrune(testSidecars[0].Slot())
|
||||
err = bs.Save(testSidecars[0])
|
||||
require.NoError(t, err)
|
||||
actual, err := bs.Get(testSidecars[0].BlockRoot(), testSidecars[0].Index)
|
||||
@@ -92,7 +92,7 @@ func TestBlobStorage_SaveBlobData(t *testing.T) {
|
||||
testSidecars1, err := verification.BlobSidecarSliceNoop(sidecars)
|
||||
require.NoError(t, err)
|
||||
// Slot in first half of epoch therefore should not prune
|
||||
require.Equal(t, false, bs.shouldPrune(testSidecars1[0].Slot()))
|
||||
bs.tryPrune(testSidecars1[0].Slot())
|
||||
err = bs.Save(testSidecars1[0])
|
||||
require.NoError(t, err)
|
||||
// Check previous saved sidecar was not pruned
|
||||
@@ -107,13 +107,13 @@ func TestBlobStorage_SaveBlobData(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
_, sidecars = util.GenerateTestDenebBlockWithSidecar(t, [32]byte{}, 131187, fieldparams.MaxBlobsPerBlock)
|
||||
testSidecars, err = verification.BlobSidecarSliceNoop(sidecars)
|
||||
testSidecars2, err := verification.BlobSidecarSliceNoop(sidecars)
|
||||
// Slot in second half of epoch therefore should prune
|
||||
require.Equal(t, true, bs.shouldPrune(testSidecars[0].Slot()))
|
||||
bs.tryPrune(testSidecars2[0].Slot())
|
||||
require.NoError(t, err)
|
||||
err = bs.Save(testSidecars[0])
|
||||
err = bs.Save(testSidecars2[0])
|
||||
require.NoError(t, err)
|
||||
err = pollUntil(t, fs, 1)
|
||||
err = pollUntil(t, fs, 3)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
@@ -143,26 +143,6 @@ func pollUntil(t *testing.T, fs afero.Fs, expected int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestShouldPrune(t *testing.T) {
|
||||
bs := NewEphemeralBlobStorage(t)
|
||||
bs.lastPrunedEpoch = 5
|
||||
|
||||
// Slot is before the midpoint of the epoch
|
||||
slot1 := primitives.Slot(100)
|
||||
p1 := bs.shouldPrune(slot1)
|
||||
require.Equal(t, false, p1)
|
||||
|
||||
// Slot is after the midpoint of the epoch, but same epoch as last pruning
|
||||
slot2 := primitives.Slot(178)
|
||||
p2 := bs.shouldPrune(slot2)
|
||||
require.Equal(t, false, p2)
|
||||
|
||||
// Slot is after the midpoint of the epoch
|
||||
slot3 := primitives.Slot(8018)
|
||||
p3 := bs.shouldPrune(slot3)
|
||||
require.Equal(t, true, p3)
|
||||
}
|
||||
|
||||
func TestBlobIndicesBounds(t *testing.T) {
|
||||
fs, bs, err := NewEphemeralBlobStorageWithFs(t)
|
||||
require.NoError(t, err)
|
||||
@@ -208,7 +188,7 @@ func TestBlobStoragePrune(t *testing.T) {
|
||||
require.NoError(t, bs.Save(sidecar))
|
||||
}
|
||||
|
||||
require.NoError(t, bs.Prune(currentSlot))
|
||||
require.NoError(t, bs.Prune(currentSlot-bs.retentionSlots))
|
||||
|
||||
remainingFolders, err := afero.ReadDir(fs, ".")
|
||||
require.NoError(t, err)
|
||||
@@ -228,7 +208,7 @@ func TestBlobStoragePrune(t *testing.T) {
|
||||
slot += 10000
|
||||
}
|
||||
|
||||
require.NoError(t, bs.Prune(currentSlot))
|
||||
require.NoError(t, bs.Prune(currentSlot-bs.retentionSlots))
|
||||
|
||||
remainingFolders, err := afero.ReadDir(fs, ".")
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
@@ -18,8 +19,11 @@ func NewEphemeralBlobStorage(_ testing.TB) *BlobStorage {
|
||||
// in order to interact with it outside the parameters of the BlobStorage api.
|
||||
func NewEphemeralBlobStorageWithFs(_ testing.TB) (afero.Fs, *BlobStorage, error) {
|
||||
fs := afero.NewMemMapFs()
|
||||
retentionEpoch := params.BeaconConfig().MinEpochsForBlobsSidecarsRequest
|
||||
return fs, &BlobStorage{fs: fs, retentionEpochs: retentionEpoch}, nil
|
||||
s, err := slots.EpochStart(params.BeaconConfig().MinEpochsForBlobsSidecarsRequest)
|
||||
if err != nil {
|
||||
return fs, &BlobStorage{}, err
|
||||
}
|
||||
return fs, &BlobStorage{fs: fs, retentionSlots: s}, nil
|
||||
}
|
||||
|
||||
type BlobMocker struct {
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
@@ -31,10 +30,7 @@ var (
|
||||
})
|
||||
)
|
||||
|
||||
func (bs *BlobStorage) Initialize(lastFinalizedSlot primitives.Slot) error {
|
||||
if err := bs.Prune(lastFinalizedSlot); err != nil {
|
||||
return fmt.Errorf("failed to prune from finalized slot %d: %w", lastFinalizedSlot, err)
|
||||
}
|
||||
func (bs *BlobStorage) Initialize() error {
|
||||
if err := bs.collectTotalBlobMetric(); err != nil {
|
||||
return fmt.Errorf("failed to initialize blob metrics: %w", err)
|
||||
}
|
||||
|
||||
@@ -105,7 +105,6 @@ go_test(
|
||||
"//beacon-chain/execution/types:go_default_library",
|
||||
"//beacon-chain/forkchoice/doubly-linked-tree:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/execution/types"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/features"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
|
||||
@@ -489,6 +488,10 @@ func (s *Service) GetPayloadBodiesByHash(ctx context.Context, executionBlockHash
|
||||
defer span.End()
|
||||
|
||||
result := make([]*pb.ExecutionPayloadBodyV1, 0)
|
||||
// Exit early if there are no execution hashes.
|
||||
if len(executionBlockHashes) == 0 {
|
||||
return result, nil
|
||||
}
|
||||
err := s.rpcClient.CallContext(ctx, &result, GetPayloadBodiesByHashV1, executionBlockHashes)
|
||||
|
||||
for i, item := range result {
|
||||
@@ -621,31 +624,15 @@ func (s *Service) ReconstructFullBellatrixBlockBatch(
|
||||
}
|
||||
|
||||
func (s *Service) retrievePayloadFromExecutionHash(ctx context.Context, executionBlockHash common.Hash, header interfaces.ExecutionData, version int) (interfaces.ExecutionData, error) {
|
||||
if features.Get().EnableOptionalEngineMethods {
|
||||
pBodies, err := s.GetPayloadBodiesByHash(ctx, []common.Hash{executionBlockHash})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get payload body by hash %#x: %v", executionBlockHash, err)
|
||||
}
|
||||
if len(pBodies) != 1 {
|
||||
return nil, errors.Errorf("could not retrieve the correct number of payload bodies: wanted 1 but got %d", len(pBodies))
|
||||
}
|
||||
bdy := pBodies[0]
|
||||
return fullPayloadFromPayloadBody(header, bdy, version)
|
||||
}
|
||||
|
||||
executionBlock, err := s.ExecutionBlockByHash(ctx, executionBlockHash, true /* with txs */)
|
||||
pBodies, err := s.GetPayloadBodiesByHash(ctx, []common.Hash{executionBlockHash})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not fetch execution block with txs by hash %#x: %v", executionBlockHash, err)
|
||||
return nil, fmt.Errorf("could not get payload body by hash %#x: %v", executionBlockHash, err)
|
||||
}
|
||||
if executionBlock == nil {
|
||||
return nil, fmt.Errorf("received nil execution block for request by hash %#x", executionBlockHash)
|
||||
if len(pBodies) != 1 {
|
||||
return nil, errors.Errorf("could not retrieve the correct number of payload bodies: wanted 1 but got %d", len(pBodies))
|
||||
}
|
||||
if bytes.Equal(executionBlock.Hash.Bytes(), []byte{}) {
|
||||
return nil, ErrEmptyBlockHash
|
||||
}
|
||||
|
||||
executionBlock.Version = version
|
||||
return fullPayloadFromExecutionBlock(version, header, executionBlock)
|
||||
bdy := pBodies[0]
|
||||
return fullPayloadFromPayloadBody(header, bdy, version)
|
||||
}
|
||||
|
||||
func (s *Service) retrievePayloadsFromExecutionHashes(
|
||||
@@ -654,19 +641,12 @@ func (s *Service) retrievePayloadsFromExecutionHashes(
|
||||
validExecPayloads []int,
|
||||
blindedBlocks []interfaces.ReadOnlySignedBeaconBlock) ([]interfaces.SignedBeaconBlock, error) {
|
||||
fullBlocks := make([]interfaces.SignedBeaconBlock, len(blindedBlocks))
|
||||
var execBlocks []*pb.ExecutionBlock
|
||||
var payloadBodies []*pb.ExecutionPayloadBodyV1
|
||||
var err error
|
||||
if features.Get().EnableOptionalEngineMethods {
|
||||
payloadBodies, err = s.GetPayloadBodiesByHash(ctx, executionHashes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not fetch payload bodies by hash %#x: %v", executionHashes, err)
|
||||
}
|
||||
} else {
|
||||
execBlocks, err = s.ExecutionBlocksByHashes(ctx, executionHashes, true /* with txs*/)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not fetch execution blocks with txs by hash %#x: %v", executionHashes, err)
|
||||
}
|
||||
|
||||
payloadBodies, err = s.GetPayloadBodiesByHash(ctx, executionHashes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not fetch payload bodies by hash %#x: %v", executionHashes, err)
|
||||
}
|
||||
|
||||
// For each valid payload, we reconstruct the full block from it with the
|
||||
@@ -674,32 +654,17 @@ func (s *Service) retrievePayloadsFromExecutionHashes(
|
||||
for sliceIdx, realIdx := range validExecPayloads {
|
||||
var payload interfaces.ExecutionData
|
||||
bblock := blindedBlocks[realIdx]
|
||||
if features.Get().EnableOptionalEngineMethods {
|
||||
b := payloadBodies[sliceIdx]
|
||||
if b == nil {
|
||||
return nil, fmt.Errorf("received nil payload body for request by hash %#x", executionHashes[sliceIdx])
|
||||
}
|
||||
header, err := bblock.Block().Body().Execution()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payload, err = fullPayloadFromPayloadBody(header, b, bblock.Version())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
b := execBlocks[sliceIdx]
|
||||
if b == nil {
|
||||
return nil, fmt.Errorf("received nil execution block for request by hash %#x", executionHashes[sliceIdx])
|
||||
}
|
||||
header, err := bblock.Block().Body().Execution()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payload, err = fullPayloadFromExecutionBlock(bblock.Version(), header, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := payloadBodies[sliceIdx]
|
||||
if b == nil {
|
||||
return nil, fmt.Errorf("received nil payload body for request by hash %#x", executionHashes[sliceIdx])
|
||||
}
|
||||
header, err := bblock.Block().Body().Execution()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payload, err = fullPayloadFromPayloadBody(header, b, bblock.Version())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fullBlock, err := blocks.BuildSignedBeaconBlockFromExecutionPayload(bblock, payload.Proto())
|
||||
if err != nil {
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/pkg/errors"
|
||||
mocks "github.com/prysmaticlabs/prysm/v4/beacon-chain/execution/testing"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/features"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
|
||||
@@ -758,26 +757,7 @@ func TestReconstructFullBellatrixBlock(t *testing.T) {
|
||||
encodedBinaryTxs[0], err = txs[0].MarshalBinary()
|
||||
require.NoError(t, err)
|
||||
payload.Transactions = encodedBinaryTxs
|
||||
jsonPayload["transactions"] = txs
|
||||
num := big.NewInt(1)
|
||||
encodedNum := hexutil.EncodeBig(num)
|
||||
jsonPayload["hash"] = hexutil.Encode(payload.BlockHash)
|
||||
jsonPayload["parentHash"] = common.BytesToHash([]byte("parent"))
|
||||
jsonPayload["sha3Uncles"] = common.BytesToHash([]byte("uncles"))
|
||||
jsonPayload["miner"] = common.BytesToAddress([]byte("miner"))
|
||||
jsonPayload["stateRoot"] = common.BytesToHash([]byte("state"))
|
||||
jsonPayload["transactionsRoot"] = common.BytesToHash([]byte("txs"))
|
||||
jsonPayload["receiptsRoot"] = common.BytesToHash([]byte("receipts"))
|
||||
jsonPayload["logsBloom"] = gethtypes.BytesToBloom([]byte("bloom"))
|
||||
jsonPayload["gasLimit"] = hexutil.EncodeUint64(1)
|
||||
jsonPayload["gasUsed"] = hexutil.EncodeUint64(2)
|
||||
jsonPayload["timestamp"] = hexutil.EncodeUint64(3)
|
||||
jsonPayload["number"] = encodedNum
|
||||
jsonPayload["extraData"] = common.BytesToHash([]byte("extra"))
|
||||
jsonPayload["totalDifficulty"] = "0x123456"
|
||||
jsonPayload["difficulty"] = encodedNum
|
||||
jsonPayload["size"] = encodedNum
|
||||
jsonPayload["baseFeePerGas"] = encodedNum
|
||||
jsonPayload["transactions"] = []hexutil.Bytes{encodedBinaryTxs[0]}
|
||||
|
||||
wrappedPayload, err := blocks.WrappedExecutionPayload(payload)
|
||||
require.NoError(t, err)
|
||||
@@ -792,7 +772,7 @@ func TestReconstructFullBellatrixBlock(t *testing.T) {
|
||||
respJSON := map[string]interface{}{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": jsonPayload,
|
||||
"result": []map[string]interface{}{jsonPayload},
|
||||
}
|
||||
require.NoError(t, json.NewEncoder(w).Encode(respJSON))
|
||||
}))
|
||||
@@ -869,26 +849,7 @@ func TestReconstructFullBellatrixBlockBatch(t *testing.T) {
|
||||
encodedBinaryTxs[0], err = txs[0].MarshalBinary()
|
||||
require.NoError(t, err)
|
||||
payload.Transactions = encodedBinaryTxs
|
||||
jsonPayload["transactions"] = txs
|
||||
num := big.NewInt(1)
|
||||
encodedNum := hexutil.EncodeBig(num)
|
||||
jsonPayload["hash"] = hexutil.Encode(payload.BlockHash)
|
||||
jsonPayload["parentHash"] = common.BytesToHash([]byte("parent"))
|
||||
jsonPayload["sha3Uncles"] = common.BytesToHash([]byte("uncles"))
|
||||
jsonPayload["miner"] = common.BytesToAddress([]byte("miner"))
|
||||
jsonPayload["stateRoot"] = common.BytesToHash([]byte("state"))
|
||||
jsonPayload["transactionsRoot"] = common.BytesToHash([]byte("txs"))
|
||||
jsonPayload["receiptsRoot"] = common.BytesToHash([]byte("receipts"))
|
||||
jsonPayload["logsBloom"] = gethtypes.BytesToBloom([]byte("bloom"))
|
||||
jsonPayload["gasLimit"] = hexutil.EncodeUint64(1)
|
||||
jsonPayload["gasUsed"] = hexutil.EncodeUint64(2)
|
||||
jsonPayload["timestamp"] = hexutil.EncodeUint64(3)
|
||||
jsonPayload["number"] = encodedNum
|
||||
jsonPayload["extraData"] = common.BytesToHash([]byte("extra"))
|
||||
jsonPayload["totalDifficulty"] = "0x123456"
|
||||
jsonPayload["difficulty"] = encodedNum
|
||||
jsonPayload["size"] = encodedNum
|
||||
jsonPayload["baseFeePerGas"] = encodedNum
|
||||
jsonPayload["transactions"] = []hexutil.Bytes{encodedBinaryTxs[0]}
|
||||
|
||||
wrappedPayload, err := blocks.WrappedExecutionPayload(payload)
|
||||
require.NoError(t, err)
|
||||
@@ -912,20 +873,12 @@ func TestReconstructFullBellatrixBlockBatch(t *testing.T) {
|
||||
require.NoError(t, r.Body.Close())
|
||||
}()
|
||||
|
||||
respJSON := []map[string]interface{}{
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": jsonPayload,
|
||||
},
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"result": jsonPayload,
|
||||
},
|
||||
respJSON := map[string]interface{}{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": []map[string]interface{}{jsonPayload, jsonPayload},
|
||||
}
|
||||
require.NoError(t, json.NewEncoder(w).Encode(respJSON))
|
||||
require.NoError(t, json.NewEncoder(w).Encode(respJSON))
|
||||
|
||||
}))
|
||||
defer srv.Close()
|
||||
@@ -1288,6 +1241,10 @@ func fixtures() map[string]interface{} {
|
||||
BlockHash: foo[:],
|
||||
Transactions: [][]byte{foo[:]},
|
||||
}
|
||||
executionPayloadBodyFixture := &pb.ExecutionPayloadBodyV1{
|
||||
Transactions: [][]byte{foo[:]},
|
||||
Withdrawals: []*pb.Withdrawal{},
|
||||
}
|
||||
executionPayloadFixtureCapella := &pb.ExecutionPayloadCapella{
|
||||
ParentHash: foo[:],
|
||||
FeeRecipient: bar,
|
||||
@@ -1459,6 +1416,7 @@ func fixtures() map[string]interface{} {
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"ExecutionBlock": executionBlock,
|
||||
"ExecutionPayloadBody": executionPayloadBodyFixture,
|
||||
"ExecutionPayload": executionPayloadFixture,
|
||||
"ExecutionPayloadCapella": executionPayloadFixtureCapella,
|
||||
"ExecutionPayloadDeneb": executionPayloadFixtureDeneb,
|
||||
@@ -2007,10 +1965,6 @@ func newPayloadV3Setup(t *testing.T, status *pb.PayloadStatus, payload *pb.Execu
|
||||
}
|
||||
|
||||
func TestCapella_PayloadBodiesByHash(t *testing.T) {
|
||||
resetFn := features.InitWithReset(&features.Flags{
|
||||
EnableOptionalEngineMethods: true,
|
||||
})
|
||||
defer resetFn()
|
||||
t.Run("empty response works", func(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
@@ -2067,7 +2021,9 @@ func TestCapella_PayloadBodiesByHash(t *testing.T) {
|
||||
service := &Service{}
|
||||
service.rpcClient = rpcClient
|
||||
|
||||
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{})
|
||||
bRoot := [32]byte{}
|
||||
copy(bRoot[:], "hash")
|
||||
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{bRoot})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(results))
|
||||
|
||||
@@ -2113,7 +2069,9 @@ func TestCapella_PayloadBodiesByHash(t *testing.T) {
|
||||
service := &Service{}
|
||||
service.rpcClient = rpcClient
|
||||
|
||||
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{})
|
||||
bRoot := [32]byte{}
|
||||
copy(bRoot[:], "hash")
|
||||
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{bRoot, bRoot, bRoot})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 3, len(results))
|
||||
|
||||
@@ -2154,7 +2112,9 @@ func TestCapella_PayloadBodiesByHash(t *testing.T) {
|
||||
service := &Service{}
|
||||
service.rpcClient = rpcClient
|
||||
|
||||
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{})
|
||||
bRoot := [32]byte{}
|
||||
copy(bRoot[:], "hash")
|
||||
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{bRoot})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(results))
|
||||
|
||||
@@ -2204,7 +2164,9 @@ func TestCapella_PayloadBodiesByHash(t *testing.T) {
|
||||
service := &Service{}
|
||||
service.rpcClient = rpcClient
|
||||
|
||||
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{})
|
||||
bRoot := [32]byte{}
|
||||
copy(bRoot[:], "hash")
|
||||
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{bRoot})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(results))
|
||||
|
||||
@@ -2247,7 +2209,9 @@ func TestCapella_PayloadBodiesByHash(t *testing.T) {
|
||||
service := &Service{}
|
||||
service.rpcClient = rpcClient
|
||||
|
||||
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{})
|
||||
bRoot := [32]byte{}
|
||||
copy(bRoot[:], "hash")
|
||||
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{bRoot, bRoot, bRoot})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 3, len(results))
|
||||
|
||||
@@ -2258,10 +2222,6 @@ func TestCapella_PayloadBodiesByHash(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCapella_PayloadBodiesByRange(t *testing.T) {
|
||||
resetFn := features.InitWithReset(&features.Flags{
|
||||
EnableOptionalEngineMethods: true,
|
||||
})
|
||||
defer resetFn()
|
||||
t.Run("empty response works", func(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
@@ -2509,10 +2469,6 @@ func TestCapella_PayloadBodiesByRange(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_ExchangeCapabilities(t *testing.T) {
|
||||
resetFn := features.InitWithReset(&features.Flags{
|
||||
EnableOptionalEngineMethods: true,
|
||||
})
|
||||
defer resetFn()
|
||||
t.Run("empty response works", func(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
@@ -28,7 +28,6 @@ go_library(
|
||||
"//beacon-chain/forkchoice:go_default_library",
|
||||
"//beacon-chain/forkchoice/types:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/forkchoice:go_default_library",
|
||||
|
||||
@@ -11,10 +11,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
||||
)
|
||||
|
||||
// orphanLateBlockFirstThreshold is the number of seconds after which we
|
||||
// consider a block to be late, and thus a candidate to being reorged.
|
||||
const orphanLateBlockFirstThreshold = 4
|
||||
|
||||
// ProcessAttestationsThreshold is the number of seconds after which we
|
||||
// process attestations for the current slot
|
||||
const ProcessAttestationsThreshold = 10
|
||||
@@ -137,7 +133,8 @@ func (n *Node) setNodeAndParentValidated(ctx context.Context) error {
|
||||
// slot will have secs = 3 below.
|
||||
func (n *Node) arrivedEarly(genesisTime uint64) (bool, error) {
|
||||
secs, err := slots.SecondsSinceSlotStart(n.slot, genesisTime, n.timestamp)
|
||||
return secs < orphanLateBlockFirstThreshold, err
|
||||
votingWindow := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot
|
||||
return secs < votingWindow, err
|
||||
}
|
||||
|
||||
// arrivedAfterOrphanCheck returns whether this block was inserted after the
|
||||
|
||||
@@ -277,6 +277,7 @@ func TestNode_TimeStampsChecks(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, late)
|
||||
|
||||
orphanLateBlockFirstThreshold := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot
|
||||
// late block
|
||||
driftGenesisTime(f, 2, orphanLateBlockFirstThreshold+1)
|
||||
root = [32]byte{'b'}
|
||||
|
||||
@@ -3,7 +3,6 @@ package doublylinkedtree
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v4/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
||||
)
|
||||
@@ -98,9 +97,6 @@ func (f *ForkChoice) ShouldOverrideFCU() (override bool) {
|
||||
// This function needs to be called only when proposing a block and all
|
||||
// attestation processing has already happened.
|
||||
func (f *ForkChoice) GetProposerHead() [32]byte {
|
||||
if features.Get().DisableReorgLateBlocks {
|
||||
return f.CachedHeadRoot()
|
||||
}
|
||||
head := f.store.headNode
|
||||
if head == nil {
|
||||
return [32]byte{}
|
||||
|
||||
@@ -28,6 +28,7 @@ func TestForkChoice_ShouldOverrideFCU(t *testing.T) {
|
||||
}
|
||||
f.ProcessAttestation(ctx, attesters, root, 0)
|
||||
|
||||
orphanLateBlockFirstThreshold := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot
|
||||
driftGenesisTime(f, 2, orphanLateBlockFirstThreshold+1)
|
||||
st, root, err = prepareForkchoiceState(ctx, 2, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 0, 0)
|
||||
require.NoError(t, err)
|
||||
@@ -125,6 +126,7 @@ func TestForkChoice_GetProposerHead(t *testing.T) {
|
||||
headRoot, err := f.Head(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, root, headRoot)
|
||||
orphanLateBlockFirstThreshold := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot
|
||||
f.store.headNode.timestamp -= params.BeaconConfig().SecondsPerSlot - orphanLateBlockFirstThreshold
|
||||
t.Run("head is weak", func(t *testing.T) {
|
||||
require.Equal(t, parentRoot, f.GetProposerHead())
|
||||
|
||||
@@ -6,6 +6,7 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/gateway",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//api:go_default_library",
|
||||
"//api/gateway:go_default_library",
|
||||
"//cmd/beacon-chain/flags:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
|
||||
@@ -2,6 +2,7 @@ package gateway
|
||||
|
||||
import (
|
||||
gwruntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"github.com/prysmaticlabs/prysm/v4/api"
|
||||
"github.com/prysmaticlabs/prysm/v4/api/gateway"
|
||||
"github.com/prysmaticlabs/prysm/v4/cmd/beacon-chain/flags"
|
||||
ethpbalpha "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
@@ -40,7 +41,7 @@ func DefaultConfig(enableDebugRPCEndpoints bool, httpModules string) MuxConfig {
|
||||
},
|
||||
}),
|
||||
gwruntime.WithMarshalerOption(
|
||||
"text/event-stream", &gwruntime.EventSourceJSONPb{},
|
||||
api.EventStreamMediaType, &gwruntime.EventSourceJSONPb{},
|
||||
),
|
||||
)
|
||||
v1AlphaPbHandler = &gateway.PbMux{
|
||||
|
||||
@@ -98,7 +98,8 @@ type BeaconNode struct {
|
||||
syncCommitteePool synccommittee.Pool
|
||||
blsToExecPool blstoexec.PoolManager
|
||||
depositCache cache.DepositCache
|
||||
proposerIdsCache *cache.ProposerPayloadIDsCache
|
||||
trackedValidatorsCache *cache.TrackedValidatorsCache
|
||||
payloadIDCache *cache.PayloadIDCache
|
||||
stateFeed *event.Feed
|
||||
blockFeed *event.Feed
|
||||
opFeed *event.Feed
|
||||
@@ -179,10 +180,11 @@ func New(cliCtx *cli.Context, cancel context.CancelFunc, opts ...Option) (*Beaco
|
||||
slashingsPool: slashings.NewPool(),
|
||||
syncCommitteePool: synccommittee.NewPool(),
|
||||
blsToExecPool: blstoexec.NewPool(),
|
||||
trackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
payloadIDCache: cache.NewPayloadIDCache(),
|
||||
slasherBlockHeadersFeed: new(event.Feed),
|
||||
slasherAttestationsFeed: new(event.Feed),
|
||||
serviceFlagOpts: &serviceFlagOpts{},
|
||||
proposerIdsCache: cache.NewProposerPayloadIDsCache(),
|
||||
}
|
||||
|
||||
beacon.initialSyncComplete = make(chan struct{})
|
||||
@@ -226,12 +228,8 @@ func New(cliCtx *cli.Context, cancel context.CancelFunc, opts ...Option) (*Beaco
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if beacon.finalizedStateAtStartUp != nil {
|
||||
if err := beacon.BlobStorage.Initialize(beacon.finalizedStateAtStartUp.Slot()); err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize blob storage: %w", err)
|
||||
}
|
||||
} else {
|
||||
log.Warn("No finalized beacon state at startup, cannot prune blobs")
|
||||
if err := beacon.BlobStorage.Initialize(); err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize blob storage: %w", err)
|
||||
}
|
||||
|
||||
log.Debugln("Registering P2P Service")
|
||||
@@ -659,10 +657,11 @@ func (b *BeaconNode) registerBlockchainService(fc forkchoice.ForkChoicer, gs *st
|
||||
blockchain.WithStateGen(b.stateGen),
|
||||
blockchain.WithSlasherAttestationsFeed(b.slasherAttestationsFeed),
|
||||
blockchain.WithFinalizedStateAtStartUp(b.finalizedStateAtStartUp),
|
||||
blockchain.WithProposerIdsCache(b.proposerIdsCache),
|
||||
blockchain.WithClockSynchronizer(gs),
|
||||
blockchain.WithSyncComplete(syncComplete),
|
||||
blockchain.WithBlobStorage(b.BlobStorage),
|
||||
blockchain.WithTrackedValidatorsCache(b.trackedValidatorsCache),
|
||||
blockchain.WithPayloadIDCache(b.payloadIDCache),
|
||||
)
|
||||
|
||||
blockchainService, err := blockchain.NewService(b.ctx, opts...)
|
||||
@@ -893,11 +892,12 @@ func (b *BeaconNode) registerRPCService(router *mux.Router) error {
|
||||
StateGen: b.stateGen,
|
||||
EnableDebugRPCEndpoints: enableDebugRPCEndpoints,
|
||||
MaxMsgSize: maxMsgSize,
|
||||
ProposerIdsCache: b.proposerIdsCache,
|
||||
BlockBuilder: b.fetchBuilderService(),
|
||||
Router: router,
|
||||
ClockWaiter: b.clockWaiter,
|
||||
BlobStorage: b.BlobStorage,
|
||||
TrackedValidatorsCache: b.trackedValidatorsCache,
|
||||
PayloadIDCache: b.payloadIDCache,
|
||||
})
|
||||
|
||||
return b.services.RegisterService(rpcService)
|
||||
|
||||
@@ -23,6 +23,7 @@ go_library(
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//consensus-types/wrapper:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/metadata:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/metadata"
|
||||
)
|
||||
@@ -45,17 +46,17 @@ func InitializeDataMaps() {
|
||||
},
|
||||
bytesutil.ToBytes4(params.BeaconConfig().BellatrixForkVersion): func() (interfaces.ReadOnlySignedBeaconBlock, error) {
|
||||
return blocks.NewSignedBeaconBlock(
|
||||
ðpb.SignedBeaconBlockBellatrix{Block: ðpb.BeaconBlockBellatrix{Body: ðpb.BeaconBlockBodyBellatrix{}}},
|
||||
ðpb.SignedBeaconBlockBellatrix{Block: ðpb.BeaconBlockBellatrix{Body: ðpb.BeaconBlockBodyBellatrix{ExecutionPayload: &enginev1.ExecutionPayload{}}}},
|
||||
)
|
||||
},
|
||||
bytesutil.ToBytes4(params.BeaconConfig().CapellaForkVersion): func() (interfaces.ReadOnlySignedBeaconBlock, error) {
|
||||
return blocks.NewSignedBeaconBlock(
|
||||
ðpb.SignedBeaconBlockCapella{Block: ðpb.BeaconBlockCapella{Body: ðpb.BeaconBlockBodyCapella{}}},
|
||||
ðpb.SignedBeaconBlockCapella{Block: ðpb.BeaconBlockCapella{Body: ðpb.BeaconBlockBodyCapella{ExecutionPayload: &enginev1.ExecutionPayloadCapella{}}}},
|
||||
)
|
||||
},
|
||||
bytesutil.ToBytes4(params.BeaconConfig().DenebForkVersion): func() (interfaces.ReadOnlySignedBeaconBlock, error) {
|
||||
return blocks.NewSignedBeaconBlock(
|
||||
ðpb.SignedBeaconBlockDeneb{Block: ðpb.BeaconBlockDeneb{Body: ðpb.BeaconBlockBodyDeneb{}}},
|
||||
ðpb.SignedBeaconBlockDeneb{Block: ðpb.BeaconBlockDeneb{Body: ðpb.BeaconBlockBodyDeneb{ExecutionPayload: &enginev1.ExecutionPayloadDeneb{}}}},
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -955,9 +955,9 @@ func (s *Server) PublishBlindedBlock(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
isSSZ := httputil.SszRequested(r)
|
||||
if isSSZ {
|
||||
s.publishBlindedBlockSSZ(ctx, w, r)
|
||||
s.publishBlindedBlockSSZ(ctx, w, r, false)
|
||||
} else {
|
||||
s.publishBlindedBlock(ctx, w, r)
|
||||
s.publishBlindedBlock(ctx, w, r, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -980,20 +980,25 @@ func (s *Server) PublishBlindedBlockV2(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
isSSZ := httputil.SszRequested(r)
|
||||
if isSSZ {
|
||||
s.publishBlindedBlockSSZ(ctx, w, r)
|
||||
s.publishBlindedBlockSSZ(ctx, w, r, true)
|
||||
} else {
|
||||
s.publishBlindedBlock(ctx, w, r)
|
||||
s.publishBlindedBlock(ctx, w, r, true)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) publishBlindedBlockSSZ(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) publishBlindedBlockSSZ(ctx context.Context, w http.ResponseWriter, r *http.Request, versionRequired bool) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
httputil.HandleError(w, "Could not read request body: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
versionHeader := r.Header.Get(api.VersionHeader)
|
||||
if versionRequired && versionHeader == "" {
|
||||
httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
denebBlock := ð.SignedBlindedBeaconBlockDeneb{}
|
||||
if err := denebBlock.UnmarshalSSZ(body); err == nil {
|
||||
if err = denebBlock.UnmarshalSSZ(body); err == nil {
|
||||
genericBlock := ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_BlindedDeneb{
|
||||
BlindedDeneb: denebBlock,
|
||||
@@ -1006,8 +1011,17 @@ func (s *Server) publishBlindedBlockSSZ(ctx context.Context, w http.ResponseWrit
|
||||
s.proposeBlock(ctx, w, genericBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Deneb) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Deneb), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
capellaBlock := ð.SignedBlindedBeaconBlockCapella{}
|
||||
if err := capellaBlock.UnmarshalSSZ(body); err == nil {
|
||||
if err = capellaBlock.UnmarshalSSZ(body); err == nil {
|
||||
genericBlock := ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_BlindedCapella{
|
||||
BlindedCapella: capellaBlock,
|
||||
@@ -1020,8 +1034,17 @@ func (s *Server) publishBlindedBlockSSZ(ctx context.Context, w http.ResponseWrit
|
||||
s.proposeBlock(ctx, w, genericBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Capella) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Capella), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
bellatrixBlock := ð.SignedBlindedBeaconBlockBellatrix{}
|
||||
if err := bellatrixBlock.UnmarshalSSZ(body); err == nil {
|
||||
if err = bellatrixBlock.UnmarshalSSZ(body); err == nil {
|
||||
genericBlock := ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_BlindedBellatrix{
|
||||
BlindedBellatrix: bellatrixBlock,
|
||||
@@ -1034,10 +1057,17 @@ func (s *Server) publishBlindedBlockSSZ(ctx context.Context, w http.ResponseWrit
|
||||
s.proposeBlock(ctx, w, genericBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Bellatrix) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Bellatrix), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// blinded is not supported before bellatrix hardfork
|
||||
altairBlock := ð.SignedBeaconBlockAltair{}
|
||||
if err := altairBlock.UnmarshalSSZ(body); err == nil {
|
||||
if err = altairBlock.UnmarshalSSZ(body); err == nil {
|
||||
genericBlock := ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_Altair{
|
||||
Altair: altairBlock,
|
||||
@@ -1050,8 +1080,17 @@ func (s *Server) publishBlindedBlockSSZ(ctx context.Context, w http.ResponseWrit
|
||||
s.proposeBlock(ctx, w, genericBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Altair) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Altair), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
phase0Block := ð.SignedBeaconBlock{}
|
||||
if err := phase0Block.UnmarshalSSZ(body); err == nil {
|
||||
if err = phase0Block.UnmarshalSSZ(body); err == nil {
|
||||
genericBlock := ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_Phase0{
|
||||
Phase0: phase0Block,
|
||||
@@ -1064,20 +1103,34 @@ func (s *Server) publishBlindedBlockSSZ(ctx context.Context, w http.ResponseWrit
|
||||
s.proposeBlock(ctx, w, genericBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Phase0) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Phase0), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
httputil.HandleError(w, "Body does not represent a valid block type", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func (s *Server) publishBlindedBlock(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) publishBlindedBlock(ctx context.Context, w http.ResponseWriter, r *http.Request, versionRequired bool) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
httputil.HandleError(w, "Could not read request body", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
versionHeader := r.Header.Get(api.VersionHeader)
|
||||
var blockVersionError string
|
||||
if versionRequired && versionHeader == "" {
|
||||
httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
var consensusBlock *eth.GenericSignedBeaconBlock
|
||||
|
||||
var denebBlock *shared.SignedBlindedBeaconBlockDeneb
|
||||
if err = unmarshalStrict(body, &denebBlock); err == nil {
|
||||
consensusBlock, err := denebBlock.ToGeneric()
|
||||
consensusBlock, err = denebBlock.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
@@ -1086,14 +1139,19 @@ func (s *Server) publishBlindedBlock(ctx context.Context, w http.ResponseWriter,
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Deneb) {
|
||||
blockVersionError = fmt.Sprintf("could not decode %s request body into consensus block: %v", version.String(version.Deneb), err.Error())
|
||||
}
|
||||
}
|
||||
if versionHeader == version.String(version.Deneb) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Deneb), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
var capellaBlock *shared.SignedBlindedBeaconBlockCapella
|
||||
if err = unmarshalStrict(body, &capellaBlock); err == nil {
|
||||
consensusBlock, err := capellaBlock.ToGeneric()
|
||||
consensusBlock, err = capellaBlock.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
@@ -1102,14 +1160,19 @@ func (s *Server) publishBlindedBlock(ctx context.Context, w http.ResponseWriter,
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Capella) {
|
||||
blockVersionError = fmt.Sprintf("could not decode %s request body into consensus block: %v", version.String(version.Capella), err.Error())
|
||||
}
|
||||
}
|
||||
if versionHeader == version.String(version.Capella) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Capella), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
var bellatrixBlock *shared.SignedBlindedBeaconBlockBellatrix
|
||||
if err = unmarshalStrict(body, &bellatrixBlock); err == nil {
|
||||
consensusBlock, err := bellatrixBlock.ToGeneric()
|
||||
consensusBlock, err = bellatrixBlock.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
@@ -1118,13 +1181,19 @@ func (s *Server) publishBlindedBlock(ctx context.Context, w http.ResponseWriter,
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Bellatrix) {
|
||||
blockVersionError = fmt.Sprintf("could not decode %s request body into consensus block: %v", version.String(version.Bellatrix), err.Error())
|
||||
}
|
||||
}
|
||||
if versionHeader == version.String(version.Bellatrix) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Bellatrix), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
var altairBlock *shared.SignedBeaconBlockAltair
|
||||
if err = unmarshalStrict(body, &altairBlock); err == nil {
|
||||
consensusBlock, err := altairBlock.ToGeneric()
|
||||
consensusBlock, err = altairBlock.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
@@ -1133,13 +1202,19 @@ func (s *Server) publishBlindedBlock(ctx context.Context, w http.ResponseWriter,
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Altair) {
|
||||
blockVersionError = fmt.Sprintf("could not decode %s request body into consensus block: %v", version.String(version.Altair), err.Error())
|
||||
}
|
||||
}
|
||||
if versionHeader == version.String(version.Altair) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Altair), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
var phase0Block *shared.SignedBeaconBlock
|
||||
if err = unmarshalStrict(body, &phase0Block); err == nil {
|
||||
consensusBlock, err := phase0Block.ToGeneric()
|
||||
consensusBlock, err = phase0Block.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
@@ -1148,14 +1223,17 @@ func (s *Server) publishBlindedBlock(ctx context.Context, w http.ResponseWriter,
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Phase0) {
|
||||
blockVersionError = fmt.Sprintf("could not decode %s request body into consensus block: %v", version.String(version.Phase0), err.Error())
|
||||
}
|
||||
}
|
||||
if versionHeader == "" {
|
||||
blockVersionError = fmt.Sprintf("please add the api header %s to see specific type errors", api.VersionHeader)
|
||||
if versionHeader == version.String(version.Phase0) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Phase0), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
httputil.HandleError(w, "Body does not represent a valid block type: "+blockVersionError, http.StatusBadRequest)
|
||||
|
||||
httputil.HandleError(w, "Body does not represent a valid block type", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// PublishBlock instructs the beacon node to broadcast a newly signed beacon block to the beacon network,
|
||||
@@ -1174,9 +1252,9 @@ func (s *Server) PublishBlock(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
isSSZ := httputil.SszRequested(r)
|
||||
if isSSZ {
|
||||
s.publishBlockSSZ(ctx, w, r)
|
||||
s.publishBlockSSZ(ctx, w, r, false)
|
||||
} else {
|
||||
s.publishBlock(ctx, w, r)
|
||||
s.publishBlock(ctx, w, r, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1197,20 +1275,26 @@ func (s *Server) PublishBlockV2(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
isSSZ := httputil.SszRequested(r)
|
||||
if isSSZ {
|
||||
s.publishBlockSSZ(ctx, w, r)
|
||||
s.publishBlockSSZ(ctx, w, r, true)
|
||||
} else {
|
||||
s.publishBlock(ctx, w, r)
|
||||
s.publishBlock(ctx, w, r, true)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *http.Request, versionRequired bool) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
httputil.HandleError(w, "Could not read request body", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
versionHeader := r.Header.Get(api.VersionHeader)
|
||||
if versionRequired && versionHeader == "" {
|
||||
httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
denebBlock := ð.SignedBeaconBlockContentsDeneb{}
|
||||
if err := denebBlock.UnmarshalSSZ(body); err == nil {
|
||||
if err = denebBlock.UnmarshalSSZ(body); err == nil {
|
||||
genericBlock := ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_Deneb{
|
||||
Deneb: denebBlock,
|
||||
@@ -1223,8 +1307,17 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
|
||||
s.proposeBlock(ctx, w, genericBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Deneb) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Deneb), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
capellaBlock := ð.SignedBeaconBlockCapella{}
|
||||
if err := capellaBlock.UnmarshalSSZ(body); err == nil {
|
||||
if err = capellaBlock.UnmarshalSSZ(body); err == nil {
|
||||
genericBlock := ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_Capella{
|
||||
Capella: capellaBlock,
|
||||
@@ -1237,8 +1330,17 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
|
||||
s.proposeBlock(ctx, w, genericBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Capella) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Capella), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
bellatrixBlock := ð.SignedBeaconBlockBellatrix{}
|
||||
if err := bellatrixBlock.UnmarshalSSZ(body); err == nil {
|
||||
if err = bellatrixBlock.UnmarshalSSZ(body); err == nil {
|
||||
genericBlock := ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_Bellatrix{
|
||||
Bellatrix: bellatrixBlock,
|
||||
@@ -1251,8 +1353,17 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
|
||||
s.proposeBlock(ctx, w, genericBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Bellatrix) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Bellatrix), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
altairBlock := ð.SignedBeaconBlockAltair{}
|
||||
if err := altairBlock.UnmarshalSSZ(body); err == nil {
|
||||
if err = altairBlock.UnmarshalSSZ(body); err == nil {
|
||||
genericBlock := ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_Altair{
|
||||
Altair: altairBlock,
|
||||
@@ -1265,8 +1376,17 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
|
||||
s.proposeBlock(ctx, w, genericBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Altair) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Altair), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
phase0Block := ð.SignedBeaconBlock{}
|
||||
if err := phase0Block.UnmarshalSSZ(body); err == nil {
|
||||
if err = phase0Block.UnmarshalSSZ(body); err == nil {
|
||||
genericBlock := ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_Phase0{
|
||||
Phase0: phase0Block,
|
||||
@@ -1279,20 +1399,35 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
|
||||
s.proposeBlock(ctx, w, genericBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Phase0) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Phase0), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
httputil.HandleError(w, "Body does not represent a valid block type", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *http.Request, versionRequired bool) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
httputil.HandleError(w, "Could not read request body", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
versionHeader := r.Header.Get(api.VersionHeader)
|
||||
var blockVersionError string
|
||||
if versionRequired && versionHeader == "" {
|
||||
httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var consensusBlock *eth.GenericSignedBeaconBlock
|
||||
|
||||
var denebBlockContents *shared.SignedBeaconBlockContentsDeneb
|
||||
if err = unmarshalStrict(body, &denebBlockContents); err == nil {
|
||||
consensusBlock, err := denebBlockContents.ToGeneric()
|
||||
consensusBlock, err = denebBlockContents.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
@@ -1301,13 +1436,19 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Deneb) {
|
||||
blockVersionError = fmt.Sprintf(": could not decode %s request body into consensus block: %v", version.String(version.Deneb), err.Error())
|
||||
}
|
||||
}
|
||||
if versionHeader == version.String(version.Deneb) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Deneb), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
var capellaBlock *shared.SignedBeaconBlockCapella
|
||||
if err = unmarshalStrict(body, &capellaBlock); err == nil {
|
||||
consensusBlock, err := capellaBlock.ToGeneric()
|
||||
consensusBlock, err = capellaBlock.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
@@ -1316,13 +1457,19 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Capella) {
|
||||
blockVersionError = fmt.Sprintf(": could not decode %s request body into consensus block: %v", version.String(version.Capella), err.Error())
|
||||
}
|
||||
}
|
||||
if versionHeader == version.String(version.Capella) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Capella), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
var bellatrixBlock *shared.SignedBeaconBlockBellatrix
|
||||
if err = unmarshalStrict(body, &bellatrixBlock); err == nil {
|
||||
consensusBlock, err := bellatrixBlock.ToGeneric()
|
||||
consensusBlock, err = bellatrixBlock.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
@@ -1331,13 +1478,19 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Bellatrix) {
|
||||
blockVersionError = fmt.Sprintf(": could not decode %s request body into consensus block: %v", version.String(version.Bellatrix), err.Error())
|
||||
}
|
||||
}
|
||||
if versionHeader == version.String(version.Bellatrix) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Bellatrix), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
var altairBlock *shared.SignedBeaconBlockAltair
|
||||
if err = unmarshalStrict(body, &altairBlock); err == nil {
|
||||
consensusBlock, err := altairBlock.ToGeneric()
|
||||
consensusBlock, err = altairBlock.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
@@ -1346,13 +1499,19 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Altair) {
|
||||
blockVersionError = fmt.Sprintf(": could not decode %s request body into consensus block: %v", version.String(version.Altair), err.Error())
|
||||
}
|
||||
}
|
||||
if versionHeader == version.String(version.Altair) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Altair), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
var phase0Block *shared.SignedBeaconBlock
|
||||
if err = unmarshalStrict(body, &phase0Block); err == nil {
|
||||
consensusBlock, err := phase0Block.ToGeneric()
|
||||
consensusBlock, err = phase0Block.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
@@ -1361,14 +1520,17 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Phase0) {
|
||||
blockVersionError = fmt.Sprintf(": Could not decode %s request body into consensus block: %v", version.String(version.Phase0), err.Error())
|
||||
}
|
||||
}
|
||||
if versionHeader == "" {
|
||||
blockVersionError = fmt.Sprintf(": please add the api header %s to see specific type errors", api.VersionHeader)
|
||||
if versionHeader == version.String(version.Phase0) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Phase0), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
httputil.HandleError(w, "Body does not represent a valid block type"+blockVersionError, http.StatusBadRequest)
|
||||
|
||||
httputil.HandleError(w, "Body does not represent a valid block type", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func (s *Server) proposeBlock(ctx context.Context, w http.ResponseWriter, blk *eth.GenericSignedBeaconBlock) {
|
||||
|
||||
@@ -501,6 +501,13 @@ func (s *Server) SubmitAttesterSlashing(w http.ResponseWriter, r *http.Request)
|
||||
httputil.HandleError(w, "Could not insert attester slashing into pool: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// notify events
|
||||
s.OperationNotifier.OperationFeed().Send(&feed.Event{
|
||||
Type: operation.AttesterSlashingReceived,
|
||||
Data: &operation.AttesterSlashingReceivedData{
|
||||
AttesterSlashing: slashing,
|
||||
},
|
||||
})
|
||||
if !features.Get().DisableBroadcastSlashings {
|
||||
if err = s.Broadcaster.Broadcast(ctx, slashing); err != nil {
|
||||
httputil.HandleError(w, "Could not broadcast slashing object: "+err.Error(), http.StatusInternalServerError)
|
||||
@@ -569,6 +576,15 @@ func (s *Server) SubmitProposerSlashing(w http.ResponseWriter, r *http.Request)
|
||||
httputil.HandleError(w, "Could not insert proposer slashing into pool: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// notify events
|
||||
s.OperationNotifier.OperationFeed().Send(&feed.Event{
|
||||
Type: operation.ProposerSlashingReceived,
|
||||
Data: &operation.ProposerSlashingReceivedData{
|
||||
ProposerSlashing: slashing,
|
||||
},
|
||||
})
|
||||
|
||||
if !features.Get().DisableBroadcastSlashings {
|
||||
if err = s.Broadcaster.Broadcast(ctx, slashing); err != nil {
|
||||
httputil.HandleError(w, "Could not broadcast slashing object: "+err.Error(), http.StatusInternalServerError)
|
||||
|
||||
@@ -1205,10 +1205,12 @@ func TestSubmitAttesterSlashing_Ok(t *testing.T) {
|
||||
}
|
||||
|
||||
broadcaster := &p2pMock.MockBroadcaster{}
|
||||
chainmock := &blockchainmock.ChainService{State: bs}
|
||||
s := &Server{
|
||||
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
|
||||
SlashingsPool: &slashingsmock.PoolMock{},
|
||||
Broadcaster: broadcaster,
|
||||
ChainInfoFetcher: chainmock,
|
||||
SlashingsPool: &slashingsmock.PoolMock{},
|
||||
Broadcaster: broadcaster,
|
||||
OperationNotifier: chainmock.OperationNotifier(),
|
||||
}
|
||||
|
||||
toSubmit := shared.AttesterSlashingsFromConsensus([]*ethpbv1alpha1.AttesterSlashing{slashing})
|
||||
@@ -1295,10 +1297,12 @@ func TestSubmitAttesterSlashing_AcrossFork(t *testing.T) {
|
||||
}
|
||||
|
||||
broadcaster := &p2pMock.MockBroadcaster{}
|
||||
chainmock := &blockchainmock.ChainService{State: bs}
|
||||
s := &Server{
|
||||
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
|
||||
SlashingsPool: &slashingsmock.PoolMock{},
|
||||
Broadcaster: broadcaster,
|
||||
ChainInfoFetcher: chainmock,
|
||||
SlashingsPool: &slashingsmock.PoolMock{},
|
||||
Broadcaster: broadcaster,
|
||||
OperationNotifier: chainmock.OperationNotifier(),
|
||||
}
|
||||
|
||||
toSubmit := shared.AttesterSlashingsFromConsensus([]*ethpbv1alpha1.AttesterSlashing{slashing})
|
||||
@@ -1404,10 +1408,12 @@ func TestSubmitProposerSlashing_Ok(t *testing.T) {
|
||||
}
|
||||
|
||||
broadcaster := &p2pMock.MockBroadcaster{}
|
||||
chainmock := &blockchainmock.ChainService{State: bs}
|
||||
s := &Server{
|
||||
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
|
||||
SlashingsPool: &slashingsmock.PoolMock{},
|
||||
Broadcaster: broadcaster,
|
||||
ChainInfoFetcher: chainmock,
|
||||
SlashingsPool: &slashingsmock.PoolMock{},
|
||||
Broadcaster: broadcaster,
|
||||
OperationNotifier: chainmock.OperationNotifier(),
|
||||
}
|
||||
|
||||
toSubmit := shared.ProposerSlashingsFromConsensus([]*ethpbv1alpha1.ProposerSlashing{slashing})
|
||||
@@ -1486,10 +1492,12 @@ func TestSubmitProposerSlashing_AcrossFork(t *testing.T) {
|
||||
}
|
||||
|
||||
broadcaster := &p2pMock.MockBroadcaster{}
|
||||
chainmock := &blockchainmock.ChainService{State: bs}
|
||||
s := &Server{
|
||||
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
|
||||
SlashingsPool: &slashingsmock.PoolMock{},
|
||||
Broadcaster: broadcaster,
|
||||
ChainInfoFetcher: chainmock,
|
||||
SlashingsPool: &slashingsmock.PoolMock{},
|
||||
Broadcaster: broadcaster,
|
||||
OperationNotifier: chainmock.OperationNotifier(),
|
||||
}
|
||||
|
||||
toSubmit := shared.ProposerSlashingsFromConsensus([]*ethpbv1alpha1.ProposerSlashing{slashing})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,8 +8,9 @@ go_library(
|
||||
"structs.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/events",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//api:go_default_library",
|
||||
"//beacon-chain/blockchain:go_default_library",
|
||||
"//beacon-chain/core/feed:go_default_library",
|
||||
"//beacon-chain/core/feed/operation:go_default_library",
|
||||
@@ -18,6 +19,7 @@ go_library(
|
||||
"//beacon-chain/core/time:go_default_library",
|
||||
"//beacon-chain/core/transition:go_default_library",
|
||||
"//beacon-chain/rpc/eth/shared:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//network/httputil:go_default_library",
|
||||
"//proto/eth/v1:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
@@ -38,6 +40,7 @@ go_test(
|
||||
"//beacon-chain/core/feed/operation:go_default_library",
|
||||
"//beacon-chain/core/feed/state:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
"//consensus-types/interfaces:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
|
||||
@@ -5,8 +5,10 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
time2 "time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/prysmaticlabs/prysm/v4/api"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/operation"
|
||||
@@ -15,6 +17,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/time"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/network/httputil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
|
||||
"github.com/prysmaticlabs/prysm/v4/runtime/version"
|
||||
@@ -44,6 +47,10 @@ const (
|
||||
PayloadAttributesTopic = "payload_attributes"
|
||||
// BlobSidecarTopic represents a new blob sidecar event topic
|
||||
BlobSidecarTopic = "blob_sidecar"
|
||||
// ProposerSlashingTopic represents a new proposer slashing event topic
|
||||
ProposerSlashingTopic = "proposer_slashing"
|
||||
// AttesterSlashingTopic represents a new attester slashing event topic
|
||||
AttesterSlashingTopic = "attester_slashing"
|
||||
)
|
||||
|
||||
const topicDataMismatch = "Event data type %T does not correspond to event topic %s"
|
||||
@@ -61,6 +68,8 @@ var casesHandled = map[string]bool{
|
||||
BLSToExecutionChangeTopic: true,
|
||||
PayloadAttributesTopic: true,
|
||||
BlobSidecarTopic: true,
|
||||
ProposerSlashingTopic: true,
|
||||
AttesterSlashingTopic: true,
|
||||
}
|
||||
|
||||
// StreamEvents provides an endpoint to subscribe to the beacon node Server-Sent-Events stream.
|
||||
@@ -100,16 +109,24 @@ func (s *Server) StreamEvents(w http.ResponseWriter, r *http.Request) {
|
||||
defer stateSub.Unsubscribe()
|
||||
|
||||
// Set up SSE response headers
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Content-Type", api.EventStreamMediaType)
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
|
||||
// Handle each event received and context cancellation.
|
||||
// We send a keepalive dummy message immediately to prevent clients
|
||||
// stalling while waiting for the first response chunk.
|
||||
// After that we send a keepalive dummy message every SECONDS_PER_SLOT
|
||||
// to prevent anyone (e.g. proxy servers) from closing connections.
|
||||
sendKeepalive(w, flusher)
|
||||
keepaliveTicker := time2.NewTicker(time2.Duration(params.BeaconConfig().SecondsPerSlot) * time2.Second)
|
||||
for {
|
||||
select {
|
||||
case event := <-opsChan:
|
||||
handleBlockOperationEvents(w, flusher, topicsMap, event)
|
||||
case event := <-stateChan:
|
||||
s.handleStateEvents(ctx, w, flusher, topicsMap, event)
|
||||
case <-keepaliveTicker.C:
|
||||
sendKeepalive(w, flusher)
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
@@ -190,6 +207,26 @@ func handleBlockOperationEvents(w http.ResponseWriter, flusher http.Flusher, req
|
||||
KzgCommitment: hexutil.Encode(blobData.Blob.KzgCommitment),
|
||||
}
|
||||
send(w, flusher, BlobSidecarTopic, blobEvent)
|
||||
case operation.AttesterSlashingReceived:
|
||||
if _, ok := requestedTopics[AttesterSlashingTopic]; !ok {
|
||||
return
|
||||
}
|
||||
attesterSlashingData, ok := event.Data.(*operation.AttesterSlashingReceivedData)
|
||||
if !ok {
|
||||
write(w, flusher, topicDataMismatch, event.Data, AttesterSlashingTopic)
|
||||
return
|
||||
}
|
||||
send(w, flusher, AttesterSlashingTopic, shared.AttesterSlashingFromConsensus(attesterSlashingData.AttesterSlashing))
|
||||
case operation.ProposerSlashingReceived:
|
||||
if _, ok := requestedTopics[ProposerSlashingTopic]; !ok {
|
||||
return
|
||||
}
|
||||
proposerSlashingData, ok := event.Data.(*operation.ProposerSlashingReceivedData)
|
||||
if !ok {
|
||||
write(w, flusher, topicDataMismatch, event.Data, ProposerSlashingTopic)
|
||||
return
|
||||
}
|
||||
send(w, flusher, ProposerSlashingTopic, shared.ProposerSlashingFromConsensus(proposerSlashingData.ProposerSlashing))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,6 +442,10 @@ func send(w http.ResponseWriter, flusher http.Flusher, name string, data interfa
|
||||
write(w, flusher, "event: %s\ndata: %s\n\n", name, string(j))
|
||||
}
|
||||
|
||||
func sendKeepalive(w http.ResponseWriter, flusher http.Flusher) {
|
||||
write(w, flusher, ":\n\n")
|
||||
}
|
||||
|
||||
func write(w http.ResponseWriter, flusher http.Flusher, format string, a ...any) {
|
||||
_, err := fmt.Fprintf(w, format, a...)
|
||||
if err != nil {
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/operation"
|
||||
statefeed "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/state"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
@@ -41,7 +42,15 @@ func TestStreamEvents_OperationsEvents(t *testing.T) {
|
||||
OperationNotifier: &mockChain.MockOperationNotifier{},
|
||||
}
|
||||
|
||||
topics := []string{AttestationTopic, VoluntaryExitTopic, SyncCommitteeContributionTopic, BLSToExecutionChangeTopic, BlobSidecarTopic}
|
||||
topics := []string{
|
||||
AttestationTopic,
|
||||
VoluntaryExitTopic,
|
||||
SyncCommitteeContributionTopic,
|
||||
BLSToExecutionChangeTopic,
|
||||
BlobSidecarTopic,
|
||||
AttesterSlashingTopic,
|
||||
ProposerSlashingTopic,
|
||||
}
|
||||
for i, topic := range topics {
|
||||
topics[i] = "topics=" + topic
|
||||
}
|
||||
@@ -124,6 +133,65 @@ func TestStreamEvents_OperationsEvents(t *testing.T) {
|
||||
Blob: &vblob,
|
||||
},
|
||||
})
|
||||
|
||||
s.OperationNotifier.OperationFeed().Send(&feed.Event{
|
||||
Type: operation.AttesterSlashingReceived,
|
||||
Data: &operation.AttesterSlashingReceivedData{
|
||||
AttesterSlashing: ð.AttesterSlashing{
|
||||
Attestation_1: ð.IndexedAttestation{
|
||||
AttestingIndices: []uint64{0, 1},
|
||||
Data: ð.AttestationData{
|
||||
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
|
||||
Source: ð.Checkpoint{
|
||||
Root: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
Target: ð.Checkpoint{
|
||||
Root: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
},
|
||||
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
},
|
||||
Attestation_2: ð.IndexedAttestation{
|
||||
AttestingIndices: []uint64{0, 1},
|
||||
Data: ð.AttestationData{
|
||||
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
|
||||
Source: ð.Checkpoint{
|
||||
Root: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
Target: ð.Checkpoint{
|
||||
Root: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
},
|
||||
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
s.OperationNotifier.OperationFeed().Send(&feed.Event{
|
||||
Type: operation.ProposerSlashingReceived,
|
||||
Data: &operation.ProposerSlashingReceivedData{
|
||||
ProposerSlashing: ð.ProposerSlashing{
|
||||
Header_1: ð.SignedBeaconBlockHeader{
|
||||
Header: ð.BeaconBlockHeader{
|
||||
ParentRoot: make([]byte, fieldparams.RootLength),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
BodyRoot: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
},
|
||||
Header_2: ð.SignedBeaconBlockHeader{
|
||||
Header: ð.BeaconBlockHeader{
|
||||
ParentRoot: make([]byte, fieldparams.RootLength),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
BodyRoot: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
request.Context().Done()
|
||||
|
||||
@@ -307,7 +375,9 @@ func TestStreamEvents_OperationsEvents(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
const operationsResult = `event: attestation
|
||||
const operationsResult = `:
|
||||
|
||||
event: attestation
|
||||
data: {"aggregation_bits":"0x00","data":{"slot":"0","index":"0","beacon_block_root":"0x0000000000000000000000000000000000000000000000000000000000000000","source":{"epoch":"0","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"target":{"epoch":"0","root":"0x0000000000000000000000000000000000000000000000000000000000000000"}},"signature":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}
|
||||
|
||||
event: attestation
|
||||
@@ -325,9 +395,17 @@ data: {"message":{"validator_index":"0","from_bls_pubkey":"0x0000000000000000000
|
||||
event: blob_sidecar
|
||||
data: {"block_root":"0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c","index":"0","slot":"0","kzg_commitment":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","versioned_hash":"0x01b0761f87b081d5cf10757ccc89f12be355c70e2e29df288b65b30710dcbcd1"}
|
||||
|
||||
event: attester_slashing
|
||||
data: {"attestation_1":{"attesting_indices":["0","1"],"data":{"slot":"0","index":"0","beacon_block_root":"0x0000000000000000000000000000000000000000000000000000000000000000","source":{"epoch":"0","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"target":{"epoch":"0","root":"0x0000000000000000000000000000000000000000000000000000000000000000"}},"signature":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},"attestation_2":{"attesting_indices":["0","1"],"data":{"slot":"0","index":"0","beacon_block_root":"0x0000000000000000000000000000000000000000000000000000000000000000","source":{"epoch":"0","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"target":{"epoch":"0","root":"0x0000000000000000000000000000000000000000000000000000000000000000"}},"signature":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}}
|
||||
|
||||
event: proposer_slashing
|
||||
data: {"signed_header_1":{"message":{"slot":"0","proposer_index":"0","parent_root":"0x0000000000000000000000000000000000000000000000000000000000000000","state_root":"0x0000000000000000000000000000000000000000000000000000000000000000","body_root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"signature":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},"signed_header_2":{"message":{"slot":"0","proposer_index":"0","parent_root":"0x0000000000000000000000000000000000000000000000000000000000000000","state_root":"0x0000000000000000000000000000000000000000000000000000000000000000","body_root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"signature":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}}
|
||||
|
||||
`
|
||||
|
||||
const stateResult = `event: head
|
||||
const stateResult = `:
|
||||
|
||||
event: head
|
||||
data: {"slot":"0","block":"0x0000000000000000000000000000000000000000000000000000000000000000","state":"0x0000000000000000000000000000000000000000000000000000000000000000","epoch_transition":true,"execution_optimistic":false,"previous_duty_dependent_root":"0x0000000000000000000000000000000000000000000000000000000000000000","current_duty_dependent_root":"0x0000000000000000000000000000000000000000000000000000000000000000"}
|
||||
|
||||
event: finalized_checkpoint
|
||||
@@ -341,17 +419,23 @@ data: {"slot":"0","block":"0xeade62f0457b2fdf48e7d3fc4b60736688286be7c7a3ac4c9a1
|
||||
|
||||
`
|
||||
|
||||
const payloadAttributesBellatrixResult = `event: payload_attributes
|
||||
const payloadAttributesBellatrixResult = `:
|
||||
|
||||
event: payload_attributes
|
||||
data: {"version":"bellatrix","data":{"proposer_index":"0","proposal_slot":"1","parent_block_number":"0","parent_block_root":"0x0000000000000000000000000000000000000000000000000000000000000000","parent_block_hash":"0x0000000000000000000000000000000000000000000000000000000000000000","payload_attributes":{"timestamp":"12","prev_randao":"0x0000000000000000000000000000000000000000000000000000000000000000","suggested_fee_recipient":"0x0000000000000000000000000000000000000000"}}}
|
||||
|
||||
`
|
||||
|
||||
const payloadAttributesCapellaResult = `event: payload_attributes
|
||||
const payloadAttributesCapellaResult = `:
|
||||
|
||||
event: payload_attributes
|
||||
data: {"version":"capella","data":{"proposer_index":"0","proposal_slot":"1","parent_block_number":"0","parent_block_root":"0x0000000000000000000000000000000000000000000000000000000000000000","parent_block_hash":"0x0000000000000000000000000000000000000000000000000000000000000000","payload_attributes":{"timestamp":"12","prev_randao":"0x0000000000000000000000000000000000000000000000000000000000000000","suggested_fee_recipient":"0x0000000000000000000000000000000000000000","withdrawals":[]}}}
|
||||
|
||||
`
|
||||
|
||||
const payloadAttributesDenebResult = `event: payload_attributes
|
||||
const payloadAttributesDenebResult = `:
|
||||
|
||||
event: payload_attributes
|
||||
data: {"version":"deneb","data":{"proposer_index":"0","proposal_slot":"1","parent_block_number":"0","parent_block_root":"0x0000000000000000000000000000000000000000000000000000000000000000","parent_block_hash":"0x0000000000000000000000000000000000000000000000000000000000000000","payload_attributes":{"timestamp":"12","prev_randao":"0x0000000000000000000000000000000000000000000000000000000000000000","suggested_fee_recipient":"0x0000000000000000000000000000000000000000","withdrawals":[],"parent_beacon_block_root":"0xbef96cb938fd48b2403d3e662664325abb0102ed12737cbb80d717520e50cf4a"}}}
|
||||
|
||||
`
|
||||
|
||||
@@ -40,7 +40,10 @@ go_library(
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["errors_test.go"],
|
||||
srcs = [
|
||||
"errors_test.go",
|
||||
"request_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/rpc/lookup:go_default_library",
|
||||
|
||||
@@ -769,32 +769,36 @@ func ProposerSlashingsToConsensus(src []*ProposerSlashing) ([]*eth.ProposerSlash
|
||||
func ProposerSlashingsFromConsensus(src []*eth.ProposerSlashing) []*ProposerSlashing {
|
||||
proposerSlashings := make([]*ProposerSlashing, len(src))
|
||||
for i, s := range src {
|
||||
proposerSlashings[i] = &ProposerSlashing{
|
||||
SignedHeader1: &SignedBeaconBlockHeader{
|
||||
Message: &BeaconBlockHeader{
|
||||
Slot: fmt.Sprintf("%d", s.Header_1.Header.Slot),
|
||||
ProposerIndex: fmt.Sprintf("%d", s.Header_1.Header.ProposerIndex),
|
||||
ParentRoot: hexutil.Encode(s.Header_1.Header.ParentRoot),
|
||||
StateRoot: hexutil.Encode(s.Header_1.Header.StateRoot),
|
||||
BodyRoot: hexutil.Encode(s.Header_1.Header.BodyRoot),
|
||||
},
|
||||
Signature: hexutil.Encode(s.Header_1.Signature),
|
||||
},
|
||||
SignedHeader2: &SignedBeaconBlockHeader{
|
||||
Message: &BeaconBlockHeader{
|
||||
Slot: fmt.Sprintf("%d", s.Header_2.Header.Slot),
|
||||
ProposerIndex: fmt.Sprintf("%d", s.Header_2.Header.ProposerIndex),
|
||||
ParentRoot: hexutil.Encode(s.Header_2.Header.ParentRoot),
|
||||
StateRoot: hexutil.Encode(s.Header_2.Header.StateRoot),
|
||||
BodyRoot: hexutil.Encode(s.Header_2.Header.BodyRoot),
|
||||
},
|
||||
Signature: hexutil.Encode(s.Header_2.Signature),
|
||||
},
|
||||
}
|
||||
proposerSlashings[i] = ProposerSlashingFromConsensus(s)
|
||||
}
|
||||
return proposerSlashings
|
||||
}
|
||||
|
||||
func ProposerSlashingFromConsensus(src *eth.ProposerSlashing) *ProposerSlashing {
|
||||
return &ProposerSlashing{
|
||||
SignedHeader1: &SignedBeaconBlockHeader{
|
||||
Message: &BeaconBlockHeader{
|
||||
Slot: fmt.Sprintf("%d", src.Header_1.Header.Slot),
|
||||
ProposerIndex: fmt.Sprintf("%d", src.Header_1.Header.ProposerIndex),
|
||||
ParentRoot: hexutil.Encode(src.Header_1.Header.ParentRoot),
|
||||
StateRoot: hexutil.Encode(src.Header_1.Header.StateRoot),
|
||||
BodyRoot: hexutil.Encode(src.Header_1.Header.BodyRoot),
|
||||
},
|
||||
Signature: hexutil.Encode(src.Header_1.Signature),
|
||||
},
|
||||
SignedHeader2: &SignedBeaconBlockHeader{
|
||||
Message: &BeaconBlockHeader{
|
||||
Slot: fmt.Sprintf("%d", src.Header_2.Header.Slot),
|
||||
ProposerIndex: fmt.Sprintf("%d", src.Header_2.Header.ProposerIndex),
|
||||
ParentRoot: hexutil.Encode(src.Header_2.Header.ParentRoot),
|
||||
StateRoot: hexutil.Encode(src.Header_2.Header.StateRoot),
|
||||
BodyRoot: hexutil.Encode(src.Header_2.Header.BodyRoot),
|
||||
},
|
||||
Signature: hexutil.Encode(src.Header_2.Signature),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func AttesterSlashingsToConsensus(src []*AttesterSlashing) ([]*eth.AttesterSlashing, error) {
|
||||
if src == nil {
|
||||
return nil, errNilValue
|
||||
@@ -875,54 +879,58 @@ func AttesterSlashingsToConsensus(src []*AttesterSlashing) ([]*eth.AttesterSlash
|
||||
func AttesterSlashingsFromConsensus(src []*eth.AttesterSlashing) []*AttesterSlashing {
|
||||
attesterSlashings := make([]*AttesterSlashing, len(src))
|
||||
for i, s := range src {
|
||||
a1AttestingIndices := make([]string, len(s.Attestation_1.AttestingIndices))
|
||||
for j, ix := range s.Attestation_1.AttestingIndices {
|
||||
a1AttestingIndices[j] = fmt.Sprintf("%d", ix)
|
||||
}
|
||||
a2AttestingIndices := make([]string, len(s.Attestation_2.AttestingIndices))
|
||||
for j, ix := range s.Attestation_2.AttestingIndices {
|
||||
a2AttestingIndices[j] = fmt.Sprintf("%d", ix)
|
||||
}
|
||||
attesterSlashings[i] = &AttesterSlashing{
|
||||
Attestation1: &IndexedAttestation{
|
||||
AttestingIndices: a1AttestingIndices,
|
||||
Data: &AttestationData{
|
||||
Slot: fmt.Sprintf("%d", s.Attestation_1.Data.Slot),
|
||||
CommitteeIndex: fmt.Sprintf("%d", s.Attestation_1.Data.CommitteeIndex),
|
||||
BeaconBlockRoot: hexutil.Encode(s.Attestation_1.Data.BeaconBlockRoot),
|
||||
Source: &Checkpoint{
|
||||
Epoch: fmt.Sprintf("%d", s.Attestation_1.Data.Source.Epoch),
|
||||
Root: hexutil.Encode(s.Attestation_1.Data.Source.Root),
|
||||
},
|
||||
Target: &Checkpoint{
|
||||
Epoch: fmt.Sprintf("%d", s.Attestation_1.Data.Target.Epoch),
|
||||
Root: hexutil.Encode(s.Attestation_1.Data.Target.Root),
|
||||
},
|
||||
},
|
||||
Signature: hexutil.Encode(s.Attestation_1.Signature),
|
||||
},
|
||||
Attestation2: &IndexedAttestation{
|
||||
AttestingIndices: a2AttestingIndices,
|
||||
Data: &AttestationData{
|
||||
Slot: fmt.Sprintf("%d", s.Attestation_2.Data.Slot),
|
||||
CommitteeIndex: fmt.Sprintf("%d", s.Attestation_2.Data.CommitteeIndex),
|
||||
BeaconBlockRoot: hexutil.Encode(s.Attestation_2.Data.BeaconBlockRoot),
|
||||
Source: &Checkpoint{
|
||||
Epoch: fmt.Sprintf("%d", s.Attestation_2.Data.Source.Epoch),
|
||||
Root: hexutil.Encode(s.Attestation_2.Data.Source.Root),
|
||||
},
|
||||
Target: &Checkpoint{
|
||||
Epoch: fmt.Sprintf("%d", s.Attestation_2.Data.Target.Epoch),
|
||||
Root: hexutil.Encode(s.Attestation_2.Data.Target.Root),
|
||||
},
|
||||
},
|
||||
Signature: hexutil.Encode(s.Attestation_2.Signature),
|
||||
},
|
||||
}
|
||||
attesterSlashings[i] = AttesterSlashingFromConsensus(s)
|
||||
}
|
||||
return attesterSlashings
|
||||
}
|
||||
|
||||
func AttesterSlashingFromConsensus(src *eth.AttesterSlashing) *AttesterSlashing {
|
||||
a1AttestingIndices := make([]string, len(src.Attestation_1.AttestingIndices))
|
||||
for j, ix := range src.Attestation_1.AttestingIndices {
|
||||
a1AttestingIndices[j] = fmt.Sprintf("%d", ix)
|
||||
}
|
||||
a2AttestingIndices := make([]string, len(src.Attestation_2.AttestingIndices))
|
||||
for j, ix := range src.Attestation_2.AttestingIndices {
|
||||
a2AttestingIndices[j] = fmt.Sprintf("%d", ix)
|
||||
}
|
||||
return &AttesterSlashing{
|
||||
Attestation1: &IndexedAttestation{
|
||||
AttestingIndices: a1AttestingIndices,
|
||||
Data: &AttestationData{
|
||||
Slot: fmt.Sprintf("%d", src.Attestation_1.Data.Slot),
|
||||
CommitteeIndex: fmt.Sprintf("%d", src.Attestation_1.Data.CommitteeIndex),
|
||||
BeaconBlockRoot: hexutil.Encode(src.Attestation_1.Data.BeaconBlockRoot),
|
||||
Source: &Checkpoint{
|
||||
Epoch: fmt.Sprintf("%d", src.Attestation_1.Data.Source.Epoch),
|
||||
Root: hexutil.Encode(src.Attestation_1.Data.Source.Root),
|
||||
},
|
||||
Target: &Checkpoint{
|
||||
Epoch: fmt.Sprintf("%d", src.Attestation_1.Data.Target.Epoch),
|
||||
Root: hexutil.Encode(src.Attestation_1.Data.Target.Root),
|
||||
},
|
||||
},
|
||||
Signature: hexutil.Encode(src.Attestation_1.Signature),
|
||||
},
|
||||
Attestation2: &IndexedAttestation{
|
||||
AttestingIndices: a2AttestingIndices,
|
||||
Data: &AttestationData{
|
||||
Slot: fmt.Sprintf("%d", src.Attestation_2.Data.Slot),
|
||||
CommitteeIndex: fmt.Sprintf("%d", src.Attestation_2.Data.CommitteeIndex),
|
||||
BeaconBlockRoot: hexutil.Encode(src.Attestation_2.Data.BeaconBlockRoot),
|
||||
Source: &Checkpoint{
|
||||
Epoch: fmt.Sprintf("%d", src.Attestation_2.Data.Source.Epoch),
|
||||
Root: hexutil.Encode(src.Attestation_2.Data.Source.Root),
|
||||
},
|
||||
Target: &Checkpoint{
|
||||
Epoch: fmt.Sprintf("%d", src.Attestation_2.Data.Target.Epoch),
|
||||
Root: hexutil.Encode(src.Attestation_2.Data.Target.Root),
|
||||
},
|
||||
},
|
||||
Signature: hexutil.Encode(src.Attestation_2.Signature),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func AttsToConsensus(src []*Attestation) ([]*eth.Attestation, error) {
|
||||
if src == nil {
|
||||
return nil, errNilValue
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/gorilla/mux"
|
||||
@@ -15,15 +16,15 @@ import (
|
||||
)
|
||||
|
||||
func UintFromQuery(w http.ResponseWriter, r *http.Request, name string, required bool) (string, uint64, bool) {
|
||||
raw := r.URL.Query().Get(name)
|
||||
if raw == "" && !required {
|
||||
trimmed := strings.ReplaceAll(r.URL.Query().Get(name), " ", "")
|
||||
if trimmed == "" && !required {
|
||||
return "", 0, true
|
||||
}
|
||||
v, valid := ValidateUint(w, name, raw)
|
||||
v, valid := ValidateUint(w, name, trimmed)
|
||||
if !valid {
|
||||
return "", 0, false
|
||||
}
|
||||
return raw, v, true
|
||||
return trimmed, v, true
|
||||
}
|
||||
|
||||
func UintFromRoute(w http.ResponseWriter, r *http.Request, name string) (string, uint64, bool) {
|
||||
|
||||
96
beacon-chain/rpc/eth/shared/request_test.go
Normal file
96
beacon-chain/rpc/eth/shared/request_test.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"math"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUintFromQuery_BuilderBoostFactor(t *testing.T) {
|
||||
type args struct {
|
||||
raw string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantRaw string
|
||||
wantValue uint64
|
||||
wantOK bool
|
||||
}{
|
||||
{
|
||||
name: "builder boost factor of 0 returns 0",
|
||||
args: args{
|
||||
raw: "0",
|
||||
},
|
||||
wantRaw: "0",
|
||||
wantValue: 0,
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "builder boost factor of the default, 100 returns 100",
|
||||
args: args{raw: "100"},
|
||||
wantRaw: "100",
|
||||
wantValue: 100,
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "builder boost factor max uint64 returns max uint64",
|
||||
args: args{raw: "18446744073709551615"},
|
||||
wantRaw: "18446744073709551615",
|
||||
wantValue: math.MaxUint64,
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "builder boost factor as a percentage returns error",
|
||||
args: args{raw: "0.30"},
|
||||
wantRaw: "",
|
||||
wantValue: 0,
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "builder boost factor negative int returns error",
|
||||
args: args{raw: "-100"},
|
||||
wantRaw: "",
|
||||
wantValue: 0,
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "builder boost factor max uint64 +1 returns error",
|
||||
args: args{raw: "18446744073709551616"},
|
||||
wantRaw: "",
|
||||
wantValue: 0,
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "builder boost factor of invalid string returns error",
|
||||
args: args{raw: "asdf"},
|
||||
wantRaw: "",
|
||||
wantValue: 0,
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "builder boost factor of number bigger than uint64 string returns error",
|
||||
args: args{raw: "9871398721983721908372190837219837129803721983719283798217390821739081273918273918273918273981273982139812739821"},
|
||||
wantRaw: "",
|
||||
wantValue: 0,
|
||||
wantOK: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
query := "builder_boost_factor"
|
||||
bbreq := httptest.NewRequest("GET", "/eth/v3/validator/blocks/{slot}?builder_boost_factor="+tt.args.raw, nil)
|
||||
w := httptest.NewRecorder()
|
||||
got, got1, got2 := UintFromQuery(w, bbreq, query, false)
|
||||
if got != tt.wantRaw {
|
||||
t.Errorf("UintFromQuery() got = %v, wantRaw %v", got, tt.wantRaw)
|
||||
}
|
||||
if got1 != tt.wantValue {
|
||||
t.Errorf("UintFromQuery() got1 = %v, wantRaw %v", got1, tt.wantValue)
|
||||
}
|
||||
if got2 != tt.wantOK {
|
||||
t.Errorf("UintFromQuery() got2 = %v, wantRaw %v", got2, tt.wantOK)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ go_library(
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/transition:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/db/kv:go_default_library",
|
||||
"//beacon-chain/operations/attestations:go_default_library",
|
||||
"//beacon-chain/operations/synccommittee:go_default_library",
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
@@ -41,13 +40,13 @@ go_library(
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
"@org_golang_google_grpc//codes:go_default_library",
|
||||
"@org_golang_google_grpc//status:go_default_library",
|
||||
"@org_golang_google_protobuf//types/known/wrapperspb:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -91,7 +90,6 @@ go_test(
|
||||
"//testing/require:go_default_library",
|
||||
"//testing/util:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_golang_mock//gomock:go_default_library",
|
||||
"@com_github_gorilla_mux//:go_default_library",
|
||||
|
||||
@@ -11,14 +11,12 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/builder"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db/kv"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/core"
|
||||
rpchelpers "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
|
||||
@@ -543,9 +541,6 @@ func (s *Server) RegisterValidator(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// PrepareBeaconProposer endpoint saves the fee recipient given a validator index, this is used when proposing a block.
|
||||
func (s *Server) PrepareBeaconProposer(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := trace.StartSpan(r.Context(), "validator.PrepareBeaconProposer")
|
||||
defer span.End()
|
||||
|
||||
var jsonFeeRecipients []*shared.FeeRecipient
|
||||
err := json.NewDecoder(r.Body).Decode(&jsonFeeRecipients)
|
||||
switch {
|
||||
@@ -556,7 +551,6 @@ func (s *Server) PrepareBeaconProposer(w http.ResponseWriter, r *http.Request) {
|
||||
httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
var feeRecipients []common.Address
|
||||
var validatorIndices []primitives.ValidatorIndex
|
||||
// filter for found fee recipients
|
||||
for _, r := range jsonFeeRecipients {
|
||||
@@ -568,28 +562,25 @@ func (s *Server) PrepareBeaconProposer(w http.ResponseWriter, r *http.Request) {
|
||||
if !valid {
|
||||
return
|
||||
}
|
||||
f, err := s.BeaconDB.FeeRecipientByValidatorID(ctx, primitives.ValidatorIndex(validatorIndex))
|
||||
switch {
|
||||
case errors.Is(err, kv.ErrNotFoundFeeRecipient):
|
||||
feeRecipients = append(feeRecipients, common.BytesToAddress(bytesutil.SafeCopyBytes(feeRecipientBytes)))
|
||||
validatorIndices = append(validatorIndices, primitives.ValidatorIndex(validatorIndex))
|
||||
case err != nil:
|
||||
httputil.HandleError(w, fmt.Sprintf("Could not get fee recipient by validator index: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
default:
|
||||
if common.BytesToAddress(feeRecipientBytes) != f {
|
||||
feeRecipients = append(feeRecipients, common.BytesToAddress(bytesutil.SafeCopyBytes(feeRecipientBytes)))
|
||||
validatorIndices = append(validatorIndices, primitives.ValidatorIndex(validatorIndex))
|
||||
// Use default address if the burn address is return
|
||||
feeRecipient := primitives.ExecutionAddress(feeRecipientBytes)
|
||||
if feeRecipient == primitives.ExecutionAddress([20]byte{}) {
|
||||
feeRecipient = primitives.ExecutionAddress(params.BeaconConfig().DefaultFeeRecipient)
|
||||
if feeRecipient == primitives.ExecutionAddress([20]byte{}) {
|
||||
log.WithField("validatorIndex", validatorIndex).Warn("fee recipient is the burn address")
|
||||
}
|
||||
}
|
||||
val := cache.TrackedValidator{
|
||||
Active: true, // TODO: either check or add the field in the request
|
||||
Index: primitives.ValidatorIndex(validatorIndex),
|
||||
FeeRecipient: feeRecipient,
|
||||
}
|
||||
s.TrackedValidatorsCache.Set(val)
|
||||
validatorIndices = append(validatorIndices, primitives.ValidatorIndex(validatorIndex))
|
||||
}
|
||||
if len(validatorIndices) == 0 {
|
||||
return
|
||||
}
|
||||
if err := s.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, validatorIndices, feeRecipients); err != nil {
|
||||
httputil.HandleError(w, fmt.Sprintf("Could not save fee recipients: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
log.WithFields(log.Fields{
|
||||
"validatorIndices": validatorIndices,
|
||||
}).Info("Updated fee recipient addresses")
|
||||
@@ -812,7 +803,6 @@ func (s *Server) GetProposerDuties(w http.ResponseWriter, r *http.Request) {
|
||||
pubkey48 := val.PublicKey()
|
||||
pubkey := pubkey48[:]
|
||||
for _, slot := range proposalSlots {
|
||||
s.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, index, [8]byte{} /* payloadID */, [32]byte{} /* head root */)
|
||||
duties = append(duties, &ProposerDuty{
|
||||
Pubkey: hexutil.Encode(pubkey),
|
||||
ValidatorIndex: strconv.FormatUint(uint64(index), 10),
|
||||
@@ -821,8 +811,6 @@ func (s *Server) GetProposerDuties(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
s.ProposerSlotIndexCache.PrunePayloadIDs(epochStartSlot)
|
||||
|
||||
dependentRoot, err := proposalDependentRoot(st, requestedEpoch)
|
||||
if err != nil {
|
||||
httputil.HandleError(w, "Could not get dependent root: "+err.Error(), http.StatusInternalServerError)
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/runtime/version"
|
||||
"go.opencensus.io/trace"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
)
|
||||
|
||||
type blockType uint8
|
||||
@@ -155,6 +156,15 @@ func (s *Server) ProduceBlockV3(w http.ResponseWriter, r *http.Request) {
|
||||
rawGraffiti := r.URL.Query().Get("graffiti")
|
||||
rawSkipRandaoVerification := r.URL.Query().Get("skip_randao_verification")
|
||||
|
||||
var bbFactor *wrapperspb.UInt64Value // default the factor via fall back
|
||||
rawBbFactor, bbValue, ok := shared.UintFromQuery(w, r, "builder_boost_factor", false)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if rawBbFactor != "" {
|
||||
bbFactor = &wrapperspb.UInt64Value{Value: bbValue}
|
||||
}
|
||||
|
||||
slot, valid := shared.ValidateUint(w, "slot", rawSlot)
|
||||
if !valid {
|
||||
return
|
||||
@@ -182,10 +192,11 @@ func (s *Server) ProduceBlockV3(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
s.produceBlockV3(ctx, w, r, ð.BlockRequest{
|
||||
Slot: primitives.Slot(slot),
|
||||
RandaoReveal: randaoReveal,
|
||||
Graffiti: graffiti,
|
||||
SkipMevBoost: false,
|
||||
Slot: primitives.Slot(slot),
|
||||
RandaoReveal: randaoReveal,
|
||||
Graffiti: graffiti,
|
||||
SkipMevBoost: false,
|
||||
BuilderBoostFactor: bbFactor,
|
||||
}, any)
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
@@ -1660,7 +1659,8 @@ func TestGetProposerDuties(t *testing.T) {
|
||||
TimeFetcher: chain,
|
||||
OptimisticModeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
}
|
||||
|
||||
request := httptest.NewRequest(http.MethodGet, "http://www.example.com/eth/v1/validator/duties/proposer/{epoch}", nil)
|
||||
@@ -1681,9 +1681,6 @@ func TestGetProposerDuties(t *testing.T) {
|
||||
expectedDuty = duty
|
||||
}
|
||||
}
|
||||
vid, _, has := s.ProposerSlotIndexCache.GetProposerPayloadIDs(11, [32]byte{})
|
||||
require.Equal(t, true, has)
|
||||
require.Equal(t, primitives.ValidatorIndex(12289), vid)
|
||||
require.NotNil(t, expectedDuty, "Expected duty for slot 11 not found")
|
||||
assert.Equal(t, "12289", expectedDuty.ValidatorIndex)
|
||||
assert.Equal(t, hexutil.Encode(pubKeys[12289]), expectedDuty.Pubkey)
|
||||
@@ -1702,7 +1699,8 @@ func TestGetProposerDuties(t *testing.T) {
|
||||
TimeFetcher: chain,
|
||||
OptimisticModeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
}
|
||||
|
||||
request := httptest.NewRequest(http.MethodGet, "http://www.example.com/eth/v1/validator/duties/proposer/{epoch}", nil)
|
||||
@@ -1723,52 +1721,10 @@ func TestGetProposerDuties(t *testing.T) {
|
||||
expectedDuty = duty
|
||||
}
|
||||
}
|
||||
vid, _, has := s.ProposerSlotIndexCache.GetProposerPayloadIDs(43, [32]byte{})
|
||||
require.Equal(t, true, has)
|
||||
require.Equal(t, primitives.ValidatorIndex(1360), vid)
|
||||
require.NotNil(t, expectedDuty, "Expected duty for slot 43 not found")
|
||||
assert.Equal(t, "1360", expectedDuty.ValidatorIndex)
|
||||
assert.Equal(t, hexutil.Encode(pubKeys[1360]), expectedDuty.Pubkey)
|
||||
})
|
||||
t.Run("prune payload ID cache", func(t *testing.T) {
|
||||
bs, err := transition.GenesisBeaconState(context.Background(), deposits, 0, eth1Data)
|
||||
require.NoError(t, err, "Could not set up genesis state")
|
||||
require.NoError(t, bs.SetSlot(params.BeaconConfig().SlotsPerEpoch))
|
||||
require.NoError(t, bs.SetBlockRoots(roots))
|
||||
chainSlot := params.BeaconConfig().SlotsPerEpoch
|
||||
chain := &mockChain.ChainService{
|
||||
State: bs, Root: genesisRoot[:], Slot: &chainSlot,
|
||||
}
|
||||
s := &Server{
|
||||
Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{params.BeaconConfig().SlotsPerEpoch: bs}},
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
OptimisticModeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
}
|
||||
|
||||
s.ProposerSlotIndexCache.SetProposerAndPayloadIDs(1, 1, [8]byte{1}, [32]byte{2})
|
||||
s.ProposerSlotIndexCache.SetProposerAndPayloadIDs(31, 2, [8]byte{2}, [32]byte{3})
|
||||
s.ProposerSlotIndexCache.SetProposerAndPayloadIDs(32, 4309, [8]byte{3}, [32]byte{4})
|
||||
|
||||
request := httptest.NewRequest(http.MethodGet, "http://www.example.com/eth/v1/validator/duties/proposer/{epoch}", nil)
|
||||
request = mux.SetURLVars(request, map[string]string{"epoch": "1"})
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.GetProposerDuties(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
vid, _, has := s.ProposerSlotIndexCache.GetProposerPayloadIDs(1, [32]byte{})
|
||||
require.Equal(t, false, has)
|
||||
require.Equal(t, primitives.ValidatorIndex(0), vid)
|
||||
vid, _, has = s.ProposerSlotIndexCache.GetProposerPayloadIDs(2, [32]byte{})
|
||||
require.Equal(t, false, has)
|
||||
require.Equal(t, primitives.ValidatorIndex(0), vid)
|
||||
vid, _, has = s.ProposerSlotIndexCache.GetProposerPayloadIDs(32, [32]byte{})
|
||||
require.Equal(t, true, has)
|
||||
require.Equal(t, primitives.ValidatorIndex(10565), vid)
|
||||
})
|
||||
t.Run("epoch out of bounds", func(t *testing.T) {
|
||||
bs, err := transition.GenesisBeaconState(context.Background(), deposits, 0, eth1Data)
|
||||
require.NoError(t, err, "Could not set up genesis state")
|
||||
@@ -1785,7 +1741,8 @@ func TestGetProposerDuties(t *testing.T) {
|
||||
TimeFetcher: chain,
|
||||
OptimisticModeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
}
|
||||
|
||||
currentEpoch := slots.ToEpoch(bs.Slot())
|
||||
@@ -1828,7 +1785,8 @@ func TestGetProposerDuties(t *testing.T) {
|
||||
TimeFetcher: chain,
|
||||
OptimisticModeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
}
|
||||
|
||||
request := httptest.NewRequest(http.MethodGet, "http://www.example.com/eth/v1/validator/duties/proposer/{epoch}", nil)
|
||||
@@ -2267,21 +2225,24 @@ func TestPrepareBeaconProposer(t *testing.T) {
|
||||
request := httptest.NewRequest(http.MethodPost, url, &body)
|
||||
writer := httptest.NewRecorder()
|
||||
db := dbutil.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
server := &Server{
|
||||
BeaconDB: db,
|
||||
BeaconDB: db,
|
||||
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
}
|
||||
server.PrepareBeaconProposer(writer, request)
|
||||
require.Equal(t, tt.code, writer.Code)
|
||||
if tt.wantErr != "" {
|
||||
require.Equal(t, strings.Contains(writer.Body.String(), tt.wantErr), true)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
address, err := server.BeaconDB.FeeRecipientByValidatorID(ctx, 1)
|
||||
require.NoError(t, err)
|
||||
feebytes, err := hexutil.Decode(tt.request[0].FeeRecipient)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, common.BytesToAddress(feebytes), address)
|
||||
index, err := strconv.ParseUint(tt.request[0].ValidatorIndex, 10, 64)
|
||||
require.NoError(t, err)
|
||||
val, tracked := server.TrackedValidatorsCache.Validator(primitives.ValidatorIndex(index))
|
||||
require.Equal(t, true, tracked)
|
||||
require.Equal(t, primitives.ExecutionAddress(feebytes), val.FeeRecipient)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -2292,7 +2253,11 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
|
||||
db := dbutil.SetupDB(t)
|
||||
|
||||
// New validator
|
||||
proposerServer := &Server{BeaconDB: db}
|
||||
proposerServer := &Server{
|
||||
BeaconDB: db,
|
||||
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
}
|
||||
req := []*shared.FeeRecipient{{
|
||||
FeeRecipient: hexutil.Encode(bytesutil.PadTo([]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF}, fieldparams.FeeRecipientLength)),
|
||||
ValidatorIndex: "1",
|
||||
@@ -2318,7 +2283,7 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
|
||||
writer = httptest.NewRecorder()
|
||||
proposerServer.PrepareBeaconProposer(writer, request)
|
||||
require.Equal(t, http.StatusOK, writer.Code)
|
||||
require.LogsDoNotContain(t, hook, "Updated fee recipient addresses")
|
||||
require.LogsContain(t, hook, "Updated fee recipient addresses")
|
||||
|
||||
// Same validator with different fee recipient
|
||||
hook.Reset()
|
||||
@@ -2368,13 +2333,16 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
|
||||
writer = httptest.NewRecorder()
|
||||
proposerServer.PrepareBeaconProposer(writer, request)
|
||||
require.Equal(t, http.StatusOK, writer.Code)
|
||||
require.LogsDoNotContain(t, hook, "Updated fee recipient addresses")
|
||||
require.LogsContain(t, hook, "Updated fee recipient addresses")
|
||||
}
|
||||
|
||||
func BenchmarkServer_PrepareBeaconProposer(b *testing.B) {
|
||||
db := dbutil.SetupDB(b)
|
||||
proposerServer := &Server{BeaconDB: db}
|
||||
|
||||
proposerServer := &Server{
|
||||
BeaconDB: db,
|
||||
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
}
|
||||
f := bytesutil.PadTo([]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF}, fieldparams.FeeRecipientLength)
|
||||
recipients := make([]*shared.FeeRecipient, 0)
|
||||
for i := 0; i < 10000; i++ {
|
||||
|
||||
@@ -29,11 +29,12 @@ type Server struct {
|
||||
OptimisticModeFetcher blockchain.OptimisticModeFetcher
|
||||
SyncCommitteePool synccommittee.Pool
|
||||
V1Alpha1Server eth.BeaconNodeValidatorServer
|
||||
ProposerSlotIndexCache *cache.ProposerPayloadIDsCache
|
||||
ChainInfoFetcher blockchain.ChainInfoFetcher
|
||||
BeaconDB db.HeadAccessDatabase
|
||||
BlockBuilder builder.BlockBuilder
|
||||
OperationNotifier operation.Notifier
|
||||
CoreService *core.Service
|
||||
BlockRewardFetcher rewards.BlockRewardsFetcher
|
||||
TrackedValidatorsCache *cache.TrackedValidatorsCache
|
||||
PayloadIDCache *cache.PayloadIDCache
|
||||
}
|
||||
|
||||
@@ -201,7 +201,6 @@ go_test(
|
||||
"status_mainnet_test.go",
|
||||
"status_test.go",
|
||||
"sync_committee_test.go",
|
||||
"unblinder_test.go",
|
||||
"validator_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
|
||||
@@ -135,7 +135,7 @@ func (vs *Server) duties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.
|
||||
return nil, status.Errorf(codes.Internal, "Could not compute committee assignments: %v", err)
|
||||
}
|
||||
// Query the next epoch assignments for committee subnet subscriptions.
|
||||
nextCommitteeAssignments, nextProposerIndexToSlots, err := helpers.CommitteeAssignments(ctx, s, req.Epoch+1)
|
||||
nextCommitteeAssignments, _, err := helpers.CommitteeAssignments(ctx, s, req.Epoch+1)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not compute next committee assignments: %v", err)
|
||||
}
|
||||
@@ -178,15 +178,6 @@ func (vs *Server) duties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.
|
||||
nextAssignment.AttesterSlot = ca.AttesterSlot
|
||||
nextAssignment.CommitteeIndex = ca.CommitteeIndex
|
||||
}
|
||||
// Cache proposer assignment for the current epoch.
|
||||
for _, slot := range proposerIndexToSlots[idx] {
|
||||
// Head root is empty because it can't be known until slot - 1. Same with payload id.
|
||||
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, idx, [8]byte{} /* payloadID */, [32]byte{} /* head root */)
|
||||
}
|
||||
// Cache proposer assignment for the next epoch.
|
||||
for _, slot := range nextProposerIndexToSlots[idx] {
|
||||
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, idx, [8]byte{} /* payloadID */, [32]byte{} /* head root */)
|
||||
}
|
||||
} else {
|
||||
// If the validator isn't in the beacon state, try finding their deposit to determine their status.
|
||||
// We don't need the lastActiveValidatorFn because we don't use the response in this.
|
||||
@@ -228,9 +219,6 @@ func (vs *Server) duties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.
|
||||
validatorAssignments = append(validatorAssignments, assignment)
|
||||
nextValidatorAssignments = append(nextValidatorAssignments, nextAssignment)
|
||||
}
|
||||
// Prune payload ID cache for any slots before request slot.
|
||||
vs.ProposerSlotIndexCache.PrunePayloadIDs(epochStartSlot)
|
||||
|
||||
return ðpb.DutiesResponse{
|
||||
Duties: validatorAssignments,
|
||||
CurrentEpochDuties: validatorAssignments,
|
||||
|
||||
@@ -60,10 +60,10 @@ func TestGetDuties_OK(t *testing.T) {
|
||||
State: bs, Root: genesisRoot[:], Genesis: time.Now(),
|
||||
}
|
||||
vs := &Server{
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
}
|
||||
|
||||
// Test the first validator in registry.
|
||||
@@ -145,11 +145,11 @@ func TestGetAltairDuties_SyncCommitteeOK(t *testing.T) {
|
||||
State: bs, Root: genesisRoot[:], Genesis: time.Now().Add(time.Duration(-1*int64(slot-1)) * time.Second),
|
||||
}
|
||||
vs := &Server{
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
Eth1InfoFetcher: &mockExecution.Chain{},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
Eth1InfoFetcher: &mockExecution.Chain{},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
}
|
||||
|
||||
// Test the first validator in registry.
|
||||
@@ -251,11 +251,11 @@ func TestGetBellatrixDuties_SyncCommitteeOK(t *testing.T) {
|
||||
State: bs, Root: genesisRoot[:], Genesis: time.Now().Add(time.Duration(-1*int64(slot-1)) * time.Second),
|
||||
}
|
||||
vs := &Server{
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
Eth1InfoFetcher: &mockExecution.Chain{},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
Eth1InfoFetcher: &mockExecution.Chain{},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
}
|
||||
|
||||
// Test the first validator in registry.
|
||||
@@ -343,12 +343,12 @@ func TestGetAltairDuties_UnknownPubkey(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
vs := &Server{
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
Eth1InfoFetcher: &mockExecution.Chain{},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
DepositFetcher: depositCache,
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
Eth1InfoFetcher: &mockExecution.Chain{},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
DepositFetcher: depositCache,
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
}
|
||||
|
||||
unknownPubkey := bytesutil.PadTo([]byte{'u'}, 48)
|
||||
@@ -401,10 +401,10 @@ func TestGetDuties_CurrentEpoch_ShouldNotFail(t *testing.T) {
|
||||
State: bState, Root: genesisRoot[:], Genesis: time.Now(),
|
||||
}
|
||||
vs := &Server{
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
}
|
||||
|
||||
// Test the first validator in registry.
|
||||
@@ -440,10 +440,10 @@ func TestGetDuties_MultipleKeys_OK(t *testing.T) {
|
||||
State: bs, Root: genesisRoot[:], Genesis: time.Now(),
|
||||
}
|
||||
vs := &Server{
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
}
|
||||
|
||||
pubkey0 := deposits[0].Data.PublicKey
|
||||
@@ -507,12 +507,12 @@ func TestStreamDuties_OK(t *testing.T) {
|
||||
Genesis: time.Now(),
|
||||
}
|
||||
vs := &Server{
|
||||
Ctx: ctx,
|
||||
HeadFetcher: &mockChain.ChainService{State: bs, Root: genesisRoot[:]},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
TimeFetcher: c,
|
||||
StateNotifier: &mockChain.MockStateNotifier{},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
Ctx: ctx,
|
||||
HeadFetcher: &mockChain.ChainService{State: bs, Root: genesisRoot[:]},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
TimeFetcher: c,
|
||||
StateNotifier: &mockChain.MockStateNotifier{},
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
}
|
||||
|
||||
// Test the first validator in registry.
|
||||
@@ -565,12 +565,12 @@ func TestStreamDuties_OK_ChainReorg(t *testing.T) {
|
||||
Genesis: time.Now(),
|
||||
}
|
||||
vs := &Server{
|
||||
Ctx: ctx,
|
||||
HeadFetcher: &mockChain.ChainService{State: bs, Root: genesisRoot[:]},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
TimeFetcher: c,
|
||||
StateNotifier: &mockChain.MockStateNotifier{},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
Ctx: ctx,
|
||||
HeadFetcher: &mockChain.ChainService{State: bs, Root: genesisRoot[:]},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
TimeFetcher: c,
|
||||
StateNotifier: &mockChain.MockStateNotifier{},
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
}
|
||||
|
||||
// Test the first validator in registry.
|
||||
@@ -625,10 +625,9 @@ func BenchmarkCommitteeAssignment(b *testing.B) {
|
||||
|
||||
chain := &mockChain.ChainService{State: bs, Root: genesisRoot[:]}
|
||||
vs := &Server{
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
HeadFetcher: chain,
|
||||
TimeFetcher: chain,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
}
|
||||
|
||||
// Create request for all validators in the system.
|
||||
|
||||
@@ -2,7 +2,6 @@ package validator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -14,8 +13,10 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/builder"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
|
||||
blockfeed "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/block"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/operation"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db/kv"
|
||||
@@ -37,9 +38,8 @@ import (
|
||||
var eth1DataNotification bool
|
||||
|
||||
const (
|
||||
// CouldNotDecodeBlock means that a signed beacon block couldn't be created from the block present in the request.
|
||||
CouldNotDecodeBlock = "Could not decode block"
|
||||
eth1dataTimeout = 2 * time.Second
|
||||
eth1dataTimeout = 2 * time.Second
|
||||
defaultBuilderBoostFactor = uint64(100)
|
||||
)
|
||||
|
||||
// GetBeaconBlock is called by a proposer during its assigned slot to request a block to sign
|
||||
@@ -69,6 +69,11 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
|
||||
parentRoot := vs.ForkchoiceFetcher.GetProposerHead()
|
||||
if parentRoot != headRoot {
|
||||
blockchain.LateBlockAttemptedReorgCount.Inc()
|
||||
log.WithFields(logrus.Fields{
|
||||
"slot": req.Slot,
|
||||
"parentRoot": fmt.Sprintf("%#x", parentRoot),
|
||||
"headRoot": fmt.Sprintf("%#x", headRoot),
|
||||
}).Warn("late block attempted reorg failed")
|
||||
}
|
||||
|
||||
// An optimistic validator MUST NOT produce a block (i.e., sign across the DOMAIN_BEACON_PROPOSER domain).
|
||||
@@ -104,7 +109,12 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
|
||||
}
|
||||
sBlk.SetProposerIndex(idx)
|
||||
|
||||
if err = vs.BuildBlockParallel(ctx, sBlk, head, req.SkipMevBoost); err != nil {
|
||||
builderBoostFactor := defaultBuilderBoostFactor
|
||||
if req.BuilderBoostFactor != nil {
|
||||
builderBoostFactor = req.BuilderBoostFactor.Value
|
||||
}
|
||||
|
||||
if err = vs.BuildBlockParallel(ctx, sBlk, head, req.SkipMevBoost, builderBoostFactor); err != nil {
|
||||
return nil, errors.Wrap(err, "could not build block in parallel")
|
||||
}
|
||||
|
||||
@@ -124,7 +134,7 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
|
||||
return vs.constructGenericBeaconBlock(sBlk, bundleCache.get(req.Slot))
|
||||
}
|
||||
|
||||
func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.SignedBeaconBlock, head state.BeaconState, skipMevBoost bool) error {
|
||||
func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.SignedBeaconBlock, head state.BeaconState, skipMevBoost bool, builderBoostFactor uint64) error {
|
||||
// Build consensus fields in background
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
@@ -182,7 +192,7 @@ func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.Signed
|
||||
}
|
||||
}
|
||||
|
||||
if err := setExecutionData(ctx, sBlk, localPayload, builderPayload, builderKzgCommitments); err != nil {
|
||||
if err := setExecutionData(ctx, sBlk, localPayload, builderPayload, builderKzgCommitments, builderBoostFactor); err != nil {
|
||||
return status.Errorf(codes.Internal, "Could not set execution data: %v", err)
|
||||
}
|
||||
|
||||
@@ -191,129 +201,171 @@ func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.Signed
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProposeBeaconBlock is called by a proposer during its assigned slot to create a block in an attempt
|
||||
// to get it processed by the beacon node as the canonical head.
|
||||
// ProposeBeaconBlock handles the proposal of beacon blocks.
|
||||
func (vs *Server) ProposeBeaconBlock(ctx context.Context, req *ethpb.GenericSignedBeaconBlock) (*ethpb.ProposeResponse, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "ProposerServer.ProposeBeaconBlock")
|
||||
defer span.End()
|
||||
|
||||
blk, err := blocks.NewSignedBeaconBlock(req.Block)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "%s: %v", CouldNotDecodeBlock, err)
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "empty request")
|
||||
}
|
||||
|
||||
unblinder, err := newUnblinder(blk, vs.BlockBuilder)
|
||||
block, err := blocks.NewSignedBeaconBlock(req.Block)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not create unblinder")
|
||||
}
|
||||
blinded := unblinder.b.IsBlinded() //
|
||||
|
||||
var scs []*ethpb.BlobSidecar
|
||||
blk, scs, err = unblinder.unblindBuilderBlock(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not unblind builder block")
|
||||
return nil, status.Errorf(codes.InvalidArgument, "%s: %v", "decode block failed", err)
|
||||
}
|
||||
|
||||
// Broadcast the new block to the network.
|
||||
blkPb, err := blk.Proto()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get protobuf block")
|
||||
var sidecars []*ethpb.BlobSidecar
|
||||
if block.IsBlinded() {
|
||||
block, sidecars, err = vs.handleBlindedBlock(ctx, block)
|
||||
} else {
|
||||
sidecars, err = vs.handleUnblindedBlock(block, req)
|
||||
}
|
||||
if err := vs.P2P.Broadcast(ctx, blkPb); err != nil {
|
||||
return nil, fmt.Errorf("could not broadcast block: %v", err)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "%s: %v", "handle block failed", err)
|
||||
}
|
||||
|
||||
root, err := blk.Block().HashTreeRoot()
|
||||
root, err := block.Block().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not tree hash block: %v", err)
|
||||
return nil, status.Errorf(codes.Internal, "Could not hash tree root: %v", err)
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"blockRoot": hex.EncodeToString(root[:]),
|
||||
}).Debug("Broadcasting block")
|
||||
|
||||
if blk.Version() >= version.Deneb {
|
||||
if !blinded {
|
||||
dbBlockContents := req.GetDeneb()
|
||||
if dbBlockContents == nil {
|
||||
return nil, errors.New("signed beacon block contents is empty")
|
||||
}
|
||||
scs, err = buildBlobSidecars(blk, dbBlockContents.Blobs, dbBlockContents.KzgProofs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not build blob sidecars: %v", err)
|
||||
}
|
||||
}
|
||||
for i, sc := range scs {
|
||||
if err := vs.P2P.BroadcastBlob(ctx, uint64(i), sc); err != nil {
|
||||
log.WithError(err).Error("Could not broadcast blob")
|
||||
}
|
||||
readOnlySc, err := blocks.NewROBlobWithRoot(sc, root)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create ROBlob: %v", err)
|
||||
}
|
||||
verifiedSc := blocks.NewVerifiedROBlob(readOnlySc)
|
||||
if err := vs.BlobReceiver.ReceiveBlob(ctx, verifiedSc); err != nil {
|
||||
log.WithError(err).Error("Could not receive blob")
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
errChan := make(chan error, 1)
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := vs.broadcastReceiveBlock(ctx, block, root); err != nil {
|
||||
errChan <- errors.Wrap(err, "broadcast/receive block failed")
|
||||
return
|
||||
}
|
||||
errChan <- nil
|
||||
}()
|
||||
|
||||
if err := vs.broadcastAndReceiveBlobs(ctx, sidecars, root); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not broadcast/receive blobs: %v", err)
|
||||
}
|
||||
|
||||
if err := vs.BlockReceiver.ReceiveBlock(ctx, blk, root); err != nil {
|
||||
return nil, fmt.Errorf("could not process beacon block: %v", err)
|
||||
wg.Wait()
|
||||
if err := <-errChan; err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not broadcast/receive block: %v", err)
|
||||
}
|
||||
|
||||
log.WithField("slot", blk.Block().Slot()).Debugf(
|
||||
"Block proposal received via RPC")
|
||||
return ðpb.ProposeResponse{BlockRoot: root[:]}, nil
|
||||
}
|
||||
|
||||
// handleBlindedBlock processes blinded beacon blocks.
|
||||
func (vs *Server) handleBlindedBlock(ctx context.Context, block interfaces.SignedBeaconBlock) (interfaces.SignedBeaconBlock, []*ethpb.BlobSidecar, error) {
|
||||
if block.Version() < version.Bellatrix {
|
||||
return nil, nil, errors.New("pre-Bellatrix blinded block")
|
||||
}
|
||||
if vs.BlockBuilder == nil || !vs.BlockBuilder.Configured() {
|
||||
return nil, nil, errors.New("unconfigured block builder")
|
||||
}
|
||||
|
||||
copiedBlock, err := block.Copy()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
payload, bundle, err := vs.BlockBuilder.SubmitBlindedBlock(ctx, block)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "submit blinded block failed")
|
||||
}
|
||||
|
||||
if err := copiedBlock.Unblind(payload); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "unblind failed")
|
||||
}
|
||||
|
||||
sidecars, err := unblindBlobsSidecars(copiedBlock, bundle)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "unblind sidecars failed")
|
||||
}
|
||||
|
||||
return copiedBlock, sidecars, nil
|
||||
}
|
||||
|
||||
// handleUnblindedBlock processes unblinded beacon blocks.
|
||||
func (vs *Server) handleUnblindedBlock(block interfaces.SignedBeaconBlock, req *ethpb.GenericSignedBeaconBlock) ([]*ethpb.BlobSidecar, error) {
|
||||
dbBlockContents := req.GetDeneb()
|
||||
if dbBlockContents == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return buildBlobSidecars(block, dbBlockContents.Blobs, dbBlockContents.KzgProofs)
|
||||
}
|
||||
|
||||
// broadcastReceiveBlock broadcasts a block and handles its reception.
|
||||
func (vs *Server) broadcastReceiveBlock(ctx context.Context, block interfaces.SignedBeaconBlock, root [32]byte) error {
|
||||
protoBlock, err := block.Proto()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "protobuf conversion failed")
|
||||
}
|
||||
if err := vs.P2P.Broadcast(ctx, protoBlock); err != nil {
|
||||
return errors.Wrap(err, "broadcast failed")
|
||||
}
|
||||
vs.BlockNotifier.BlockFeed().Send(&feed.Event{
|
||||
Type: blockfeed.ReceivedBlock,
|
||||
Data: &blockfeed.ReceivedBlockData{SignedBlock: blk},
|
||||
Data: &blockfeed.ReceivedBlockData{SignedBlock: block},
|
||||
})
|
||||
return vs.BlockReceiver.ReceiveBlock(ctx, block, root)
|
||||
}
|
||||
|
||||
return ðpb.ProposeResponse{
|
||||
BlockRoot: root[:],
|
||||
}, nil
|
||||
// broadcastAndReceiveBlobs handles the broadcasting and reception of blob sidecars.
|
||||
func (vs *Server) broadcastAndReceiveBlobs(ctx context.Context, sidecars []*ethpb.BlobSidecar, root [32]byte) error {
|
||||
for i, sc := range sidecars {
|
||||
if err := vs.P2P.BroadcastBlob(ctx, uint64(i), sc); err != nil {
|
||||
return errors.Wrap(err, "broadcast blob failed")
|
||||
}
|
||||
|
||||
readOnlySc, err := blocks.NewROBlobWithRoot(sc, root)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ROBlob creation failed")
|
||||
}
|
||||
verifiedBlob := blocks.NewVerifiedROBlob(readOnlySc)
|
||||
if err := vs.BlobReceiver.ReceiveBlob(ctx, verifiedBlob); err != nil {
|
||||
return errors.Wrap(err, "receive blob failed")
|
||||
}
|
||||
vs.OperationNotifier.OperationFeed().Send(&feed.Event{
|
||||
Type: operation.BlobSidecarReceived,
|
||||
Data: &operation.BlobSidecarReceivedData{Blob: &verifiedBlob},
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrepareBeaconProposer caches and updates the fee recipient for the given proposer.
|
||||
func (vs *Server) PrepareBeaconProposer(
|
||||
ctx context.Context, request *ethpb.PrepareBeaconProposerRequest,
|
||||
_ context.Context, request *ethpb.PrepareBeaconProposerRequest,
|
||||
) (*emptypb.Empty, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "validator.PrepareBeaconProposer")
|
||||
defer span.End()
|
||||
var feeRecipients []common.Address
|
||||
var validatorIndices []primitives.ValidatorIndex
|
||||
|
||||
newRecipients := make([]*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer, 0, len(request.Recipients))
|
||||
for _, r := range request.Recipients {
|
||||
f, err := vs.BeaconDB.FeeRecipientByValidatorID(ctx, r.ValidatorIndex)
|
||||
switch {
|
||||
case errors.Is(err, kv.ErrNotFoundFeeRecipient):
|
||||
newRecipients = append(newRecipients, r)
|
||||
case err != nil:
|
||||
return nil, status.Errorf(codes.Internal, "Could not get fee recipient by validator index: %v", err)
|
||||
default:
|
||||
if common.BytesToAddress(r.FeeRecipient) != f {
|
||||
newRecipients = append(newRecipients, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(newRecipients) == 0 {
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
for _, recipientContainer := range newRecipients {
|
||||
recipient := hexutil.Encode(recipientContainer.FeeRecipient)
|
||||
recipient := hexutil.Encode(r.FeeRecipient)
|
||||
if !common.IsHexAddress(recipient) {
|
||||
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("Invalid fee recipient address: %v", recipient))
|
||||
}
|
||||
feeRecipients = append(feeRecipients, common.BytesToAddress(recipientContainer.FeeRecipient))
|
||||
validatorIndices = append(validatorIndices, recipientContainer.ValidatorIndex)
|
||||
// Use default address if the burn address is return
|
||||
feeRecipient := primitives.ExecutionAddress(r.FeeRecipient)
|
||||
if feeRecipient == primitives.ExecutionAddress([20]byte{}) {
|
||||
feeRecipient = primitives.ExecutionAddress(params.BeaconConfig().DefaultFeeRecipient)
|
||||
if feeRecipient == primitives.ExecutionAddress([20]byte{}) {
|
||||
log.WithField("validatorIndex", r.ValidatorIndex).Warn("fee recipient is the burn address")
|
||||
}
|
||||
}
|
||||
val := cache.TrackedValidator{
|
||||
Active: true, // TODO: either check or add the field in the request
|
||||
Index: r.ValidatorIndex,
|
||||
FeeRecipient: feeRecipient,
|
||||
}
|
||||
vs.TrackedValidatorsCache.Set(val)
|
||||
validatorIndices = append(validatorIndices, r.ValidatorIndex)
|
||||
}
|
||||
if err := vs.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, validatorIndices, feeRecipients); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not save fee recipients: %v", err)
|
||||
if len(validatorIndices) != 0 {
|
||||
log.WithFields(logrus.Fields{
|
||||
"validatorCount": len(validatorIndices),
|
||||
}).Info("Updated fee recipient addresses for validator indices")
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"validatorIndices": validatorIndices,
|
||||
}).Info("Updated fee recipient addresses for validator indices")
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ var emptyTransactionsRoot = [32]byte{127, 254, 36, 30, 166, 1, 135, 253, 176, 24
|
||||
const blockBuilderTimeout = 1 * time.Second
|
||||
|
||||
// Sets the execution data for the block. Execution data can come from local EL client or remote builder depends on validator registration and circuit breaker conditions.
|
||||
func setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, localPayload, builderPayload interfaces.ExecutionData, builderKzgCommitments [][]byte) error {
|
||||
func setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, localPayload, builderPayload interfaces.ExecutionData, builderKzgCommitments [][]byte, builderBoostFactor uint64) error {
|
||||
_, span := trace.StartSpan(ctx, "ProposerServer.setExecutionData")
|
||||
defer span.End()
|
||||
|
||||
@@ -80,16 +80,22 @@ func setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, loc
|
||||
}
|
||||
|
||||
// Use builder payload if the following in true:
|
||||
// builder_bid_value * 100 > local_block_value * (local-block-value-boost + 100)
|
||||
// builder_bid_value * builderBoostFactor(default 100) > local_block_value * (local-block-value-boost + 100)
|
||||
boost := params.BeaconConfig().LocalBlockValueBoost
|
||||
higherValueBuilder := builderValueGwei*100 > localValueGwei*(100+boost)
|
||||
higherValueBuilder := builderValueGwei*builderBoostFactor > localValueGwei*(100+boost)
|
||||
if boost > 0 && builderBoostFactor != defaultBuilderBoostFactor {
|
||||
log.WithFields(logrus.Fields{
|
||||
"localGweiValue": localValueGwei,
|
||||
"localBoostPercentage": boost,
|
||||
"builderGweiValue": builderValueGwei,
|
||||
"builderBoostFactor": builderBoostFactor,
|
||||
}).Warn("Proposer: both local boost and builder boost are using non default values")
|
||||
}
|
||||
|
||||
// If we can't get the builder value, just use local block.
|
||||
if higherValueBuilder && withdrawalsMatched { // Builder value is higher and withdrawals match.
|
||||
blk.SetBlinded(true)
|
||||
if err := setBuilderExecution(blk, builderPayload, builderKzgCommitments); err != nil {
|
||||
log.WithError(err).Warn("Proposer: failed to set builder payload")
|
||||
blk.SetBlinded(false)
|
||||
return setLocalExecution(blk, localPayload)
|
||||
} else {
|
||||
return nil
|
||||
@@ -100,20 +106,20 @@ func setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, loc
|
||||
"localGweiValue": localValueGwei,
|
||||
"localBoostPercentage": boost,
|
||||
"builderGweiValue": builderValueGwei,
|
||||
"builderBoostFactor": builderBoostFactor,
|
||||
}).Warn("Proposer: using local execution payload because higher value")
|
||||
}
|
||||
span.AddAttributes(
|
||||
trace.BoolAttribute("higherValueBuilder", higherValueBuilder),
|
||||
trace.Int64Attribute("localGweiValue", int64(localValueGwei)), // lint:ignore uintcast -- This is OK for tracing.
|
||||
trace.Int64Attribute("localBoostPercentage", int64(boost)), // lint:ignore uintcast -- This is OK for tracing.
|
||||
trace.Int64Attribute("builderGweiValue", int64(builderValueGwei)), // lint:ignore uintcast -- This is OK for tracing.
|
||||
trace.Int64Attribute("localGweiValue", int64(localValueGwei)), // lint:ignore uintcast -- This is OK for tracing.
|
||||
trace.Int64Attribute("localBoostPercentage", int64(boost)), // lint:ignore uintcast -- This is OK for tracing.
|
||||
trace.Int64Attribute("builderGweiValue", int64(builderValueGwei)), // lint:ignore uintcast -- This is OK for tracing.
|
||||
trace.Int64Attribute("builderBoostFactor", int64(builderBoostFactor)), // lint:ignore uintcast -- This is OK for tracing.
|
||||
)
|
||||
return setLocalExecution(blk, localPayload)
|
||||
default: // Bellatrix case.
|
||||
blk.SetBlinded(true)
|
||||
if err := setBuilderExecution(blk, builderPayload, builderKzgCommitments); err != nil {
|
||||
log.WithError(err).Warn("Proposer: failed to set builder payload")
|
||||
blk.SetBlinded(false)
|
||||
return setLocalExecution(blk, localPayload)
|
||||
} else {
|
||||
return nil
|
||||
@@ -315,9 +321,6 @@ func setExecution(blk interfaces.SignedBeaconBlock, execution interfaces.Executi
|
||||
return errors.New("execution is nil")
|
||||
}
|
||||
|
||||
// Set the blinded status of the block
|
||||
blk.SetBlinded(isBlinded)
|
||||
|
||||
// Set the execution data for the block
|
||||
errMessage := "failed to set local execution"
|
||||
if isBlinded {
|
||||
|
||||
@@ -2,6 +2,7 @@ package validator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -81,9 +82,10 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
HeadFetcher: &blockchainTest.ChainService{State: capellaTransitionState},
|
||||
FinalizationFetcher: &blockchainTest.ChainService{},
|
||||
BeaconDB: beaconDB,
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true, Cfg: &builderTest.Config{BeaconDB: beaconDB}},
|
||||
ForkchoiceFetcher: &blockchainTest.ChainService{},
|
||||
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
}
|
||||
|
||||
t.Run("No builder configured. Use local block", func(t *testing.T) {
|
||||
@@ -95,7 +97,7 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
|
||||
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments))
|
||||
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
|
||||
e, err := blk.Block().Body().Execution()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(1), e.BlockNumber()) // Local block
|
||||
@@ -155,7 +157,7 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
|
||||
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments))
|
||||
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
|
||||
e, err := blk.Block().Body().Execution()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(1), e.BlockNumber()) // Local block because incorrect withdrawals
|
||||
@@ -218,11 +220,137 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
|
||||
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments))
|
||||
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
|
||||
e, err := blk.Block().Body().Execution()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(2), e.BlockNumber()) // Builder block
|
||||
})
|
||||
t.Run("Max builder boost factor should return builder", func(t *testing.T) {
|
||||
blk, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockCapella())
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, vs.BeaconDB.SaveRegistrationsByValidatorIDs(ctx, []primitives.ValidatorIndex{blk.Block().ProposerIndex()},
|
||||
[]*ethpb.ValidatorRegistrationV1{{FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), Timestamp: uint64(time.Now().Unix()), Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}}))
|
||||
ti, err := slots.ToTime(uint64(time.Now().Unix()), 0)
|
||||
require.NoError(t, err)
|
||||
sk, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
wr, err := ssz.WithdrawalSliceRoot(withdrawals, fieldparams.MaxWithdrawalsPerPayload)
|
||||
require.NoError(t, err)
|
||||
builderValue := bytesutil.ReverseByteOrder(big.NewInt(1e9).Bytes())
|
||||
bid := ðpb.BuilderBidCapella{
|
||||
Header: &v1.ExecutionPayloadHeaderCapella{
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
TransactionsRoot: bytesutil.PadTo([]byte{1}, fieldparams.RootLength),
|
||||
ParentHash: params.BeaconConfig().ZeroHash[:],
|
||||
Timestamp: uint64(ti.Unix()),
|
||||
BlockNumber: 2,
|
||||
WithdrawalsRoot: wr[:],
|
||||
},
|
||||
Pubkey: sk.PublicKey().Marshal(),
|
||||
Value: bytesutil.PadTo(builderValue, 32),
|
||||
}
|
||||
d := params.BeaconConfig().DomainApplicationBuilder
|
||||
domain, err := signing.ComputeDomain(d, nil, nil)
|
||||
require.NoError(t, err)
|
||||
sr, err := signing.ComputeSigningRoot(bid, domain)
|
||||
require.NoError(t, err)
|
||||
sBid := ðpb.SignedBuilderBidCapella{
|
||||
Message: bid,
|
||||
Signature: sk.Sign(sr[:]).Marshal(),
|
||||
}
|
||||
vs.BlockBuilder = &builderTest.MockBuilderService{
|
||||
BidCapella: sBid,
|
||||
HasConfigured: true,
|
||||
Cfg: &builderTest.Config{BeaconDB: beaconDB},
|
||||
}
|
||||
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
|
||||
require.NoError(t, err)
|
||||
chain := &blockchainTest.ChainService{ForkChoiceStore: doublylinkedtree.New(), Genesis: time.Now(), Block: wb}
|
||||
vs.ForkFetcher = chain
|
||||
vs.ForkchoiceFetcher.SetForkChoiceGenesisTime(uint64(time.Now().Unix()))
|
||||
vs.TimeFetcher = chain
|
||||
vs.HeadFetcher = chain
|
||||
|
||||
b := blk.Block()
|
||||
localPayload, _, err := vs.getLocalPayload(ctx, b, capellaTransitionState)
|
||||
require.NoError(t, err)
|
||||
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
|
||||
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, math.MaxUint64))
|
||||
e, err := blk.Block().Body().Execution()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(2), e.BlockNumber()) // builder block
|
||||
})
|
||||
t.Run("Builder builder has higher value but forced to local payload with builder boost factor", func(t *testing.T) {
|
||||
blk, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockCapella())
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, vs.BeaconDB.SaveRegistrationsByValidatorIDs(ctx, []primitives.ValidatorIndex{blk.Block().ProposerIndex()},
|
||||
[]*ethpb.ValidatorRegistrationV1{{FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), Timestamp: uint64(time.Now().Unix()), Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}}))
|
||||
ti, err := slots.ToTime(uint64(time.Now().Unix()), 0)
|
||||
require.NoError(t, err)
|
||||
sk, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
wr, err := ssz.WithdrawalSliceRoot(withdrawals, fieldparams.MaxWithdrawalsPerPayload)
|
||||
require.NoError(t, err)
|
||||
builderValue := bytesutil.ReverseByteOrder(big.NewInt(1e9).Bytes())
|
||||
bid := ðpb.BuilderBidCapella{
|
||||
Header: &v1.ExecutionPayloadHeaderCapella{
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
TransactionsRoot: bytesutil.PadTo([]byte{1}, fieldparams.RootLength),
|
||||
ParentHash: params.BeaconConfig().ZeroHash[:],
|
||||
Timestamp: uint64(ti.Unix()),
|
||||
BlockNumber: 2,
|
||||
WithdrawalsRoot: wr[:],
|
||||
},
|
||||
Pubkey: sk.PublicKey().Marshal(),
|
||||
Value: bytesutil.PadTo(builderValue, 32),
|
||||
}
|
||||
d := params.BeaconConfig().DomainApplicationBuilder
|
||||
domain, err := signing.ComputeDomain(d, nil, nil)
|
||||
require.NoError(t, err)
|
||||
sr, err := signing.ComputeSigningRoot(bid, domain)
|
||||
require.NoError(t, err)
|
||||
sBid := ðpb.SignedBuilderBidCapella{
|
||||
Message: bid,
|
||||
Signature: sk.Sign(sr[:]).Marshal(),
|
||||
}
|
||||
vs.BlockBuilder = &builderTest.MockBuilderService{
|
||||
BidCapella: sBid,
|
||||
HasConfigured: true,
|
||||
Cfg: &builderTest.Config{BeaconDB: beaconDB},
|
||||
}
|
||||
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
|
||||
require.NoError(t, err)
|
||||
chain := &blockchainTest.ChainService{ForkChoiceStore: doublylinkedtree.New(), Genesis: time.Now(), Block: wb}
|
||||
vs.ForkFetcher = chain
|
||||
vs.ForkchoiceFetcher.SetForkChoiceGenesisTime(uint64(time.Now().Unix()))
|
||||
vs.TimeFetcher = chain
|
||||
vs.HeadFetcher = chain
|
||||
|
||||
b := blk.Block()
|
||||
localPayload, _, err := vs.getLocalPayload(ctx, b, capellaTransitionState)
|
||||
require.NoError(t, err)
|
||||
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
|
||||
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, 0))
|
||||
e, err := blk.Block().Body().Execution()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(1), e.BlockNumber()) // local block
|
||||
})
|
||||
t.Run("Builder configured. Local block has higher value", func(t *testing.T) {
|
||||
blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
|
||||
require.NoError(t, err)
|
||||
@@ -233,14 +361,14 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
|
||||
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments))
|
||||
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
|
||||
e, err := blk.Block().Body().Execution()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(3), e.BlockNumber()) // Local block
|
||||
|
||||
require.LogsContain(t, hook, "builderGweiValue=1 localBoostPercentage=0 localGweiValue=2")
|
||||
})
|
||||
t.Run("Builder configured. Local block and boost has higher value", func(t *testing.T) {
|
||||
t.Run("Builder configured. Local block and local boost has higher value", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.LocalBlockValueBoost = 1 // Boost 1%.
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
@@ -254,7 +382,7 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
|
||||
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments))
|
||||
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
|
||||
e, err := blk.Block().Body().Execution()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(3), e.BlockNumber()) // Local block
|
||||
@@ -276,7 +404,7 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
|
||||
require.ErrorIs(t, consensus_types.ErrNilObjectWrapped, err) // Builder returns fault. Use local block
|
||||
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
|
||||
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments))
|
||||
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
|
||||
e, err := blk.Block().Body().Execution()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(4), e.BlockNumber()) // Local block
|
||||
@@ -384,7 +512,7 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
|
||||
localPayload, _, err := vs.getLocalPayload(ctx, blk.Block(), denebTransitionState)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments))
|
||||
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
|
||||
|
||||
got, err := blk.Block().Body().BlobKzgCommitments()
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -57,7 +57,7 @@ func (c *blobsBundleCache) prune(minSlot primitives.Slot) {
|
||||
}
|
||||
|
||||
// buildBlobSidecars given a block, builds the blob sidecars for the block.
|
||||
func buildBlobSidecars(blk interfaces.SignedBeaconBlock, blobs [][]byte, kzgproofs [][]byte) ([]*ethpb.BlobSidecar, error) {
|
||||
func buildBlobSidecars(blk interfaces.SignedBeaconBlock, blobs [][]byte, kzgProofs [][]byte) ([]*ethpb.BlobSidecar, error) {
|
||||
if blk.Version() < version.Deneb {
|
||||
return nil, nil // No blobs before deneb.
|
||||
}
|
||||
@@ -66,7 +66,7 @@ func buildBlobSidecars(blk interfaces.SignedBeaconBlock, blobs [][]byte, kzgproo
|
||||
return nil, err
|
||||
}
|
||||
cLen := len(denebBlk.Block.Body.BlobKzgCommitments)
|
||||
if cLen != len(blobs) || cLen != len(kzgproofs) {
|
||||
if cLen != len(blobs) || cLen != len(kzgProofs) {
|
||||
return nil, errors.New("blob KZG commitments don't match number of blobs or KZG proofs")
|
||||
}
|
||||
blobSidecars := make([]*ethpb.BlobSidecar, cLen)
|
||||
@@ -84,7 +84,7 @@ func buildBlobSidecars(blk interfaces.SignedBeaconBlock, blobs [][]byte, kzgproo
|
||||
Index: uint64(i),
|
||||
Blob: blobs[i],
|
||||
KzgCommitment: denebBlk.Block.Body.BlobKzgCommitments[i],
|
||||
KzgProof: kzgproofs[i],
|
||||
KzgProof: kzgProofs[i],
|
||||
SignedBlockHeader: header,
|
||||
CommitmentInclusionProof: proof,
|
||||
}
|
||||
|
||||
@@ -5,14 +5,12 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/time"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db/kv"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
@@ -53,44 +51,36 @@ func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBe
|
||||
slot := blk.Slot()
|
||||
vIdx := blk.ProposerIndex()
|
||||
headRoot := blk.ParentRoot()
|
||||
proposerID, payloadId, ok := vs.ProposerSlotIndexCache.GetProposerPayloadIDs(slot, headRoot)
|
||||
feeRecipient := params.BeaconConfig().DefaultFeeRecipient
|
||||
recipient, err := vs.BeaconDB.FeeRecipientByValidatorID(ctx, vIdx)
|
||||
switch err == nil {
|
||||
case true:
|
||||
feeRecipient = recipient
|
||||
case errors.As(err, kv.ErrNotFoundFeeRecipient):
|
||||
// If fee recipient is not found in DB and not set from beacon node CLI,
|
||||
// use the burn address.
|
||||
if feeRecipient.String() == params.BeaconConfig().EthBurnAddressHex {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"validatorIndex": vIdx,
|
||||
"burnAddress": params.BeaconConfig().EthBurnAddressHex,
|
||||
}).Warn("Fee recipient is currently using the burn address, " +
|
||||
"you will not be rewarded transaction fees on this setting. " +
|
||||
"Please set a different eth address as the fee recipient. " +
|
||||
"Please refer to our documentation for instructions")
|
||||
}
|
||||
default:
|
||||
return nil, false, errors.Wrap(err, "could not get fee recipient in db")
|
||||
logFields := logrus.Fields{
|
||||
"validatorIndex": vIdx,
|
||||
"slot": slot,
|
||||
"headRoot": fmt.Sprintf("%#x", headRoot),
|
||||
}
|
||||
payloadId, ok := vs.PayloadIDCache.PayloadID(slot, headRoot)
|
||||
|
||||
val, tracked := vs.TrackedValidatorsCache.Validator(vIdx)
|
||||
if !tracked {
|
||||
logrus.WithFields(logFields).Warn("could not find tracked proposer index")
|
||||
}
|
||||
|
||||
if ok && proposerID == vIdx && payloadId != [8]byte{} { // Payload ID is cache hit. Return the cached payload ID.
|
||||
var pid [8]byte
|
||||
var err error
|
||||
if ok && payloadId != [8]byte{} {
|
||||
// Payload ID is cache hit. Return the cached payload ID.
|
||||
var pid primitives.PayloadID
|
||||
copy(pid[:], payloadId[:])
|
||||
payloadIDCacheHit.Inc()
|
||||
payload, bundle, overrideBuilder, err := vs.ExecutionEngineCaller.GetPayload(ctx, pid, slot)
|
||||
switch {
|
||||
case err == nil:
|
||||
bundleCache.add(slot, bundle)
|
||||
warnIfFeeRecipientDiffers(payload, feeRecipient)
|
||||
warnIfFeeRecipientDiffers(payload, val.FeeRecipient)
|
||||
return payload, overrideBuilder, nil
|
||||
case errors.Is(err, context.DeadlineExceeded):
|
||||
default:
|
||||
return nil, false, errors.Wrap(err, "could not get cached payload from execution client")
|
||||
}
|
||||
}
|
||||
|
||||
log.WithFields(logFields).Debug("payload ID cache miss")
|
||||
parentHash, err := vs.getParentBlockHash(ctx, st, slot)
|
||||
switch {
|
||||
case errors.Is(err, errActivationNotReached) || errors.Is(err, errNoTerminalBlockHash):
|
||||
@@ -112,7 +102,7 @@ func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBe
|
||||
finalizedBlockHash := [32]byte{}
|
||||
justifiedBlockHash := [32]byte{}
|
||||
// Blocks before Bellatrix don't have execution payloads. Use zeros as the hash.
|
||||
if st.Version() >= version.Altair {
|
||||
if st.Version() >= version.Bellatrix {
|
||||
finalizedBlockHash = vs.FinalizationFetcher.FinalizedBlockHash()
|
||||
justifiedBlockHash = vs.FinalizationFetcher.UnrealizedJustifiedPayloadBlockHash()
|
||||
}
|
||||
@@ -137,7 +127,7 @@ func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBe
|
||||
attr, err = payloadattribute.New(&enginev1.PayloadAttributesV3{
|
||||
Timestamp: uint64(t.Unix()),
|
||||
PrevRandao: random,
|
||||
SuggestedFeeRecipient: feeRecipient.Bytes(),
|
||||
SuggestedFeeRecipient: val.FeeRecipient[:],
|
||||
Withdrawals: withdrawals,
|
||||
ParentBeaconBlockRoot: headRoot[:],
|
||||
})
|
||||
@@ -152,7 +142,7 @@ func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBe
|
||||
attr, err = payloadattribute.New(&enginev1.PayloadAttributesV2{
|
||||
Timestamp: uint64(t.Unix()),
|
||||
PrevRandao: random,
|
||||
SuggestedFeeRecipient: feeRecipient.Bytes(),
|
||||
SuggestedFeeRecipient: val.FeeRecipient[:],
|
||||
Withdrawals: withdrawals,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -162,7 +152,7 @@ func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBe
|
||||
attr, err = payloadattribute.New(&enginev1.PayloadAttributes{
|
||||
Timestamp: uint64(t.Unix()),
|
||||
PrevRandao: random,
|
||||
SuggestedFeeRecipient: feeRecipient.Bytes(),
|
||||
SuggestedFeeRecipient: val.FeeRecipient[:],
|
||||
})
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
@@ -182,13 +172,17 @@ func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBe
|
||||
return nil, false, err
|
||||
}
|
||||
bundleCache.add(slot, bundle)
|
||||
warnIfFeeRecipientDiffers(payload, feeRecipient)
|
||||
warnIfFeeRecipientDiffers(payload, val.FeeRecipient)
|
||||
localValueGwei, err := payload.ValueInGwei()
|
||||
if err == nil {
|
||||
log.WithField("value", localValueGwei).Debug("received execution payload from local engine")
|
||||
}
|
||||
return payload, overrideBuilder, nil
|
||||
}
|
||||
|
||||
// warnIfFeeRecipientDiffers logs a warning if the fee recipient in the included payload does not
|
||||
// match the requested one.
|
||||
func warnIfFeeRecipientDiffers(payload interfaces.ExecutionData, feeRecipient common.Address) {
|
||||
func warnIfFeeRecipientDiffers(payload interfaces.ExecutionData, feeRecipient primitives.ExecutionAddress) {
|
||||
// Warn if the fee recipient is not the value we expect.
|
||||
if payload != nil && !bytes.Equal(payload.FeeRecipient(), feeRecipient[:]) {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
@@ -299,7 +293,7 @@ func getParentBlockHashPostMerge(st state.BeaconState) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get post merge payload header")
|
||||
}
|
||||
return header.ParentHash(), nil
|
||||
return header.BlockHash(), nil
|
||||
}
|
||||
|
||||
// getParentBlockHashPreMerge retrieves the parent block hash before the merge has completed.
|
||||
|
||||
@@ -71,8 +71,6 @@ func TestServer_getExecutionPayload(t *testing.T) {
|
||||
Root: b2rCapella[:],
|
||||
}))
|
||||
|
||||
require.NoError(t, beaconDB.SaveFeeRecipientsByValidatorIDs(context.Background(), []primitives.ValidatorIndex{0}, []common.Address{{}}))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
st state.BeaconState
|
||||
@@ -86,9 +84,10 @@ func TestServer_getExecutionPayload(t *testing.T) {
|
||||
wantedOverride bool
|
||||
}{
|
||||
{
|
||||
name: "transition completed, nil payload id",
|
||||
st: transitionSt,
|
||||
errString: "nil payload with block hash",
|
||||
name: "transition completed, nil payload id",
|
||||
st: transitionSt,
|
||||
validatorIndx: 2,
|
||||
errString: "nil payload with block hash",
|
||||
},
|
||||
{
|
||||
name: "transition completed, happy case (has fee recipient in Db)",
|
||||
@@ -110,6 +109,7 @@ func TestServer_getExecutionPayload(t *testing.T) {
|
||||
{
|
||||
name: "transition completed, happy case, (payload ID cached)",
|
||||
st: transitionSt,
|
||||
payloadID: &pb.PayloadIDBytes{0x1},
|
||||
validatorIndx: 100,
|
||||
},
|
||||
{
|
||||
@@ -134,6 +134,7 @@ func TestServer_getExecutionPayload(t *testing.T) {
|
||||
st: transitionSt,
|
||||
validatorIndx: 100,
|
||||
override: true,
|
||||
payloadID: &pb.PayloadIDBytes{0x1},
|
||||
wantedOverride: true,
|
||||
},
|
||||
}
|
||||
@@ -149,9 +150,13 @@ func TestServer_getExecutionPayload(t *testing.T) {
|
||||
HeadFetcher: &chainMock.ChainService{State: tt.st},
|
||||
FinalizationFetcher: &chainMock.ChainService{},
|
||||
BeaconDB: beaconDB,
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
}
|
||||
vs.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, Index: tt.validatorIndx})
|
||||
if tt.payloadID != nil {
|
||||
vs.PayloadIDCache.Set(tt.st.Slot(), [32]byte{'a'}, [8]byte(*tt.payloadID))
|
||||
}
|
||||
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(tt.st.Slot(), 100, [8]byte{100}, [32]byte{'a'})
|
||||
blk := util.NewBeaconBlockBellatrix()
|
||||
blk.Block.Slot = tt.st.Slot()
|
||||
blk.Block.ProposerIndex = tt.validatorIndx
|
||||
@@ -192,9 +197,10 @@ func TestServer_getExecutionPayloadContextTimeout(t *testing.T) {
|
||||
ExecutionEngineCaller: &powtesting.EngineClient{PayloadIDBytes: &pb.PayloadIDBytes{}, ErrGetPayload: context.DeadlineExceeded, ExecutionPayload: &pb.ExecutionPayload{}},
|
||||
HeadFetcher: &chainMock.ChainService{State: nonTransitionSt},
|
||||
BeaconDB: beaconDB,
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
}
|
||||
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(nonTransitionSt.Slot(), 100, [8]byte{100}, [32]byte{'a'})
|
||||
vs.PayloadIDCache.Set(nonTransitionSt.Slot(), [32]byte{'a'}, [8]byte{100})
|
||||
|
||||
blk := util.NewBeaconBlockBellatrix()
|
||||
blk.Block.Slot = nonTransitionSt.Slot()
|
||||
@@ -231,10 +237,6 @@ func TestServer_getExecutionPayload_UnexpectedFeeRecipient(t *testing.T) {
|
||||
}))
|
||||
|
||||
feeRecipient := common.BytesToAddress([]byte("a"))
|
||||
require.NoError(t, beaconDB.SaveFeeRecipientsByValidatorIDs(context.Background(), []primitives.ValidatorIndex{0}, []common.Address{
|
||||
feeRecipient,
|
||||
}))
|
||||
|
||||
payloadID := &pb.PayloadIDBytes{0x1}
|
||||
payload := emptyPayload()
|
||||
payload.FeeRecipient = feeRecipient[:]
|
||||
@@ -246,8 +248,15 @@ func TestServer_getExecutionPayload_UnexpectedFeeRecipient(t *testing.T) {
|
||||
HeadFetcher: &chainMock.ChainService{State: transitionSt},
|
||||
FinalizationFetcher: &chainMock.ChainService{},
|
||||
BeaconDB: beaconDB,
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
}
|
||||
val := cache.TrackedValidator{
|
||||
Active: true,
|
||||
FeeRecipient: primitives.ExecutionAddress(feeRecipient),
|
||||
Index: 0,
|
||||
}
|
||||
vs.TrackedValidatorsCache.Set(val)
|
||||
|
||||
blk := util.NewBeaconBlockBellatrix()
|
||||
blk.Block.Slot = transitionSt.Slot()
|
||||
@@ -257,6 +266,7 @@ func TestServer_getExecutionPayload_UnexpectedFeeRecipient(t *testing.T) {
|
||||
gotPayload, _, err := vs.getLocalPayload(context.Background(), b.Block(), transitionSt)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, gotPayload)
|
||||
require.Equal(t, common.Address(gotPayload.FeeRecipient()), feeRecipient)
|
||||
|
||||
// We should NOT be getting the warning.
|
||||
require.LogsDoNotContain(t, hook, "Fee recipient address from execution client is not what was expected")
|
||||
@@ -264,7 +274,7 @@ func TestServer_getExecutionPayload_UnexpectedFeeRecipient(t *testing.T) {
|
||||
|
||||
evilRecipientAddress := common.BytesToAddress([]byte("evil"))
|
||||
payload.FeeRecipient = evilRecipientAddress[:]
|
||||
vs.ProposerSlotIndexCache = cache.NewProposerPayloadIDsCache()
|
||||
vs.PayloadIDCache = cache.NewPayloadIDCache()
|
||||
|
||||
gotPayload, _, err = vs.getLocalPayload(context.Background(), b.Block(), transitionSt)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -195,7 +195,6 @@ func TestServer_GetBeaconBlock_Altair(t *testing.T) {
|
||||
func TestServer_GetBeaconBlock_Bellatrix(t *testing.T) {
|
||||
db := dbutil.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
hook := logTest.NewGlobal()
|
||||
|
||||
terminalBlockHash := bytesutil.PadTo([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 32)
|
||||
@@ -306,7 +305,6 @@ func TestServer_GetBeaconBlock_Bellatrix(t *testing.T) {
|
||||
assert.DeepEqual(t, randaoReveal, bellatrixBlk.Bellatrix.Body.RandaoReveal, "Expected block to have correct randao reveal")
|
||||
assert.DeepEqual(t, req.Graffiti, bellatrixBlk.Bellatrix.Body.Graffiti, "Expected block to have correct Graffiti")
|
||||
|
||||
require.LogsContain(t, hook, "Fee recipient is currently using the burn address")
|
||||
require.DeepEqual(t, payload, bellatrixBlk.Bellatrix.Body.ExecutionPayload) // Payload should equal.
|
||||
|
||||
// Operator sets default fee recipient to not be burned through beacon node cli.
|
||||
@@ -599,7 +597,8 @@ func getProposerServer(db db.HeadAccessDatabase, headState state.BeaconState, he
|
||||
TimeFetcher: &testutil.MockGenesisTimeFetcher{
|
||||
Genesis: time.Now(),
|
||||
},
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
PayloadIDCache: cache.NewPayloadIDCache(),
|
||||
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
BeaconDB: db,
|
||||
BLSChangesPool: blstoexec.NewPool(),
|
||||
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true},
|
||||
@@ -629,9 +628,10 @@ func injectSlashings(t *testing.T, st state.BeaconState, keys []bls.SecretKey, s
|
||||
|
||||
func TestProposer_ProposeBlock_OK(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
block func([32]byte) *ethpb.GenericSignedBeaconBlock
|
||||
err string
|
||||
name string
|
||||
block func([32]byte) *ethpb.GenericSignedBeaconBlock
|
||||
err string
|
||||
useBuilder bool
|
||||
}{
|
||||
{
|
||||
name: "phase0",
|
||||
@@ -678,6 +678,24 @@ func TestProposer_ProposeBlock_OK(t *testing.T) {
|
||||
blk := ðpb.GenericSignedBeaconBlock_BlindedCapella{BlindedCapella: blockToPropose}
|
||||
return ðpb.GenericSignedBeaconBlock{Block: blk}
|
||||
},
|
||||
useBuilder: true,
|
||||
},
|
||||
{
|
||||
name: "blind capella no builder",
|
||||
block: func(parent [32]byte) *ethpb.GenericSignedBeaconBlock {
|
||||
blockToPropose := util.NewBlindedBeaconBlockCapella()
|
||||
blockToPropose.Block.Slot = 5
|
||||
blockToPropose.Block.ParentRoot = parent[:]
|
||||
txRoot, err := ssz.TransactionsRoot([][]byte{})
|
||||
require.NoError(t, err)
|
||||
withdrawalsRoot, err := ssz.WithdrawalSliceRoot([]*enginev1.Withdrawal{}, fieldparams.MaxWithdrawalsPerPayload)
|
||||
require.NoError(t, err)
|
||||
blockToPropose.Block.Body.ExecutionPayloadHeader.TransactionsRoot = txRoot[:]
|
||||
blockToPropose.Block.Body.ExecutionPayloadHeader.WithdrawalsRoot = withdrawalsRoot[:]
|
||||
blk := ðpb.GenericSignedBeaconBlock_BlindedCapella{BlindedCapella: blockToPropose}
|
||||
return ðpb.GenericSignedBeaconBlock{Block: blk}
|
||||
},
|
||||
err: "unconfigured block builder",
|
||||
},
|
||||
{
|
||||
name: "bellatrix",
|
||||
@@ -699,6 +717,69 @@ func TestProposer_ProposeBlock_OK(t *testing.T) {
|
||||
return ðpb.GenericSignedBeaconBlock{Block: blk}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deneb block some blobs",
|
||||
block: func(parent [32]byte) *ethpb.GenericSignedBeaconBlock {
|
||||
blockToPropose := util.NewBeaconBlockContentsDeneb()
|
||||
blockToPropose.Block.Block.Slot = 5
|
||||
blockToPropose.Block.Block.ParentRoot = parent[:]
|
||||
blockToPropose.Blobs = [][]byte{{0x01}, {0x02}, {0x03}}
|
||||
blockToPropose.KzgProofs = [][]byte{{0x01}, {0x02}, {0x03}}
|
||||
blockToPropose.Block.Block.Body.BlobKzgCommitments = [][]byte{bytesutil.PadTo([]byte("kc"), 48), bytesutil.PadTo([]byte("kc1"), 48), bytesutil.PadTo([]byte("kc2"), 48)}
|
||||
blk := ðpb.GenericSignedBeaconBlock_Deneb{Deneb: blockToPropose}
|
||||
return ðpb.GenericSignedBeaconBlock{Block: blk}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deneb block some blobs (kzg and blob count missmatch)",
|
||||
block: func(parent [32]byte) *ethpb.GenericSignedBeaconBlock {
|
||||
blockToPropose := util.NewBeaconBlockContentsDeneb()
|
||||
blockToPropose.Block.Block.Slot = 5
|
||||
blockToPropose.Block.Block.ParentRoot = parent[:]
|
||||
blockToPropose.Blobs = [][]byte{{0x01}, {0x02}, {0x03}}
|
||||
blockToPropose.KzgProofs = [][]byte{{0x01}, {0x02}, {0x03}}
|
||||
blk := ðpb.GenericSignedBeaconBlock_Deneb{Deneb: blockToPropose}
|
||||
return ðpb.GenericSignedBeaconBlock{Block: blk}
|
||||
},
|
||||
err: "blob KZG commitments don't match number of blobs or KZG proofs",
|
||||
},
|
||||
{
|
||||
name: "blind deneb block some blobs",
|
||||
block: func(parent [32]byte) *ethpb.GenericSignedBeaconBlock {
|
||||
blockToPropose := util.NewBlindedBeaconBlockDeneb()
|
||||
blockToPropose.Message.Slot = 5
|
||||
blockToPropose.Message.ParentRoot = parent[:]
|
||||
txRoot, err := ssz.TransactionsRoot([][]byte{})
|
||||
require.NoError(t, err)
|
||||
withdrawalsRoot, err := ssz.WithdrawalSliceRoot([]*enginev1.Withdrawal{}, fieldparams.MaxWithdrawalsPerPayload)
|
||||
require.NoError(t, err)
|
||||
blockToPropose.Message.Body.ExecutionPayloadHeader.TransactionsRoot = txRoot[:]
|
||||
blockToPropose.Message.Body.ExecutionPayloadHeader.WithdrawalsRoot = withdrawalsRoot[:]
|
||||
blockToPropose.Message.Body.BlobKzgCommitments = [][]byte{bytesutil.PadTo([]byte{0x01}, 48)}
|
||||
blk := ðpb.GenericSignedBeaconBlock_BlindedDeneb{BlindedDeneb: blockToPropose}
|
||||
return ðpb.GenericSignedBeaconBlock{Block: blk}
|
||||
},
|
||||
useBuilder: true,
|
||||
},
|
||||
{
|
||||
name: "blind deneb block some blobs (commitment value does not match blob)",
|
||||
block: func(parent [32]byte) *ethpb.GenericSignedBeaconBlock {
|
||||
blockToPropose := util.NewBlindedBeaconBlockDeneb()
|
||||
blockToPropose.Message.Slot = 5
|
||||
blockToPropose.Message.ParentRoot = parent[:]
|
||||
txRoot, err := ssz.TransactionsRoot([][]byte{})
|
||||
require.NoError(t, err)
|
||||
withdrawalsRoot, err := ssz.WithdrawalSliceRoot([]*enginev1.Withdrawal{}, fieldparams.MaxWithdrawalsPerPayload)
|
||||
require.NoError(t, err)
|
||||
blockToPropose.Message.Body.ExecutionPayloadHeader.TransactionsRoot = txRoot[:]
|
||||
blockToPropose.Message.Body.ExecutionPayloadHeader.WithdrawalsRoot = withdrawalsRoot[:]
|
||||
blockToPropose.Message.Body.BlobKzgCommitments = [][]byte{bytesutil.PadTo([]byte("kc"), 48)}
|
||||
blk := ðpb.GenericSignedBeaconBlock_BlindedDeneb{BlindedDeneb: blockToPropose}
|
||||
return ðpb.GenericSignedBeaconBlock{Block: blk}
|
||||
},
|
||||
useBuilder: true,
|
||||
err: "unblind sidecars failed: commitment value doesn't match block",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -716,8 +797,11 @@ func TestProposer_ProposeBlock_OK(t *testing.T) {
|
||||
BlockReceiver: c,
|
||||
BlockNotifier: c.BlockNotifier(),
|
||||
P2P: mockp2p.NewTestP2P(t),
|
||||
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true, PayloadCapella: emptyPayloadCapella(), PayloadDeneb: emptyPayloadDeneb(), BlobBundle: &enginev1.BlobsBundle{KzgCommitments: [][]byte{{0x01}}, Proofs: [][]byte{{0x02}}, Blobs: [][]byte{{0x03}}}},
|
||||
BeaconDB: db,
|
||||
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: tt.useBuilder, PayloadCapella: emptyPayloadCapella(), PayloadDeneb: emptyPayloadDeneb(),
|
||||
BlobBundle: &enginev1.BlobsBundle{KzgCommitments: [][]byte{bytesutil.PadTo([]byte{0x01}, 48)}, Proofs: [][]byte{{0x02}}, Blobs: [][]byte{{0x03}}}},
|
||||
BeaconDB: db,
|
||||
BlobReceiver: c,
|
||||
OperationNotifier: c.OperationNotifier(),
|
||||
}
|
||||
blockToPropose := tt.block(bsRoot)
|
||||
res, err := proposerServer.ProposeBeaconBlock(context.Background(), blockToPropose)
|
||||
@@ -2609,16 +2693,19 @@ func TestProposer_PrepareBeaconProposer(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db := dbutil.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
proposerServer := &Server{BeaconDB: db}
|
||||
proposerServer := &Server{
|
||||
BeaconDB: db,
|
||||
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
}
|
||||
_, err := proposerServer.PrepareBeaconProposer(ctx, tt.args.request)
|
||||
if tt.wantErr != "" {
|
||||
require.ErrorContains(t, tt.wantErr, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
address, err := proposerServer.BeaconDB.FeeRecipientByValidatorID(ctx, 1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, common.BytesToAddress(tt.args.request.Recipients[0].FeeRecipient), address)
|
||||
val, tracked := proposerServer.TrackedValidatorsCache.Validator(1)
|
||||
require.Equal(t, true, tracked)
|
||||
require.Equal(t, primitives.ExecutionAddress(tt.args.request.Recipients[0].FeeRecipient), val.FeeRecipient)
|
||||
|
||||
})
|
||||
}
|
||||
@@ -2628,7 +2715,10 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
db := dbutil.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
proposerServer := &Server{BeaconDB: db}
|
||||
proposerServer := &Server{
|
||||
BeaconDB: db,
|
||||
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
}
|
||||
|
||||
// New validator
|
||||
f := bytesutil.PadTo([]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF}, fieldparams.FeeRecipientLength)
|
||||
@@ -2645,7 +2735,7 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
|
||||
hook.Reset()
|
||||
_, err = proposerServer.PrepareBeaconProposer(ctx, req)
|
||||
require.NoError(t, err)
|
||||
require.LogsDoNotContain(t, hook, "Updated fee recipient addresses for validator indices")
|
||||
require.LogsContain(t, hook, "Updated fee recipient addresses for validator indices")
|
||||
|
||||
// Same validator with different fee recipient
|
||||
hook.Reset()
|
||||
@@ -2676,14 +2766,16 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
|
||||
hook.Reset()
|
||||
_, err = proposerServer.PrepareBeaconProposer(ctx, req)
|
||||
require.NoError(t, err)
|
||||
require.LogsDoNotContain(t, hook, "Updated fee recipient addresses for validator indices")
|
||||
require.LogsContain(t, hook, "Updated fee recipient addresses for validator indices")
|
||||
}
|
||||
|
||||
func BenchmarkServer_PrepareBeaconProposer(b *testing.B) {
|
||||
db := dbutil.SetupDB(b)
|
||||
ctx := context.Background()
|
||||
proposerServer := &Server{BeaconDB: db}
|
||||
|
||||
proposerServer := &Server{
|
||||
BeaconDB: db,
|
||||
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
}
|
||||
f := bytesutil.PadTo([]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF}, fieldparams.FeeRecipientLength)
|
||||
recipients := make([]*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer, 0)
|
||||
for i := 0; i < 10000; i++ {
|
||||
|
||||
@@ -44,7 +44,8 @@ import (
|
||||
// and more.
|
||||
type Server struct {
|
||||
Ctx context.Context
|
||||
ProposerSlotIndexCache *cache.ProposerPayloadIDsCache
|
||||
PayloadIDCache *cache.PayloadIDCache
|
||||
TrackedValidatorsCache *cache.TrackedValidatorsCache
|
||||
HeadFetcher blockchain.HeadFetcher
|
||||
ForkFetcher blockchain.ForkFetcher
|
||||
ForkchoiceFetcher blockchain.ForkchoiceFetcher
|
||||
|
||||
@@ -2,125 +2,18 @@ package validator
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/builder"
|
||||
consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types"
|
||||
consensusblocks "github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/runtime/version"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type unblinder struct {
|
||||
b interfaces.SignedBeaconBlock
|
||||
builder builder.BlockBuilder
|
||||
}
|
||||
|
||||
func newUnblinder(b interfaces.SignedBeaconBlock, builder builder.BlockBuilder) (*unblinder, error) {
|
||||
if err := consensusblocks.BeaconBlockIsNil(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if builder == nil {
|
||||
return nil, errors.New("nil builder provided")
|
||||
}
|
||||
return &unblinder{
|
||||
b: b,
|
||||
builder: builder,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (u *unblinder) unblindBuilderBlock(ctx context.Context) (interfaces.SignedBeaconBlock, []*ethpb.BlobSidecar, error) {
|
||||
if !u.b.IsBlinded() || u.b.Version() < version.Bellatrix {
|
||||
return u.b, nil, nil
|
||||
}
|
||||
if u.b.IsBlinded() && !u.builder.Configured() {
|
||||
return nil, nil, errors.New("builder not configured")
|
||||
}
|
||||
|
||||
psb, err := u.blindedProtoBlock()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get blinded proto block")
|
||||
}
|
||||
sb, err := consensusblocks.NewSignedBeaconBlock(psb)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not create signed block")
|
||||
}
|
||||
if err = copyBlockData(u.b, sb); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not copy block data")
|
||||
}
|
||||
h, err := u.b.Block().Body().Execution()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get execution")
|
||||
}
|
||||
if err = sb.SetExecution(h); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not set execution")
|
||||
}
|
||||
payload, blobsBundle, err := u.builder.SubmitBlindedBlock(ctx, sb)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not submit blinded block")
|
||||
}
|
||||
headerRoot, err := h.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get header root")
|
||||
}
|
||||
payloadRoot, err := payload.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get payload root")
|
||||
}
|
||||
if headerRoot != payloadRoot {
|
||||
return nil, nil, fmt.Errorf("header and payload root do not match, consider disconnect from relay to avoid further issues, "+
|
||||
"%#x != %#x", headerRoot, payloadRoot)
|
||||
}
|
||||
|
||||
bb, err := u.protoBlock()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get proto block")
|
||||
}
|
||||
wb, err := consensusblocks.NewSignedBeaconBlock(bb)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not create signed block")
|
||||
}
|
||||
if err = copyBlockData(sb, wb); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not copy block data")
|
||||
}
|
||||
if err = wb.SetExecution(payload); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not set execution")
|
||||
}
|
||||
|
||||
txs, err := payload.Transactions()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get transactions from payload")
|
||||
}
|
||||
|
||||
if wb.Version() >= version.Deneb && blobsBundle != nil {
|
||||
log.WithField("blobCount", len(blobsBundle.Blobs))
|
||||
}
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"blockHash": fmt.Sprintf("%#x", h.BlockHash()),
|
||||
"feeRecipient": fmt.Sprintf("%#x", h.FeeRecipient()),
|
||||
"gasUsed": h.GasUsed(),
|
||||
"slot": u.b.Block().Slot(),
|
||||
"txs": len(txs),
|
||||
}).Info("Retrieved full payload from builder")
|
||||
|
||||
sidecars, err := unblindBlobsSidecars(u.b, blobsBundle)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not unblind blobs sidecars")
|
||||
}
|
||||
|
||||
return wb, sidecars, nil
|
||||
}
|
||||
|
||||
func unblindBlobsSidecars(block interfaces.SignedBeaconBlock, bundle *enginev1.BlobsBundle) ([]*ethpb.BlobSidecar, error) {
|
||||
if bundle == nil {
|
||||
if block.Version() < version.Deneb || bundle == nil {
|
||||
return nil, nil
|
||||
}
|
||||
header, err := block.Header()
|
||||
@@ -168,98 +61,3 @@ func unblindBlobsSidecars(block interfaces.SignedBeaconBlock, bundle *enginev1.B
|
||||
}
|
||||
return sidecars, nil
|
||||
}
|
||||
|
||||
func copyBlockData(src interfaces.SignedBeaconBlock, dst interfaces.SignedBeaconBlock) error {
|
||||
agg, err := src.Block().Body().SyncAggregate()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get sync aggregate")
|
||||
}
|
||||
parentRoot := src.Block().ParentRoot()
|
||||
stateRoot := src.Block().StateRoot()
|
||||
randaoReveal := src.Block().Body().RandaoReveal()
|
||||
graffiti := src.Block().Body().Graffiti()
|
||||
sig := src.Signature()
|
||||
blsToExecChanges, err := src.Block().Body().BLSToExecutionChanges()
|
||||
if err != nil && !errors.Is(err, consensus_types.ErrUnsupportedField) {
|
||||
return errors.Wrap(err, "could not get bls to execution changes")
|
||||
}
|
||||
kzgCommitments, err := src.Block().Body().BlobKzgCommitments()
|
||||
if err != nil && !errors.Is(err, consensus_types.ErrUnsupportedField) {
|
||||
return errors.Wrap(err, "could not get blob kzg commitments")
|
||||
}
|
||||
|
||||
dst.SetSlot(src.Block().Slot())
|
||||
dst.SetProposerIndex(src.Block().ProposerIndex())
|
||||
dst.SetParentRoot(parentRoot[:])
|
||||
dst.SetStateRoot(stateRoot[:])
|
||||
dst.SetRandaoReveal(randaoReveal[:])
|
||||
dst.SetEth1Data(src.Block().Body().Eth1Data())
|
||||
dst.SetGraffiti(graffiti[:])
|
||||
dst.SetProposerSlashings(src.Block().Body().ProposerSlashings())
|
||||
dst.SetAttesterSlashings(src.Block().Body().AttesterSlashings())
|
||||
dst.SetAttestations(src.Block().Body().Attestations())
|
||||
dst.SetDeposits(src.Block().Body().Deposits())
|
||||
dst.SetVoluntaryExits(src.Block().Body().VoluntaryExits())
|
||||
if err = dst.SetSyncAggregate(agg); err != nil {
|
||||
return errors.Wrap(err, "could not set sync aggregate")
|
||||
}
|
||||
dst.SetSignature(sig[:])
|
||||
if err = dst.SetBLSToExecutionChanges(blsToExecChanges); err != nil && !errors.Is(err, consensus_types.ErrUnsupportedField) {
|
||||
return errors.Wrap(err, "could not set bls to execution changes")
|
||||
}
|
||||
if err = dst.SetBlobKzgCommitments(kzgCommitments); err != nil && !errors.Is(err, consensus_types.ErrUnsupportedField) {
|
||||
return errors.Wrap(err, "could not set bls to execution changes")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *unblinder) blindedProtoBlock() (proto.Message, error) {
|
||||
switch u.b.Version() {
|
||||
case version.Bellatrix:
|
||||
return ðpb.SignedBlindedBeaconBlockBellatrix{
|
||||
Block: ðpb.BlindedBeaconBlockBellatrix{
|
||||
Body: ðpb.BlindedBeaconBlockBodyBellatrix{},
|
||||
},
|
||||
}, nil
|
||||
case version.Capella:
|
||||
return ðpb.SignedBlindedBeaconBlockCapella{
|
||||
Block: ðpb.BlindedBeaconBlockCapella{
|
||||
Body: ðpb.BlindedBeaconBlockBodyCapella{},
|
||||
},
|
||||
}, nil
|
||||
case version.Deneb:
|
||||
return ðpb.SignedBlindedBeaconBlockDeneb{
|
||||
Message: ðpb.BlindedBeaconBlockDeneb{
|
||||
Body: ðpb.BlindedBeaconBlockBodyDeneb{},
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid version %s", version.String(u.b.Version()))
|
||||
}
|
||||
}
|
||||
|
||||
func (u *unblinder) protoBlock() (proto.Message, error) {
|
||||
switch u.b.Version() {
|
||||
case version.Bellatrix:
|
||||
return ðpb.SignedBeaconBlockBellatrix{
|
||||
Block: ðpb.BeaconBlockBellatrix{
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{},
|
||||
},
|
||||
}, nil
|
||||
case version.Capella:
|
||||
return ðpb.SignedBeaconBlockCapella{
|
||||
Block: ðpb.BeaconBlockCapella{
|
||||
Body: ðpb.BeaconBlockBodyCapella{},
|
||||
},
|
||||
}, nil
|
||||
case version.Deneb:
|
||||
return ðpb.SignedBeaconBlockDeneb{
|
||||
Block: ðpb.BeaconBlockDeneb{
|
||||
Body: ðpb.BeaconBlockBodyDeneb{},
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid version %s", version.String(u.b.Version()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,409 +0,0 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
builderTest "github.com/prysmaticlabs/prysm/v4/beacon-chain/builder/testing"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/ssz"
|
||||
v1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
|
||||
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/util"
|
||||
)
|
||||
|
||||
func Test_unblindBuilderBlock(t *testing.T) {
|
||||
p := emptyPayload()
|
||||
p.GasLimit = 123
|
||||
pCapella := emptyPayloadCapella()
|
||||
pCapella.GasLimit = 123
|
||||
pDeneb := emptyPayloadDeneb()
|
||||
pDeneb.GasLimit = 123
|
||||
pDeneb.ExcessBlobGas = 456
|
||||
pDeneb.BlobGasUsed = 789
|
||||
|
||||
denebblk, denebsidecars := util.GenerateTestDenebBlockWithSidecar(t, [32]byte{}, 0, fieldparams.MaxBlobsPerBlock)
|
||||
denebCommitments, err := denebblk.Block().Body().BlobKzgCommitments()
|
||||
require.NoError(t, err)
|
||||
execution, err := denebblk.Block().Body().Execution()
|
||||
require.NoError(t, err)
|
||||
denebPayload, err := execution.PbDeneb()
|
||||
require.NoError(t, err)
|
||||
|
||||
blobs := make([][]byte, len(denebsidecars))
|
||||
for i, sidecar := range denebsidecars {
|
||||
blobs[i] = sidecar.BlobSidecar.Blob
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
blk interfaces.SignedBeaconBlock
|
||||
mock *builderTest.MockBuilderService
|
||||
err string
|
||||
returnedBlk interfaces.SignedBeaconBlock
|
||||
returnedBlobSidecars []blocks.ROBlob
|
||||
}{
|
||||
{
|
||||
name: "old block version",
|
||||
blk: func() interfaces.SignedBeaconBlock {
|
||||
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
returnedBlk: func() interfaces.SignedBeaconBlock {
|
||||
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
},
|
||||
{
|
||||
name: "blinded without configured builder",
|
||||
blk: func() interfaces.SignedBeaconBlock {
|
||||
wb, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockBellatrix())
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
mock: &builderTest.MockBuilderService{
|
||||
HasConfigured: false,
|
||||
},
|
||||
err: "builder not configured",
|
||||
},
|
||||
{
|
||||
name: "non-blinded without configured builder",
|
||||
blk: func() interfaces.SignedBeaconBlock {
|
||||
b := util.NewBeaconBlockBellatrix()
|
||||
b.Block.Slot = 1
|
||||
b.Block.ProposerIndex = 2
|
||||
b.Block.Body.ExecutionPayload = &v1.ExecutionPayload{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
Transactions: make([][]byte, 0),
|
||||
GasLimit: 123,
|
||||
}
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
mock: &builderTest.MockBuilderService{
|
||||
HasConfigured: false,
|
||||
Payload: p,
|
||||
},
|
||||
returnedBlk: func() interfaces.SignedBeaconBlock {
|
||||
b := util.NewBeaconBlockBellatrix()
|
||||
b.Block.Slot = 1
|
||||
b.Block.ProposerIndex = 2
|
||||
b.Block.Body.ExecutionPayload = p
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
},
|
||||
{
|
||||
name: "submit blind block error",
|
||||
blk: func() interfaces.SignedBeaconBlock {
|
||||
b := util.NewBlindedBeaconBlockBellatrix()
|
||||
b.Block.Slot = 1
|
||||
b.Block.ProposerIndex = 2
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
mock: &builderTest.MockBuilderService{
|
||||
Payload: &v1.ExecutionPayload{},
|
||||
HasConfigured: true,
|
||||
ErrSubmitBlindedBlock: errors.New("can't submit"),
|
||||
},
|
||||
err: "can't submit",
|
||||
},
|
||||
{
|
||||
name: "head and payload root mismatch",
|
||||
blk: func() interfaces.SignedBeaconBlock {
|
||||
b := util.NewBlindedBeaconBlockBellatrix()
|
||||
b.Block.Slot = 1
|
||||
b.Block.ProposerIndex = 2
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
mock: &builderTest.MockBuilderService{
|
||||
HasConfigured: true,
|
||||
Payload: p,
|
||||
},
|
||||
returnedBlk: func() interfaces.SignedBeaconBlock {
|
||||
b := util.NewBeaconBlockBellatrix()
|
||||
b.Block.Slot = 1
|
||||
b.Block.ProposerIndex = 2
|
||||
b.Block.Body.ExecutionPayload = p
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
err: "header and payload root do not match",
|
||||
},
|
||||
{
|
||||
name: "can get payload Bellatrix",
|
||||
blk: func() interfaces.SignedBeaconBlock {
|
||||
b := util.NewBlindedBeaconBlockBellatrix()
|
||||
b.Block.Slot = 1
|
||||
b.Block.ProposerIndex = 2
|
||||
txRoot, err := ssz.TransactionsRoot([][]byte{})
|
||||
require.NoError(t, err)
|
||||
b.Block.Body.ExecutionPayloadHeader = &v1.ExecutionPayloadHeader{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
TransactionsRoot: txRoot[:],
|
||||
GasLimit: 123,
|
||||
}
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
mock: &builderTest.MockBuilderService{
|
||||
HasConfigured: true,
|
||||
Payload: p,
|
||||
},
|
||||
returnedBlk: func() interfaces.SignedBeaconBlock {
|
||||
b := util.NewBeaconBlockBellatrix()
|
||||
b.Block.Slot = 1
|
||||
b.Block.ProposerIndex = 2
|
||||
b.Block.Body.ExecutionPayload = p
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
},
|
||||
{
|
||||
name: "can get payload Capella",
|
||||
blk: func() interfaces.SignedBeaconBlock {
|
||||
b := util.NewBlindedBeaconBlockCapella()
|
||||
b.Block.Slot = 1
|
||||
b.Block.ProposerIndex = 2
|
||||
b.Block.Body.BlsToExecutionChanges = []*eth.SignedBLSToExecutionChange{
|
||||
{
|
||||
Message: ð.BLSToExecutionChange{
|
||||
ValidatorIndex: 123,
|
||||
FromBlsPubkey: []byte{'a'},
|
||||
ToExecutionAddress: []byte{'a'},
|
||||
},
|
||||
Signature: []byte("sig123"),
|
||||
},
|
||||
{
|
||||
Message: ð.BLSToExecutionChange{
|
||||
ValidatorIndex: 456,
|
||||
FromBlsPubkey: []byte{'b'},
|
||||
ToExecutionAddress: []byte{'b'},
|
||||
},
|
||||
Signature: []byte("sig456"),
|
||||
},
|
||||
}
|
||||
txRoot, err := ssz.TransactionsRoot([][]byte{})
|
||||
require.NoError(t, err)
|
||||
withdrawalsRoot, err := ssz.WithdrawalSliceRoot([]*v1.Withdrawal{}, fieldparams.MaxWithdrawalsPerPayload)
|
||||
require.NoError(t, err)
|
||||
b.Block.Body.ExecutionPayloadHeader = &v1.ExecutionPayloadHeaderCapella{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
TransactionsRoot: txRoot[:],
|
||||
WithdrawalsRoot: withdrawalsRoot[:],
|
||||
GasLimit: 123,
|
||||
}
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
mock: &builderTest.MockBuilderService{
|
||||
HasConfigured: true,
|
||||
PayloadCapella: pCapella,
|
||||
},
|
||||
returnedBlk: func() interfaces.SignedBeaconBlock {
|
||||
b := util.NewBeaconBlockCapella()
|
||||
b.Block.Slot = 1
|
||||
b.Block.ProposerIndex = 2
|
||||
b.Block.Body.BlsToExecutionChanges = []*eth.SignedBLSToExecutionChange{
|
||||
{
|
||||
Message: ð.BLSToExecutionChange{
|
||||
ValidatorIndex: 123,
|
||||
FromBlsPubkey: []byte{'a'},
|
||||
ToExecutionAddress: []byte{'a'},
|
||||
},
|
||||
Signature: []byte("sig123"),
|
||||
},
|
||||
{
|
||||
Message: ð.BLSToExecutionChange{
|
||||
ValidatorIndex: 456,
|
||||
FromBlsPubkey: []byte{'b'},
|
||||
ToExecutionAddress: []byte{'b'},
|
||||
},
|
||||
Signature: []byte("sig456"),
|
||||
},
|
||||
}
|
||||
b.Block.Body.ExecutionPayload = pCapella
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
},
|
||||
{
|
||||
name: "can get payload and blobs Deneb",
|
||||
blk: func() interfaces.SignedBeaconBlock {
|
||||
blindedBlock, err := denebblk.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
b, err := blindedBlock.PbBlindedDenebBlock()
|
||||
require.NoError(t, err)
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
mock: &builderTest.MockBuilderService{
|
||||
HasConfigured: true,
|
||||
PayloadDeneb: denebPayload,
|
||||
BlobBundle: &v1.BlobsBundle{
|
||||
KzgCommitments: denebCommitments,
|
||||
Proofs: [][]byte{{'d', 0}, {'d', 1}, {'d', 2}, {'d', 3}, {'d', 4}, {'d', 5}},
|
||||
Blobs: blobs,
|
||||
},
|
||||
},
|
||||
returnedBlk: func() interfaces.SignedBeaconBlock {
|
||||
b, err := denebblk.PbDenebBlock()
|
||||
require.NoError(t, err)
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
returnedBlobSidecars: denebsidecars,
|
||||
},
|
||||
{
|
||||
name: "deneb mismatch commitments count",
|
||||
blk: func() interfaces.SignedBeaconBlock {
|
||||
blindedBlock, err := denebblk.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
b, err := blindedBlock.PbBlindedDenebBlock()
|
||||
require.NoError(t, err)
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
mock: &builderTest.MockBuilderService{
|
||||
HasConfigured: true,
|
||||
PayloadDeneb: denebPayload,
|
||||
BlobBundle: &v1.BlobsBundle{
|
||||
KzgCommitments: [][]byte{{'c', 0}, {'c', 1}, {'c', 2}, {'c', 3}, {'c', 4}},
|
||||
Proofs: [][]byte{{'d', 0}, {'d', 1}, {'d', 2}, {'d', 3}, {'d', 4}, {'d', 5}},
|
||||
Blobs: blobs,
|
||||
},
|
||||
},
|
||||
err: "mismatch commitments count",
|
||||
},
|
||||
{
|
||||
name: "deneb mismatch proofs count",
|
||||
blk: func() interfaces.SignedBeaconBlock {
|
||||
blindedBlock, err := denebblk.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
b, err := blindedBlock.PbBlindedDenebBlock()
|
||||
require.NoError(t, err)
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
mock: &builderTest.MockBuilderService{
|
||||
HasConfigured: true,
|
||||
PayloadDeneb: denebPayload,
|
||||
BlobBundle: &v1.BlobsBundle{
|
||||
KzgCommitments: [][]byte{{'c', 0}, {'c', 1}, {'c', 2}, {'c', 3}, {'c', 4}, {'c', 5}},
|
||||
Proofs: [][]byte{{'d', 0}, {'d', 1}, {'d', 2}, {'d', 3}, {'d', 4}},
|
||||
Blobs: blobs,
|
||||
},
|
||||
},
|
||||
err: "mismatch proofs count",
|
||||
},
|
||||
{
|
||||
name: "deneb different count commitments bundle vs block",
|
||||
blk: func() interfaces.SignedBeaconBlock {
|
||||
blindedBlock, err := denebblk.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
b, err := blindedBlock.PbBlindedDenebBlock()
|
||||
require.NoError(t, err)
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
mock: &builderTest.MockBuilderService{
|
||||
HasConfigured: true,
|
||||
PayloadDeneb: denebPayload,
|
||||
BlobBundle: &v1.BlobsBundle{
|
||||
KzgCommitments: [][]byte{{'c', 0}, {'c', 1}, {'c', 2}, {'c', 3}, {'c', 4}},
|
||||
Proofs: [][]byte{{'d', 0}, {'d', 1}, {'d', 2}, {'d', 3}, {'d', 4}},
|
||||
Blobs: blobs[:5],
|
||||
},
|
||||
},
|
||||
err: "commitment count doesn't match block",
|
||||
},
|
||||
{
|
||||
name: "deneb different value commitments bundle vs block",
|
||||
blk: func() interfaces.SignedBeaconBlock {
|
||||
blindedBlock, err := denebblk.ToBlinded()
|
||||
require.NoError(t, err)
|
||||
b, err := blindedBlock.PbBlindedDenebBlock()
|
||||
require.NoError(t, err)
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
}(),
|
||||
mock: &builderTest.MockBuilderService{
|
||||
HasConfigured: true,
|
||||
PayloadDeneb: denebPayload,
|
||||
BlobBundle: &v1.BlobsBundle{
|
||||
KzgCommitments: [][]byte{{'c', 0}, {'c', 1}, {'c', 2}, {'c', 3}, {'c', 4}, {'c', 5}},
|
||||
Proofs: [][]byte{{'d', 0}, {'d', 1}, {'d', 2}, {'d', 3}, {'d', 4}, {'d', 5}},
|
||||
Blobs: blobs,
|
||||
},
|
||||
},
|
||||
err: "commitment value doesn't match block",
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
unblinder, err := newUnblinder(tc.blk, tc.mock)
|
||||
require.NoError(t, err)
|
||||
gotBlk, gotBlobs, err := unblinder.unblindBuilderBlock(context.Background())
|
||||
if tc.err != "" {
|
||||
require.ErrorContains(t, tc.err, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, tc.returnedBlk, gotBlk)
|
||||
if tc.returnedBlobSidecars != nil {
|
||||
blobs := make([]blocks.ROBlob, len(gotBlobs))
|
||||
for i := range gotBlobs {
|
||||
blobs[i], err = blocks.NewROBlob(gotBlobs[i])
|
||||
require.NoError(t, err)
|
||||
// TODO: update this check when generate function is updated for inclusion proofs require.DeepEqual(t, tc.returnedBlobSidecars[i].CommitmentInclusionProof, blobs[i].CommitmentInclusionProof)
|
||||
require.DeepEqual(t, tc.returnedBlobSidecars[i].SignedBlockHeader, blobs[i].SignedBlockHeader)
|
||||
require.Equal(t, len(tc.returnedBlobSidecars[i].Blob), len(blobs[i].Blob))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -128,12 +128,13 @@ type Config struct {
|
||||
StateGen *stategen.State
|
||||
MaxMsgSize int
|
||||
ExecutionEngineCaller execution.EngineCaller
|
||||
ProposerIdsCache *cache.ProposerPayloadIDsCache
|
||||
OptimisticModeFetcher blockchain.OptimisticModeFetcher
|
||||
BlockBuilder builder.BlockBuilder
|
||||
Router *mux.Router
|
||||
ClockWaiter startup.ClockWaiter
|
||||
BlobStorage *filesystem.BlobStorage
|
||||
TrackedValidatorsCache *cache.TrackedValidatorsCache
|
||||
PayloadIDCache *cache.PayloadIDCache
|
||||
}
|
||||
|
||||
// NewService instantiates a new RPC service instance that will
|
||||
@@ -401,11 +402,12 @@ func (s *Service) Start() {
|
||||
ReplayerBuilder: ch,
|
||||
ExecutionEngineCaller: s.cfg.ExecutionEngineCaller,
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
ProposerSlotIndexCache: s.cfg.ProposerIdsCache,
|
||||
BlockBuilder: s.cfg.BlockBuilder,
|
||||
BLSChangesPool: s.cfg.BLSChangesPool,
|
||||
ClockWaiter: s.cfg.ClockWaiter,
|
||||
CoreService: coreService,
|
||||
TrackedValidatorsCache: s.cfg.TrackedValidatorsCache,
|
||||
PayloadIDCache: s.cfg.PayloadIDCache,
|
||||
}
|
||||
s.initializeValidatorServerRoutes(&validator.Server{
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
@@ -418,11 +420,12 @@ func (s *Service) Start() {
|
||||
V1Alpha1Server: validatorServer,
|
||||
Stater: stater,
|
||||
SyncCommitteePool: s.cfg.SyncCommitteeObjectPool,
|
||||
ProposerSlotIndexCache: s.cfg.ProposerIdsCache,
|
||||
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
BlockBuilder: s.cfg.BlockBuilder,
|
||||
OperationNotifier: s.cfg.OperationNotifier,
|
||||
TrackedValidatorsCache: s.cfg.TrackedValidatorsCache,
|
||||
PayloadIDCache: s.cfg.PayloadIDCache,
|
||||
CoreService: coreService,
|
||||
BlockRewardFetcher: rewardFetcher,
|
||||
})
|
||||
|
||||
@@ -164,7 +164,7 @@ func (b *BeaconState) ToProtoUnsafe() interface{} {
|
||||
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint,
|
||||
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint,
|
||||
FinalizedCheckpoint: b.finalizedCheckpoint,
|
||||
InactivityScores: b.inactivityScores,
|
||||
InactivityScores: b.inactivityScoresVal(),
|
||||
CurrentSyncCommittee: b.currentSyncCommittee,
|
||||
NextSyncCommittee: b.nextSyncCommittee,
|
||||
LatestExecutionPayloadHeader: b.latestExecutionPayloadHeaderDeneb,
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
statenative "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
@@ -733,3 +734,41 @@ func TestBeaconState_ValidatorMutation_Bellatrix(t *testing.T) {
|
||||
|
||||
assert.Equal(t, rt, rt2)
|
||||
}
|
||||
|
||||
func TestBeaconState_InitializeInactivityScoresCorrectly_Deneb(t *testing.T) {
|
||||
resetCfg := features.InitWithReset(&features.Flags{
|
||||
EnableExperimentalState: true,
|
||||
})
|
||||
defer resetCfg()
|
||||
st, _ := util.DeterministicGenesisStateDeneb(t, 200)
|
||||
_, err := st.HashTreeRoot(context.Background())
|
||||
require.NoError(t, err)
|
||||
ic, err := st.InactivityScores()
|
||||
require.NoError(t, err)
|
||||
ic[10] = 10000
|
||||
ic[100] = 1000
|
||||
|
||||
err = st.SetInactivityScores(ic)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = st.HashTreeRoot(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
ic[150] = 2390239
|
||||
err = st.SetInactivityScores(ic)
|
||||
require.NoError(t, err)
|
||||
rt, err := st.HashTreeRoot(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
copiedSt, ok := st.ToProtoUnsafe().(*ethpb.BeaconStateDeneb)
|
||||
if !ok {
|
||||
t.Error("not ok")
|
||||
}
|
||||
newSt, err := statenative.InitializeFromProtoUnsafeDeneb(copiedSt)
|
||||
require.NoError(t, err)
|
||||
|
||||
newRt, err := newSt.HashTreeRoot(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
require.DeepSSZEqual(t, rt, newRt)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
||||
)
|
||||
@@ -98,7 +99,7 @@ func TestExtractBlockDataType(t *testing.T) {
|
||||
chain: &mock.ChainService{ValidatorsRoot: [32]byte{}},
|
||||
},
|
||||
want: func() interfaces.ReadOnlySignedBeaconBlock {
|
||||
wsb, err := blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockBellatrix{Block: ðpb.BeaconBlockBellatrix{Body: ðpb.BeaconBlockBodyBellatrix{}}})
|
||||
wsb, err := blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockBellatrix{Block: ðpb.BeaconBlockBellatrix{Body: ðpb.BeaconBlockBodyBellatrix{ExecutionPayload: &enginev1.ExecutionPayload{}}}})
|
||||
require.NoError(t, err)
|
||||
return wsb
|
||||
}(),
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/operation"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v4/container/slice"
|
||||
@@ -74,6 +76,14 @@ func (s *Service) validateAttesterSlashing(ctx context.Context, pid peer.ID, msg
|
||||
}
|
||||
s.cfg.chain.ReceiveAttesterSlashing(ctx, slashing)
|
||||
|
||||
// notify events
|
||||
s.cfg.operationNotifier.OperationFeed().Send(&feed.Event{
|
||||
Type: operation.AttesterSlashingReceived,
|
||||
Data: &operation.AttesterSlashingReceivedData{
|
||||
AttesterSlashing: slashing,
|
||||
},
|
||||
})
|
||||
|
||||
msg.ValidatorData = slashing // Used in downstream subscriber
|
||||
return pubsub.ValidationAccept, nil
|
||||
}
|
||||
|
||||
@@ -84,10 +84,11 @@ func TestValidateAttesterSlashing_ValidSlashing(t *testing.T) {
|
||||
chain := &mock.ChainService{State: s, Genesis: time.Now()}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
p2p: p,
|
||||
chain: chain,
|
||||
clock: startup.NewClock(chain.Genesis, chain.ValidatorsRoot),
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
p2p: p,
|
||||
chain: chain,
|
||||
clock: startup.NewClock(chain.Genesis, chain.ValidatorsRoot),
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
operationNotifier: chain.OperationNotifier(),
|
||||
},
|
||||
seenAttesterSlashingCache: make(map[uint64]bool),
|
||||
subHandler: newSubTopicHandler(),
|
||||
@@ -129,10 +130,11 @@ func TestValidateAttesterSlashing_InvalidSlashing_WithdrawableEpoch(t *testing.T
|
||||
chain := &mock.ChainService{State: s, Genesis: time.Now()}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
p2p: p,
|
||||
chain: chain,
|
||||
clock: startup.NewClock(chain.Genesis, chain.ValidatorsRoot),
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
p2p: p,
|
||||
chain: chain,
|
||||
clock: startup.NewClock(chain.Genesis, chain.ValidatorsRoot),
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
operationNotifier: chain.OperationNotifier(),
|
||||
},
|
||||
seenAttesterSlashingCache: make(map[uint64]bool),
|
||||
subHandler: newSubTopicHandler(),
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/operation"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v4/monitoring/tracing"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
@@ -55,6 +57,14 @@ func (s *Service) validateProposerSlashing(ctx context.Context, pid peer.ID, msg
|
||||
return pubsub.ValidationReject, err
|
||||
}
|
||||
|
||||
// notify events
|
||||
s.cfg.operationNotifier.OperationFeed().Send(&feed.Event{
|
||||
Type: operation.ProposerSlashingReceived,
|
||||
Data: &operation.ProposerSlashingReceivedData{
|
||||
ProposerSlashing: slashing,
|
||||
},
|
||||
})
|
||||
|
||||
msg.ValidatorData = slashing // Used in downstream subscriber
|
||||
return pubsub.ValidationAccept, nil
|
||||
}
|
||||
|
||||
@@ -117,10 +117,11 @@ func TestValidateProposerSlashing_ValidSlashing(t *testing.T) {
|
||||
chain := &mock.ChainService{State: s, Genesis: time.Now()}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
p2p: p,
|
||||
chain: chain,
|
||||
clock: startup.NewClock(chain.Genesis, chain.ValidatorsRoot),
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
p2p: p,
|
||||
chain: chain,
|
||||
clock: startup.NewClock(chain.Genesis, chain.ValidatorsRoot),
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
operationNotifier: chain.OperationNotifier(),
|
||||
},
|
||||
seenProposerSlashingCache: lruwrpr.New(10),
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ var log = logrus.WithField("prefix", "db")
|
||||
var Commands = &cli.Command{
|
||||
Name: "db",
|
||||
Category: "db",
|
||||
Usage: "defines commands for interacting with the Ethereum Beacon Node database",
|
||||
Usage: "Defines commands for interacting with the Ethereum Beacon Node database",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "restore",
|
||||
|
||||
@@ -80,7 +80,7 @@ var (
|
||||
// MonitoringPortFlag defines the http port used to serve prometheus metrics.
|
||||
MonitoringPortFlag = &cli.IntFlag{
|
||||
Name: "monitoring-port",
|
||||
Usage: "Port used to listening and respond metrics for prometheus.",
|
||||
Usage: "Port used to listening and respond metrics for Prometheus.",
|
||||
Value: 8080,
|
||||
}
|
||||
// CertFlag defines a flag for the node's TLS certificate.
|
||||
|
||||
51
cmd/flags.go
51
cmd/flags.go
@@ -15,12 +15,12 @@ var (
|
||||
// MinimalConfigFlag declares to use the minimal config for running Ethereum consensus.
|
||||
MinimalConfigFlag = &cli.BoolFlag{
|
||||
Name: "minimal-config",
|
||||
Usage: "Use minimal config with parameters as defined in the spec.",
|
||||
Usage: "Uses minimal config with parameters as defined in the spec.",
|
||||
}
|
||||
// E2EConfigFlag declares to use a testing specific config for running Ethereum consensus in end-to-end testing.
|
||||
E2EConfigFlag = &cli.BoolFlag{
|
||||
Name: "e2e-config",
|
||||
Usage: "Use the E2E testing config, only for use within end-to-end testing.",
|
||||
Usage: "Enables the E2E testing config, only for use within end-to-end testing.",
|
||||
}
|
||||
// RPCMaxPageSizeFlag defines the maximum numbers per page returned in RPC responses from this
|
||||
// beacon node (default: 500).
|
||||
@@ -31,34 +31,35 @@ var (
|
||||
// VerbosityFlag defines the logrus configuration.
|
||||
VerbosityFlag = &cli.StringFlag{
|
||||
Name: "verbosity",
|
||||
Usage: "Logging verbosity (trace, debug, info=default, warn, error, fatal, panic)",
|
||||
Usage: "Logging verbosity. (trace, debug, info, warn, error, fatal, panic)",
|
||||
Value: "info",
|
||||
}
|
||||
// DataDirFlag defines a path on disk where Prysm databases are stored.
|
||||
DataDirFlag = &cli.StringFlag{
|
||||
Name: "datadir",
|
||||
Usage: "Data directory for the databases",
|
||||
Usage: "Data directory for the databases.",
|
||||
Value: DefaultDataDir(),
|
||||
}
|
||||
// EnableBackupWebhookFlag for users to trigger db backups via an HTTP webhook.
|
||||
EnableBackupWebhookFlag = &cli.BoolFlag{
|
||||
Name: "enable-db-backup-webhook",
|
||||
Usage: "Serve HTTP handler to initiate database backups. The handler is served on the monitoring port at path /db/backup.",
|
||||
Name: "enable-db-backup-webhook",
|
||||
Usage: `Serves HTTP handler to initiate database backups.
|
||||
The handler is served on the monitoring port at path /db/backup.`,
|
||||
}
|
||||
// BackupWebhookOutputDir to customize the output directory for db backups.
|
||||
BackupWebhookOutputDir = &cli.StringFlag{
|
||||
Name: "db-backup-output-dir",
|
||||
Usage: "Output directory for db backups",
|
||||
Usage: "Output directory for db backups.",
|
||||
}
|
||||
// EnableTracingFlag defines a flag to enable p2p message tracing.
|
||||
EnableTracingFlag = &cli.BoolFlag{
|
||||
Name: "enable-tracing",
|
||||
Usage: "Enable request tracing.",
|
||||
Usage: "Enables request tracing.",
|
||||
}
|
||||
// TracingProcessNameFlag defines a flag to specify a process name.
|
||||
TracingProcessNameFlag = &cli.StringFlag{
|
||||
Name: "tracing-process-name",
|
||||
Usage: "The name to apply to tracing tag \"process_name\"",
|
||||
Usage: "Name to apply to tracing tag `process_name`.",
|
||||
}
|
||||
// TracingEndpointFlag flag defines the http endpoint for serving traces to Jaeger.
|
||||
TracingEndpointFlag = &cli.StringFlag{
|
||||
@@ -70,7 +71,7 @@ var (
|
||||
// messages are sampled for tracing.
|
||||
TraceSampleFractionFlag = &cli.Float64Flag{
|
||||
Name: "trace-sample-fraction",
|
||||
Usage: "Indicate what fraction of p2p messages are sampled for tracing.",
|
||||
Usage: "Indicates what fraction of p2p messages are sampled for tracing.",
|
||||
Value: 0.20,
|
||||
}
|
||||
// MonitoringHostFlag defines the host used to serve prometheus metrics.
|
||||
@@ -82,13 +83,13 @@ var (
|
||||
// DisableMonitoringFlag defines a flag to disable the metrics collection.
|
||||
DisableMonitoringFlag = &cli.BoolFlag{
|
||||
Name: "disable-monitoring",
|
||||
Usage: "Disable monitoring service.",
|
||||
Usage: "Disables monitoring service.",
|
||||
}
|
||||
// NoDiscovery specifies whether we are running a local network and have no need for connecting
|
||||
// to the bootstrap nodes in the cloud
|
||||
NoDiscovery = &cli.BoolFlag{
|
||||
Name: "no-discovery",
|
||||
Usage: "Enable only local network p2p and do not connect to cloud bootstrap nodes.",
|
||||
Usage: "Enable only local network p2p and do not connect to cloud bootstrap nodes",
|
||||
}
|
||||
// StaticPeers specifies a set of peers to connect to explicitly.
|
||||
StaticPeers = &cli.StringSliceFlag{
|
||||
@@ -185,17 +186,17 @@ var (
|
||||
// ForceClearDB removes any previously stored data at the data directory.
|
||||
ForceClearDB = &cli.BoolFlag{
|
||||
Name: "force-clear-db",
|
||||
Usage: "Clear any previously stored data at the data directory",
|
||||
Usage: "Clears any previously stored data at the data directory.",
|
||||
}
|
||||
// ClearDB prompts user to see if they want to remove any previously stored data at the data directory.
|
||||
ClearDB = &cli.BoolFlag{
|
||||
Name: "clear-db",
|
||||
Usage: "Prompt for clearing any previously stored data at the data directory",
|
||||
Usage: "Prompt for clearing any previously stored data at the data directory.",
|
||||
}
|
||||
// LogFormat specifies the log output format.
|
||||
LogFormat = &cli.StringFlag{
|
||||
Name: "log-format",
|
||||
Usage: "Specify log formatting. Supports: text, json, fluentd, journald.",
|
||||
Usage: "Specifies log formatting. Supports: text, json, fluentd, journald.",
|
||||
Value: "text",
|
||||
}
|
||||
// MaxGoroutines specifies the maximum amount of goroutines tolerated, before a status check fails.
|
||||
@@ -207,7 +208,7 @@ var (
|
||||
// LogFileName specifies the log output file name.
|
||||
LogFileName = &cli.StringFlag{
|
||||
Name: "log-file",
|
||||
Usage: "Specify log file name, relative or absolute",
|
||||
Usage: "Specifies log file name, relative or absolute.",
|
||||
}
|
||||
// EnableUPnPFlag specifies if UPnP should be enabled or not. The default value is false.
|
||||
EnableUPnPFlag = &cli.BoolFlag{
|
||||
@@ -217,27 +218,27 @@ var (
|
||||
// ConfigFileFlag specifies the filepath to load flag values.
|
||||
ConfigFileFlag = &cli.StringFlag{
|
||||
Name: "config-file",
|
||||
Usage: "The filepath to a yaml file with flag values",
|
||||
Usage: "Filepath to a yaml file with flag values.",
|
||||
}
|
||||
// ChainConfigFileFlag specifies the filepath to load flag values.
|
||||
ChainConfigFileFlag = &cli.StringFlag{
|
||||
Name: "chain-config-file",
|
||||
Usage: "The path to a YAML file with chain config values",
|
||||
Usage: "Path to a YAML file with chain config values.",
|
||||
}
|
||||
// GrpcMaxCallRecvMsgSizeFlag defines the max call message size for GRPC
|
||||
GrpcMaxCallRecvMsgSizeFlag = &cli.IntFlag{
|
||||
Name: "grpc-max-msg-size",
|
||||
Usage: "Integer to define max receive message call size. If serving a public gRPC server, " +
|
||||
"set this to a more reasonable size to avoid resource exhaustion from large messages. " +
|
||||
"Validators with as many as 10000 keys can be run with a max message size of less than " +
|
||||
"50Mb. The default here is set to a very high value for local users. " +
|
||||
"(default: 2147483647 (2Gi)).",
|
||||
Usage: `Integer to define max receive message call size (in bytes).
|
||||
If serving a public gRPC server, set this to a more reasonable size to avoid
|
||||
resource exhaustion from large messages.
|
||||
Validators with as many as 10000 keys can be run with a max message size of less than
|
||||
50Mb. The default here is set to a very high value for local users.`,
|
||||
Value: math.MaxInt32,
|
||||
}
|
||||
// AcceptTosFlag specifies user acceptance of ToS for non-interactive environments.
|
||||
AcceptTosFlag = &cli.BoolFlag{
|
||||
Name: "accept-terms-of-use",
|
||||
Usage: "Accept Terms and Conditions (for non-interactive environments)",
|
||||
Usage: "Accepts Terms and Conditions (for non-interactive environments).",
|
||||
}
|
||||
// ValidatorMonitorIndicesFlag specifies a list of validator indices to
|
||||
// track for performance updates
|
||||
@@ -261,7 +262,7 @@ var (
|
||||
// ApiTimeoutFlag specifies the timeout value for API requests in seconds. A timeout of zero means no timeout.
|
||||
ApiTimeoutFlag = &cli.IntFlag{
|
||||
Name: "api-timeout",
|
||||
Usage: "Specifies the timeout value for API requests in seconds",
|
||||
Usage: "Specifies the timeout value for API requests in seconds.",
|
||||
Value: 120,
|
||||
}
|
||||
// JwtOutputFileFlag specifies the JWT file path that gets generated into when invoked by generate-jwt-secret.
|
||||
|
||||
@@ -17,7 +17,7 @@ var log = logrus.WithField("prefix", "accounts")
|
||||
var Commands = &cli.Command{
|
||||
Name: "accounts",
|
||||
Category: "accounts",
|
||||
Usage: "defines commands for interacting with Ethereum validator accounts",
|
||||
Usage: "Defines commands for interacting with Ethereum validator accounts.",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "delete",
|
||||
|
||||
@@ -14,7 +14,7 @@ var log = logrus.WithField("prefix", "db")
|
||||
var Commands = &cli.Command{
|
||||
Name: "db",
|
||||
Category: "db",
|
||||
Usage: "defines commands for interacting with the Prysm validator database",
|
||||
Usage: "Defines commands for interacting with the Prysm validator database.",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "restore",
|
||||
|
||||
@@ -24,26 +24,26 @@ var (
|
||||
// DisableAccountMetricsFlag disables the prometheus metrics for validator accounts, default false.
|
||||
DisableAccountMetricsFlag = &cli.BoolFlag{
|
||||
Name: "disable-account-metrics",
|
||||
Usage: "Disable prometheus metrics for validator accounts. Operators with high volumes " +
|
||||
"of validating keys may wish to disable granular prometheus metrics as it increases " +
|
||||
"the data cardinality.",
|
||||
Usage: `Disables prometheus metrics for validator accounts. Operators with high volumes
|
||||
of validating keys may wish to disable granular prometheus metrics as it increases
|
||||
the data cardinality.`,
|
||||
}
|
||||
// BeaconRPCProviderFlag defines a beacon node RPC endpoint.
|
||||
BeaconRPCProviderFlag = &cli.StringFlag{
|
||||
Name: "beacon-rpc-provider",
|
||||
Usage: "Beacon node RPC provider endpoint",
|
||||
Usage: "Beacon node RPC provider endpoint.",
|
||||
Value: "127.0.0.1:4000",
|
||||
}
|
||||
// BeaconRPCGatewayProviderFlag defines a beacon node JSON-RPC endpoint.
|
||||
BeaconRPCGatewayProviderFlag = &cli.StringFlag{
|
||||
Name: "beacon-rpc-gateway-provider",
|
||||
Usage: "Beacon node RPC gateway provider endpoint",
|
||||
Usage: "Beacon node RPC gateway provider endpoint.",
|
||||
Value: "127.0.0.1:3500",
|
||||
}
|
||||
// BeaconRESTApiProviderFlag defines a beacon node REST API endpoint.
|
||||
BeaconRESTApiProviderFlag = &cli.StringFlag{
|
||||
Name: "beacon-rest-api-provider",
|
||||
Usage: "Beacon node REST API provider endpoint",
|
||||
Usage: "Beacon node REST API provider endpoint.",
|
||||
Value: "http://127.0.0.1:3500",
|
||||
}
|
||||
// CertFlag defines a flag for the node's TLS certificate.
|
||||
@@ -54,25 +54,25 @@ var (
|
||||
// EnableRPCFlag enables controlling the validator client via gRPC (without web UI).
|
||||
EnableRPCFlag = &cli.BoolFlag{
|
||||
Name: "rpc",
|
||||
Usage: "Enables the RPC server for the validator client (without Web UI)",
|
||||
Usage: "Enables the RPC server for the validator client (without Web UI).",
|
||||
Value: false,
|
||||
}
|
||||
// RPCHost defines the host on which the RPC server should listen.
|
||||
RPCHost = &cli.StringFlag{
|
||||
Name: "rpc-host",
|
||||
Usage: "Host on which the RPC server should listen",
|
||||
Usage: "Host on which the RPC server should listen.",
|
||||
Value: "127.0.0.1",
|
||||
}
|
||||
// RPCPort defines a validator client RPC port to open.
|
||||
RPCPort = &cli.IntFlag{
|
||||
Name: "rpc-port",
|
||||
Usage: "RPC port exposed by a validator client",
|
||||
Usage: "RPC port exposed by a validator client.",
|
||||
Value: 7000,
|
||||
}
|
||||
// SlasherRPCProviderFlag defines a slasher node RPC endpoint.
|
||||
SlasherRPCProviderFlag = &cli.StringFlag{
|
||||
Name: "slasher-rpc-provider",
|
||||
Usage: "Slasher node RPC provider endpoint",
|
||||
Usage: "Slasher node RPC provider endpoint.",
|
||||
Value: "127.0.0.1:4002",
|
||||
}
|
||||
// SlasherCertFlag defines a flag for the slasher node's TLS certificate.
|
||||
@@ -83,85 +83,86 @@ var (
|
||||
// DisablePenaltyRewardLogFlag defines the ability to not log reward/penalty information during deployment
|
||||
DisablePenaltyRewardLogFlag = &cli.BoolFlag{
|
||||
Name: "disable-rewards-penalties-logging",
|
||||
Usage: "Disable reward/penalty logging during cluster deployment",
|
||||
Usage: "Disables reward/penalty logging during cluster deployment.",
|
||||
}
|
||||
// GraffitiFlag defines the graffiti value included in proposed blocks
|
||||
GraffitiFlag = &cli.StringFlag{
|
||||
Name: "graffiti",
|
||||
Usage: "String to include in proposed blocks",
|
||||
Usage: "String to include in proposed blocks.",
|
||||
}
|
||||
// GrpcRetriesFlag defines the number of times to retry a failed gRPC request.
|
||||
GrpcRetriesFlag = &cli.UintFlag{
|
||||
Name: "grpc-retries",
|
||||
Usage: "Number of attempts to retry gRPC requests",
|
||||
Usage: "Number of attempts to retry gRPC requests.",
|
||||
Value: 5,
|
||||
}
|
||||
// GrpcRetryDelayFlag defines the interval to retry a failed gRPC request.
|
||||
GrpcRetryDelayFlag = &cli.DurationFlag{
|
||||
Name: "grpc-retry-delay",
|
||||
Usage: "The amount of time between gRPC retry requests.",
|
||||
Usage: "Amount of time between gRPC retry requests.",
|
||||
Value: 1 * time.Second,
|
||||
}
|
||||
// GrpcHeadersFlag defines a list of headers to send with all gRPC requests.
|
||||
GrpcHeadersFlag = &cli.StringFlag{
|
||||
Name: "grpc-headers",
|
||||
Usage: "A comma separated list of key value pairs to pass as gRPC headers for all gRPC " +
|
||||
"calls. Example: --grpc-headers=key=value",
|
||||
Usage: `Comma separated list of key value pairs to pass as gRPC headers for all gRPC calls.
|
||||
Example: --grpc-headers=key=value`,
|
||||
}
|
||||
// GRPCGatewayHost specifies a gRPC gateway host for the validator client.
|
||||
GRPCGatewayHost = &cli.StringFlag{
|
||||
Name: "grpc-gateway-host",
|
||||
Usage: "The host on which the gateway server runs on",
|
||||
Usage: "Host on which the gateway server runs on.",
|
||||
Value: DefaultGatewayHost,
|
||||
}
|
||||
// GRPCGatewayPort enables a gRPC gateway to be exposed for the validator client.
|
||||
GRPCGatewayPort = &cli.IntFlag{
|
||||
Name: "grpc-gateway-port",
|
||||
Usage: "Enable gRPC gateway for JSON requests",
|
||||
Usage: "Enables gRPC gateway for JSON requests.",
|
||||
Value: 7500,
|
||||
}
|
||||
// GPRCGatewayCorsDomain serves preflight requests when serving gRPC JSON gateway.
|
||||
GPRCGatewayCorsDomain = &cli.StringFlag{
|
||||
Name: "grpc-gateway-corsdomain",
|
||||
Usage: "Comma separated list of domains from which to accept cross origin requests " +
|
||||
"(browser enforced). This flag has no effect if not used with --grpc-gateway-port.",
|
||||
Usage: `Comma separated list of domains from which to accept cross origin requests (browser enforced).
|
||||
This flag has no effect if not used with --grpc-gateway-port.
|
||||
`,
|
||||
Value: "http://localhost:7500,http://127.0.0.1:7500,http://0.0.0.0:7500,http://localhost:4242,http://127.0.0.1:4242,http://localhost:4200,http://0.0.0.0:4242,http://127.0.0.1:4200,http://0.0.0.0:4200,http://localhost:3000,http://0.0.0.0:3000,http://127.0.0.1:3000"}
|
||||
// MonitoringPortFlag defines the http port used to serve prometheus metrics.
|
||||
MonitoringPortFlag = &cli.IntFlag{
|
||||
Name: "monitoring-port",
|
||||
Usage: "Port used to listening and respond metrics for prometheus.",
|
||||
Usage: "Port used to listening and respond metrics for Prometheus.",
|
||||
Value: 8081,
|
||||
}
|
||||
// WalletDirFlag defines the path to a wallet directory for Prysm accounts.
|
||||
WalletDirFlag = &cli.StringFlag{
|
||||
Name: "wallet-dir",
|
||||
Usage: "Path to a wallet directory on-disk for Prysm validator accounts",
|
||||
Usage: "Path to a wallet directory on-disk for Prysm validator accounts.",
|
||||
Value: filepath.Join(DefaultValidatorDir(), WalletDefaultDirName),
|
||||
}
|
||||
// AccountPasswordFileFlag is path to a file containing a password for a validator account.
|
||||
AccountPasswordFileFlag = &cli.StringFlag{
|
||||
Name: "account-password-file",
|
||||
Usage: "Path to a plain-text, .txt file containing a password for a validator account",
|
||||
Usage: "Path to a plain-text, .txt file containing a password for a validator account.",
|
||||
}
|
||||
// WalletPasswordFileFlag is the path to a file containing your wallet password.
|
||||
WalletPasswordFileFlag = &cli.StringFlag{
|
||||
Name: "wallet-password-file",
|
||||
Usage: "Path to a plain-text, .txt file containing your wallet password",
|
||||
Usage: "Path to a plain-text, .txt file containing your wallet password.",
|
||||
}
|
||||
// Mnemonic25thWordFileFlag defines a path to a file containing a "25th" word mnemonic passphrase for advanced users.
|
||||
Mnemonic25thWordFileFlag = &cli.StringFlag{
|
||||
Name: "mnemonic-25th-word-file",
|
||||
Usage: "(Advanced) Path to a plain-text, .txt file containing a 25th word passphrase for your mnemonic for HD wallets",
|
||||
Usage: "(Advanced) Path to a plain-text, `.txt` file containing a 25th word passphrase for your mnemonic for HD wallets.",
|
||||
}
|
||||
// SkipMnemonic25thWordCheckFlag allows for skipping a check for mnemonic 25th word passphrases for HD wallets.
|
||||
SkipMnemonic25thWordCheckFlag = &cli.StringFlag{
|
||||
Name: "skip-mnemonic-25th-word-check",
|
||||
Usage: "Allows for skipping the check for a mnemonic 25th word passphrase for HD wallets",
|
||||
Usage: "Allows for skipping the check for a mnemonic 25th word passphrase for HD wallets.",
|
||||
}
|
||||
// ImportPrivateKeyFileFlag allows for directly importing a private key hex string as an account.
|
||||
ImportPrivateKeyFileFlag = &cli.StringFlag{
|
||||
Name: "import-private-key-file",
|
||||
Usage: "Path to a plain-text, .txt file containing a hex string representation of a private key to import",
|
||||
Usage: "Path to a plain-text, .txt file containing a hex string representation of a private key to import.",
|
||||
}
|
||||
// MnemonicFileFlag is used to enter a file to mnemonic phrase for new wallet creation, non-interactively.
|
||||
MnemonicFileFlag = &cli.StringFlag{
|
||||
@@ -171,113 +172,113 @@ var (
|
||||
// MnemonicLanguageFlag is used to specify the language of the mnemonic.
|
||||
MnemonicLanguageFlag = &cli.StringFlag{
|
||||
Name: "mnemonic-language",
|
||||
Usage: "Allows specifying mnemonic language. Supported languages are: english|chinese_traditional|chinese_simplified|czech|french|japanese|korean|italian|spanish",
|
||||
Usage: "Allows specifying mnemonic language. Supported languages are: english|chinese_traditional|chinese_simplified|czech|french|japanese|korean|italian|spanish.",
|
||||
}
|
||||
// ShowDepositDataFlag for accounts.
|
||||
ShowDepositDataFlag = &cli.BoolFlag{
|
||||
Name: "show-deposit-data",
|
||||
Usage: "Display raw eth1 tx deposit data for validator accounts",
|
||||
Usage: "Displays raw eth1 tx deposit data for validator accounts.",
|
||||
Value: false,
|
||||
}
|
||||
// ShowPrivateKeysFlag for accounts.
|
||||
ShowPrivateKeysFlag = &cli.BoolFlag{
|
||||
Name: "show-private-keys",
|
||||
Usage: "Display the private keys for validator accounts",
|
||||
Usage: "Displays the private keys for validator accounts.",
|
||||
Value: false,
|
||||
}
|
||||
// ListValidatorIndices for accounts.
|
||||
ListValidatorIndices = &cli.BoolFlag{
|
||||
Name: "list-validator-indices",
|
||||
Usage: "List validator indices",
|
||||
Usage: "Lists validator indices.",
|
||||
Value: false,
|
||||
}
|
||||
// NumAccountsFlag defines the amount of accounts to generate for derived wallets.
|
||||
NumAccountsFlag = &cli.IntFlag{
|
||||
Name: "num-accounts",
|
||||
Usage: "Number of accounts to generate for derived wallets",
|
||||
Usage: "Number of accounts to generate for derived wallets.",
|
||||
Value: 1,
|
||||
}
|
||||
// DeletePublicKeysFlag defines a comma-separated list of hex string public keys
|
||||
// for accounts which a user desires to delete from their wallet.
|
||||
DeletePublicKeysFlag = &cli.StringFlag{
|
||||
Name: "delete-public-keys",
|
||||
Usage: "Comma-separated list of public key hex strings to specify which validator accounts to delete",
|
||||
Usage: "Comma separated list of public key hex strings to specify which validator accounts to delete.",
|
||||
Value: "",
|
||||
}
|
||||
// BackupPublicKeysFlag defines a comma-separated list of hex string public keys
|
||||
// for accounts which a user desires to backup from their wallet.
|
||||
BackupPublicKeysFlag = &cli.StringFlag{
|
||||
Name: "backup-public-keys",
|
||||
Usage: "Comma-separated list of public key hex strings to specify which validator accounts to backup",
|
||||
Usage: "Comma separated list of public key hex strings to specify which validator accounts to backup.",
|
||||
Value: "",
|
||||
}
|
||||
// VoluntaryExitPublicKeysFlag defines a comma-separated list of hex string public keys
|
||||
// for accounts on which a user wants to perform a voluntary exit.
|
||||
VoluntaryExitPublicKeysFlag = &cli.StringFlag{
|
||||
Name: "public-keys",
|
||||
Usage: "Comma-separated list of public key hex strings to specify on which validator accounts to perform " +
|
||||
"a voluntary exit",
|
||||
Usage: "Comma separated list of public key hex strings to specify on which validator accounts to perform " +
|
||||
"a voluntary exit.",
|
||||
Value: "",
|
||||
}
|
||||
// ExitAllFlag allows stakers to select all validating keys for exit. This will still require the staker
|
||||
// to confirm a userprompt for this action given it is a dangerous one.
|
||||
ExitAllFlag = &cli.BoolFlag{
|
||||
Name: "exit-all",
|
||||
Usage: "Exit all validators. This will still require the staker to confirm a userprompt for the action",
|
||||
Usage: "Exits all validators. This will still require the staker to confirm a userprompt for the action.",
|
||||
}
|
||||
// ForceExitFlag to exit without displaying the confirmation prompt.
|
||||
ForceExitFlag = &cli.BoolFlag{
|
||||
Name: "force-exit",
|
||||
Usage: "Exit without displaying the confirmation prompt",
|
||||
Usage: "Exits without displaying the confirmation prompt.",
|
||||
}
|
||||
VoluntaryExitJSONOutputPath = &cli.StringFlag{
|
||||
Name: "exit-json-output-dir",
|
||||
Usage: "The output directory to write voluntary exits as individual unencrypted JSON " +
|
||||
Usage: "Output directory to write voluntary exits as individual unencrypted JSON " +
|
||||
"files. If this flag is provided, voluntary exits will be written to the provided " +
|
||||
"directory and will not be broadcasted.",
|
||||
}
|
||||
// BackupPasswordFile for encrypting accounts a user wishes to back up.
|
||||
BackupPasswordFile = &cli.StringFlag{
|
||||
Name: "backup-password-file",
|
||||
Usage: "Path to a plain-text, .txt file containing the desired password for your backed up accounts",
|
||||
Usage: "Path to a plain-text, .txt file containing the desired password for your backed up accounts.",
|
||||
Value: "",
|
||||
}
|
||||
// BackupDirFlag defines the path for the zip backup of the wallet will be created.
|
||||
BackupDirFlag = &cli.StringFlag{
|
||||
Name: "backup-dir",
|
||||
Usage: "Path to a directory where accounts will be backed up into a zip file",
|
||||
Usage: "Path to a directory where accounts will be backed up into a zip file.",
|
||||
Value: DefaultValidatorDir(),
|
||||
}
|
||||
// SlashingProtectionJSONFileFlag is used to enter the file path of the slashing protection JSON.
|
||||
SlashingProtectionJSONFileFlag = &cli.StringFlag{
|
||||
Name: "slashing-protection-json-file",
|
||||
Usage: "Path to an EIP-3076 compliant JSON file containing a user's slashing protection history",
|
||||
Usage: "Path to an EIP-3076 compliant JSON file containing a user's slashing protection history.",
|
||||
}
|
||||
// KeysDirFlag defines the path for a directory where keystores to be imported at stored.
|
||||
KeysDirFlag = &cli.StringFlag{
|
||||
Name: "keys-dir",
|
||||
Usage: "Path to a directory where keystores to be imported are stored",
|
||||
Usage: "Path to a directory where keystores to be imported are stored.",
|
||||
}
|
||||
|
||||
// RemoteSignerCertPathFlag defines the path to a client.crt file for a wallet to connect to
|
||||
// a secure signer via TLS and gRPC.
|
||||
RemoteSignerCertPathFlag = &cli.StringFlag{
|
||||
Name: "remote-signer-crt-path",
|
||||
Usage: "/path/to/client.crt for establishing a secure, TLS gRPC connection to a remote signer server",
|
||||
Usage: "/path/to/client.crt for establishing a secure, TLS gRPC connection to a remote signer server.",
|
||||
Value: "",
|
||||
}
|
||||
// RemoteSignerKeyPathFlag defines the path to a client.key file for a wallet to connect to
|
||||
// a secure signer via TLS and gRPC.
|
||||
RemoteSignerKeyPathFlag = &cli.StringFlag{
|
||||
Name: "remote-signer-key-path",
|
||||
Usage: "/path/to/client.key for establishing a secure, TLS gRPC connection to a remote signer server",
|
||||
Usage: "/path/to/client.key for establishing a secure, TLS gRPC connection to a remote signer server.",
|
||||
Value: "",
|
||||
}
|
||||
// RemoteSignerCACertPathFlag defines the path to a ca.crt file for a wallet to connect to
|
||||
// a secure signer via TLS and gRPC.
|
||||
RemoteSignerCACertPathFlag = &cli.StringFlag{
|
||||
Name: "remote-signer-ca-crt-path",
|
||||
Usage: "/path/to/ca.crt for establishing a secure, TLS gRPC connection to a remote signer server",
|
||||
Usage: "/path/to/ca.crt for establishing a secure, TLS gRPC connection to a remote signer server.",
|
||||
Value: "",
|
||||
}
|
||||
// Web3SignerURLFlag defines the URL for a web3signer to connect to.
|
||||
@@ -285,7 +286,7 @@ var (
|
||||
// web3signer documentation can be found in Consensys' web3signer project docs
|
||||
Web3SignerURLFlag = &cli.StringFlag{
|
||||
Name: "validators-external-signer-url",
|
||||
Usage: "URL for consensys' web3signer software to use with the Prysm validator client",
|
||||
Usage: "URL for consensys' web3signer software to use with the Prysm validator client.",
|
||||
Value: "",
|
||||
}
|
||||
|
||||
@@ -295,65 +296,70 @@ var (
|
||||
// web3signer documentation can be found in Consensys' web3signer project docs```
|
||||
Web3SignerPublicValidatorKeysFlag = &cli.StringSliceFlag{
|
||||
Name: "validators-external-signer-public-keys",
|
||||
Usage: "comma separated list of public keys OR an external url endpoint for the validator to retrieve public keys from for usage with web3signer",
|
||||
Usage: "Comma separated list of public keys OR an external url endpoint for the validator to retrieve public keys from for usage with web3signer.",
|
||||
}
|
||||
|
||||
// KeymanagerKindFlag defines the kind of keymanager desired by a user during wallet creation.
|
||||
KeymanagerKindFlag = &cli.StringFlag{
|
||||
Name: "keymanager-kind",
|
||||
Usage: "Kind of keymanager, either imported, derived, or remote, specified during wallet creation",
|
||||
Usage: "Kind of keymanager, either imported, derived, or remote, specified during wallet creation.",
|
||||
Value: "",
|
||||
}
|
||||
// SkipDepositConfirmationFlag skips the y/n confirmation userprompt for sending a deposit to the deposit contract.
|
||||
SkipDepositConfirmationFlag = &cli.BoolFlag{
|
||||
Name: "skip-deposit-confirmation",
|
||||
Usage: "Skips the y/n confirmation userprompt for sending a deposit to the deposit contract",
|
||||
Usage: "Skips the y/n confirmation userprompt for sending a deposit to the deposit contract.",
|
||||
Value: false,
|
||||
}
|
||||
// EnableWebFlag enables controlling the validator client via the Prysm web ui. This is a work in progress.
|
||||
EnableWebFlag = &cli.BoolFlag{
|
||||
Name: "web",
|
||||
Usage: "Enables the web portal for the validator client (work in progress)",
|
||||
Usage: "(Work in progress): Enables the web portal for the validator client.",
|
||||
Value: false,
|
||||
}
|
||||
// SlashingProtectionExportDirFlag allows specifying the outpt directory
|
||||
// for a validator's slashing protection history.
|
||||
SlashingProtectionExportDirFlag = &cli.StringFlag{
|
||||
Name: "slashing-protection-export-dir",
|
||||
Usage: "Allows users to specify the output directory to export their slashing protection EIP-3076 standard JSON File",
|
||||
Usage: "Allows users to specify the output directory to export their slashing protection EIP-3076 standard JSON File.",
|
||||
Value: "",
|
||||
}
|
||||
// GraffitiFileFlag specifies the file path to load graffiti values.
|
||||
GraffitiFileFlag = &cli.StringFlag{
|
||||
Name: "graffiti-file",
|
||||
Usage: "The path to a YAML file with graffiti values",
|
||||
Usage: "Path to a YAML file with graffiti values.",
|
||||
}
|
||||
// ProposerSettingsFlag defines the path or URL to a file with proposer config.
|
||||
ProposerSettingsFlag = &cli.StringFlag{
|
||||
Name: "proposer-settings-file",
|
||||
Usage: "Set path to a YAML or JSON file containing validator settings used when proposing blocks such as (fee recipient and gas limit) (i.e. --proposer-settings-file=/path/to/proposer.json). File format found in docs",
|
||||
Name: "proposer-settings-file",
|
||||
Usage: `Sets path to a YAML or JSON file containing validator settings used when proposing blocks such as
|
||||
fee recipient and gas limit. File format found in docs.`,
|
||||
Value: "",
|
||||
}
|
||||
// ProposerSettingsURLFlag defines the path or URL to a file with proposer config.
|
||||
ProposerSettingsURLFlag = &cli.StringFlag{
|
||||
Name: "proposer-settings-url",
|
||||
Usage: "Set URL to a REST endpoint containing validator settings used when proposing blocks such as (fee recipient) (i.e. --proposer-settings-url=https://example.com/api/getConfig). File format found in docs",
|
||||
Name: "proposer-settings-url",
|
||||
Usage: `Sets URL to a REST endpoint containing validator settings used when proposing blocks such as
|
||||
fee recipient and gas limit. File format found in docs`,
|
||||
Value: "",
|
||||
}
|
||||
|
||||
// SuggestedFeeRecipientFlag defines the address of the fee recipient.
|
||||
SuggestedFeeRecipientFlag = &cli.StringFlag{
|
||||
Name: "suggested-fee-recipient",
|
||||
Usage: "Sets ALL validators' mapping to a suggested eth address to receive gas fees when proposing a block." +
|
||||
" note that this is only a suggestion when integrating with a Builder API, which may choose to specify a different fee recipient as payment for the blocks it builds." +
|
||||
" For additional setting overrides use the --" + ProposerSettingsFlag.Name + " or --" + ProposerSettingsURLFlag.Name + " flags. ",
|
||||
Usage: `Sets ALL validators' mapping to a suggested eth address to receive gas fees when proposing a block.
|
||||
Note that this is only a suggestion when integrating with a Builder API, which may choose to specify
|
||||
a different fee recipient as payment for the blocks it builds.For additional setting overrides use the
|
||||
--` + ProposerSettingsFlag.Name + " or --" + ProposerSettingsURLFlag.Name + " flags.",
|
||||
Value: params.BeaconConfig().EthBurnAddressHex,
|
||||
}
|
||||
|
||||
// EnableBuilderFlag enables the periodic validator registration API calls that will update the custom builder with validator settings.
|
||||
EnableBuilderFlag = &cli.BoolFlag{
|
||||
Name: "enable-builder",
|
||||
Usage: "Enables Builder validator registration APIs for the validator client to update settings such as fee recipient and gas limit. Note* this flag is not required if using proposer settings config file",
|
||||
Name: "enable-builder",
|
||||
Usage: `Enables builder validator registration APIs for the validator client to update settings
|
||||
such as fee recipient and gas limit. This flag is not required if using proposer
|
||||
settings config file.`,
|
||||
Value: false,
|
||||
Aliases: []string{"enable-validator-registration"},
|
||||
}
|
||||
@@ -361,13 +367,13 @@ var (
|
||||
// BuilderGasLimitFlag defines the gas limit for the builder to use for constructing a payload.
|
||||
BuilderGasLimitFlag = &cli.StringFlag{
|
||||
Name: "suggested-gas-limit",
|
||||
Usage: "Sets gas limit for the builder to use for constructing a payload for all the validators",
|
||||
Usage: "Sets gas limit for the builder to use for constructing a payload for all the validators.",
|
||||
Value: fmt.Sprint(params.BeaconConfig().DefaultBuilderGasLimit),
|
||||
}
|
||||
|
||||
// ValidatorRegistrationBatchSizeFlag sets the maximum size for one batch of validator registrations. Use a non-positive value to disable batching.
|
||||
ValidatorRegistrationBatchSizeFlag = &cli.IntFlag{
|
||||
Name: "validator-registration-batch-size",
|
||||
// ValidatorsRegistrationBatchSizeFlag sets the maximum size for one batch of validator registrations. Use a non-positive value to disable batching.
|
||||
ValidatorsRegistrationBatchSizeFlag = &cli.IntFlag{
|
||||
Name: "validators-registration-batch-size",
|
||||
Usage: "Sets the maximum size for one batch of validator registrations. Use a non-positive value to disable batching.",
|
||||
Value: 0,
|
||||
}
|
||||
|
||||
@@ -8,14 +8,12 @@ import (
|
||||
var (
|
||||
InteropStartIndex = &cli.Uint64Flag{
|
||||
Name: "interop-start-index",
|
||||
Usage: "The start index to deterministically generate validator keys when used in combination with " +
|
||||
"--interop-num-validators. Example: --interop-start-index=5 --interop-num-validators=3 would generate " +
|
||||
"keys from index 5 to 7.",
|
||||
Usage: `Start index to deterministically generate validator keys when used in combination with --interop-num-validators.
|
||||
Example: --interop-start-index=5 --interop-num-validators=3 would generate keys from index 5 to 7.`,
|
||||
}
|
||||
InteropNumValidators = &cli.Uint64Flag{
|
||||
Name: "interop-num-validators",
|
||||
Usage: "The number of validators to deterministically generate. " +
|
||||
"Example: --interop-start-index=5 --interop-num-validators=3 would generate " +
|
||||
"keys from index 5 to 7.",
|
||||
Usage: `Number of validators to deterministically generate.
|
||||
Example: --interop-start-index=5 --interop-num-validators=3 would generate keys from index 5 to 7.`,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -79,7 +79,7 @@ var appFlags = []cli.Flag{
|
||||
flags.ProposerSettingsFlag,
|
||||
flags.EnableBuilderFlag,
|
||||
flags.BuilderGasLimitFlag,
|
||||
flags.ValidatorRegistrationBatchSizeFlag,
|
||||
flags.ValidatorsRegistrationBatchSizeFlag,
|
||||
////////////////////
|
||||
cmd.DisableMonitoringFlag,
|
||||
cmd.MonitoringHostFlag,
|
||||
@@ -117,81 +117,79 @@ func init() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := cli.App{}
|
||||
app.Name = "validator"
|
||||
app.Usage = `launches an Ethereum validator client that interacts with a beacon chain, starts proposer and attester services, p2p connections, and more`
|
||||
app.Version = version.Version()
|
||||
app.Action = func(ctx *cli.Context) error {
|
||||
if err := startNode(ctx); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
app.Commands = []*cli.Command{
|
||||
walletcommands.Commands,
|
||||
accountcommands.Commands,
|
||||
slashingprotectioncommands.Commands,
|
||||
dbcommands.Commands,
|
||||
web.Commands,
|
||||
}
|
||||
|
||||
app.Flags = appFlags
|
||||
|
||||
app.Before = func(ctx *cli.Context) error {
|
||||
// Load flags from config file, if specified.
|
||||
if err := cmd.LoadFlagsFromConfig(ctx, app.Flags); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
format := ctx.String(cmd.LogFormat.Name)
|
||||
switch format {
|
||||
case "text":
|
||||
formatter := new(prefixed.TextFormatter)
|
||||
formatter.TimestampFormat = "2006-01-02 15:04:05"
|
||||
formatter.FullTimestamp = true
|
||||
// If persistent log files are written - we disable the log messages coloring because
|
||||
// the colors are ANSI codes and seen as Gibberish in the log files.
|
||||
formatter.DisableColors = ctx.String(cmd.LogFileName.Name) != ""
|
||||
logrus.SetFormatter(formatter)
|
||||
case "fluentd":
|
||||
f := joonix.NewFormatter()
|
||||
if err := joonix.DisableTimestampFormat(f); err != nil {
|
||||
panic(err)
|
||||
app := cli.App{
|
||||
Name: "validator",
|
||||
Usage: "Launches an Ethereum validator client that interacts with a beacon chain, starts proposer and attester services, p2p connections, and more.",
|
||||
Version: version.Version(),
|
||||
Action: func(ctx *cli.Context) error {
|
||||
if err := startNode(ctx); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
logrus.SetFormatter(f)
|
||||
case "json":
|
||||
logrus.SetFormatter(&logrus.JSONFormatter{})
|
||||
case "journald":
|
||||
if err := journald.Enable(); err != nil {
|
||||
return nil
|
||||
},
|
||||
Commands: []*cli.Command{
|
||||
walletcommands.Commands,
|
||||
accountcommands.Commands,
|
||||
slashingprotectioncommands.Commands,
|
||||
dbcommands.Commands,
|
||||
web.Commands,
|
||||
},
|
||||
Flags: appFlags,
|
||||
Before: func(ctx *cli.Context) error {
|
||||
// Load flags from config file, if specified.
|
||||
if err := cmd.LoadFlagsFromConfig(ctx, appFlags); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown log format %s", format)
|
||||
}
|
||||
|
||||
logFileName := ctx.String(cmd.LogFileName.Name)
|
||||
if logFileName != "" {
|
||||
if err := logs.ConfigurePersistentLogging(logFileName); err != nil {
|
||||
log.WithError(err).Error("Failed to configuring logging to disk.")
|
||||
format := ctx.String(cmd.LogFormat.Name)
|
||||
switch format {
|
||||
case "text":
|
||||
formatter := new(prefixed.TextFormatter)
|
||||
formatter.TimestampFormat = "2006-01-02 15:04:05"
|
||||
formatter.FullTimestamp = true
|
||||
// If persistent log files are written - we disable the log messages coloring because
|
||||
// the colors are ANSI codes and seen as Gibberish in the log files.
|
||||
formatter.DisableColors = ctx.String(cmd.LogFileName.Name) != ""
|
||||
logrus.SetFormatter(formatter)
|
||||
case "fluentd":
|
||||
f := joonix.NewFormatter()
|
||||
if err := joonix.DisableTimestampFormat(f); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
logrus.SetFormatter(f)
|
||||
case "json":
|
||||
logrus.SetFormatter(&logrus.JSONFormatter{})
|
||||
case "journald":
|
||||
if err := journald.Enable(); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown log format %s", format)
|
||||
}
|
||||
}
|
||||
|
||||
// Fix data dir for Windows users.
|
||||
outdatedDataDir := filepath.Join(file.HomeDir(), "AppData", "Roaming", "Eth2Validators")
|
||||
currentDataDir := flags.DefaultValidatorDir()
|
||||
if err := cmd.FixDefaultDataDir(outdatedDataDir, currentDataDir); err != nil {
|
||||
log.WithError(err).Error("Cannot update data directory")
|
||||
}
|
||||
logFileName := ctx.String(cmd.LogFileName.Name)
|
||||
if logFileName != "" {
|
||||
if err := logs.ConfigurePersistentLogging(logFileName); err != nil {
|
||||
log.WithError(err).Error("Failed to configuring logging to disk.")
|
||||
}
|
||||
}
|
||||
|
||||
if err := debug.Setup(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return cmd.ValidateNoArgs(ctx)
|
||||
}
|
||||
// Fix data dir for Windows users.
|
||||
outdatedDataDir := filepath.Join(file.HomeDir(), "AppData", "Roaming", "Eth2Validators")
|
||||
currentDataDir := flags.DefaultValidatorDir()
|
||||
if err := cmd.FixDefaultDataDir(outdatedDataDir, currentDataDir); err != nil {
|
||||
log.WithError(err).Error("Cannot update data directory")
|
||||
}
|
||||
|
||||
app.After = func(ctx *cli.Context) error {
|
||||
debug.Exit(ctx)
|
||||
return nil
|
||||
if err := debug.Setup(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return cmd.ValidateNoArgs(ctx)
|
||||
},
|
||||
After: func(ctx *cli.Context) error {
|
||||
debug.Exit(ctx)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
defer func() {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
var Commands = &cli.Command{
|
||||
Name: "slashing-protection-history",
|
||||
Category: "slashing-protection-history",
|
||||
Usage: "defines commands for interacting your validator's slashing protection history",
|
||||
Usage: "Defines commands for interacting your validator's slashing protection history.",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "export",
|
||||
|
||||
@@ -14,25 +14,33 @@ import (
|
||||
|
||||
var appHelpTemplate = `NAME:
|
||||
{{.App.Name}} - {{.App.Usage}}
|
||||
|
||||
USAGE:
|
||||
{{.App.HelpName}} [options]{{if .App.Commands}} command [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}}
|
||||
{{if .App.Version}}
|
||||
AUTHOR:
|
||||
{{range .App.Authors}}{{ . }}{{end}}
|
||||
{{end}}{{if .App.Commands}}
|
||||
GLOBAL OPTIONS:
|
||||
{{if .App.Version}}
|
||||
VERSION:
|
||||
{{.App.Version}}
|
||||
{{end -}}
|
||||
{{if len .App.Authors}}
|
||||
AUTHORS:
|
||||
{{range .App.Authors}}{{ . }}
|
||||
{{end -}}
|
||||
{{end -}}
|
||||
{{if .App.Commands}}
|
||||
global OPTIONS:
|
||||
{{range .App.Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
|
||||
{{end}}{{end}}{{if .FlagGroups}}
|
||||
{{end -}}
|
||||
{{end -}}
|
||||
{{if .FlagGroups}}
|
||||
{{range .FlagGroups}}{{.Name}} OPTIONS:
|
||||
{{range .Flags}}{{.}}
|
||||
{{end}}
|
||||
{{end}}{{end}}{{if .App.Copyright }}
|
||||
{{end -}}
|
||||
{{end -}}
|
||||
{{if .App.Copyright }}
|
||||
COPYRIGHT:
|
||||
{{.App.Copyright}}
|
||||
VERSION:
|
||||
{{.App.Version}}
|
||||
{{end}}{{if len .App.Authors}}
|
||||
{{end}}
|
||||
{{end -}}
|
||||
`
|
||||
|
||||
type flagGroup struct {
|
||||
@@ -113,7 +121,7 @@ var appHelpFlagGroups = []flagGroup{
|
||||
flags.SuggestedFeeRecipientFlag,
|
||||
flags.EnableBuilderFlag,
|
||||
flags.BuilderGasLimitFlag,
|
||||
flags.ValidatorRegistrationBatchSizeFlag,
|
||||
flags.ValidatorsRegistrationBatchSizeFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@ var log = logrus.WithField("prefix", "wallet")
|
||||
var Commands = &cli.Command{
|
||||
Name: "wallet",
|
||||
Category: "wallet",
|
||||
Usage: "defines commands for interacting with Ethereum validator wallets",
|
||||
Usage: "Defines commands for interacting with Ethereum validator wallets.",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "create",
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
var Commands = &cli.Command{
|
||||
Name: "web",
|
||||
Category: "web",
|
||||
Usage: "defines commands for interacting with the Prysm web interface",
|
||||
Usage: "Defines commands for interacting with the Prysm web interface.",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "generate-auth-token",
|
||||
|
||||
@@ -40,7 +40,6 @@ type Flags struct {
|
||||
EnableExperimentalState bool // EnableExperimentalState turns on the latest and greatest (but potentially unstable) changes to the beacon state.
|
||||
WriteSSZStateTransitions bool // WriteSSZStateTransitions to tmp directory.
|
||||
EnablePeerScorer bool // EnablePeerScorer enables experimental peer scoring in p2p.
|
||||
DisableReorgLateBlocks bool // DisableReorgLateBlocks disables reorgs of late blocks.
|
||||
WriteWalletPasswordOnWebOnboarding bool // WriteWalletPasswordOnWebOnboarding writes the password to disk after Prysm web signup.
|
||||
EnableDoppelGanger bool // EnableDoppelGanger enables doppelganger protection on startup for the validator.
|
||||
EnableHistoricalSpaceRepresentation bool // EnableHistoricalSpaceRepresentation enables the saving of registry validators in separate buckets to save space
|
||||
@@ -65,7 +64,6 @@ type Flags struct {
|
||||
DisableStakinContractCheck bool // Disables check for deposit contract when proposing blocks
|
||||
|
||||
EnableVerboseSigVerification bool // EnableVerboseSigVerification specifies whether to verify individual signature if batch verification fails
|
||||
EnableOptionalEngineMethods bool // EnableOptionalEngineMethods specifies whether to activate capella specific engine methods
|
||||
EnableEIP4881 bool // EnableEIP4881 specifies whether to use the deposit tree from EIP4881
|
||||
|
||||
PrepareAllPayloads bool // PrepareAllPayloads informs the engine to prepare a block on every slot.
|
||||
@@ -192,10 +190,6 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
|
||||
cfg.DisableGRPCConnectionLogs = true
|
||||
}
|
||||
|
||||
if ctx.Bool(disableReorgLateBlocks.Name) {
|
||||
logEnabled(disableReorgLateBlocks)
|
||||
cfg.DisableReorgLateBlocks = true
|
||||
}
|
||||
cfg.EnablePeerScorer = true
|
||||
if ctx.Bool(disablePeerScorer.Name) {
|
||||
logDisabled(disablePeerScorer)
|
||||
@@ -233,11 +227,6 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
|
||||
logEnabled(enableVerboseSigVerification)
|
||||
cfg.EnableVerboseSigVerification = true
|
||||
}
|
||||
cfg.EnableOptionalEngineMethods = true
|
||||
if ctx.IsSet(disableOptionalEngineMethods.Name) {
|
||||
logEnabled(disableOptionalEngineMethods)
|
||||
cfg.EnableOptionalEngineMethods = false
|
||||
}
|
||||
if ctx.IsSet(prepareAllPayloads.Name) {
|
||||
logEnabled(prepareAllPayloads)
|
||||
cfg.PrepareAllPayloads = true
|
||||
|
||||
@@ -53,6 +53,16 @@ var (
|
||||
Usage: deprecatedUsage,
|
||||
Hidden: true,
|
||||
}
|
||||
deprecatedDisableReorgLateBlocks = &cli.BoolFlag{
|
||||
Name: "disable-reorg-late-blocks",
|
||||
Usage: deprecatedUsage,
|
||||
Hidden: true,
|
||||
}
|
||||
deprecatedDisableOptionalEngineMethods = &cli.BoolFlag{
|
||||
Name: "disable-optional-engine-methods",
|
||||
Usage: deprecatedUsage,
|
||||
Hidden: true,
|
||||
}
|
||||
)
|
||||
|
||||
// Deprecated flags for both the beacon node and validator client.
|
||||
@@ -66,6 +76,8 @@ var deprecatedFlags = []cli.Flag{
|
||||
deprecatedAggregateParallel,
|
||||
deprecatedEnableOptionalEngineMethods,
|
||||
deprecatedDisableBuildBlockParallel,
|
||||
deprecatedDisableReorgLateBlocks,
|
||||
deprecatedDisableOptionalEngineMethods,
|
||||
}
|
||||
|
||||
// deprecatedBeaconFlags contains flags that are still used by other components
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user