mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 13:28:01 -05:00
Compare commits
35 Commits
d929e1dcaa
...
safe_head
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d24dfaaa7 | ||
|
|
1cd60e1606 | ||
|
|
97e4131e27 | ||
|
|
0eb845153b | ||
|
|
37e35d5931 | ||
|
|
5cf74ac498 | ||
|
|
f5a0de1564 | ||
|
|
1c07d053e3 | ||
|
|
36e4875444 | ||
|
|
2192f36f56 | ||
|
|
e5758025bc | ||
|
|
a020475758 | ||
|
|
69d9db30e0 | ||
|
|
b977e0cbca | ||
|
|
e901d2bf8a | ||
|
|
2a6cf3fa4e | ||
|
|
9a30442fc6 | ||
|
|
2d2de83667 | ||
|
|
c159646656 | ||
|
|
186eaf2c6e | ||
|
|
ea10c35825 | ||
|
|
738f4a6fc3 | ||
|
|
21af707e82 | ||
|
|
67d3427d49 | ||
|
|
f28d87b4d6 | ||
|
|
7807a92a0f | ||
|
|
0293362e02 | ||
|
|
acb21807c5 | ||
|
|
ce0aeb0fbb | ||
|
|
31503b59b8 | ||
|
|
b5dddb0550 | ||
|
|
d2666cc869 | ||
|
|
caec9ac555 | ||
|
|
71af44bc16 | ||
|
|
515cfa2260 |
@@ -7,7 +7,6 @@ go_library(
|
||||
"error.go",
|
||||
"head.go",
|
||||
"head_sync_committee_info.go",
|
||||
"info.go",
|
||||
"init_sync_process_block.go",
|
||||
"log.go",
|
||||
"metrics.go",
|
||||
@@ -72,7 +71,6 @@ go_library(
|
||||
"//runtime/version:go_default_library",
|
||||
"//time:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_emicklei_dot//:go_default_library",
|
||||
"@com_github_holiman_uint256//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
@@ -100,7 +98,6 @@ go_test(
|
||||
"checktags_test.go",
|
||||
"head_sync_committee_info_test.go",
|
||||
"head_test.go",
|
||||
"info_test.go",
|
||||
"init_test.go",
|
||||
"log_test.go",
|
||||
"metrics_test.go",
|
||||
|
||||
@@ -54,8 +54,8 @@ type HeadFetcher interface {
|
||||
HeadValidatorIndexToPublicKey(ctx context.Context, index types.ValidatorIndex) ([fieldparams.BLSPubkeyLength]byte, error)
|
||||
ProtoArrayStore() *protoarray.Store
|
||||
ChainHeads() ([][32]byte, []types.Slot)
|
||||
IsOptimistic(ctx context.Context) (bool, error)
|
||||
IsOptimisticForRoot(ctx context.Context, root [32]byte, slot types.Slot) (bool, error)
|
||||
IsOptimistic() (bool, error)
|
||||
IsOptimisticForRoot(root [32]byte) (bool, error)
|
||||
HeadSyncCommitteeFetcher
|
||||
HeadDomainFetcher
|
||||
}
|
||||
@@ -287,23 +287,7 @@ func (s *Service) IsCanonical(ctx context.Context, blockRoot [32]byte) (bool, er
|
||||
// ChainHeads returns all possible chain heads (leaves of fork choice tree).
|
||||
// Heads roots and heads slots are returned.
|
||||
func (s *Service) ChainHeads() ([][32]byte, []types.Slot) {
|
||||
nodes := s.ProtoArrayStore().Nodes()
|
||||
|
||||
// Deliberate choice to not preallocate space for below.
|
||||
// Heads cant be more than 2-3 in the worst case where pre-allocation will be 64 to begin with.
|
||||
headsRoots := make([][32]byte, 0)
|
||||
headsSlots := make([]types.Slot, 0)
|
||||
|
||||
nonExistentNode := ^uint64(0)
|
||||
for _, node := range nodes {
|
||||
// Possible heads have no children.
|
||||
if node.BestDescendant() == nonExistentNode && node.BestChild() == nonExistentNode {
|
||||
headsRoots = append(headsRoots, node.Root())
|
||||
headsSlots = append(headsSlots, node.Slot())
|
||||
}
|
||||
}
|
||||
|
||||
return headsRoots, headsSlots
|
||||
return s.cfg.ForkChoiceStore.Tips()
|
||||
}
|
||||
|
||||
// HeadPublicKeyToValidatorIndex returns the validator index of the `pubkey` in current head state.
|
||||
@@ -331,16 +315,16 @@ func (s *Service) HeadValidatorIndexToPublicKey(_ context.Context, index types.V
|
||||
}
|
||||
|
||||
// IsOptimistic returns true if the current head is optimistic.
|
||||
func (s *Service) IsOptimistic(ctx context.Context) (bool, error) {
|
||||
func (s *Service) IsOptimistic() (bool, error) {
|
||||
s.headLock.RLock()
|
||||
defer s.headLock.RUnlock()
|
||||
return s.cfg.ForkChoiceStore.Optimistic(ctx, s.head.root, s.head.slot)
|
||||
return s.cfg.ForkChoiceStore.IsOptimistic(s.head.root)
|
||||
}
|
||||
|
||||
// IsOptimisticForRoot takes the root and slot as aguments instead of the current head
|
||||
// and returns true if it is optimistic.
|
||||
func (s *Service) IsOptimisticForRoot(ctx context.Context, root [32]byte, slot types.Slot) (bool, error) {
|
||||
return s.cfg.ForkChoiceStore.Optimistic(ctx, root, slot)
|
||||
func (s *Service) IsOptimisticForRoot(root [32]byte) (bool, error) {
|
||||
return s.cfg.ForkChoiceStore.IsOptimistic(root)
|
||||
}
|
||||
|
||||
// SetGenesisTime sets the genesis time of beacon chain.
|
||||
|
||||
@@ -279,19 +279,19 @@ func TestService_HeadGenesisValidatorsRoot(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestService_ProtoArrayStore(t *testing.T) {
|
||||
c := &Service{cfg: &config{ForkChoiceStore: protoarray.New(0, 0, [32]byte{})}}
|
||||
c := &Service{cfg: &config{ForkChoiceStore: protoarray.New(0, 0)}}
|
||||
p := c.ProtoArrayStore()
|
||||
require.Equal(t, 0, int(p.FinalizedEpoch()))
|
||||
}
|
||||
|
||||
func TestService_ChainHeads(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := &Service{cfg: &config{ForkChoiceStore: protoarray.New(0, 0, [32]byte{})}}
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, [32]byte{}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, [32]byte{}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, [32]byte{}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 103, [32]byte{'d'}, [32]byte{}, [32]byte{}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 104, [32]byte{'e'}, [32]byte{'b'}, [32]byte{}, 0, 0))
|
||||
c := &Service{cfg: &config{ForkChoiceStore: protoarray.New(0, 0)}}
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, 0, 0, false))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 0, 0, false))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 0, 0, false))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 103, [32]byte{'d'}, [32]byte{}, 0, 0, false))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 104, [32]byte{'e'}, [32]byte{'b'}, 0, 0, false))
|
||||
|
||||
roots, slots := c.ChainHeads()
|
||||
require.DeepEqual(t, [][32]byte{{'c'}, {'d'}, {'e'}}, roots)
|
||||
@@ -356,24 +356,35 @@ func TestService_HeadValidatorIndexToPublicKeyNil(t *testing.T) {
|
||||
require.Equal(t, [fieldparams.BLSPubkeyLength]byte{}, p)
|
||||
}
|
||||
|
||||
func TestService_IsOptimistic_NotOptimistic(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := &Service{cfg: &config{ForkChoiceStore: protoarray.New(0, 0)}, head: &head{slot: 101, root: [32]byte{'b'}}}
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, 0, 0, false))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 0, 0, false))
|
||||
|
||||
opt, err := c.IsOptimistic()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, opt)
|
||||
}
|
||||
|
||||
func TestService_IsOptimistic(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := &Service{cfg: &config{ForkChoiceStore: protoarray.New(0, 0, [32]byte{})}, head: &head{slot: 101, root: [32]byte{'b'}}}
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, [32]byte{}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, [32]byte{}, 0, 0))
|
||||
c := &Service{cfg: &config{ForkChoiceStore: protoarray.New(0, 0)}, head: &head{slot: 101, root: [32]byte{'b'}}}
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, 0, 0, true))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 0, 0, true))
|
||||
|
||||
opt, err := c.IsOptimistic(ctx)
|
||||
opt, err := c.IsOptimistic()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, opt)
|
||||
}
|
||||
|
||||
func TestService_IsOptimisticForRoot(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := &Service{cfg: &config{ForkChoiceStore: protoarray.New(0, 0, [32]byte{})}, head: &head{slot: 101, root: [32]byte{'b'}}}
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, [32]byte{}, 0, 0))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, [32]byte{}, 0, 0))
|
||||
c := &Service{cfg: &config{ForkChoiceStore: protoarray.New(0, 0)}, head: &head{slot: 101, root: [32]byte{'b'}}}
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, 0, 0, true))
|
||||
require.NoError(t, c.cfg.ForkChoiceStore.ProcessBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 0, 0, true))
|
||||
|
||||
opt, err := c.IsOptimisticForRoot(ctx, [32]byte{'a'}, 100)
|
||||
opt, err := c.IsOptimisticForRoot([32]byte{'a'})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, opt)
|
||||
}
|
||||
|
||||
@@ -77,8 +77,9 @@ func (s *Service) updateHead(ctx context.Context, balances []uint64) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.cfg.ForkChoiceStore = protoarray.New(j.Epoch, f.Epoch, bytesutil.ToBytes32(f.Root))
|
||||
if err := s.insertBlockToForkChoiceStore(ctx, jb.Block(), headStartRoot, f, j); err != nil {
|
||||
s.cfg.ForkChoiceStore = protoarray.New(j.Epoch, f.Epoch)
|
||||
// TODO(10261) send optimistic status
|
||||
if err := s.insertBlockToForkChoiceStore(ctx, jb.Block(), headStartRoot, f, j, false /* optimistic status */); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/dot"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
)
|
||||
|
||||
const template = `<html>
|
||||
<head>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/viz.js/2.1.2/viz.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/viz.js/2.1.2/full.render.js"></script>
|
||||
<body>
|
||||
<script type="application/javascript">
|
||||
var graph = ` + "`%s`;" + `
|
||||
var viz = new Viz();
|
||||
viz.renderSVGElement(graph) // reading the graph.
|
||||
.then(function(element) {
|
||||
document.body.appendChild(element); // appends to document.
|
||||
})
|
||||
.catch(error => {
|
||||
// Create a new Viz instance (@see Caveats page for more info)
|
||||
viz = new Viz();
|
||||
// Possibly display the error
|
||||
console.error(error);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
// TreeHandler is a handler to serve /tree page in metrics.
|
||||
func (s *Service) TreeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
headState, err := s.HeadState(r.Context())
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not get head state")
|
||||
return
|
||||
}
|
||||
if headState == nil || headState.IsNil() {
|
||||
if _, err := w.Write([]byte("Unavailable during initial syncing")); err != nil {
|
||||
log.WithError(err).Error("Failed to render p2p info page")
|
||||
}
|
||||
}
|
||||
|
||||
nodes := s.cfg.ForkChoiceStore.Nodes()
|
||||
|
||||
graph := dot.NewGraph(dot.Directed)
|
||||
graph.Attr("rankdir", "RL")
|
||||
graph.Attr("labeljust", "l")
|
||||
|
||||
dotNodes := make([]*dot.Node, len(nodes))
|
||||
avgBalance := uint64(averageBalance(headState.Balances()))
|
||||
|
||||
for i := len(nodes) - 1; i >= 0; i-- {
|
||||
// Construct label for each node.
|
||||
slot := fmt.Sprintf("%d", nodes[i].Slot())
|
||||
weight := fmt.Sprintf("%d", nodes[i].Weight()/1e9) // Convert unit Gwei to unit ETH.
|
||||
votes := fmt.Sprintf("%d", nodes[i].Weight()/1e9/avgBalance)
|
||||
index := fmt.Sprintf("%d", i)
|
||||
g := nodes[i].Graffiti()
|
||||
graffiti := hex.EncodeToString(g[:8])
|
||||
label := "slot: " + slot + "\n votes: " + votes + "\n weight: " + weight + "\n graffiti: " + graffiti
|
||||
var dotN dot.Node
|
||||
if nodes[i].Parent() != ^uint64(0) {
|
||||
dotN = graph.Node(index).Box().Attr("label", label)
|
||||
}
|
||||
|
||||
if nodes[i].Slot() == s.HeadSlot() &&
|
||||
nodes[i].BestDescendant() == ^uint64(0) &&
|
||||
nodes[i].Parent() != ^uint64(0) {
|
||||
dotN = dotN.Attr("color", "green")
|
||||
}
|
||||
|
||||
dotNodes[i] = &dotN
|
||||
}
|
||||
|
||||
for i := len(nodes) - 1; i >= 0; i-- {
|
||||
if nodes[i].Parent() != ^uint64(0) && nodes[i].Parent() < uint64(len(dotNodes)) {
|
||||
graph.Edge(*dotNodes[i], *dotNodes[nodes[i].Parent()])
|
||||
}
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
if _, err := fmt.Fprintf(w, template, graph.String()); err != nil {
|
||||
log.WithError(err).Error("Failed to render p2p info page")
|
||||
}
|
||||
}
|
||||
|
||||
func averageBalance(balances []uint64) float64 {
|
||||
total := uint64(0)
|
||||
for i := 0; i < len(balances); i++ {
|
||||
total += balances[i]
|
||||
}
|
||||
return float64(total) / float64(len(balances)) / float64(params.BeaconConfig().GweiPerEth)
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/testing/util"
|
||||
)
|
||||
|
||||
func TestService_TreeHandler(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "/tree", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
headState, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, headState.SetBalances([]uint64{params.BeaconConfig().GweiPerEth}))
|
||||
fcs := protoarray.New(
|
||||
0, // justifiedEpoch
|
||||
0, // finalizedEpoch
|
||||
[32]byte{'a'},
|
||||
)
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
WithForkChoiceStore(fcs),
|
||||
}
|
||||
s, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.cfg.ForkChoiceStore.ProcessBlock(ctx, 0, [32]byte{'a'}, [32]byte{'g'}, [32]byte{'c'}, 0, 0))
|
||||
require.NoError(t, s.cfg.ForkChoiceStore.ProcessBlock(ctx, 1, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'c'}, 0, 0))
|
||||
s.setHead([32]byte{'a'}, wrapper.WrappedPhase0SignedBeaconBlock(util.NewBeaconBlock()), headState)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(s.TreeHandler)
|
||||
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
func testServiceOptsWithDB(t *testing.T) []Option {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
fcs := protoarray.New(0, 0, [32]byte{'a'})
|
||||
fcs := protoarray.New(0, 0)
|
||||
return []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
|
||||
func TestService_newSlot(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
fcs := protoarray.New(0, 0, [32]byte{})
|
||||
fcs := protoarray.New(0, 0)
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
@@ -25,11 +25,11 @@ func TestService_newSlot(t *testing.T) {
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
require.NoError(t, fcs.ProcessBlock(ctx, 0, [32]byte{}, [32]byte{}, [32]byte{}, 0, 0)) // genesis
|
||||
require.NoError(t, fcs.ProcessBlock(ctx, 32, [32]byte{'a'}, [32]byte{}, [32]byte{}, 0, 0)) // finalized
|
||||
require.NoError(t, fcs.ProcessBlock(ctx, 64, [32]byte{'b'}, [32]byte{'a'}, [32]byte{}, 0, 0)) // justified
|
||||
require.NoError(t, fcs.ProcessBlock(ctx, 96, [32]byte{'c'}, [32]byte{'a'}, [32]byte{}, 0, 0)) // best justified
|
||||
require.NoError(t, fcs.ProcessBlock(ctx, 97, [32]byte{'d'}, [32]byte{}, [32]byte{}, 0, 0)) // bad
|
||||
require.NoError(t, fcs.ProcessBlock(ctx, 0, [32]byte{}, [32]byte{}, 0, 0, true)) // genesis
|
||||
require.NoError(t, fcs.ProcessBlock(ctx, 32, [32]byte{'a'}, [32]byte{}, 0, 0, true)) // finalized
|
||||
require.NoError(t, fcs.ProcessBlock(ctx, 64, [32]byte{'b'}, [32]byte{'a'}, 0, 0, true)) // justified
|
||||
require.NoError(t, fcs.ProcessBlock(ctx, 96, [32]byte{'c'}, [32]byte{'a'}, 0, 0, true)) // best justified
|
||||
require.NoError(t, fcs.ProcessBlock(ctx, 97, [32]byte{'d'}, [32]byte{}, 0, 0, true)) // bad
|
||||
|
||||
type args struct {
|
||||
slot types.Slot
|
||||
|
||||
@@ -3,8 +3,6 @@ package blockchain
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
@@ -33,21 +31,3 @@ func (s *Service) optimisticCandidateBlock(ctx context.Context, blk block.Beacon
|
||||
}
|
||||
return blocks.ExecutionBlock(jBlock.Block().Body())
|
||||
}
|
||||
|
||||
// loadSyncedTips loads a previously saved synced Tips from DB
|
||||
// if no synced tips are saved, then it creates one from the given
|
||||
// root and slot number.
|
||||
func (s *Service) loadSyncedTips(root [32]byte, slot types.Slot) error {
|
||||
// Initialize synced tips
|
||||
tips, err := s.cfg.BeaconDB.ValidatedTips(s.ctx)
|
||||
if err != nil || len(tips) == 0 {
|
||||
tips[root] = slot
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("Could not read synced tips from DB, using finalized checkpoint as synced tip")
|
||||
}
|
||||
}
|
||||
if err := s.cfg.ForkChoiceStore.SetSyncedTips(tips); err != nil {
|
||||
return errors.Wrap(err, "could not set synced tips")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
|
||||
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
fcs := protoarray.New(0, 0, [32]byte{'a'})
|
||||
fcs := protoarray.New(0, 0)
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestStore_OnAttestation_ErrorConditions(t *testing.T) {
|
||||
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithForkChoiceStore(protoarray.New(0, 0, [32]byte{})),
|
||||
WithForkChoiceStore(protoarray.New(0, 0)),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
}
|
||||
service, err := NewService(ctx, opts...)
|
||||
@@ -131,7 +131,7 @@ func TestStore_OnAttestation_Ok(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
fcs := protoarray.New(0, 0, [32]byte{'a'})
|
||||
fcs := protoarray.New(0, 0)
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
@@ -149,7 +149,7 @@ func TestStore_OnAttestation_Ok(t *testing.T) {
|
||||
copied, err = transition.ProcessSlots(ctx, copied, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, copied, tRoot))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.ProcessBlock(ctx, 0, tRoot, tRoot, tRoot, 1, 1))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.ProcessBlock(ctx, 0, tRoot, tRoot, 1, 1, false))
|
||||
require.NoError(t, service.OnAttestation(ctx, att[0]))
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ func TestVerifyFinalizedConsistency_InconsistentRoot(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
fcs := protoarray.New(0, 0, [32]byte{'a'})
|
||||
fcs := protoarray.New(0, 0)
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
@@ -407,8 +407,8 @@ func TestVerifyFinalizedConsistency_IsCanonical(t *testing.T) {
|
||||
r33, err := b33.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.ProcessBlock(ctx, b32.Block.Slot, r32, [32]byte{}, [32]byte{}, 0, 0))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.ProcessBlock(ctx, b33.Block.Slot, r33, r32, [32]byte{}, 0, 0))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.ProcessBlock(ctx, b32.Block.Slot, r32, [32]byte{}, 0, 0, false))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.ProcessBlock(ctx, b33.Block.Slot, r33, r32, 0, 0, false))
|
||||
|
||||
_, err = service.cfg.ForkChoiceStore.Head(ctx, 0, r32, []uint64{}, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -111,7 +111,7 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.savePostStateInfo(ctx, blockRoot, signed, postState, false /* reg sync */); err != nil {
|
||||
if err := s.savePostStateInfo(ctx, blockRoot, signed, postState, false /* reg sync */, false /* optimistic */); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -175,10 +175,6 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b
|
||||
log.WithError(err).Warn("Could not update head")
|
||||
}
|
||||
|
||||
if err := s.saveSyncedTipsDB(ctx); err != nil {
|
||||
return errors.Wrap(err, "could not save synced tips")
|
||||
}
|
||||
|
||||
if err := s.pruneCanonicalAttsFromPool(ctx, blockRoot, signed); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -327,16 +323,13 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []block.SignedBeaconBlo
|
||||
// handles a block after the block's batch has been verified, where we can save blocks
|
||||
// their state summaries and split them off to relative hot/cold storage.
|
||||
func (s *Service) handleBlockAfterBatchVerify(ctx context.Context, signed block.SignedBeaconBlock,
|
||||
blockRoot [32]byte, fCheckpoint, jCheckpoint *ethpb.Checkpoint) error {
|
||||
blockRoot [32]byte, fCheckpoint, jCheckpoint *ethpb.Checkpoint, optimistic bool) error {
|
||||
b := signed.Block()
|
||||
|
||||
s.saveInitSyncBlock(blockRoot, signed)
|
||||
if err := s.insertBlockToForkChoiceStore(ctx, b, blockRoot, fCheckpoint, jCheckpoint); err != nil {
|
||||
if err := s.insertBlockToForkChoiceStore(ctx, b, blockRoot, fCheckpoint, jCheckpoint, optimistic); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.saveSyncedTipsDB(ctx); err != nil {
|
||||
return errors.Wrap(err, "could not save synced tips")
|
||||
}
|
||||
|
||||
if err := s.cfg.BeaconDB.SaveStateSummary(ctx, ðpb.StateSummary{
|
||||
Slot: signed.Block().Slot(),
|
||||
@@ -422,13 +415,13 @@ func (s *Service) handleEpochBoundary(ctx context.Context, postState state.Beaco
|
||||
// This feeds in the block and block's attestations to fork choice store. It's allows fork choice store
|
||||
// to gain information on the most current chain.
|
||||
func (s *Service) insertBlockAndAttestationsToForkChoiceStore(ctx context.Context, blk block.BeaconBlock, root [32]byte,
|
||||
st state.BeaconState) error {
|
||||
st state.BeaconState, optimistic bool) error {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.insertBlockAndAttestationsToForkChoiceStore")
|
||||
defer span.End()
|
||||
|
||||
fCheckpoint := st.FinalizedCheckpoint()
|
||||
jCheckpoint := st.CurrentJustifiedCheckpoint()
|
||||
if err := s.insertBlockToForkChoiceStore(ctx, blk, root, fCheckpoint, jCheckpoint); err != nil {
|
||||
if err := s.insertBlockToForkChoiceStore(ctx, blk, root, fCheckpoint, jCheckpoint, optimistic); err != nil {
|
||||
return err
|
||||
}
|
||||
// Feed in block's attestations to fork choice store.
|
||||
@@ -447,15 +440,15 @@ func (s *Service) insertBlockAndAttestationsToForkChoiceStore(ctx context.Contex
|
||||
}
|
||||
|
||||
func (s *Service) insertBlockToForkChoiceStore(ctx context.Context, blk block.BeaconBlock,
|
||||
root [32]byte, fCheckpoint, jCheckpoint *ethpb.Checkpoint) error {
|
||||
root [32]byte, fCheckpoint, jCheckpoint *ethpb.Checkpoint, optimistic bool) error {
|
||||
//TODO(10261) check if the blocks are optimistic or not when filling fork choice
|
||||
if err := s.fillInForkChoiceMissingBlocks(ctx, blk, fCheckpoint, jCheckpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
// Feed in block to fork choice store.
|
||||
if err := s.cfg.ForkChoiceStore.ProcessBlock(ctx,
|
||||
blk.Slot(), root, bytesutil.ToBytes32(blk.ParentRoot()), bytesutil.ToBytes32(blk.Body().Graffiti()),
|
||||
jCheckpoint.Epoch,
|
||||
fCheckpoint.Epoch); err != nil {
|
||||
blk.Slot(), root, bytesutil.ToBytes32(blk.ParentRoot()),
|
||||
jCheckpoint.Epoch, fCheckpoint.Epoch, optimistic); err != nil {
|
||||
return errors.Wrap(err, "could not process block for proto array fork choice")
|
||||
}
|
||||
return nil
|
||||
@@ -463,7 +456,7 @@ func (s *Service) insertBlockToForkChoiceStore(ctx context.Context, blk block.Be
|
||||
|
||||
// This saves post state info to DB or cache. This also saves post state info to fork choice store.
|
||||
// Post state info consists of processed block and state. Do not call this method unless the block and state are verified.
|
||||
func (s *Service) savePostStateInfo(ctx context.Context, r [32]byte, b block.SignedBeaconBlock, st state.BeaconState, initSync bool) error {
|
||||
func (s *Service) savePostStateInfo(ctx context.Context, r [32]byte, b block.SignedBeaconBlock, st state.BeaconState, initSync bool, optimistic bool) error {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.savePostStateInfo")
|
||||
defer span.End()
|
||||
if initSync {
|
||||
@@ -474,7 +467,7 @@ func (s *Service) savePostStateInfo(ctx context.Context, r [32]byte, b block.Sig
|
||||
if err := s.cfg.StateGen.SaveState(ctx, r, st); err != nil {
|
||||
return errors.Wrap(err, "could not save state")
|
||||
}
|
||||
if err := s.insertBlockAndAttestationsToForkChoiceStore(ctx, b.Block(), r, st); err != nil {
|
||||
if err := s.insertBlockAndAttestationsToForkChoiceStore(ctx, b.Block(), r, st, optimistic); err != nil {
|
||||
return errors.Wrapf(err, "could not insert block %d to fork choice store", b.Block().Slot())
|
||||
}
|
||||
return nil
|
||||
@@ -509,12 +502,3 @@ func (s *Service) pruneCanonicalAttsFromPool(ctx context.Context, r [32]byte, b
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Saves synced and validated tips to DB.
|
||||
func (s *Service) saveSyncedTipsDB(ctx context.Context) error {
|
||||
tips := s.cfg.ForkChoiceStore.SyncedTips()
|
||||
if len(tips) == 0 {
|
||||
return nil
|
||||
}
|
||||
return s.cfg.BeaconDB.UpdateValidatedTips(ctx, tips)
|
||||
}
|
||||
|
||||
@@ -367,9 +367,9 @@ func (s *Service) fillInForkChoiceMissingBlocks(ctx context.Context, blk block.B
|
||||
b := pendingNodes[i]
|
||||
r := pendingRoots[i]
|
||||
if err := s.cfg.ForkChoiceStore.ProcessBlock(ctx,
|
||||
b.Slot(), r, bytesutil.ToBytes32(b.ParentRoot()), bytesutil.ToBytes32(b.Body().Graffiti()),
|
||||
b.Slot(), r, bytesutil.ToBytes32(b.ParentRoot()),
|
||||
jCheckpoint.Epoch,
|
||||
fCheckpoint.Epoch); err != nil {
|
||||
fCheckpoint.Epoch, false /* optimistic status */); err != nil {
|
||||
return errors.Wrap(err, "could not process block for proto array fork choice")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ func TestStore_OnBlock(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
fcs := protoarray.New(0, 0, [32]byte{'a'})
|
||||
fcs := protoarray.New(0, 0)
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
@@ -148,7 +148,7 @@ func TestStore_OnBlockBatch(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
service.store.SetFinalizedCheckpt(ðpb.Checkpoint{Root: gRoot[:]})
|
||||
|
||||
service.cfg.ForkChoiceStore = protoarray.New(0, 0, [32]byte{})
|
||||
service.cfg.ForkChoiceStore = protoarray.New(0, 0)
|
||||
service.saveInitSyncBlock(gRoot, wrapper.WrappedPhase0SignedBeaconBlock(genesis))
|
||||
|
||||
st, keys := util.DeterministicGenesisState(t, 64)
|
||||
@@ -223,7 +223,7 @@ func TestShouldUpdateJustified_ReturnFalse(t *testing.T) {
|
||||
opts := testServiceOptsWithDB(t)
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
service.cfg.ForkChoiceStore = protoarray.New(0, 0, [32]byte{})
|
||||
service.cfg.ForkChoiceStore = protoarray.New(0, 0)
|
||||
lastJustifiedBlk := util.NewBeaconBlock()
|
||||
lastJustifiedBlk.Block.ParentRoot = bytesutil.PadTo([]byte{'G'}, 32)
|
||||
lastJustifiedRoot, err := lastJustifiedBlk.Block.HashTreeRoot()
|
||||
@@ -264,7 +264,7 @@ func TestCachedPreState_CanGetFromStateSummary(t *testing.T) {
|
||||
gRoot, err := genesis.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
service.store.SetFinalizedCheckpt(ðpb.Checkpoint{Root: gRoot[:]})
|
||||
service.cfg.ForkChoiceStore = protoarray.New(0, 0, [32]byte{})
|
||||
service.cfg.ForkChoiceStore = protoarray.New(0, 0)
|
||||
service.saveInitSyncBlock(gRoot, wrapper.WrappedPhase0SignedBeaconBlock(genesis))
|
||||
|
||||
b := util.NewBeaconBlock()
|
||||
@@ -292,7 +292,7 @@ func TestCachedPreState_CanGetFromDB(t *testing.T) {
|
||||
gRoot, err := genesis.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
service.store.SetFinalizedCheckpt(ðpb.Checkpoint{Root: gRoot[:]})
|
||||
service.cfg.ForkChoiceStore = protoarray.New(0, 0, [32]byte{})
|
||||
service.cfg.ForkChoiceStore = protoarray.New(0, 0)
|
||||
service.saveInitSyncBlock(gRoot, wrapper.WrappedPhase0SignedBeaconBlock(genesis))
|
||||
|
||||
b := util.NewBeaconBlock()
|
||||
@@ -317,7 +317,7 @@ func TestUpdateJustified_CouldUpdateBest(t *testing.T) {
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
WithForkChoiceStore(protoarray.New(0, 0, [32]byte{})),
|
||||
WithForkChoiceStore(protoarray.New(0, 0)),
|
||||
}
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
@@ -357,7 +357,7 @@ func TestFillForkChoiceMissingBlocks_CanSave(t *testing.T) {
|
||||
}
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
service.cfg.ForkChoiceStore = protoarray.New(0, 0, [32]byte{'A'})
|
||||
service.cfg.ForkChoiceStore = protoarray.New(0, 0)
|
||||
service.store.SetFinalizedCheckpt(ðpb.Checkpoint{Root: make([]byte, 32)})
|
||||
|
||||
genesisStateRoot := [32]byte{}
|
||||
@@ -382,7 +382,7 @@ func TestFillForkChoiceMissingBlocks_CanSave(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// 5 nodes from the block tree 1. B0 - B3 - B4 - B6 - B8
|
||||
assert.Equal(t, 5, len(service.cfg.ForkChoiceStore.Nodes()), "Miss match nodes")
|
||||
assert.Equal(t, 5, service.cfg.ForkChoiceStore.NodeCount(), "Miss match nodes")
|
||||
assert.Equal(t, true, service.cfg.ForkChoiceStore.HasNode(bytesutil.ToBytes32(roots[4])), "Didn't save node")
|
||||
assert.Equal(t, true, service.cfg.ForkChoiceStore.HasNode(bytesutil.ToBytes32(roots[6])), "Didn't save node")
|
||||
assert.Equal(t, true, service.cfg.ForkChoiceStore.HasNode(bytesutil.ToBytes32(roots[8])), "Didn't save node")
|
||||
@@ -398,7 +398,7 @@ func TestFillForkChoiceMissingBlocks_RootsMatch(t *testing.T) {
|
||||
}
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
service.cfg.ForkChoiceStore = protoarray.New(0, 0, [32]byte{'A'})
|
||||
service.cfg.ForkChoiceStore = protoarray.New(0, 0)
|
||||
service.store.SetFinalizedCheckpt(ðpb.Checkpoint{Root: make([]byte, 32)})
|
||||
|
||||
genesisStateRoot := [32]byte{}
|
||||
@@ -423,7 +423,7 @@ func TestFillForkChoiceMissingBlocks_RootsMatch(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// 5 nodes from the block tree 1. B0 - B3 - B4 - B6 - B8
|
||||
assert.Equal(t, 5, len(service.cfg.ForkChoiceStore.Nodes()), "Miss match nodes")
|
||||
assert.Equal(t, 5, service.cfg.ForkChoiceStore.NodeCount(), "Miss match nodes")
|
||||
// Ensure all roots and their respective blocks exist.
|
||||
wantedRoots := [][]byte{roots[0], roots[3], roots[4], roots[6], roots[8]}
|
||||
for i, rt := range wantedRoots {
|
||||
@@ -442,7 +442,7 @@ func TestFillForkChoiceMissingBlocks_FilterFinalized(t *testing.T) {
|
||||
}
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
service.cfg.ForkChoiceStore = protoarray.New(0, 0, [32]byte{'A'})
|
||||
service.cfg.ForkChoiceStore = protoarray.New(0, 0)
|
||||
// Set finalized epoch to 1.
|
||||
service.store.SetFinalizedCheckpt(ðpb.Checkpoint{Epoch: 1})
|
||||
|
||||
@@ -479,7 +479,7 @@ func TestFillForkChoiceMissingBlocks_FilterFinalized(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// There should be 2 nodes, block 65 and block 64.
|
||||
assert.Equal(t, 2, len(service.cfg.ForkChoiceStore.Nodes()), "Miss match nodes")
|
||||
assert.Equal(t, 2, service.cfg.ForkChoiceStore.NodeCount(), "Miss match nodes")
|
||||
|
||||
// Block with slot 63 should be in fork choice because it's less than finalized epoch 1.
|
||||
assert.Equal(t, true, service.cfg.ForkChoiceStore.HasNode(r63), "Didn't save node")
|
||||
@@ -595,7 +595,7 @@ func TestAncestor_HandleSkipSlot(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
fcs := protoarray.New(0, 0, [32]byte{'a'})
|
||||
fcs := protoarray.New(0, 0)
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
@@ -668,7 +668,7 @@ func TestAncestor_CanUseForkchoice(t *testing.T) {
|
||||
beaconBlock.Block.ParentRoot = bytesutil.PadTo(b.Block.ParentRoot, 32)
|
||||
r, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.ProcessBlock(context.Background(), b.Block.Slot, r, bytesutil.ToBytes32(b.Block.ParentRoot), [32]byte{}, 0, 0)) // Saves blocks to fork choice store.
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.ProcessBlock(context.Background(), b.Block.Slot, r, bytesutil.ToBytes32(b.Block.ParentRoot), 0, 0, false)) // Saves blocks to fork choice store.
|
||||
}
|
||||
|
||||
r, err := service.ancestor(context.Background(), r200[:], 150)
|
||||
@@ -682,7 +682,7 @@ func TestAncestor_CanUseDB(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
fcs := protoarray.New(0, 0, [32]byte{'a'})
|
||||
fcs := protoarray.New(0, 0)
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
@@ -713,7 +713,7 @@ func TestAncestor_CanUseDB(t *testing.T) {
|
||||
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(beaconBlock))) // Saves blocks to DB.
|
||||
}
|
||||
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.ProcessBlock(context.Background(), 200, r200, r200, [32]byte{}, 0, 0))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.ProcessBlock(context.Background(), 200, r200, r200, 0, 0, false))
|
||||
|
||||
r, err := service.ancestor(context.Background(), r200[:], 150)
|
||||
require.NoError(t, err)
|
||||
@@ -740,7 +740,7 @@ func TestVerifyBlkDescendant(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
fcs := protoarray.New(0, 0, [32]byte{'a'})
|
||||
fcs := protoarray.New(0, 0)
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
@@ -870,7 +870,7 @@ func TestHandleEpochBoundary_UpdateFirstSlot(t *testing.T) {
|
||||
func TestOnBlock_CanFinalize(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
fcs := protoarray.New(0, 0, [32]byte{'a'})
|
||||
fcs := protoarray.New(0, 0)
|
||||
depositCache, err := depositcache.New()
|
||||
require.NoError(t, err)
|
||||
opts := []Option{
|
||||
@@ -996,49 +996,3 @@ func TestRemoveBlockAttestationsInPool_NonCanonical(t *testing.T) {
|
||||
require.NoError(t, service.pruneCanonicalAttsFromPool(ctx, r, wrapper.WrappedPhase0SignedBeaconBlock(b)))
|
||||
require.Equal(t, 1, service.cfg.AttPool.AggregatedAttestationCount())
|
||||
}
|
||||
|
||||
func TestService_saveSyncedTipsDB(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
service := setupBeaconChain(t, beaconDB)
|
||||
|
||||
b1 := util.NewBeaconBlock()
|
||||
b1.Block.Slot = 1
|
||||
b1.Block.ParentRoot = bytesutil.PadTo([]byte{'a'}, 32)
|
||||
r1, err := b1.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
b100 := util.NewBeaconBlock()
|
||||
b100.Block.Slot = 100
|
||||
b100.Block.ParentRoot = r1[:]
|
||||
r100, err := b100.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
b200 := util.NewBeaconBlock()
|
||||
b200.Block.Slot = 200
|
||||
b200.Block.ParentRoot = r1[:]
|
||||
r200, err := b200.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
for _, b := range []*ethpb.SignedBeaconBlock{b1, b100, b200} {
|
||||
beaconBlock := util.NewBeaconBlock()
|
||||
beaconBlock.Block.Slot = b.Block.Slot
|
||||
beaconBlock.Block.ParentRoot = bytesutil.PadTo(b.Block.ParentRoot, 32)
|
||||
r, err := b.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.ProcessBlock(context.Background(), b.Block.Slot, r, bytesutil.ToBytes32(b.Block.ParentRoot), [32]byte{}, 0, 0))
|
||||
}
|
||||
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.UpdateSyncedTipsWithValidRoot(ctx, r100))
|
||||
require.NoError(t, service.saveSyncedTipsDB(ctx))
|
||||
savedTips, err := service.cfg.BeaconDB.ValidatedTips(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(savedTips))
|
||||
require.Equal(t, types.Slot(1), savedTips[r1])
|
||||
require.Equal(t, types.Slot(100), savedTips[r100])
|
||||
|
||||
// Delete invalid root
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.UpdateSyncedTipsWithInvalidRoot(ctx, r200))
|
||||
require.NoError(t, service.saveSyncedTipsDB(ctx))
|
||||
savedTips, err = service.cfg.BeaconDB.ValidatedTips(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(savedTips))
|
||||
require.Equal(t, types.Slot(100), savedTips[r100])
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ func TestProcessAttestations_Ok(t *testing.T) {
|
||||
copied, err = transition.ProcessSlots(ctx, copied, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, copied, tRoot))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.ProcessBlock(ctx, 0, tRoot, tRoot, tRoot, 1, 1))
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.ProcessBlock(ctx, 0, tRoot, tRoot, 1, 1, false))
|
||||
require.NoError(t, service.cfg.AttPool.SaveForkchoiceAttestations(atts))
|
||||
service.processAttestations(ctx)
|
||||
require.Equal(t, 0, len(service.cfg.AttPool.ForkchoiceAttestations()))
|
||||
|
||||
@@ -86,7 +86,9 @@ func (s *Service) ReceiveBlockBatch(ctx context.Context, blocks []block.SignedBe
|
||||
|
||||
for i, b := range blocks {
|
||||
blockCopy := b.Copy()
|
||||
if err = s.handleBlockAfterBatchVerify(ctx, blockCopy, blkRoots[i], fCheckpoints[i], jCheckpoints[i]); err != nil {
|
||||
if err = s.handleBlockAfterBatchVerify(ctx, blockCopy,
|
||||
blkRoots[i], fCheckpoints[i],
|
||||
jCheckpoints[i], false /* optimistic status */); err != nil {
|
||||
tracing.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ func TestService_ReceiveBlock(t *testing.T) {
|
||||
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithForkChoiceStore(protoarray.New(0, 0, genesisBlockRoot)),
|
||||
WithForkChoiceStore(protoarray.New(0, 0)),
|
||||
WithAttestationPool(attestations.NewPool()),
|
||||
WithExitPool(voluntaryexits.NewPool()),
|
||||
WithStateNotifier(&blockchainTesting.MockStateNotifier{RecordEvents: true}),
|
||||
@@ -164,7 +164,7 @@ func TestService_ReceiveBlockUpdateHead(t *testing.T) {
|
||||
require.NoError(t, beaconDB.SaveState(ctx, genesis, genesisBlockRoot))
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithForkChoiceStore(protoarray.New(0, 0, genesisBlockRoot)),
|
||||
WithForkChoiceStore(protoarray.New(0, 0)),
|
||||
WithAttestationPool(attestations.NewPool()),
|
||||
WithExitPool(voluntaryexits.NewPool()),
|
||||
WithStateNotifier(&blockchainTesting.MockStateNotifier{RecordEvents: true}),
|
||||
@@ -192,7 +192,7 @@ func TestService_ReceiveBlockUpdateHead(t *testing.T) {
|
||||
t.Errorf("Received %d state notifications, expected at least 1", recvd)
|
||||
}
|
||||
// Verify fork choice has processed the block. (Genesis block and the new block)
|
||||
assert.Equal(t, 2, len(s.cfg.ForkChoiceStore.Nodes()))
|
||||
assert.Equal(t, 2, s.cfg.ForkChoiceStore.NodeCount())
|
||||
}
|
||||
|
||||
func TestService_ReceiveBlockBatch(t *testing.T) {
|
||||
@@ -240,11 +240,9 @@ func TestService_ReceiveBlockBatch(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
genesisBlockRoot, err := genesis.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithForkChoiceStore(protoarray.New(0, 0, genesisBlockRoot)),
|
||||
WithForkChoiceStore(protoarray.New(0, 0)),
|
||||
WithStateNotifier(&blockchainTesting.MockStateNotifier{RecordEvents: true}),
|
||||
WithStateGen(stategen.New(beaconDB)),
|
||||
}
|
||||
|
||||
@@ -184,13 +184,9 @@ func (s *Service) startFromSavedState(saved state.BeaconState) error {
|
||||
}
|
||||
s.store = store.New(justified, finalized)
|
||||
|
||||
store := protoarray.New(justified.Epoch, finalized.Epoch, bytesutil.ToBytes32(finalized.Root))
|
||||
store := protoarray.New(justified.Epoch, finalized.Epoch)
|
||||
s.cfg.ForkChoiceStore = store
|
||||
|
||||
if err := s.loadSyncedTips(originRoot, saved.Slot()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ss, err := slots.EpochStart(finalized.Epoch)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get start slot of finalized epoch")
|
||||
@@ -449,9 +445,8 @@ func (s *Service) saveGenesisData(ctx context.Context, genesisState state.Beacon
|
||||
genesisBlk.Block().Slot(),
|
||||
genesisBlkRoot,
|
||||
params.BeaconConfig().ZeroHash,
|
||||
[32]byte{},
|
||||
genesisCheckpoint.Epoch,
|
||||
genesisCheckpoint.Epoch); err != nil {
|
||||
genesisCheckpoint.Epoch, false); err != nil {
|
||||
log.Fatalf("Could not process genesis block for fork choice: %v", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/async/event"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain/store"
|
||||
mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
|
||||
@@ -122,7 +121,7 @@ func setupBeaconChain(t *testing.T, beaconDB db.Database) *Service {
|
||||
WithAttestationPool(attestations.NewPool()),
|
||||
WithP2PBroadcaster(&mockBroadcaster{}),
|
||||
WithStateNotifier(&mockBeaconNode{}),
|
||||
WithForkChoiceStore(protoarray.New(0, 0, params.BeaconConfig().ZeroHash)),
|
||||
WithForkChoiceStore(protoarray.New(0, 0)),
|
||||
WithAttestationService(attService),
|
||||
WithStateGen(stateGen),
|
||||
}
|
||||
@@ -164,74 +163,6 @@ func TestChainStartStop_Initialized(t *testing.T) {
|
||||
require.LogsContain(t, hook, "data already exists")
|
||||
}
|
||||
|
||||
func TestChainStart_SyncedTipsInDB(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
chainService := setupBeaconChain(t, beaconDB)
|
||||
|
||||
genesisBlk := util.NewBeaconBlock()
|
||||
blkRoot, err := genesisBlk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(genesisBlk)))
|
||||
s, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.SetSlot(1))
|
||||
require.NoError(t, beaconDB.SaveState(ctx, s, blkRoot))
|
||||
require.NoError(t, beaconDB.SaveHeadBlockRoot(ctx, blkRoot))
|
||||
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, blkRoot))
|
||||
require.NoError(t, beaconDB.SaveJustifiedCheckpoint(ctx, ðpb.Checkpoint{Root: blkRoot[:]}))
|
||||
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{Root: blkRoot[:]}))
|
||||
chainService.cfg.FinalizedStateAtStartUp = s
|
||||
|
||||
tips := make(map[[32]byte]types.Slot)
|
||||
tips[bytesutil.ToBytes32([]byte{'a'})] = 1
|
||||
tips[bytesutil.ToBytes32([]byte{'b'})] = 2
|
||||
require.NoError(t, beaconDB.UpdateValidatedTips(ctx, tips))
|
||||
|
||||
// Test the start function.
|
||||
chainService.Start()
|
||||
|
||||
// Test synced Tips in DB
|
||||
tips2 := chainService.cfg.ForkChoiceStore.SyncedTips()
|
||||
require.Equal(t, len(tips2), len(tips))
|
||||
for k, v := range tips {
|
||||
v2, ok := tips2[k]
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, v, v2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainStart_SyncedTipsNotInDB(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
chainService := setupBeaconChain(t, beaconDB)
|
||||
|
||||
genesisBlk := util.NewBeaconBlock()
|
||||
blkRoot, err := genesisBlk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(genesisBlk)))
|
||||
s, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.SetSlot(1))
|
||||
require.NoError(t, beaconDB.SaveState(ctx, s, blkRoot))
|
||||
require.NoError(t, beaconDB.SaveHeadBlockRoot(ctx, blkRoot))
|
||||
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, blkRoot))
|
||||
require.NoError(t, beaconDB.SaveJustifiedCheckpoint(ctx, ðpb.Checkpoint{Root: blkRoot[:]}))
|
||||
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{Root: blkRoot[:]}))
|
||||
chainService.cfg.FinalizedStateAtStartUp = s
|
||||
// Test the start function.
|
||||
chainService.Start()
|
||||
|
||||
// Test synced Tips in DB
|
||||
tips := chainService.cfg.ForkChoiceStore.SyncedTips()
|
||||
require.Equal(t, 1, len(tips))
|
||||
slot, ok := tips[blkRoot]
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, types.Slot(1), slot)
|
||||
}
|
||||
|
||||
func TestChainStartStop_GenesisZeroHashes(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
ctx := context.Background()
|
||||
@@ -519,7 +450,7 @@ func TestHasBlock_ForkChoiceAndDB(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
s := &Service{
|
||||
cfg: &config{ForkChoiceStore: protoarray.New(0, 0, [32]byte{}), BeaconDB: beaconDB},
|
||||
cfg: &config{ForkChoiceStore: protoarray.New(0, 0), BeaconDB: beaconDB},
|
||||
store: &store.Store{},
|
||||
}
|
||||
s.store.SetFinalizedCheckpt(ðpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]})
|
||||
@@ -528,7 +459,7 @@ func TestHasBlock_ForkChoiceAndDB(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
beaconState, err := util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.insertBlockAndAttestationsToForkChoiceStore(ctx, wrapper.WrappedPhase0SignedBeaconBlock(block).Block(), r, beaconState))
|
||||
require.NoError(t, s.insertBlockAndAttestationsToForkChoiceStore(ctx, wrapper.WrappedPhase0SignedBeaconBlock(block).Block(), r, beaconState, false))
|
||||
|
||||
assert.Equal(t, false, s.hasBlock(ctx, [32]byte{}), "Should not have block")
|
||||
assert.Equal(t, true, s.hasBlock(ctx, r), "Should have block")
|
||||
@@ -586,7 +517,7 @@ func BenchmarkHasBlockForkChoiceStore(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(b)
|
||||
s := &Service{
|
||||
cfg: &config{ForkChoiceStore: protoarray.New(0, 0, [32]byte{}), BeaconDB: beaconDB},
|
||||
cfg: &config{ForkChoiceStore: protoarray.New(0, 0), BeaconDB: beaconDB},
|
||||
store: &store.Store{},
|
||||
}
|
||||
s.store.SetFinalizedCheckpt(ðpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]})
|
||||
@@ -596,7 +527,7 @@ func BenchmarkHasBlockForkChoiceStore(b *testing.B) {
|
||||
bs := ðpb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{Root: make([]byte, 32)}, CurrentJustifiedCheckpoint: ðpb.Checkpoint{Root: make([]byte, 32)}}
|
||||
beaconState, err := v1.InitializeFromProto(bs)
|
||||
require.NoError(b, err)
|
||||
require.NoError(b, s.insertBlockAndAttestationsToForkChoiceStore(ctx, wrapper.WrappedPhase0SignedBeaconBlock(block).Block(), r, beaconState))
|
||||
require.NoError(b, s.insertBlockAndAttestationsToForkChoiceStore(ctx, wrapper.WrappedPhase0SignedBeaconBlock(block).Block(), r, beaconState, false))
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
||||
@@ -441,11 +441,11 @@ func (s *ChainService) HeadSyncContributionProofDomain(_ context.Context, _ type
|
||||
}
|
||||
|
||||
// IsOptimistic mocks the same method in the chain service.
|
||||
func (s *ChainService) IsOptimistic(_ context.Context) (bool, error) {
|
||||
func (s *ChainService) IsOptimistic() (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// IsOptimisticForRoot mocks the same method in the chain service.
|
||||
func (s *ChainService) IsOptimisticForRoot(_ context.Context, _ [32]byte, _ types.Slot) (bool, error) {
|
||||
func (s *ChainService) IsOptimisticForRoot(_ [32]byte) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -16,18 +16,18 @@ type ForkChoicer interface {
|
||||
Pruner // to clean old data for fork choice.
|
||||
Getter // to retrieve fork choice information.
|
||||
ProposerBooster // ability to boost timely-proposed block roots.
|
||||
SyncTipper // to update and retrieve validated sync tips.
|
||||
}
|
||||
|
||||
// HeadRetriever retrieves head root and optimistic info of the current chain.
|
||||
type HeadRetriever interface {
|
||||
Head(context.Context, types.Epoch, [32]byte, []uint64, types.Epoch) ([32]byte, error)
|
||||
Optimistic(ctx context.Context, root [32]byte, slot types.Slot) (bool, error)
|
||||
Tips() ([][32]byte, []types.Slot)
|
||||
IsOptimistic(root [32]byte) (bool, error)
|
||||
}
|
||||
|
||||
// BlockProcessor processes the block that's used for accounting fork choice.
|
||||
type BlockProcessor interface {
|
||||
ProcessBlock(context.Context, types.Slot, [32]byte, [32]byte, [32]byte, types.Epoch, types.Epoch) error
|
||||
ProcessBlock(context.Context, types.Slot, [32]byte, [32]byte, types.Epoch, types.Epoch, bool) error
|
||||
}
|
||||
|
||||
// AttestationProcessor processes the attestation that's used for accounting fork choice.
|
||||
@@ -48,19 +48,10 @@ type ProposerBooster interface {
|
||||
|
||||
// Getter returns fork choice related information.
|
||||
type Getter interface {
|
||||
Nodes() []*protoarray.Node
|
||||
Node([32]byte) *protoarray.Node
|
||||
HasNode([32]byte) bool
|
||||
Store() *protoarray.Store
|
||||
HasParent(root [32]byte) bool
|
||||
AncestorRoot(ctx context.Context, root [32]byte, slot types.Slot) ([]byte, error)
|
||||
IsCanonical(root [32]byte) bool
|
||||
}
|
||||
|
||||
// SyncTipper returns sync tips related information.
|
||||
type SyncTipper interface {
|
||||
SyncedTips() map[[32]byte]types.Slot
|
||||
SetSyncedTips(tips map[[32]byte]types.Slot) error
|
||||
UpdateSyncedTipsWithValidRoot(ctx context.Context, root [32]byte) error
|
||||
UpdateSyncedTipsWithInvalidRoot(ctx context.Context, root [32]byte) error
|
||||
NodeCount() int
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ go_library(
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"errors.go",
|
||||
"helpers.go",
|
||||
"forkchoice.go",
|
||||
"metrics.go",
|
||||
"node.go",
|
||||
"optimistic_sync.go",
|
||||
@@ -21,7 +21,6 @@ go_library(
|
||||
deps = [
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
@@ -35,7 +34,7 @@ go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"ffg_update_test.go",
|
||||
"helpers_test.go",
|
||||
"forkchoice_test.go",
|
||||
"no_vote_test.go",
|
||||
"node_test.go",
|
||||
"optimistic_sync_test.go",
|
||||
|
||||
@@ -2,13 +2,9 @@ package protoarray
|
||||
|
||||
import "errors"
|
||||
|
||||
var errNilNode = errors.New("invalid nil or unknown node")
|
||||
var errInvalidBalance = errors.New("invalid node balance")
|
||||
var errInvalidProposerBoostRoot = errors.New("invalid proposer boost root")
|
||||
var errUnknownFinalizedRoot = errors.New("unknown finalized root")
|
||||
var errUnknownJustifiedRoot = errors.New("unknown justified root")
|
||||
var errInvalidNodeIndex = errors.New("node index is invalid")
|
||||
var errInvalidJustifiedIndex = errors.New("justified index is invalid")
|
||||
var errInvalidBestChildIndex = errors.New("best child index is invalid")
|
||||
var errInvalidBestDescendantIndex = errors.New("best descendant index is invalid")
|
||||
var errInvalidParentDelta = errors.New("parent delta is invalid")
|
||||
var errInvalidNodeDelta = errors.New("node delta is invalid")
|
||||
var errInvalidDeltaLength = errors.New("delta length is invalid")
|
||||
var errInvalidSyncedTips = errors.New("invalid synced tips")
|
||||
var errInvalidOptimisticStatus = errors.New("invalid optimistic status")
|
||||
|
||||
@@ -27,9 +27,9 @@ func TestFFGUpdates_OneBranch(t *testing.T) {
|
||||
// 2 <- justified: 1, finalized: 0
|
||||
// |
|
||||
// 3 <- justified: 2, finalized: 1
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, [32]byte{}, 0, 0))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 2, indexToHash(2), indexToHash(1), [32]byte{}, 1, 0))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 3, indexToHash(3), indexToHash(2), [32]byte{}, 2, 1))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 2, indexToHash(2), indexToHash(1), 1, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 3, indexToHash(3), indexToHash(2), 2, 1, false))
|
||||
|
||||
// With starting justified epoch at 0, the head should be 3:
|
||||
// 0 <- start
|
||||
@@ -89,17 +89,17 @@ func TestFFGUpdates_TwoBranches(t *testing.T) {
|
||||
// | |
|
||||
// justified: 2, finalized: 0 -> 9 10 <- justified: 2, finalized: 0
|
||||
// Left branch.
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, [32]byte{}, 0, 0))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 2, indexToHash(3), indexToHash(1), [32]byte{}, 1, 0))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 3, indexToHash(5), indexToHash(3), [32]byte{}, 1, 0))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 4, indexToHash(7), indexToHash(5), [32]byte{}, 1, 0))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 4, indexToHash(9), indexToHash(7), [32]byte{}, 2, 0))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 2, indexToHash(3), indexToHash(1), 1, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 3, indexToHash(5), indexToHash(3), 1, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 4, indexToHash(7), indexToHash(5), 1, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 4, indexToHash(9), indexToHash(7), 2, 0, false))
|
||||
// Right branch.
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 1, indexToHash(2), params.BeaconConfig().ZeroHash, [32]byte{}, 0, 0))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 2, indexToHash(4), indexToHash(2), [32]byte{}, 0, 0))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 3, indexToHash(6), indexToHash(4), [32]byte{}, 0, 0))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 4, indexToHash(8), indexToHash(6), [32]byte{}, 1, 0))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 4, indexToHash(10), indexToHash(8), [32]byte{}, 2, 0))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 1, indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 2, indexToHash(4), indexToHash(2), 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 3, indexToHash(6), indexToHash(4), 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 4, indexToHash(8), indexToHash(6), 1, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 4, indexToHash(10), indexToHash(8), 2, 0, false))
|
||||
|
||||
// With start at 0, the head should be 10:
|
||||
// 0 <-- start
|
||||
@@ -183,18 +183,11 @@ func TestFFGUpdates_TwoBranches(t *testing.T) {
|
||||
}
|
||||
|
||||
func setup(justifiedEpoch, finalizedEpoch types.Epoch) *ForkChoice {
|
||||
f := New(0, 0, params.BeaconConfig().ZeroHash)
|
||||
f.store.nodesIndices[params.BeaconConfig().ZeroHash] = 0
|
||||
f.store.nodes = append(f.store.nodes, &Node{
|
||||
slot: 0,
|
||||
root: params.BeaconConfig().ZeroHash,
|
||||
parent: NonExistentNode,
|
||||
justifiedEpoch: justifiedEpoch,
|
||||
finalizedEpoch: finalizedEpoch,
|
||||
bestChild: NonExistentNode,
|
||||
bestDescendant: NonExistentNode,
|
||||
weight: 0,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
f := New(justifiedEpoch, finalizedEpoch)
|
||||
err := f.ProcessBlock(ctx, 0, params.BeaconConfig().ZeroHash, [32]byte{}, justifiedEpoch, finalizedEpoch, false)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
274
beacon-chain/forkchoice/protoarray/forkchoice.go
Normal file
274
beacon-chain/forkchoice/protoarray/forkchoice.go
Normal file
@@ -0,0 +1,274 @@
|
||||
package protoarray
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// This tracks the last reported head root. Used for metrics.
|
||||
var lastHeadRoot [32]byte
|
||||
|
||||
// New initializes a new fork choice store.
|
||||
func New(justifiedEpoch, finalizedEpoch types.Epoch) *ForkChoice {
|
||||
s := &Store{
|
||||
justifiedEpoch: justifiedEpoch,
|
||||
finalizedEpoch: finalizedEpoch,
|
||||
proposerBoostRoot: [32]byte{},
|
||||
nodeByRoot: make(map[[fieldparams.RootLength]byte]*Node),
|
||||
pruneThreshold: defaultPruneThreshold,
|
||||
}
|
||||
|
||||
b := make([]uint64, 0)
|
||||
v := make([]Vote, 0)
|
||||
return &ForkChoice{store: s, balances: b, votes: v}
|
||||
}
|
||||
|
||||
// NodeCount returns the current number of nodes in the Store
|
||||
func (f *ForkChoice) NodeCount() int {
|
||||
return len(f.store.nodeByRoot)
|
||||
}
|
||||
|
||||
// Head returns the head root from fork choice store.
|
||||
// It firsts computes validator's balance changes then recalculates block tree from leaves to root.
|
||||
func (f *ForkChoice) Head(
|
||||
ctx context.Context,
|
||||
justifiedEpoch types.Epoch,
|
||||
justifiedRoot [32]byte,
|
||||
justifiedStateBalances []uint64,
|
||||
finalizedEpoch types.Epoch,
|
||||
) ([32]byte, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.Head")
|
||||
defer span.End()
|
||||
f.votesLock.Lock()
|
||||
defer f.votesLock.Unlock()
|
||||
|
||||
calledHeadCount.Inc()
|
||||
|
||||
// Using the write lock here because `applyWeightChanges` that gets called subsequently requires a write operation.
|
||||
f.store.nodesLock.Lock()
|
||||
defer f.store.nodesLock.Unlock()
|
||||
|
||||
f.store.updateCheckpoints(justifiedEpoch, finalizedEpoch)
|
||||
|
||||
if err := f.updateBalances(justifiedStateBalances); err != nil {
|
||||
return [32]byte{}, errors.Wrap(err, "could not update balances")
|
||||
}
|
||||
|
||||
if err := f.store.applyProposerBoostScore(justifiedStateBalances); err != nil {
|
||||
return [32]byte{}, errors.Wrap(err, "could not apply proposer boost score")
|
||||
}
|
||||
|
||||
if err := f.store.treeRootNode.applyWeightChanges(ctx); err != nil {
|
||||
return [32]byte{}, errors.Wrap(err, "could not apply weight changes")
|
||||
}
|
||||
|
||||
if err := f.store.treeRootNode.updateBestDescendant(ctx, justifiedEpoch, finalizedEpoch); err != nil {
|
||||
return [32]byte{}, errors.Wrap(err, "could not update best descendant")
|
||||
}
|
||||
|
||||
return f.store.head(ctx, justifiedRoot)
|
||||
}
|
||||
|
||||
// ProcessAttestation processes attestation for vote accounting, it iterates around validator indices
|
||||
// and update their votes accordingly.
|
||||
func (f *ForkChoice) ProcessAttestation(ctx context.Context, validatorIndices []uint64, blockRoot [32]byte, targetEpoch types.Epoch) {
|
||||
_, span := trace.StartSpan(ctx, "protoArrayForkChoice.ProcessAttestation")
|
||||
defer span.End()
|
||||
f.votesLock.Lock()
|
||||
defer f.votesLock.Unlock()
|
||||
|
||||
for _, index := range validatorIndices {
|
||||
// Validator indices will grow the vote cache.
|
||||
for index >= uint64(len(f.votes)) {
|
||||
f.votes = append(f.votes, Vote{currentRoot: params.BeaconConfig().ZeroHash, nextRoot: params.BeaconConfig().ZeroHash})
|
||||
}
|
||||
|
||||
// Newly allocated vote if the root fields are untouched.
|
||||
newVote := f.votes[index].nextRoot == params.BeaconConfig().ZeroHash &&
|
||||
f.votes[index].currentRoot == params.BeaconConfig().ZeroHash
|
||||
|
||||
// Vote gets updated if it's newly allocated or high target epoch.
|
||||
if newVote || targetEpoch > f.votes[index].nextEpoch {
|
||||
f.votes[index].nextEpoch = targetEpoch
|
||||
f.votes[index].nextRoot = blockRoot
|
||||
}
|
||||
}
|
||||
|
||||
processedAttestationCount.Inc()
|
||||
}
|
||||
|
||||
// ProcessBlock processes a new block by inserting it to the fork choice store.
|
||||
func (f *ForkChoice) ProcessBlock(
|
||||
ctx context.Context,
|
||||
slot types.Slot,
|
||||
blockRoot, parentRoot [fieldparams.RootLength]byte,
|
||||
justifiedEpoch, finalizedEpoch types.Epoch, optimistic bool,
|
||||
) error {
|
||||
ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.ProcessBlock")
|
||||
defer span.End()
|
||||
|
||||
return f.store.insert(ctx, slot, blockRoot, parentRoot, justifiedEpoch, finalizedEpoch, optimistic)
|
||||
}
|
||||
|
||||
// Prune prunes the fork choice store with the new finalized root. The store is only pruned if the input
|
||||
// root is different than the current store finalized root, and the number of the store has met prune threshold.
|
||||
func (f *ForkChoice) Prune(ctx context.Context, finalizedRoot [32]byte) error {
|
||||
return f.store.prune(ctx, finalizedRoot)
|
||||
}
|
||||
|
||||
// HasNode returns true if the node exists in fork choice store,
|
||||
// false else wise.
|
||||
func (f *ForkChoice) HasNode(root [32]byte) bool {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
_, ok := f.store.nodeByRoot[root]
|
||||
return ok
|
||||
}
|
||||
|
||||
// HasParent returns true if the node parent exists in fork choice store,
|
||||
// false else wise.
|
||||
func (f *ForkChoice) HasParent(root [32]byte) bool {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
node, ok := f.store.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return node.parent != nil
|
||||
}
|
||||
|
||||
// IsCanonical returns true if the given root is part of the canonical chain.
|
||||
func (f *ForkChoice) IsCanonical(root [32]byte) bool {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
node, ok := f.store.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if node.bestDescendant == nil {
|
||||
if f.store.headNode.bestDescendant == nil {
|
||||
return node == f.store.headNode
|
||||
}
|
||||
return node == f.store.headNode.bestDescendant
|
||||
}
|
||||
if f.store.headNode.bestDescendant == nil {
|
||||
return node.bestDescendant == f.store.headNode
|
||||
}
|
||||
return node.bestDescendant == f.store.headNode.bestDescendant
|
||||
}
|
||||
|
||||
// IsOptimistic returns true if the given root has been optimistically synced.
|
||||
func (f *ForkChoice) IsOptimistic(root [32]byte) (bool, error) {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
node, ok := f.store.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
return false, errNilNode
|
||||
}
|
||||
|
||||
return node.optimistic, nil
|
||||
}
|
||||
|
||||
// Store returns the fork choice store
|
||||
func (f *ForkChoice) Store() *Store {
|
||||
return f.store
|
||||
}
|
||||
|
||||
// AncestorRoot returns the ancestor root of input block root at a given slot.
|
||||
func (f *ForkChoice) AncestorRoot(ctx context.Context, root [32]byte, slot types.Slot) ([]byte, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "protoArray.AncestorRoot")
|
||||
defer span.End()
|
||||
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
node, ok := f.store.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
return nil, errNilNode
|
||||
}
|
||||
|
||||
n := node
|
||||
for n != nil && n.slot > slot {
|
||||
if ctx.Err() != nil {
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
n = n.parent
|
||||
}
|
||||
|
||||
if n == nil {
|
||||
return nil, errNilNode
|
||||
}
|
||||
|
||||
return n.root[:], nil
|
||||
}
|
||||
|
||||
// updateBalances updates the balances that directly voted for each block taking into account the
|
||||
// validators' latest votes.
|
||||
func (f *ForkChoice) updateBalances(newBalances []uint64) error {
|
||||
for index, vote := range f.votes {
|
||||
// Skip if validator has never voted for current root and next root (i.e. if the
|
||||
// votes are zero hash aka genesis block), there's nothing to compute.
|
||||
if vote.currentRoot == params.BeaconConfig().ZeroHash && vote.nextRoot == params.BeaconConfig().ZeroHash {
|
||||
continue
|
||||
}
|
||||
|
||||
oldBalance := uint64(0)
|
||||
newBalance := uint64(0)
|
||||
// If the validator index did not exist in `f.balances` or
|
||||
// `newBalances` list above, the balance is just 0.
|
||||
if index < len(f.balances) {
|
||||
oldBalance = f.balances[index]
|
||||
}
|
||||
if index < len(newBalances) {
|
||||
newBalance = newBalances[index]
|
||||
}
|
||||
|
||||
// Update only if the validator's balance or vote has changed.
|
||||
if vote.currentRoot != vote.nextRoot || oldBalance != newBalance {
|
||||
// Ignore the vote if the root is not in fork choice
|
||||
// store, that means we have not seen the block before.
|
||||
nextNode, ok := f.store.nodeByRoot[vote.nextRoot]
|
||||
if ok && vote.nextRoot != params.BeaconConfig().ZeroHash {
|
||||
// Protection against nil node
|
||||
if nextNode == nil {
|
||||
return errNilNode
|
||||
}
|
||||
nextNode.balance += newBalance
|
||||
}
|
||||
|
||||
currentNode, ok := f.store.nodeByRoot[vote.currentRoot]
|
||||
if ok && vote.currentRoot != params.BeaconConfig().ZeroHash {
|
||||
// Protection against nil node
|
||||
if currentNode == nil {
|
||||
return errNilNode
|
||||
}
|
||||
if currentNode.balance < oldBalance {
|
||||
return errInvalidBalance
|
||||
}
|
||||
currentNode.balance -= oldBalance
|
||||
}
|
||||
}
|
||||
|
||||
// Rotate the validator vote.
|
||||
f.votes[index].currentRoot = vote.nextRoot
|
||||
}
|
||||
f.balances = newBalances
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tips returns a list of possible heads from fork choice store, it returns the
|
||||
// roots and the slots of the leaf nodes.
|
||||
func (f *ForkChoice) Tips() ([][32]byte, []types.Slot) {
|
||||
return f.store.tips()
|
||||
}
|
||||
168
beacon-chain/forkchoice/protoarray/forkchoice_test.go
Normal file
168
beacon-chain/forkchoice/protoarray/forkchoice_test.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package protoarray
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/crypto/hash"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func TestForkChoice_UpdateBalancesPositiveChange(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 2, indexToHash(2), indexToHash(1), 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 3, indexToHash(3), indexToHash(2), 0, 0, false))
|
||||
|
||||
f.votes = []Vote{
|
||||
{indexToHash(1), indexToHash(1), 0},
|
||||
{indexToHash(2), indexToHash(2), 0},
|
||||
{indexToHash(3), indexToHash(3), 0},
|
||||
}
|
||||
|
||||
// Each node gets one unique vote. The weight should look like 103 <- 102 <- 101 because
|
||||
// they get propagated back.
|
||||
require.NoError(t, f.updateBalances([]uint64{10, 20, 30}))
|
||||
s := f.store
|
||||
assert.Equal(t, uint64(10), s.nodeByRoot[indexToHash(1)].balance)
|
||||
assert.Equal(t, uint64(20), s.nodeByRoot[indexToHash(2)].balance)
|
||||
assert.Equal(t, uint64(30), s.nodeByRoot[indexToHash(3)].balance)
|
||||
}
|
||||
|
||||
func TestForkChoice_UpdateBalancesNegativeChange(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 2, indexToHash(2), indexToHash(1), 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 3, indexToHash(3), indexToHash(2), 0, 0, false))
|
||||
s := f.store
|
||||
s.nodeByRoot[indexToHash(1)].balance = 100
|
||||
s.nodeByRoot[indexToHash(2)].balance = 100
|
||||
s.nodeByRoot[indexToHash(3)].balance = 100
|
||||
|
||||
f.balances = []uint64{100, 100, 100}
|
||||
f.votes = []Vote{
|
||||
{indexToHash(1), indexToHash(1), 0},
|
||||
{indexToHash(2), indexToHash(2), 0},
|
||||
{indexToHash(3), indexToHash(3), 0},
|
||||
}
|
||||
|
||||
require.NoError(t, f.updateBalances([]uint64{10, 20, 30}))
|
||||
assert.Equal(t, uint64(10), s.nodeByRoot[indexToHash(1)].balance)
|
||||
assert.Equal(t, uint64(20), s.nodeByRoot[indexToHash(2)].balance)
|
||||
assert.Equal(t, uint64(30), s.nodeByRoot[indexToHash(3)].balance)
|
||||
}
|
||||
|
||||
func TestForkChoice_IsCanonical(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 3, indexToHash(3), indexToHash(1), 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 4, indexToHash(4), indexToHash(2), 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 5, indexToHash(5), indexToHash(4), 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 6, indexToHash(6), indexToHash(5), 1, 1, false))
|
||||
|
||||
require.Equal(t, true, f.IsCanonical(params.BeaconConfig().ZeroHash))
|
||||
require.Equal(t, false, f.IsCanonical(indexToHash(1)))
|
||||
require.Equal(t, true, f.IsCanonical(indexToHash(2)))
|
||||
require.Equal(t, false, f.IsCanonical(indexToHash(3)))
|
||||
require.Equal(t, true, f.IsCanonical(indexToHash(4)))
|
||||
require.Equal(t, true, f.IsCanonical(indexToHash(5)))
|
||||
require.Equal(t, true, f.IsCanonical(indexToHash(6)))
|
||||
}
|
||||
|
||||
func TestForkChoice_IsCanonicalReorg(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 2, [32]byte{'2'}, params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 3, [32]byte{'3'}, [32]byte{'1'}, 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 4, [32]byte{'4'}, [32]byte{'2'}, 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 5, [32]byte{'5'}, [32]byte{'4'}, 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 6, [32]byte{'6'}, [32]byte{'5'}, 1, 1, false))
|
||||
|
||||
f.store.nodesLock.Lock()
|
||||
f.store.nodeByRoot[[32]byte{'3'}].balance = 10
|
||||
require.NoError(t, f.store.treeRootNode.applyWeightChanges(ctx))
|
||||
require.Equal(t, uint64(10), f.store.nodeByRoot[[32]byte{'1'}].weight)
|
||||
require.Equal(t, uint64(0), f.store.nodeByRoot[[32]byte{'2'}].weight)
|
||||
|
||||
require.NoError(t, f.store.treeRootNode.updateBestDescendant(ctx, 1, 1))
|
||||
require.DeepEqual(t, [32]byte{'3'}, f.store.treeRootNode.bestDescendant.root)
|
||||
f.store.nodesLock.Unlock()
|
||||
|
||||
h, err := f.store.head(ctx, [32]byte{'1'})
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, [32]byte{'3'}, h)
|
||||
require.DeepEqual(t, h, f.store.headNode.root)
|
||||
|
||||
require.Equal(t, true, f.IsCanonical(params.BeaconConfig().ZeroHash))
|
||||
require.Equal(t, true, f.IsCanonical([32]byte{'1'}))
|
||||
require.Equal(t, false, f.IsCanonical([32]byte{'2'}))
|
||||
require.Equal(t, true, f.IsCanonical([32]byte{'3'}))
|
||||
require.Equal(t, false, f.IsCanonical([32]byte{'4'}))
|
||||
require.Equal(t, false, f.IsCanonical([32]byte{'5'}))
|
||||
require.Equal(t, false, f.IsCanonical([32]byte{'6'}))
|
||||
}
|
||||
|
||||
func TestForkChoice_AncestorRoot(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 2, indexToHash(2), indexToHash(1), 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 5, indexToHash(3), indexToHash(2), 1, 1, false))
|
||||
f.store.treeRootNode = f.store.nodeByRoot[indexToHash(1)]
|
||||
f.store.treeRootNode.parent = nil
|
||||
|
||||
r, err := f.AncestorRoot(ctx, indexToHash(3), 6)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, bytesutil.ToBytes32(r), indexToHash(3))
|
||||
|
||||
_, err = f.AncestorRoot(ctx, indexToHash(3), 0)
|
||||
assert.ErrorContains(t, errNilNode.Error(), err)
|
||||
|
||||
root, err := f.AncestorRoot(ctx, indexToHash(3), 5)
|
||||
require.NoError(t, err)
|
||||
hash3 := indexToHash(3)
|
||||
require.DeepEqual(t, hash3[:], root)
|
||||
root, err = f.AncestorRoot(ctx, indexToHash(3), 1)
|
||||
require.NoError(t, err)
|
||||
hash1 := indexToHash(1)
|
||||
require.DeepEqual(t, hash1[:], root)
|
||||
}
|
||||
|
||||
func TestForkChoice_AncestorEqualSlot(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.ProcessBlock(ctx, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 101, [32]byte{'3'}, [32]byte{'1'}, 1, 1, false))
|
||||
|
||||
r, err := f.AncestorRoot(ctx, [32]byte{'3'}, 100)
|
||||
require.NoError(t, err)
|
||||
root := bytesutil.ToBytes32(r)
|
||||
require.Equal(t, root, [32]byte{'1'})
|
||||
}
|
||||
|
||||
func TestForkChoice_AncestorLowerSlot(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.ProcessBlock(ctx, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 200, [32]byte{'3'}, [32]byte{'1'}, 1, 1, false))
|
||||
|
||||
r, err := f.AncestorRoot(ctx, [32]byte{'3'}, 150)
|
||||
require.NoError(t, err)
|
||||
root := bytesutil.ToBytes32(r)
|
||||
require.Equal(t, root, [32]byte{'1'})
|
||||
}
|
||||
|
||||
func indexToHash(i uint64) [32]byte {
|
||||
var b [8]byte
|
||||
binary.LittleEndian.PutUint64(b[:], i)
|
||||
return hash.Hash(b[:])
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package protoarray
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// This computes validator balance delta from validator votes.
|
||||
// It returns a list of deltas that represents the difference between old balances and new balances.
|
||||
func computeDeltas(
|
||||
ctx context.Context,
|
||||
blockIndices map[[32]byte]uint64,
|
||||
votes []Vote,
|
||||
oldBalances, newBalances []uint64,
|
||||
) ([]int, []Vote, error) {
|
||||
_, span := trace.StartSpan(ctx, "protoArrayForkChoice.computeDeltas")
|
||||
defer span.End()
|
||||
|
||||
deltas := make([]int, len(blockIndices))
|
||||
|
||||
for validatorIndex, vote := range votes {
|
||||
oldBalance := uint64(0)
|
||||
newBalance := uint64(0)
|
||||
|
||||
// Skip if validator has never voted for current root and next root (i.e. if the
|
||||
// votes are zero hash aka genesis block), there's nothing to compute.
|
||||
if vote.currentRoot == params.BeaconConfig().ZeroHash && vote.nextRoot == params.BeaconConfig().ZeroHash {
|
||||
continue
|
||||
}
|
||||
|
||||
// If the validator index did not exist in `oldBalance` or `newBalance` list above, the balance is just 0.
|
||||
if validatorIndex < len(oldBalances) {
|
||||
oldBalance = oldBalances[validatorIndex]
|
||||
}
|
||||
if validatorIndex < len(newBalances) {
|
||||
newBalance = newBalances[validatorIndex]
|
||||
}
|
||||
|
||||
// Perform delta only if the validator's balance or vote has changed.
|
||||
if vote.currentRoot != vote.nextRoot || oldBalance != newBalance {
|
||||
// Ignore the vote if it's not known in `blockIndices`,
|
||||
// that means we have not seen the block before.
|
||||
nextDeltaIndex, ok := blockIndices[vote.nextRoot]
|
||||
if ok {
|
||||
// Protection against out of bound, the `nextDeltaIndex` which defines
|
||||
// the block location in the dag can not exceed the total `delta` length.
|
||||
if int(nextDeltaIndex) >= len(deltas) {
|
||||
return nil, nil, errInvalidNodeDelta
|
||||
}
|
||||
deltas[nextDeltaIndex] += int(newBalance)
|
||||
}
|
||||
|
||||
currentDeltaIndex, ok := blockIndices[vote.currentRoot]
|
||||
if ok {
|
||||
// Protection against out of bound (same as above)
|
||||
if int(currentDeltaIndex) >= len(deltas) {
|
||||
return nil, nil, errInvalidNodeDelta
|
||||
}
|
||||
deltas[currentDeltaIndex] -= int(oldBalance)
|
||||
}
|
||||
}
|
||||
|
||||
// Rotate the validator vote.
|
||||
vote.currentRoot = vote.nextRoot
|
||||
votes[validatorIndex] = vote
|
||||
}
|
||||
|
||||
return deltas, votes, nil
|
||||
}
|
||||
|
||||
// This return a copy of the proto array node object.
|
||||
func copyNode(node *Node) *Node {
|
||||
if node == nil {
|
||||
return &Node{}
|
||||
}
|
||||
|
||||
copiedRoot := [32]byte{}
|
||||
copy(copiedRoot[:], node.root[:])
|
||||
|
||||
return &Node{
|
||||
slot: node.slot,
|
||||
root: copiedRoot,
|
||||
parent: node.parent,
|
||||
justifiedEpoch: node.justifiedEpoch,
|
||||
finalizedEpoch: node.finalizedEpoch,
|
||||
weight: node.weight,
|
||||
bestChild: node.bestChild,
|
||||
bestDescendant: node.bestDescendant,
|
||||
}
|
||||
}
|
||||
@@ -1,249 +0,0 @@
|
||||
package protoarray
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/crypto/hash"
|
||||
"github.com/prysmaticlabs/prysm/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func TestComputeDelta_ZeroHash(t *testing.T) {
|
||||
validatorCount := uint64(16)
|
||||
indices := make(map[[32]byte]uint64)
|
||||
votes := make([]Vote, 0)
|
||||
oldBalances := make([]uint64, 0)
|
||||
newBalances := make([]uint64, 0)
|
||||
|
||||
for i := uint64(0); i < validatorCount; i++ {
|
||||
indices[indexToHash(i)] = i
|
||||
votes = append(votes, Vote{params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0})
|
||||
oldBalances = append(oldBalances, 0)
|
||||
newBalances = append(newBalances, 0)
|
||||
}
|
||||
|
||||
delta, _, err := computeDeltas(context.Background(), indices, votes, oldBalances, newBalances)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int(validatorCount), len(delta))
|
||||
|
||||
for _, d := range delta {
|
||||
assert.Equal(t, 0, d)
|
||||
}
|
||||
for _, vote := range votes {
|
||||
assert.Equal(t, vote.currentRoot, vote.nextRoot, "The vote should not have changed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeDelta_AllVoteTheSame(t *testing.T) {
|
||||
validatorCount := uint64(16)
|
||||
balance := uint64(32)
|
||||
indices := make(map[[32]byte]uint64)
|
||||
votes := make([]Vote, 0)
|
||||
oldBalances := make([]uint64, 0)
|
||||
newBalances := make([]uint64, 0)
|
||||
|
||||
for i := uint64(0); i < validatorCount; i++ {
|
||||
indices[indexToHash(i)] = i
|
||||
votes = append(votes, Vote{params.BeaconConfig().ZeroHash, indexToHash(0), 0})
|
||||
oldBalances = append(oldBalances, balance)
|
||||
newBalances = append(newBalances, balance)
|
||||
}
|
||||
|
||||
delta, _, err := computeDeltas(context.Background(), indices, votes, oldBalances, newBalances)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int(validatorCount), len(delta))
|
||||
|
||||
for i, d := range delta {
|
||||
if i == 0 {
|
||||
assert.Equal(t, balance*validatorCount, uint64(d))
|
||||
} else {
|
||||
assert.Equal(t, 0, d)
|
||||
}
|
||||
}
|
||||
|
||||
for _, vote := range votes {
|
||||
assert.Equal(t, vote.currentRoot, vote.nextRoot, "The vote should not have changed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeDelta_DifferentVotes(t *testing.T) {
|
||||
validatorCount := uint64(16)
|
||||
balance := uint64(32)
|
||||
indices := make(map[[32]byte]uint64)
|
||||
votes := make([]Vote, 0)
|
||||
oldBalances := make([]uint64, 0)
|
||||
newBalances := make([]uint64, 0)
|
||||
|
||||
for i := uint64(0); i < validatorCount; i++ {
|
||||
indices[indexToHash(i)] = i
|
||||
votes = append(votes, Vote{params.BeaconConfig().ZeroHash, indexToHash(i), 0})
|
||||
oldBalances = append(oldBalances, balance)
|
||||
newBalances = append(newBalances, balance)
|
||||
}
|
||||
|
||||
delta, _, err := computeDeltas(context.Background(), indices, votes, oldBalances, newBalances)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int(validatorCount), len(delta))
|
||||
|
||||
for _, d := range delta {
|
||||
assert.Equal(t, balance, uint64(d))
|
||||
}
|
||||
|
||||
for _, vote := range votes {
|
||||
assert.Equal(t, vote.currentRoot, vote.nextRoot, "The vote should not have changed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeDelta_MovingVotes(t *testing.T) {
|
||||
validatorCount := uint64(16)
|
||||
balance := uint64(32)
|
||||
indices := make(map[[32]byte]uint64)
|
||||
votes := make([]Vote, 0)
|
||||
oldBalances := make([]uint64, 0)
|
||||
newBalances := make([]uint64, 0)
|
||||
|
||||
lastIndex := uint64(len(indices) - 1)
|
||||
for i := uint64(0); i < validatorCount; i++ {
|
||||
indices[indexToHash(i)] = i
|
||||
votes = append(votes, Vote{indexToHash(0), indexToHash(lastIndex), 0})
|
||||
oldBalances = append(oldBalances, balance)
|
||||
newBalances = append(newBalances, balance)
|
||||
}
|
||||
|
||||
delta, _, err := computeDeltas(context.Background(), indices, votes, oldBalances, newBalances)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int(validatorCount), len(delta))
|
||||
|
||||
for i, d := range delta {
|
||||
if i == 0 {
|
||||
assert.Equal(t, -int(balance*validatorCount), d, "First root should have negative delta")
|
||||
} else if i == int(lastIndex) {
|
||||
assert.Equal(t, int(balance*validatorCount), d, "Last root should have positive delta")
|
||||
} else {
|
||||
assert.Equal(t, 0, d)
|
||||
}
|
||||
}
|
||||
|
||||
for _, vote := range votes {
|
||||
assert.Equal(t, vote.currentRoot, vote.nextRoot, "The vote should not have changed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeDelta_MoveOutOfTree(t *testing.T) {
|
||||
balance := uint64(32)
|
||||
indices := make(map[[32]byte]uint64)
|
||||
votes := make([]Vote, 0)
|
||||
oldBalances := []uint64{balance, balance}
|
||||
newBalances := []uint64{balance, balance}
|
||||
|
||||
indices[indexToHash(1)] = 0
|
||||
|
||||
votes = append(votes,
|
||||
Vote{indexToHash(1), params.BeaconConfig().ZeroHash, 0},
|
||||
Vote{indexToHash(1), [32]byte{'A'}, 0})
|
||||
|
||||
delta, _, err := computeDeltas(context.Background(), indices, votes, oldBalances, newBalances)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, len(delta))
|
||||
assert.Equal(t, 0-2*int(balance), delta[0])
|
||||
|
||||
for _, vote := range votes {
|
||||
assert.Equal(t, vote.currentRoot, vote.nextRoot, "The vote should not have changed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeDelta_ChangingBalances(t *testing.T) {
|
||||
oldBalance := uint64(32)
|
||||
newBalance := oldBalance * 2
|
||||
validatorCount := uint64(16)
|
||||
indices := make(map[[32]byte]uint64)
|
||||
votes := make([]Vote, 0)
|
||||
oldBalances := make([]uint64, 0)
|
||||
newBalances := make([]uint64, 0)
|
||||
|
||||
indices[indexToHash(1)] = 0
|
||||
|
||||
for i := uint64(0); i < validatorCount; i++ {
|
||||
indices[indexToHash(i)] = i
|
||||
votes = append(votes, Vote{indexToHash(0), indexToHash(1), 0})
|
||||
oldBalances = append(oldBalances, oldBalance)
|
||||
newBalances = append(newBalances, newBalance)
|
||||
}
|
||||
|
||||
delta, _, err := computeDeltas(context.Background(), indices, votes, oldBalances, newBalances)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 16, len(delta))
|
||||
|
||||
for i, d := range delta {
|
||||
if i == 0 {
|
||||
assert.Equal(t, -int(oldBalance*validatorCount), d, "First root should have negative delta")
|
||||
} else if i == 1 {
|
||||
assert.Equal(t, int(newBalance*validatorCount), d, "Last root should have positive delta")
|
||||
} else {
|
||||
assert.Equal(t, 0, d)
|
||||
}
|
||||
}
|
||||
|
||||
for _, vote := range votes {
|
||||
assert.Equal(t, vote.currentRoot, vote.nextRoot, "The vote should not have changed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeDelta_ValidatorAppear(t *testing.T) {
|
||||
balance := uint64(32)
|
||||
indices := make(map[[32]byte]uint64)
|
||||
votes := make([]Vote, 0)
|
||||
oldBalances := []uint64{balance}
|
||||
newBalances := []uint64{balance, balance}
|
||||
|
||||
indices[indexToHash(1)] = 0
|
||||
indices[indexToHash(2)] = 1
|
||||
|
||||
votes = append(votes,
|
||||
Vote{indexToHash(1), indexToHash(2), 0},
|
||||
Vote{indexToHash(1), indexToHash(2), 0})
|
||||
|
||||
delta, _, err := computeDeltas(context.Background(), indices, votes, oldBalances, newBalances)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, len(delta))
|
||||
assert.Equal(t, 0-int(balance), delta[0])
|
||||
assert.Equal(t, 2*int(balance), delta[1])
|
||||
|
||||
for _, vote := range votes {
|
||||
assert.Equal(t, vote.currentRoot, vote.nextRoot, "The vote should not have changed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeDelta_ValidatorDisappears(t *testing.T) {
|
||||
balance := uint64(32)
|
||||
indices := make(map[[32]byte]uint64)
|
||||
votes := make([]Vote, 0)
|
||||
oldBalances := []uint64{balance, balance}
|
||||
newBalances := []uint64{balance}
|
||||
|
||||
indices[indexToHash(1)] = 0
|
||||
indices[indexToHash(2)] = 1
|
||||
|
||||
votes = append(votes,
|
||||
Vote{indexToHash(1), indexToHash(2), 0},
|
||||
Vote{indexToHash(1), indexToHash(2), 0})
|
||||
|
||||
delta, _, err := computeDeltas(context.Background(), indices, votes, oldBalances, newBalances)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, len(delta))
|
||||
assert.Equal(t, 0-2*int(balance), delta[0])
|
||||
assert.Equal(t, int(balance), delta[1])
|
||||
|
||||
for _, vote := range votes {
|
||||
assert.Equal(t, vote.currentRoot, vote.nextRoot, "The vote should not have changed")
|
||||
}
|
||||
}
|
||||
|
||||
func indexToHash(i uint64) [32]byte {
|
||||
var b [8]byte
|
||||
binary.LittleEndian.PutUint64(b[:], i)
|
||||
return hash.Hash(b[:])
|
||||
}
|
||||
@@ -48,16 +48,10 @@ var (
|
||||
Help: "The number of times pruning happened.",
|
||||
},
|
||||
)
|
||||
lastSyncedTipSlot = promauto.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "proto_array_last_synced_tip_slot",
|
||||
Help: "The slot of the last fully validated block added to the proto array.",
|
||||
},
|
||||
)
|
||||
syncedTipsCount = promauto.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "proto_array_synced_tips_count",
|
||||
Help: "The number of elements in the syncedTips structure.",
|
||||
optimisticCount = promauto.NewCounter(
|
||||
prometheus.CounterOpts{
|
||||
Name: "proto_array_optimistic_count",
|
||||
Help: "The number of blocks that have been optimistically synced.",
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// 0
|
||||
// /
|
||||
// 2 <- head
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
|
||||
@@ -33,7 +33,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// 0
|
||||
// / \
|
||||
// head -> 2 1
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
|
||||
@@ -44,7 +44,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// head -> 2 1
|
||||
// |
|
||||
// 3
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(3), indexToHash(1), [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(3), indexToHash(1), 1, 1, false))
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
|
||||
@@ -55,7 +55,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// 2 1
|
||||
// | |
|
||||
// head -> 4 3
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(4), indexToHash(2), [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(4), indexToHash(2), 1, 1, false))
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1")
|
||||
@@ -68,7 +68,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// head -> 4 3
|
||||
// |
|
||||
// 5 <- justified epoch = 2
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(5), indexToHash(4), [32]byte{}, 2, 1))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(5), indexToHash(4), 2, 1, false))
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1")
|
||||
@@ -107,7 +107,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
|
||||
// 5
|
||||
// |
|
||||
// 6 <- head
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(6), indexToHash(5), [32]byte{}, 2, 1))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(6), indexToHash(5), 2, 1, false))
|
||||
r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, indexToHash(6), r, "Incorrect head for with justified epoch at 2")
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package protoarray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
)
|
||||
|
||||
// Slot of the fork choice node.
|
||||
@@ -9,13 +13,23 @@ func (n *Node) Slot() types.Slot {
|
||||
return n.slot
|
||||
}
|
||||
|
||||
// Balance returns the current balance of the Node
|
||||
func (n *Node) Balance() uint64 {
|
||||
return n.balance
|
||||
}
|
||||
|
||||
// Optimistic returns the optimistic status of the node
|
||||
func (n *Node) Optimistic() bool {
|
||||
return n.optimistic
|
||||
}
|
||||
|
||||
// Root of the fork choice node.
|
||||
func (n *Node) Root() [32]byte {
|
||||
return n.root
|
||||
}
|
||||
|
||||
// Parent of the fork choice node.
|
||||
func (n *Node) Parent() uint64 {
|
||||
func (n *Node) Parent() *Node {
|
||||
return n.parent
|
||||
}
|
||||
|
||||
@@ -34,17 +48,126 @@ func (n *Node) Weight() uint64 {
|
||||
return n.weight
|
||||
}
|
||||
|
||||
// BestChild of the fork choice node.
|
||||
func (n *Node) BestChild() uint64 {
|
||||
return n.bestChild
|
||||
// Children returns the children of this node
|
||||
func (n *Node) Children() []*Node {
|
||||
return n.children
|
||||
}
|
||||
|
||||
// depth returns the length of the path to the root of Fork Choice
|
||||
func (n *Node) depth() uint64 {
|
||||
ret := uint64(0)
|
||||
for node := n.parent; node != nil; node = node.parent {
|
||||
ret += 1
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// applyWeightChanges recomputes the weight of the node passed as an argument and all of its descendants,
|
||||
// using the current balance stored in each node. This function requires a lock
|
||||
// in Store.nodesLock
|
||||
func (n *Node) applyWeightChanges(ctx context.Context) error {
|
||||
// Recursively calling the children to sum their weights.
|
||||
childrenWeight := uint64(0)
|
||||
for _, child := range n.children {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
if err := child.applyWeightChanges(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
childrenWeight += child.weight
|
||||
}
|
||||
if n.root == params.BeaconConfig().ZeroHash {
|
||||
return nil
|
||||
}
|
||||
n.weight = n.balance + childrenWeight
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateBestDescendant updates the best descendant of this node and its children.
|
||||
func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finalizedEpoch types.Epoch) error {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
if len(n.children) == 0 {
|
||||
n.bestDescendant = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
var bestChild *Node
|
||||
bestWeight := uint64(0)
|
||||
hasViableDescendant := false
|
||||
for _, child := range n.children {
|
||||
if child == nil {
|
||||
return errNilNode
|
||||
}
|
||||
if err := child.updateBestDescendant(ctx, justifiedEpoch, finalizedEpoch); err != nil {
|
||||
return err
|
||||
}
|
||||
childLeadsToViableHead := child.leadsToViableHead(justifiedEpoch, finalizedEpoch)
|
||||
if childLeadsToViableHead && !hasViableDescendant {
|
||||
// The child leads to a viable head, but the current
|
||||
// parent's best child doesn't.
|
||||
bestWeight = child.weight
|
||||
bestChild = child
|
||||
hasViableDescendant = true
|
||||
} else if childLeadsToViableHead {
|
||||
// If both are viable, compare their weights.
|
||||
if child.weight == bestWeight {
|
||||
// Tie-breaker of equal weights by root.
|
||||
if bytes.Compare(child.root[:], bestChild.root[:]) > 0 {
|
||||
bestChild = child
|
||||
}
|
||||
} else if child.weight > bestWeight {
|
||||
bestChild = child
|
||||
bestWeight = child.weight
|
||||
}
|
||||
}
|
||||
}
|
||||
if hasViableDescendant {
|
||||
if bestChild.bestDescendant == nil {
|
||||
n.bestDescendant = bestChild
|
||||
} else {
|
||||
n.bestDescendant = bestChild.bestDescendant
|
||||
}
|
||||
} else {
|
||||
n.bestDescendant = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// viableForHead returns true if the node is viable to head.
|
||||
// Any node with different finalized or justified epoch than
|
||||
// the ones in fork choice store should not be viable to head.
|
||||
func (n *Node) viableForHead(justifiedEpoch, finalizedEpoch types.Epoch) bool {
|
||||
justified := justifiedEpoch == n.justifiedEpoch || justifiedEpoch == 0
|
||||
finalized := finalizedEpoch == n.finalizedEpoch || finalizedEpoch == 0
|
||||
|
||||
return justified && finalized
|
||||
}
|
||||
|
||||
func (n *Node) leadsToViableHead(justifiedEpoch, finalizedEpoch types.Epoch) bool {
|
||||
if n.bestDescendant == nil {
|
||||
return n.viableForHead(justifiedEpoch, finalizedEpoch)
|
||||
}
|
||||
return n.bestDescendant.viableForHead(justifiedEpoch, finalizedEpoch)
|
||||
}
|
||||
|
||||
// BestDescendant of the fork choice node.
|
||||
func (n *Node) BestDescendant() uint64 {
|
||||
func (n *Node) BestDescendant() *Node {
|
||||
return n.bestDescendant
|
||||
}
|
||||
|
||||
// Graffiti of the fork choice node.
|
||||
func (n *Node) Graffiti() [32]byte {
|
||||
return n.graffiti
|
||||
// setNodeAndParentValidated sets the current node and the parent as validated (i.e. non-optimistic).
|
||||
func (n *Node) setNodeAndParentValidated(ctx context.Context) error {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
if !n.optimistic || n.parent == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
n.optimistic = false
|
||||
return n.parent.setNodeAndParentValidated(ctx)
|
||||
}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
package protoarray
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func TestNode_Getters(t *testing.T) {
|
||||
slot := types.Slot(100)
|
||||
root := [32]byte{'a'}
|
||||
parent := uint64(10)
|
||||
parent := &Node{}
|
||||
jEpoch := types.Epoch(20)
|
||||
fEpoch := types.Epoch(30)
|
||||
weight := uint64(10000)
|
||||
bestChild := uint64(5)
|
||||
bestDescendant := uint64(4)
|
||||
graffiti := [32]byte{'b'}
|
||||
balance := uint64(10)
|
||||
n := &Node{
|
||||
slot: slot,
|
||||
root: root,
|
||||
@@ -24,9 +25,7 @@ func TestNode_Getters(t *testing.T) {
|
||||
justifiedEpoch: jEpoch,
|
||||
finalizedEpoch: fEpoch,
|
||||
weight: weight,
|
||||
bestChild: bestChild,
|
||||
bestDescendant: bestDescendant,
|
||||
graffiti: graffiti,
|
||||
balance: balance,
|
||||
}
|
||||
|
||||
require.Equal(t, slot, n.Slot())
|
||||
@@ -35,7 +34,199 @@ func TestNode_Getters(t *testing.T) {
|
||||
require.Equal(t, jEpoch, n.JustifiedEpoch())
|
||||
require.Equal(t, fEpoch, n.FinalizedEpoch())
|
||||
require.Equal(t, weight, n.Weight())
|
||||
require.Equal(t, bestChild, n.BestChild())
|
||||
require.Equal(t, bestDescendant, n.BestDescendant())
|
||||
require.Equal(t, graffiti, n.Graffiti())
|
||||
descendantNil := n.bestDescendant == nil
|
||||
require.Equal(t, true, descendantNil)
|
||||
}
|
||||
|
||||
func TestNode_ApplyWeightChanges_PositiveChange(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 2, indexToHash(2), indexToHash(1), 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 3, indexToHash(3), indexToHash(2), 0, 0, false))
|
||||
|
||||
// The updated balances of each node is 100
|
||||
s := f.store
|
||||
|
||||
s.nodesLock.Lock()
|
||||
defer s.nodesLock.Unlock()
|
||||
s.nodeByRoot[indexToHash(1)].balance = 100
|
||||
s.nodeByRoot[indexToHash(2)].balance = 100
|
||||
s.nodeByRoot[indexToHash(3)].balance = 100
|
||||
|
||||
assert.NoError(t, s.treeRootNode.applyWeightChanges(ctx))
|
||||
|
||||
assert.Equal(t, uint64(300), s.nodeByRoot[indexToHash(1)].weight)
|
||||
assert.Equal(t, uint64(200), s.nodeByRoot[indexToHash(2)].weight)
|
||||
assert.Equal(t, uint64(100), s.nodeByRoot[indexToHash(3)].weight)
|
||||
}
|
||||
|
||||
func TestNode_ApplyWeightChanges_NegativeChange(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 2, indexToHash(2), indexToHash(1), 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 3, indexToHash(3), indexToHash(2), 0, 0, false))
|
||||
|
||||
// The updated balances of each node is 100
|
||||
s := f.store
|
||||
s.nodesLock.Lock()
|
||||
defer s.nodesLock.Unlock()
|
||||
s.nodeByRoot[indexToHash(1)].weight = 400
|
||||
s.nodeByRoot[indexToHash(2)].weight = 400
|
||||
s.nodeByRoot[indexToHash(3)].weight = 400
|
||||
|
||||
s.nodeByRoot[indexToHash(1)].balance = 100
|
||||
s.nodeByRoot[indexToHash(2)].balance = 100
|
||||
s.nodeByRoot[indexToHash(3)].balance = 100
|
||||
|
||||
assert.NoError(t, s.treeRootNode.applyWeightChanges(ctx))
|
||||
|
||||
assert.Equal(t, uint64(300), s.nodeByRoot[indexToHash(1)].weight)
|
||||
assert.Equal(t, uint64(200), s.nodeByRoot[indexToHash(2)].weight)
|
||||
assert.Equal(t, uint64(100), s.nodeByRoot[indexToHash(3)].weight)
|
||||
}
|
||||
|
||||
func TestNode_UpdateBestDescendant_NonViableChild(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
// Input child is not viable.
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 2, 3, false))
|
||||
|
||||
// Verify parent's best child and best descendant are `none`.
|
||||
s := f.store
|
||||
assert.Equal(t, 1, len(s.treeRootNode.children))
|
||||
nilBestDescendant := s.treeRootNode.bestDescendant == nil
|
||||
assert.Equal(t, true, nilBestDescendant)
|
||||
}
|
||||
|
||||
func TestNode_UpdateBestDescendant_ViableChild(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
// Input child is best descendant
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
|
||||
s := f.store
|
||||
assert.Equal(t, 1, len(s.treeRootNode.children))
|
||||
assert.Equal(t, s.treeRootNode.children[0], s.treeRootNode.bestDescendant)
|
||||
}
|
||||
|
||||
func TestNode_UpdateBestDescendant_HigherWeightChild(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
// Input child is best descendant
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
|
||||
s := f.store
|
||||
s.nodeByRoot[indexToHash(1)].weight = 100
|
||||
s.nodeByRoot[indexToHash(2)].weight = 200
|
||||
assert.NoError(t, s.treeRootNode.updateBestDescendant(ctx, 1, 1))
|
||||
|
||||
assert.Equal(t, 2, len(s.treeRootNode.children))
|
||||
assert.Equal(t, s.treeRootNode.children[1], s.treeRootNode.bestDescendant)
|
||||
}
|
||||
|
||||
func TestNode_UpdateBestDescendant_LowerWeightChild(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
// Input child is best descendant
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
|
||||
s := f.store
|
||||
s.nodeByRoot[indexToHash(1)].weight = 200
|
||||
s.nodeByRoot[indexToHash(2)].weight = 100
|
||||
assert.NoError(t, s.treeRootNode.updateBestDescendant(ctx, 1, 1))
|
||||
|
||||
assert.Equal(t, 2, len(s.treeRootNode.children))
|
||||
assert.Equal(t, s.treeRootNode.children[0], s.treeRootNode.bestDescendant)
|
||||
}
|
||||
|
||||
func TestNode_TestDepth(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
// Input child is best descendant
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 2, indexToHash(2), indexToHash(1), 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 3, indexToHash(3), params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
|
||||
s := f.store
|
||||
require.Equal(t, s.nodeByRoot[indexToHash(2)].depth(), uint64(2))
|
||||
require.Equal(t, s.nodeByRoot[indexToHash(3)].depth(), uint64(1))
|
||||
}
|
||||
|
||||
func TestNode_ViableForHead(t *testing.T) {
|
||||
tests := []struct {
|
||||
n *Node
|
||||
justifiedEpoch types.Epoch
|
||||
finalizedEpoch types.Epoch
|
||||
want bool
|
||||
}{
|
||||
{&Node{}, 0, 0, true},
|
||||
{&Node{}, 1, 0, false},
|
||||
{&Node{}, 0, 1, false},
|
||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 1, 1, true},
|
||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 2, 2, false},
|
||||
{&Node{finalizedEpoch: 3, justifiedEpoch: 4}, 4, 3, true},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := tc.n.viableForHead(tc.justifiedEpoch, tc.finalizedEpoch)
|
||||
assert.Equal(t, tc.want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNode_LeadsToViableHead(t *testing.T) {
|
||||
f := setup(4, 3)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 3, indexToHash(3), indexToHash(1), 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 4, indexToHash(4), indexToHash(2), 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 5, indexToHash(5), indexToHash(3), 4, 3, false))
|
||||
|
||||
require.Equal(t, true, f.store.treeRootNode.leadsToViableHead(4, 3))
|
||||
require.Equal(t, true, f.store.nodeByRoot[indexToHash(5)].leadsToViableHead(4, 3))
|
||||
require.Equal(t, false, f.store.nodeByRoot[indexToHash(2)].leadsToViableHead(4, 3))
|
||||
require.Equal(t, false, f.store.nodeByRoot[indexToHash(4)].leadsToViableHead(4, 3))
|
||||
}
|
||||
|
||||
func TestNode_SetFullyValidated(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
// insert blocks in the fork pattern (optimistic status in parenthesis)
|
||||
//
|
||||
// 0 (false) -- 1 (false) -- 2 (false) -- 3 (true) -- 4 (true)
|
||||
// \
|
||||
// -- 5 (true)
|
||||
//
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 2, indexToHash(2), indexToHash(1), 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 3, indexToHash(3), indexToHash(2), 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 4, indexToHash(4), indexToHash(3), 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 5, indexToHash(5), indexToHash(1), 1, 1, true))
|
||||
|
||||
opt, err := f.IsOptimistic(indexToHash(5))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, opt)
|
||||
|
||||
opt, err = f.IsOptimistic(indexToHash(4))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, opt)
|
||||
|
||||
require.NoError(t, f.store.nodeByRoot[indexToHash(4)].setNodeAndParentValidated(ctx))
|
||||
|
||||
// block 5 should still be optimistic
|
||||
opt, err = f.IsOptimistic(indexToHash(5))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, opt)
|
||||
|
||||
// block 4 and 3 should now be valid
|
||||
opt, err = f.IsOptimistic(indexToHash(4))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, opt)
|
||||
|
||||
opt, err = f.IsOptimistic(indexToHash(3))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, opt)
|
||||
}
|
||||
|
||||
@@ -2,321 +2,49 @@ package protoarray
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
)
|
||||
|
||||
// This returns the minimum and maximum slot of the synced_tips tree
|
||||
func (f *ForkChoice) boundarySyncedTips() (types.Slot, types.Slot) {
|
||||
f.syncedTips.RLock()
|
||||
defer f.syncedTips.RUnlock()
|
||||
// removeNode removes the node with the given root and all of its children
|
||||
// from the Fork Choice Store.
|
||||
func (s *Store) removeNode(ctx context.Context, root [32]byte) error {
|
||||
s.nodesLock.Lock()
|
||||
defer s.nodesLock.Unlock()
|
||||
|
||||
min := params.BeaconConfig().FarFutureSlot
|
||||
max := types.Slot(0)
|
||||
for _, slot := range f.syncedTips.validatedTips {
|
||||
if slot > max {
|
||||
max = slot
|
||||
}
|
||||
if slot < min {
|
||||
min = slot
|
||||
}
|
||||
node, ok := s.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
return errNilNode
|
||||
}
|
||||
return min, max
|
||||
}
|
||||
|
||||
// Optimistic returns true if this node is optimistically synced
|
||||
// A optimistically synced block is synced as usual, but its
|
||||
// execution payload is not validated, while the EL is still syncing.
|
||||
// WARNING: this function does not check if slot corresponds to the
|
||||
// block with the given root. An incorrect response may be
|
||||
// returned when requesting earlier than finalized epoch due
|
||||
// to pruning of non-canonical branches. A requests for a
|
||||
// combination root/slot of an available block is guaranteed
|
||||
// to yield the correct result. The caller is responsible for
|
||||
// checking the block's availability. A consensus bug could be
|
||||
// a cause of getting this wrong, so think twice before passing
|
||||
// a wrong pair.
|
||||
func (f *ForkChoice) Optimistic(ctx context.Context, root [32]byte, slot types.Slot) (bool, error) {
|
||||
if ctx.Err() != nil {
|
||||
return false, ctx.Err()
|
||||
if !node.optimistic || node.parent == nil {
|
||||
return errInvalidOptimisticStatus
|
||||
}
|
||||
// If the node is a synced tip, then it's fully validated
|
||||
f.syncedTips.RLock()
|
||||
_, ok := f.syncedTips.validatedTips[root]
|
||||
if ok {
|
||||
return false, nil
|
||||
}
|
||||
f.syncedTips.RUnlock()
|
||||
|
||||
// If the slot is higher than the max synced tip, it's optimistic
|
||||
min, max := f.boundarySyncedTips()
|
||||
if slot > max {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// If the slot is lower than the min synced tip, it's fully validated
|
||||
if slot <= min {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// If we reached this point then the block has to be in the Fork Choice
|
||||
// Store!
|
||||
f.store.nodesLock.RLock()
|
||||
index, ok := f.store.nodesIndices[root]
|
||||
if !ok {
|
||||
// This should not happen
|
||||
f.store.nodesLock.RUnlock()
|
||||
return false, fmt.Errorf("invalid root, slot combination, got %#x, %d",
|
||||
bytesutil.Trunc(root[:]), slot)
|
||||
}
|
||||
node := f.store.nodes[index]
|
||||
|
||||
// if the node is a leaf of the Fork Choice tree, then it's
|
||||
// optimistic
|
||||
childIndex := node.BestChild()
|
||||
if childIndex == NonExistentNode {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// recurse to the child
|
||||
child := f.store.nodes[childIndex]
|
||||
root = child.root
|
||||
slot = child.slot
|
||||
f.store.nodesLock.RUnlock()
|
||||
return f.Optimistic(ctx, root, slot)
|
||||
}
|
||||
|
||||
// This function returns the index of sync tip node that's ancestor to the input node.
|
||||
// In the event of none, `NonExistentNode` is returned.
|
||||
// This internal method assumes the caller holds a lock on syncedTips and s.nodesLock
|
||||
func (s *Store) findSyncedTip(ctx context.Context, node *Node, syncedTips *optimisticStore) (uint64, error) {
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
return 0, ctx.Err()
|
||||
}
|
||||
if _, ok := syncedTips.validatedTips[node.root]; ok {
|
||||
return s.nodesIndices[node.root], nil
|
||||
}
|
||||
if node.parent == NonExistentNode {
|
||||
return NonExistentNode, nil
|
||||
}
|
||||
node = s.nodes[node.parent]
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateSyncedTipsWithValidRoot is called with the root of a block that was returned as
|
||||
// VALID by the EL. This routine recomputes and updates the synced_tips map to
|
||||
// account for this new tip.
|
||||
func (f *ForkChoice) UpdateSyncedTipsWithValidRoot(ctx context.Context, root [32]byte) error {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
// We can only update if given root is in Fork Choice
|
||||
index, ok := f.store.nodesIndices[root]
|
||||
if !ok {
|
||||
return errInvalidNodeIndex
|
||||
}
|
||||
|
||||
// We can only update if root is a leaf in Fork Choice
|
||||
node := f.store.nodes[index]
|
||||
if node.bestChild != NonExistentNode {
|
||||
return errInvalidBestChildIndex
|
||||
}
|
||||
|
||||
// Stop early if the root is part of validated tips
|
||||
f.syncedTips.Lock()
|
||||
defer f.syncedTips.Unlock()
|
||||
_, ok = f.syncedTips.validatedTips[root]
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cache root and slot to validated tips
|
||||
newTips := make(map[[32]byte]types.Slot)
|
||||
newValidSlot := node.slot
|
||||
newTips[root] = newValidSlot
|
||||
|
||||
// Compute the full valid path from the given node to its previous synced tip
|
||||
// This path will now consist of fully validated blocks. Notice that
|
||||
// the previous tip may have been outside the Fork Choice store.
|
||||
// In this case, only one block can be in syncedTips as the whole
|
||||
// Fork Choice would be a descendant of this block.
|
||||
validPath := make(map[uint64]bool)
|
||||
validPath[index] = true
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
parentIndex := node.parent
|
||||
if parentIndex == NonExistentNode {
|
||||
break
|
||||
}
|
||||
if parentIndex >= uint64(len(f.store.nodes)) {
|
||||
return errInvalidNodeIndex
|
||||
}
|
||||
node = f.store.nodes[parentIndex]
|
||||
_, ok = f.syncedTips.validatedTips[node.root]
|
||||
if ok {
|
||||
break
|
||||
}
|
||||
validPath[parentIndex] = true
|
||||
}
|
||||
|
||||
// Retrieve the list of leaves in the Fork Choice
|
||||
// These are all the nodes that have NonExistentNode as best child.
|
||||
leaves, err := f.store.leaves()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// For each leaf, recompute the new tip.
|
||||
for _, i := range leaves {
|
||||
node = f.store.nodes[i]
|
||||
j := i
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
// Stop if we reached the previous tip
|
||||
_, ok = f.syncedTips.validatedTips[node.root]
|
||||
if ok {
|
||||
newTips[node.root] = node.slot
|
||||
break
|
||||
}
|
||||
|
||||
// Stop if we reach valid path
|
||||
_, ok = validPath[j]
|
||||
if ok {
|
||||
newTips[node.root] = node.slot
|
||||
break
|
||||
}
|
||||
|
||||
j = node.parent
|
||||
if j == NonExistentNode {
|
||||
break
|
||||
}
|
||||
if j >= uint64(len(f.store.nodes)) {
|
||||
return errInvalidNodeIndex
|
||||
}
|
||||
node = f.store.nodes[j]
|
||||
}
|
||||
}
|
||||
|
||||
f.syncedTips.validatedTips = newTips
|
||||
lastSyncedTipSlot.Set(float64(newValidSlot))
|
||||
syncedTipsCount.Set(float64(len(newTips)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateSyncedTipsWithInvalidRoot updates the synced_tips map when the block with the given root becomes INVALID.
|
||||
func (f *ForkChoice) UpdateSyncedTipsWithInvalidRoot(ctx context.Context, root [32]byte) error {
|
||||
f.store.nodesLock.Lock()
|
||||
defer f.store.nodesLock.Unlock()
|
||||
idx, ok := f.store.nodesIndices[root]
|
||||
if !ok {
|
||||
return errInvalidNodeIndex
|
||||
}
|
||||
node := f.store.nodes[idx]
|
||||
// We only support changing status for the tips in Fork Choice store.
|
||||
if node.bestChild != NonExistentNode {
|
||||
return errInvalidNodeIndex
|
||||
}
|
||||
|
||||
parentIndex := node.parent
|
||||
// This should not happen
|
||||
if parentIndex == NonExistentNode {
|
||||
return errInvalidNodeIndex
|
||||
}
|
||||
// Update the weights of the nodes subtracting the INVALID node's weight
|
||||
weight := node.weight
|
||||
node = f.store.nodes[parentIndex]
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
node.weight -= weight
|
||||
if node.parent == NonExistentNode {
|
||||
break
|
||||
}
|
||||
node = f.store.nodes[node.parent]
|
||||
}
|
||||
parent := copyNode(f.store.nodes[parentIndex])
|
||||
|
||||
// delete the invalid node, order is important
|
||||
f.store.nodes = append(f.store.nodes[:idx], f.store.nodes[idx+1:]...)
|
||||
delete(f.store.nodesIndices, root)
|
||||
// Fix parent and best child for each node
|
||||
for _, node := range f.store.nodes {
|
||||
if node.parent == NonExistentNode {
|
||||
node.parent = NonExistentNode
|
||||
} else if node.parent > idx {
|
||||
node.parent -= 1
|
||||
}
|
||||
if node.bestChild == NonExistentNode || node.bestChild == idx {
|
||||
node.bestChild = NonExistentNode
|
||||
} else if node.bestChild > idx {
|
||||
node.bestChild -= 1
|
||||
}
|
||||
if node.bestDescendant == NonExistentNode || node.bestDescendant == idx {
|
||||
node.bestDescendant = NonExistentNode
|
||||
} else if node.bestDescendant > idx {
|
||||
node.bestDescendant -= 1
|
||||
}
|
||||
}
|
||||
|
||||
// Update the parent's best child and best descendant if necessary.
|
||||
if parent.bestChild == idx || parent.bestDescendant == idx {
|
||||
for childIndex, child := range f.store.nodes {
|
||||
if child.parent == parentIndex {
|
||||
err := f.store.updateBestChildAndDescendant(
|
||||
parentIndex, uint64(childIndex))
|
||||
if err != nil {
|
||||
return err
|
||||
children := node.parent.children
|
||||
if len(children) == 1 {
|
||||
node.parent.children = []*Node{}
|
||||
} else {
|
||||
for i, n := range children {
|
||||
if n == node {
|
||||
if i != len(children)-1 {
|
||||
children[i] = children[len(children)-1]
|
||||
}
|
||||
node.parent.children = children[:len(children)-2]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return s.removeNodeAndChildren(ctx, node)
|
||||
}
|
||||
|
||||
// Return early if the parent is not a synced_tip.
|
||||
f.syncedTips.Lock()
|
||||
defer f.syncedTips.Unlock()
|
||||
parentRoot := parent.root
|
||||
_, ok = f.syncedTips.validatedTips[parentRoot]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
leaves, err := f.store.leaves()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, i := range leaves {
|
||||
node = f.store.nodes[i]
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
// Return early if the parent is still a synced tip
|
||||
if node.root == parentRoot {
|
||||
return nil
|
||||
}
|
||||
_, ok = f.syncedTips.validatedTips[node.root]
|
||||
if ok {
|
||||
break
|
||||
}
|
||||
if node.parent == NonExistentNode {
|
||||
break
|
||||
}
|
||||
node = f.store.nodes[node.parent]
|
||||
// removeNodeAndChildren removes `node` and all of its descendant from the Store
|
||||
func (s *Store) removeNodeAndChildren(ctx context.Context, node *Node) error {
|
||||
for _, child := range node.children {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
if err := s.removeNodeAndChildren(ctx, child); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
delete(f.syncedTips.validatedTips, parentRoot)
|
||||
syncedTipsCount.Set(float64(len(f.syncedTips.validatedTips)))
|
||||
|
||||
delete(s.nodeByRoot, node.root)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,206 +4,12 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
// We test the algorithm to check the optimistic status of a node. The
|
||||
// status for this test is the following branching diagram
|
||||
//
|
||||
// -- E -- F
|
||||
// /
|
||||
// -- C -- D
|
||||
// /
|
||||
// 0 -- 1 -- A -- B -- J -- K
|
||||
// \ /
|
||||
// -- G -- H -- I
|
||||
//
|
||||
// Here nodes 0, 1, A, B, C, D are fully validated and nodes
|
||||
// E, F, G, H, J, K are optimistic.
|
||||
// Synced Tips are nodes B, C, D
|
||||
// nodes 0 and 1 are outside the Fork Choice Store.
|
||||
|
||||
func TestOptimistic(t *testing.T) {
|
||||
root0 := bytesutil.ToBytes32([]byte("hello0"))
|
||||
slot0 := types.Slot(98)
|
||||
root1 := bytesutil.ToBytes32([]byte("hello1"))
|
||||
slot1 := types.Slot(99)
|
||||
|
||||
nodeA := &Node{
|
||||
slot: types.Slot(100),
|
||||
root: bytesutil.ToBytes32([]byte("helloA")),
|
||||
bestChild: 1,
|
||||
}
|
||||
nodeB := &Node{
|
||||
slot: types.Slot(101),
|
||||
root: bytesutil.ToBytes32([]byte("helloB")),
|
||||
bestChild: 2,
|
||||
parent: 0,
|
||||
}
|
||||
nodeC := &Node{
|
||||
slot: types.Slot(102),
|
||||
root: bytesutil.ToBytes32([]byte("helloC")),
|
||||
bestChild: 3,
|
||||
parent: 1,
|
||||
}
|
||||
nodeD := &Node{
|
||||
slot: types.Slot(103),
|
||||
root: bytesutil.ToBytes32([]byte("helloD")),
|
||||
bestChild: NonExistentNode,
|
||||
parent: 2,
|
||||
}
|
||||
nodeE := &Node{
|
||||
slot: types.Slot(103),
|
||||
root: bytesutil.ToBytes32([]byte("helloE")),
|
||||
bestChild: 5,
|
||||
parent: 2,
|
||||
}
|
||||
nodeF := &Node{
|
||||
slot: types.Slot(104),
|
||||
root: bytesutil.ToBytes32([]byte("helloF")),
|
||||
bestChild: NonExistentNode,
|
||||
parent: 4,
|
||||
}
|
||||
nodeG := &Node{
|
||||
slot: types.Slot(102),
|
||||
root: bytesutil.ToBytes32([]byte("helloG")),
|
||||
bestChild: 7,
|
||||
parent: 1,
|
||||
}
|
||||
nodeH := &Node{
|
||||
slot: types.Slot(103),
|
||||
root: bytesutil.ToBytes32([]byte("helloH")),
|
||||
bestChild: 8,
|
||||
parent: 6,
|
||||
}
|
||||
nodeI := &Node{
|
||||
slot: types.Slot(104),
|
||||
root: bytesutil.ToBytes32([]byte("helloI")),
|
||||
bestChild: NonExistentNode,
|
||||
parent: 7,
|
||||
}
|
||||
nodeJ := &Node{
|
||||
slot: types.Slot(103),
|
||||
root: bytesutil.ToBytes32([]byte("helloJ")),
|
||||
bestChild: 10,
|
||||
parent: 6,
|
||||
}
|
||||
nodeK := &Node{
|
||||
slot: types.Slot(104),
|
||||
root: bytesutil.ToBytes32([]byte("helloK")),
|
||||
bestChild: NonExistentNode,
|
||||
parent: 9,
|
||||
}
|
||||
nodes := []*Node{
|
||||
nodeA,
|
||||
nodeB,
|
||||
nodeC,
|
||||
nodeD,
|
||||
nodeE,
|
||||
nodeF,
|
||||
nodeG,
|
||||
nodeH,
|
||||
nodeI,
|
||||
nodeJ,
|
||||
nodeK,
|
||||
}
|
||||
ni := map[[32]byte]uint64{
|
||||
nodeA.root: 0,
|
||||
nodeB.root: 1,
|
||||
nodeC.root: 2,
|
||||
nodeD.root: 3,
|
||||
nodeE.root: 4,
|
||||
nodeF.root: 5,
|
||||
nodeG.root: 6,
|
||||
nodeH.root: 7,
|
||||
nodeI.root: 8,
|
||||
nodeJ.root: 9,
|
||||
nodeK.root: 10,
|
||||
}
|
||||
|
||||
s := &Store{
|
||||
nodes: nodes,
|
||||
nodesIndices: ni,
|
||||
}
|
||||
|
||||
tips := map[[32]byte]types.Slot{
|
||||
nodeB.root: nodeB.slot,
|
||||
nodeC.root: nodeC.slot,
|
||||
nodeD.root: nodeD.slot,
|
||||
}
|
||||
st := &optimisticStore{
|
||||
validatedTips: tips,
|
||||
}
|
||||
f := &ForkChoice{
|
||||
store: s,
|
||||
syncedTips: st,
|
||||
}
|
||||
ctx := context.Background()
|
||||
// We test the implementation of boundarySyncedTips
|
||||
min, max := f.boundarySyncedTips()
|
||||
require.Equal(t, min, types.Slot(101), "minimum tip slot is different")
|
||||
require.Equal(t, max, types.Slot(103), "maximum tip slot is different")
|
||||
|
||||
// We test first nodes outside the Fork Choice store
|
||||
op, err := f.Optimistic(ctx, root0, slot0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, op, false)
|
||||
|
||||
op, err = f.Optimistic(ctx, root1, slot1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, op, false)
|
||||
|
||||
// We check all nodes in the Fork Choice store.
|
||||
op, err = f.Optimistic(ctx, nodeA.root, nodeA.slot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, op, false)
|
||||
|
||||
op, err = f.Optimistic(ctx, nodeB.root, nodeB.slot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, op, false)
|
||||
|
||||
op, err = f.Optimistic(ctx, nodeC.root, nodeC.slot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, op, false)
|
||||
|
||||
op, err = f.Optimistic(ctx, nodeD.root, nodeD.slot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, op, false)
|
||||
|
||||
op, err = f.Optimistic(ctx, nodeE.root, nodeE.slot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, op, true)
|
||||
|
||||
op, err = f.Optimistic(ctx, nodeF.root, nodeF.slot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, op, true)
|
||||
|
||||
op, err = f.Optimistic(ctx, nodeG.root, nodeG.slot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, op, true)
|
||||
|
||||
op, err = f.Optimistic(ctx, nodeH.root, nodeH.slot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, op, true)
|
||||
|
||||
op, err = f.Optimistic(ctx, nodeI.root, nodeI.slot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, op, true)
|
||||
|
||||
op, err = f.Optimistic(ctx, nodeJ.root, nodeJ.slot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, op, true)
|
||||
|
||||
op, err = f.Optimistic(ctx, nodeK.root, nodeK.slot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, op, true)
|
||||
}
|
||||
|
||||
// This tests the algorithm to update syncedTips
|
||||
// We start with the following diagram
|
||||
// We test the algorithm to update a node from SYNCING to INVALID
|
||||
// We start with the same diagram as above:
|
||||
//
|
||||
// E -- F
|
||||
// /
|
||||
@@ -213,338 +19,52 @@ func TestOptimistic(t *testing.T) {
|
||||
// \ \
|
||||
// J -- K -- L
|
||||
//
|
||||
// And every block in the Fork choice is optimistic. Synced_Tips contains a
|
||||
// single block that is outside of Fork choice
|
||||
// And every block in the Fork choice is optimistic.
|
||||
//
|
||||
func TestUpdateSyncTipsWithValidRoots(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
f := setup(1, 1)
|
||||
|
||||
require.NoError(t, f.ProcessBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, [32]byte{}, 1, 1))
|
||||
func TestPruneInvalid(t *testing.T) {
|
||||
tests := []struct {
|
||||
root [32]byte // the root of the new VALID block
|
||||
tips map[[32]byte]types.Slot // the old synced tips
|
||||
newTips map[[32]byte]types.Slot // the updated synced tips
|
||||
wantedErr error
|
||||
root [32]byte // the root of the new INVALID block
|
||||
wantedNodeNumber int
|
||||
}{
|
||||
{
|
||||
[32]byte{'i'},
|
||||
map[[32]byte]types.Slot{[32]byte{'z'}: 90},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
[32]byte{'d'}: 103,
|
||||
[32]byte{'g'}: 104,
|
||||
[32]byte{'i'}: 106,
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[32]byte{'i'},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
[32]byte{'d'}: 103,
|
||||
},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
[32]byte{'d'}: 103,
|
||||
[32]byte{'g'}: 104,
|
||||
[32]byte{'i'}: 106,
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[32]byte{'i'},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
[32]byte{'d'}: 103,
|
||||
[32]byte{'e'}: 103,
|
||||
},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
[32]byte{'e'}: 104,
|
||||
[32]byte{'g'}: 104,
|
||||
[32]byte{'i'}: 106,
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[32]byte{'j'},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
[32]byte{'f'}: 105,
|
||||
[32]byte{'g'}: 104,
|
||||
[32]byte{'i'}: 106,
|
||||
},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'f'}: 105,
|
||||
[32]byte{'g'}: 104,
|
||||
[32]byte{'i'}: 106,
|
||||
[32]byte{'j'}: 102,
|
||||
},
|
||||
nil,
|
||||
12,
|
||||
},
|
||||
{
|
||||
[32]byte{'c'},
|
||||
4,
|
||||
},
|
||||
{
|
||||
[32]byte{'i'},
|
||||
12,
|
||||
},
|
||||
{
|
||||
[32]byte{'h'},
|
||||
11,
|
||||
},
|
||||
{
|
||||
[32]byte{'g'},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
[32]byte{'f'}: 105,
|
||||
[32]byte{'g'}: 104,
|
||||
[32]byte{'i'}: 106,
|
||||
},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'f'}: 105,
|
||||
[32]byte{'g'}: 104,
|
||||
[32]byte{'i'}: 106,
|
||||
[32]byte{'j'}: 102,
|
||||
},
|
||||
errInvalidBestChildIndex,
|
||||
},
|
||||
{
|
||||
[32]byte{'p'},
|
||||
map[[32]byte]types.Slot{},
|
||||
map[[32]byte]types.Slot{},
|
||||
errInvalidNodeIndex,
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
f.syncedTips.Lock()
|
||||
f.syncedTips.validatedTips = tc.tips
|
||||
f.syncedTips.Unlock()
|
||||
err := f.UpdateSyncedTipsWithValidRoot(context.Background(), tc.root)
|
||||
if tc.wantedErr != nil {
|
||||
require.ErrorIs(t, err, tc.wantedErr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
f.syncedTips.RLock()
|
||||
require.DeepEqual(t, f.syncedTips.validatedTips, tc.newTips)
|
||||
f.syncedTips.RUnlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We test the algorithm to update a node from SYNCING to INVALID
|
||||
// We start with the same diagram as above:
|
||||
//
|
||||
// E(2) -- F(1)
|
||||
// /
|
||||
// C(7) -- D(6)
|
||||
// / \
|
||||
// A(10) -- B(9) G(3) -- H(1) -- I(0)
|
||||
// \ \
|
||||
// J(1) -- K(1) -- L(0)
|
||||
//
|
||||
// And every block in the Fork choice is optimistic. Synced_Tips contains a
|
||||
// single block that is outside of Fork choice. The numbers in parentheses are
|
||||
// the weights of the nodes before removal
|
||||
//
|
||||
func TestUpdateSyncTipsWithInvalidRoot(t *testing.T) {
|
||||
tests := []struct {
|
||||
root [32]byte // the root of the new INVALID block
|
||||
tips map[[32]byte]types.Slot // the old synced tips
|
||||
wantedParentTip bool
|
||||
newBestChild uint64
|
||||
newBestDescendant uint64
|
||||
newParentWeight uint64
|
||||
}{
|
||||
{
|
||||
[32]byte{'j'},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
[32]byte{'d'}: 103,
|
||||
[32]byte{'g'}: 104,
|
||||
},
|
||||
false,
|
||||
3,
|
||||
4,
|
||||
8,
|
||||
},
|
||||
{
|
||||
[32]byte{'j'},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
},
|
||||
true,
|
||||
3,
|
||||
4,
|
||||
8,
|
||||
},
|
||||
{
|
||||
[32]byte{'i'},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
[32]byte{'d'}: 103,
|
||||
[32]byte{'g'}: 104,
|
||||
[32]byte{'h'}: 105,
|
||||
},
|
||||
true,
|
||||
NonExistentNode,
|
||||
NonExistentNode,
|
||||
1,
|
||||
},
|
||||
{
|
||||
[32]byte{'i'},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
[32]byte{'d'}: 103,
|
||||
[32]byte{'g'}: 104,
|
||||
},
|
||||
false,
|
||||
NonExistentNode,
|
||||
NonExistentNode,
|
||||
1,
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
ctx := context.Background()
|
||||
f := setup(1, 1)
|
||||
|
||||
require.NoError(t, f.ProcessBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, [32]byte{}, 1, 1))
|
||||
weights := []uint64{10, 10, 9, 7, 1, 6, 2, 3, 1, 1, 1, 0, 0}
|
||||
f.syncedTips.Lock()
|
||||
f.syncedTips.validatedTips = tc.tips
|
||||
f.syncedTips.Unlock()
|
||||
f.store.nodesLock.Lock()
|
||||
for i, node := range f.store.nodes {
|
||||
node.weight = weights[i]
|
||||
}
|
||||
// Make j be the best child and descendant of b
|
||||
nodeB := f.store.nodes[2]
|
||||
nodeB.bestChild = 4
|
||||
nodeB.bestDescendant = 4
|
||||
idx := f.store.nodesIndices[tc.root]
|
||||
node := f.store.nodes[idx]
|
||||
parentIndex := node.parent
|
||||
require.NotEqual(t, NonExistentNode, parentIndex)
|
||||
parent := f.store.nodes[parentIndex]
|
||||
f.store.nodesLock.Unlock()
|
||||
err := f.UpdateSyncedTipsWithInvalidRoot(context.Background(), tc.root)
|
||||
require.NoError(t, err)
|
||||
f.syncedTips.RLock()
|
||||
_, parentSyncedTip := f.syncedTips.validatedTips[parent.root]
|
||||
f.syncedTips.RUnlock()
|
||||
require.Equal(t, tc.wantedParentTip, parentSyncedTip)
|
||||
require.Equal(t, tc.newBestChild, parent.bestChild)
|
||||
require.Equal(t, tc.newBestDescendant, parent.bestDescendant)
|
||||
require.Equal(t, tc.newParentWeight, parent.weight)
|
||||
}
|
||||
}
|
||||
|
||||
// This tests the algorithm to find the tip of a given node
|
||||
// We start with the following diagram
|
||||
//
|
||||
// E -- F
|
||||
// /
|
||||
// C -- D
|
||||
// / \
|
||||
// A -- B G -- H -- I
|
||||
// \ \
|
||||
// J -- K -- L
|
||||
//
|
||||
//
|
||||
func TestFindSyncedTip(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
f := setup(1, 1)
|
||||
|
||||
require.NoError(t, f.ProcessBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, [32]byte{}, 1, 1))
|
||||
tests := []struct {
|
||||
root [32]byte // the root of the block
|
||||
tips map[[32]byte]types.Slot // the synced tips
|
||||
wanted [32]byte // the root of expected tip
|
||||
}{
|
||||
{
|
||||
[32]byte{'i'},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
[32]byte{'d'}: 103,
|
||||
[32]byte{'g'}: 104,
|
||||
},
|
||||
[32]byte{'g'},
|
||||
},
|
||||
{
|
||||
[32]byte{'g'},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
[32]byte{'d'}: 103,
|
||||
[32]byte{'h'}: 104,
|
||||
[32]byte{'k'}: 106,
|
||||
},
|
||||
[32]byte{'d'},
|
||||
},
|
||||
{
|
||||
[32]byte{'e'},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
[32]byte{'d'}: 103,
|
||||
[32]byte{'g'}: 103,
|
||||
},
|
||||
[32]byte{'d'},
|
||||
},
|
||||
{
|
||||
[32]byte{'j'},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
[32]byte{'f'}: 105,
|
||||
[32]byte{'g'}: 104,
|
||||
[32]byte{'i'}: 106,
|
||||
},
|
||||
[32]byte{'b'},
|
||||
},
|
||||
{
|
||||
[32]byte{'g'},
|
||||
map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
[32]byte{'f'}: 105,
|
||||
[32]byte{'g'}: 104,
|
||||
[32]byte{'i'}: 106,
|
||||
},
|
||||
[32]byte{'g'},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
f.store.nodesLock.RLock()
|
||||
node := f.store.nodes[f.store.nodesIndices[tc.root]]
|
||||
syncedTips := &optimisticStore{
|
||||
validatedTips: tc.tips,
|
||||
}
|
||||
syncedTips.RLock()
|
||||
idx, err := f.store.findSyncedTip(ctx, node, syncedTips)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.wanted, f.store.nodes[idx].root)
|
||||
|
||||
f.store.nodesLock.RUnlock()
|
||||
syncedTips.RUnlock()
|
||||
require.NoError(t, f.ProcessBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, 1, 1, true))
|
||||
|
||||
require.NoError(t, f.store.removeNode(context.Background(), tc.root))
|
||||
require.Equal(t, tc.wantedNodeNumber, f.store.NodeNumber())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
zeroHash := params.BeaconConfig().ZeroHash
|
||||
graffiti := [32]byte{}
|
||||
balances := make([]uint64, 64) // 64 active validators.
|
||||
for i := 0; i < len(balances); i++ {
|
||||
balances[i] = 10
|
||||
@@ -47,9 +46,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
slot,
|
||||
newRoot,
|
||||
headRoot,
|
||||
graffiti,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
true,
|
||||
),
|
||||
)
|
||||
f.ProcessAttestation(ctx, []uint64{0}, newRoot, fEpoch)
|
||||
@@ -71,9 +70,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
slot,
|
||||
newRoot,
|
||||
headRoot,
|
||||
graffiti,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
true,
|
||||
),
|
||||
)
|
||||
f.ProcessAttestation(ctx, []uint64{1}, newRoot, fEpoch)
|
||||
@@ -89,17 +88,17 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
// 2
|
||||
// |
|
||||
// 3 <- HEAD
|
||||
slot = types.Slot(2)
|
||||
newRoot = indexToHash(2)
|
||||
slot = types.Slot(3)
|
||||
newRoot = indexToHash(3)
|
||||
require.NoError(t,
|
||||
f.ProcessBlock(
|
||||
ctx,
|
||||
slot,
|
||||
newRoot,
|
||||
headRoot,
|
||||
graffiti,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
true,
|
||||
),
|
||||
)
|
||||
f.ProcessAttestation(ctx, []uint64{2}, newRoot, fEpoch)
|
||||
@@ -123,9 +122,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
slot,
|
||||
newRoot,
|
||||
headRoot,
|
||||
graffiti,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
true,
|
||||
),
|
||||
)
|
||||
f.ProcessAttestation(ctx, []uint64{3}, newRoot, fEpoch)
|
||||
@@ -137,11 +136,11 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
assert.Equal(t, newRoot, headRoot, "Incorrect head for justified epoch at slot 3")
|
||||
|
||||
// Check the ancestor scores from the store.
|
||||
require.Equal(t, 4, len(f.store.nodes))
|
||||
require.Equal(t, 5, len(f.store.nodeByRoot))
|
||||
|
||||
// Expect nodes to have a boosted, back-propagated score.
|
||||
// Ancestors have the added weights of their children. Genesis is a special exception at 0 weight,
|
||||
require.Equal(t, f.store.nodes[0].weight, uint64(0))
|
||||
require.Equal(t, f.store.treeRootNode.weight, uint64(0))
|
||||
|
||||
// Otherwise, assuming a block, A, that is not-genesis:
|
||||
//
|
||||
@@ -157,14 +156,17 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
//
|
||||
// In this case, we have a small fork:
|
||||
//
|
||||
// (A: 54) -> (B: 44) -> (C: 24)
|
||||
// \_->(D: 10)
|
||||
// (A: 54) -> (B: 44) -> (C: 34)
|
||||
// \_->(D: 24)
|
||||
//
|
||||
// So B has its own weight, 10, and the sum of both C and D. That's why we see weight 54 in the
|
||||
// middle instead of the normal progression of (44 -> 34 -> 24).
|
||||
require.Equal(t, f.store.nodes[1].weight, uint64(54))
|
||||
require.Equal(t, f.store.nodes[2].weight, uint64(44))
|
||||
require.Equal(t, f.store.nodes[3].weight, uint64(24))
|
||||
node1 := f.store.nodeByRoot[indexToHash(1)]
|
||||
require.Equal(t, node1.weight, uint64(54))
|
||||
node2 := f.store.nodeByRoot[indexToHash(2)]
|
||||
require.Equal(t, node2.weight, uint64(44))
|
||||
node3 := f.store.nodeByRoot[indexToHash(4)]
|
||||
require.Equal(t, node3.weight, uint64(24))
|
||||
})
|
||||
t.Run("vanilla ex ante attack", func(t *testing.T) {
|
||||
f := setup(jEpoch, fEpoch)
|
||||
@@ -190,9 +192,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
honestBlockSlot,
|
||||
honestBlock,
|
||||
zeroHash,
|
||||
graffiti,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
true,
|
||||
),
|
||||
)
|
||||
r, err = f.Head(ctx, jEpoch, zeroHash, balances, fEpoch)
|
||||
@@ -207,9 +209,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
maliciouslyWithheldBlockSlot,
|
||||
maliciouslyWithheldBlock,
|
||||
zeroHash,
|
||||
graffiti,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
true,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -256,9 +258,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
honestBlockSlot,
|
||||
honestBlock,
|
||||
zeroHash,
|
||||
graffiti,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
true,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -275,9 +277,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
maliciouslyWithheldBlockSlot,
|
||||
maliciouslyWithheldBlock,
|
||||
zeroHash,
|
||||
graffiti,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
true,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -331,9 +333,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
cSlot,
|
||||
c,
|
||||
a, // parent
|
||||
graffiti,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
true,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -355,9 +357,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
bSlot,
|
||||
b,
|
||||
a, // parent
|
||||
graffiti,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
true,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -379,9 +381,9 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
|
||||
dSlot,
|
||||
d,
|
||||
b, // parent
|
||||
graffiti,
|
||||
jEpoch,
|
||||
fEpoch,
|
||||
true,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package protoarray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
@@ -16,235 +14,45 @@ import (
|
||||
// before getting pruned upon new finalization.
|
||||
const defaultPruneThreshold = 256
|
||||
|
||||
// This tracks the last reported head root. Used for metrics.
|
||||
var lastHeadRoot [32]byte
|
||||
// This defines the largest staked power that an attacker is willing to
|
||||
// spend in an attack to "safe head"
|
||||
const safeHeadAttackersWeight = uint64(4096 * 32000000000)
|
||||
|
||||
// New initializes a new fork choice store.
|
||||
func New(justifiedEpoch, finalizedEpoch types.Epoch, finalizedRoot [32]byte) *ForkChoice {
|
||||
s := &Store{
|
||||
justifiedEpoch: justifiedEpoch,
|
||||
finalizedEpoch: finalizedEpoch,
|
||||
finalizedRoot: finalizedRoot,
|
||||
proposerBoostRoot: [32]byte{},
|
||||
nodes: make([]*Node, 0),
|
||||
nodesIndices: make(map[[32]byte]uint64),
|
||||
canonicalNodes: make(map[[32]byte]bool),
|
||||
pruneThreshold: defaultPruneThreshold,
|
||||
// applyProposerBoostScore applies the current proposer boost scores to the
|
||||
// relevant nodes
|
||||
func (s *Store) applyProposerBoostScore(newBalances []uint64) error {
|
||||
s.proposerBoostLock.Lock()
|
||||
defer s.proposerBoostLock.Unlock()
|
||||
|
||||
proposerScore := uint64(0)
|
||||
var err error
|
||||
if s.previousProposerBoostRoot != params.BeaconConfig().ZeroHash {
|
||||
previousNode, ok := s.nodeByRoot[s.previousProposerBoostRoot]
|
||||
if !ok || previousNode == nil {
|
||||
return errInvalidProposerBoostRoot
|
||||
}
|
||||
previousNode.balance -= s.previousProposerBoostScore
|
||||
}
|
||||
|
||||
b := make([]uint64, 0)
|
||||
v := make([]Vote, 0)
|
||||
st := &optimisticStore{
|
||||
validatedTips: make(map[[32]byte]types.Slot),
|
||||
if s.proposerBoostRoot != params.BeaconConfig().ZeroHash {
|
||||
currentNode, ok := s.nodeByRoot[s.proposerBoostRoot]
|
||||
if !ok || currentNode == nil {
|
||||
return errInvalidProposerBoostRoot
|
||||
}
|
||||
proposerScore, err = computeProposerBoostScore(newBalances)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentNode.balance += proposerScore
|
||||
}
|
||||
return &ForkChoice{store: s, balances: b, votes: v, syncedTips: st}
|
||||
}
|
||||
|
||||
// SetSyncedTips sets the synced and validated tips from the passed map
|
||||
func (f *ForkChoice) SetSyncedTips(tips map[[32]byte]types.Slot) error {
|
||||
if len(tips) == 0 {
|
||||
return errInvalidSyncedTips
|
||||
}
|
||||
newTips := make(map[[32]byte]types.Slot, len(tips))
|
||||
for k, v := range tips {
|
||||
newTips[k] = v
|
||||
}
|
||||
f.syncedTips.Lock()
|
||||
defer f.syncedTips.Unlock()
|
||||
f.syncedTips.validatedTips = newTips
|
||||
s.previousProposerBoostRoot = s.proposerBoostRoot
|
||||
s.previousProposerBoostScore = proposerScore
|
||||
return nil
|
||||
}
|
||||
|
||||
// SyncedTips returns the synced and validated tips from the fork choice store.
|
||||
func (f *ForkChoice) SyncedTips() map[[32]byte]types.Slot {
|
||||
f.syncedTips.RLock()
|
||||
defer f.syncedTips.RUnlock()
|
||||
|
||||
m := make(map[[32]byte]types.Slot)
|
||||
for k, v := range f.syncedTips.validatedTips {
|
||||
m[k] = v
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Head returns the head root from fork choice store.
|
||||
// It firsts computes validator's balance changes then recalculates block tree from leaves to root.
|
||||
func (f *ForkChoice) Head(
|
||||
ctx context.Context,
|
||||
justifiedEpoch types.Epoch,
|
||||
justifiedRoot [32]byte,
|
||||
justifiedStateBalances []uint64,
|
||||
finalizedEpoch types.Epoch,
|
||||
) ([32]byte, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.Head")
|
||||
defer span.End()
|
||||
f.votesLock.Lock()
|
||||
defer f.votesLock.Unlock()
|
||||
|
||||
calledHeadCount.Inc()
|
||||
|
||||
newBalances := justifiedStateBalances
|
||||
|
||||
// Using the write lock here because `updateCanonicalNodes` that gets called subsequently requires a write operation.
|
||||
f.store.nodesLock.Lock()
|
||||
defer f.store.nodesLock.Unlock()
|
||||
deltas, newVotes, err := computeDeltas(ctx, f.store.nodesIndices, f.votes, f.balances, newBalances)
|
||||
if err != nil {
|
||||
return [32]byte{}, errors.Wrap(err, "Could not compute deltas")
|
||||
}
|
||||
f.votes = newVotes
|
||||
|
||||
if err := f.store.applyWeightChanges(ctx, justifiedEpoch, finalizedEpoch, newBalances, deltas); err != nil {
|
||||
return [32]byte{}, errors.Wrap(err, "Could not apply score changes")
|
||||
}
|
||||
f.balances = newBalances
|
||||
|
||||
return f.store.head(ctx, justifiedRoot)
|
||||
}
|
||||
|
||||
// ProcessAttestation processes attestation for vote accounting, it iterates around validator indices
|
||||
// and update their votes accordingly.
|
||||
func (f *ForkChoice) ProcessAttestation(ctx context.Context, validatorIndices []uint64, blockRoot [32]byte, targetEpoch types.Epoch) {
|
||||
_, span := trace.StartSpan(ctx, "protoArrayForkChoice.ProcessAttestation")
|
||||
defer span.End()
|
||||
f.votesLock.Lock()
|
||||
defer f.votesLock.Unlock()
|
||||
|
||||
for _, index := range validatorIndices {
|
||||
// Validator indices will grow the vote cache.
|
||||
for index >= uint64(len(f.votes)) {
|
||||
f.votes = append(f.votes, Vote{currentRoot: params.BeaconConfig().ZeroHash, nextRoot: params.BeaconConfig().ZeroHash})
|
||||
}
|
||||
|
||||
// Newly allocated vote if the root fields are untouched.
|
||||
newVote := f.votes[index].nextRoot == params.BeaconConfig().ZeroHash &&
|
||||
f.votes[index].currentRoot == params.BeaconConfig().ZeroHash
|
||||
|
||||
// Vote gets updated if it's newly allocated or high target epoch.
|
||||
if newVote || targetEpoch > f.votes[index].nextEpoch {
|
||||
f.votes[index].nextEpoch = targetEpoch
|
||||
f.votes[index].nextRoot = blockRoot
|
||||
}
|
||||
}
|
||||
|
||||
processedAttestationCount.Inc()
|
||||
}
|
||||
|
||||
// ProcessBlock processes a new block by inserting it to the fork choice store.
|
||||
func (f *ForkChoice) ProcessBlock(
|
||||
ctx context.Context,
|
||||
slot types.Slot,
|
||||
blockRoot, parentRoot, graffiti [32]byte,
|
||||
justifiedEpoch, finalizedEpoch types.Epoch,
|
||||
) error {
|
||||
ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.ProcessBlock")
|
||||
defer span.End()
|
||||
|
||||
return f.store.insert(ctx, slot, blockRoot, parentRoot, graffiti, justifiedEpoch, finalizedEpoch)
|
||||
}
|
||||
|
||||
// Prune prunes the fork choice store with the new finalized root. The store is only pruned if the input
|
||||
// root is different than the current store finalized root, and the number of the store has met prune threshold.
|
||||
func (f *ForkChoice) Prune(ctx context.Context, finalizedRoot [32]byte) error {
|
||||
return f.store.prune(ctx, finalizedRoot, f.syncedTips)
|
||||
}
|
||||
|
||||
// Nodes returns the copied list of block nodes in the fork choice store.
|
||||
func (f *ForkChoice) Nodes() []*Node {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
cpy := make([]*Node, len(f.store.nodes))
|
||||
copy(cpy, f.store.nodes)
|
||||
return cpy
|
||||
}
|
||||
|
||||
// Store returns the fork choice store object which contains all the information regarding proto array fork choice.
|
||||
func (f *ForkChoice) Store() *Store {
|
||||
f.store.nodesLock.Lock()
|
||||
defer f.store.nodesLock.Unlock()
|
||||
return f.store
|
||||
}
|
||||
|
||||
// Node returns the copied node in the fork choice store.
|
||||
func (f *ForkChoice) Node(root [32]byte) *Node {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
index, ok := f.store.nodesIndices[root]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return copyNode(f.store.nodes[index])
|
||||
}
|
||||
|
||||
// HasNode returns true if the node exists in fork choice store,
|
||||
// false else wise.
|
||||
func (f *ForkChoice) HasNode(root [32]byte) bool {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
_, ok := f.store.nodesIndices[root]
|
||||
return ok
|
||||
}
|
||||
|
||||
// HasParent returns true if the node parent exists in fork choice store,
|
||||
// false else wise.
|
||||
func (f *ForkChoice) HasParent(root [32]byte) bool {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
i, ok := f.store.nodesIndices[root]
|
||||
if !ok || i >= uint64(len(f.store.nodes)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return f.store.nodes[i].parent != NonExistentNode
|
||||
}
|
||||
|
||||
// IsCanonical returns true if the given root is part of the canonical chain.
|
||||
func (f *ForkChoice) IsCanonical(root [32]byte) bool {
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
return f.store.canonicalNodes[root]
|
||||
}
|
||||
|
||||
// AncestorRoot returns the ancestor root of input block root at a given slot.
|
||||
func (f *ForkChoice) AncestorRoot(ctx context.Context, root [32]byte, slot types.Slot) ([]byte, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "protoArray.AncestorRoot")
|
||||
defer span.End()
|
||||
|
||||
f.store.nodesLock.RLock()
|
||||
defer f.store.nodesLock.RUnlock()
|
||||
|
||||
i, ok := f.store.nodesIndices[root]
|
||||
if !ok {
|
||||
return nil, errors.New("node does not exist")
|
||||
}
|
||||
if i >= uint64(len(f.store.nodes)) {
|
||||
return nil, errors.New("node index out of range")
|
||||
}
|
||||
|
||||
for f.store.nodes[i].slot > slot {
|
||||
if ctx.Err() != nil {
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
i = f.store.nodes[i].parent
|
||||
|
||||
if i >= uint64(len(f.store.nodes)) {
|
||||
return nil, errors.New("node index out of range")
|
||||
}
|
||||
}
|
||||
|
||||
return f.store.nodes[i].root[:], nil
|
||||
}
|
||||
|
||||
// PruneThreshold of fork choice store.
|
||||
func (s *Store) PruneThreshold() uint64 {
|
||||
return s.pruneThreshold
|
||||
// NodeNumber returns the current number of nodes in the Store
|
||||
func (s *Store) NodeNumber() int {
|
||||
return len(s.nodeByRoot)
|
||||
}
|
||||
|
||||
// JustifiedEpoch of fork choice store.
|
||||
@@ -264,117 +72,51 @@ func (s *Store) ProposerBoost() [fieldparams.RootLength]byte {
|
||||
return s.proposerBoostRoot
|
||||
}
|
||||
|
||||
// Nodes of fork choice store.
|
||||
func (s *Store) Nodes() []*Node {
|
||||
s.nodesLock.RLock()
|
||||
defer s.nodesLock.RUnlock()
|
||||
return s.nodes
|
||||
}
|
||||
|
||||
// NodesIndices of fork choice store.
|
||||
func (s *Store) NodesIndices() map[[32]byte]uint64 {
|
||||
s.nodesLock.RLock()
|
||||
defer s.nodesLock.RUnlock()
|
||||
return s.nodesIndices
|
||||
// PruneThreshold of fork choice store.
|
||||
func (s *Store) PruneThreshold() uint64 {
|
||||
return s.pruneThreshold
|
||||
}
|
||||
|
||||
// head starts from justified root and then follows the best descendant links
|
||||
// to find the best block for head.
|
||||
// to find the best block for head. This function assumes a lock on s.nodesLock
|
||||
func (s *Store) head(ctx context.Context, justifiedRoot [32]byte) ([32]byte, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.head")
|
||||
defer span.End()
|
||||
|
||||
// Justified index has to be valid in node indices map, and can not be out of bound.
|
||||
justifiedIndex, ok := s.nodesIndices[justifiedRoot]
|
||||
if !ok {
|
||||
// JustifiedRoot has to be known
|
||||
justifiedNode, ok := s.nodeByRoot[justifiedRoot]
|
||||
if !ok || justifiedNode == nil {
|
||||
return [32]byte{}, errUnknownJustifiedRoot
|
||||
}
|
||||
if justifiedIndex >= uint64(len(s.nodes)) {
|
||||
return [32]byte{}, errInvalidJustifiedIndex
|
||||
}
|
||||
|
||||
justifiedNode := s.nodes[justifiedIndex]
|
||||
bestDescendantIndex := justifiedNode.bestDescendant
|
||||
// If the justified node doesn't have a best descendant,
|
||||
// the best node is itself.
|
||||
if bestDescendantIndex == NonExistentNode {
|
||||
bestDescendantIndex = justifiedIndex
|
||||
}
|
||||
if bestDescendantIndex >= uint64(len(s.nodes)) {
|
||||
return [32]byte{}, errInvalidBestDescendantIndex
|
||||
bestDescendant := justifiedNode.bestDescendant
|
||||
if bestDescendant == nil {
|
||||
bestDescendant = justifiedNode
|
||||
}
|
||||
|
||||
bestNode := s.nodes[bestDescendantIndex]
|
||||
|
||||
if !s.viableForHead(bestNode) {
|
||||
if !bestDescendant.viableForHead(s.justifiedEpoch, s.finalizedEpoch) {
|
||||
return [32]byte{}, fmt.Errorf("head at slot %d with weight %d is not eligible, finalizedEpoch %d != %d, justifiedEpoch %d != %d",
|
||||
bestNode.slot, bestNode.weight/10e9, bestNode.finalizedEpoch, s.finalizedEpoch, bestNode.justifiedEpoch, s.justifiedEpoch)
|
||||
bestDescendant.slot, bestDescendant.weight/10e9, bestDescendant.finalizedEpoch, s.finalizedEpoch, bestDescendant.justifiedEpoch, s.justifiedEpoch)
|
||||
}
|
||||
|
||||
// Update metrics.
|
||||
if bestNode.root != lastHeadRoot {
|
||||
if bestDescendant != s.headNode {
|
||||
headChangesCount.Inc()
|
||||
headSlotNumber.Set(float64(bestNode.slot))
|
||||
lastHeadRoot = bestNode.root
|
||||
headSlotNumber.Set(float64(bestDescendant.slot))
|
||||
s.headNode = bestDescendant
|
||||
}
|
||||
|
||||
// Update canonical mapping given the head root.
|
||||
if err := s.updateCanonicalNodes(ctx, bestNode.root); err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
|
||||
return bestNode.root, nil
|
||||
}
|
||||
|
||||
// updateCanonicalNodes updates the canonical nodes mapping given the input block root.
|
||||
func (s *Store) updateCanonicalNodes(ctx context.Context, root [32]byte) error {
|
||||
ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.updateCanonicalNodes")
|
||||
defer span.End()
|
||||
|
||||
// Set the input node to canonical.
|
||||
i := s.nodesIndices[root]
|
||||
var newCanonicalRoots [][32]byte
|
||||
var n *Node
|
||||
for i != NonExistentNode {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
// Get the parent node, if the node is already in canonical mapping,
|
||||
// we can be sure rest of the ancestors are canonical. Exit early.
|
||||
n = s.nodes[i]
|
||||
if s.canonicalNodes[n.root] {
|
||||
break
|
||||
}
|
||||
|
||||
// Set parent node to canonical. Repeat until parent node index is undefined.
|
||||
newCanonicalRoots = append(newCanonicalRoots, n.root)
|
||||
i = n.parent
|
||||
}
|
||||
|
||||
// i is either NonExistentNode or has the index of the last canonical
|
||||
// node before the last head update.
|
||||
if i == NonExistentNode {
|
||||
s.canonicalNodes = make(map[[fieldparams.RootLength]byte]bool)
|
||||
} else {
|
||||
for j := i + 1; j < uint64(len(s.nodes)); j++ {
|
||||
delete(s.canonicalNodes, s.nodes[j].root)
|
||||
}
|
||||
}
|
||||
|
||||
for _, canonicalRoot := range newCanonicalRoots {
|
||||
s.canonicalNodes[canonicalRoot] = true
|
||||
}
|
||||
|
||||
return nil
|
||||
return bestDescendant.root, nil
|
||||
}
|
||||
|
||||
// insert registers a new block node to the fork choice store's node list.
|
||||
// It then updates the new node's parent with best child and descendant node.
|
||||
func (s *Store) insert(ctx context.Context,
|
||||
slot types.Slot,
|
||||
root, parent, graffiti [32]byte,
|
||||
justifiedEpoch, finalizedEpoch types.Epoch) error {
|
||||
root, parentRoot [fieldparams.RootLength]byte,
|
||||
justifiedEpoch, finalizedEpoch types.Epoch, optimistic bool) error {
|
||||
_, span := trace.StartSpan(ctx, "protoArrayForkChoice.insert")
|
||||
defer span.End()
|
||||
|
||||
@@ -382,365 +124,155 @@ func (s *Store) insert(ctx context.Context,
|
||||
defer s.nodesLock.Unlock()
|
||||
|
||||
// Return if the block has been inserted into Store before.
|
||||
if _, ok := s.nodesIndices[root]; ok {
|
||||
if _, ok := s.nodeByRoot[root]; ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
index := uint64(len(s.nodes))
|
||||
parentIndex, ok := s.nodesIndices[parent]
|
||||
// Mark genesis block's parent as non-existent.
|
||||
if !ok {
|
||||
parentIndex = NonExistentNode
|
||||
}
|
||||
parent := s.nodeByRoot[parentRoot]
|
||||
|
||||
n := &Node{
|
||||
slot: slot,
|
||||
root: root,
|
||||
graffiti: graffiti,
|
||||
parent: parentIndex,
|
||||
parent: parent,
|
||||
justifiedEpoch: justifiedEpoch,
|
||||
finalizedEpoch: finalizedEpoch,
|
||||
bestChild: NonExistentNode,
|
||||
bestDescendant: NonExistentNode,
|
||||
weight: 0,
|
||||
optimistic: optimistic,
|
||||
}
|
||||
|
||||
s.nodesIndices[root] = index
|
||||
s.nodes = append(s.nodes, n)
|
||||
|
||||
// Update parent with the best child and descendant only if it's available.
|
||||
if n.parent != NonExistentNode {
|
||||
if err := s.updateBestChildAndDescendant(parentIndex, index); err != nil {
|
||||
s.nodeByRoot[root] = n
|
||||
if parent != nil {
|
||||
parent.children = append(parent.children, n)
|
||||
if err := s.treeRootNode.updateBestDescendant(ctx, s.justifiedEpoch, s.finalizedEpoch); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Update metrics.
|
||||
processedBlockCount.Inc()
|
||||
nodeCount.Set(float64(len(s.nodes)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyWeightChanges iterates backwards through the nodes in store. It checks all nodes parent
|
||||
// and its best child. For each node, it updates the weight with input delta and
|
||||
// back propagate the nodes' delta to its parents' delta. After scoring changes,
|
||||
// the best child is then updated along with the best descendant.
|
||||
func (s *Store) applyWeightChanges(
|
||||
ctx context.Context, justifiedEpoch, finalizedEpoch types.Epoch, newBalances []uint64, delta []int,
|
||||
) error {
|
||||
_, span := trace.StartSpan(ctx, "protoArrayForkChoice.applyWeightChanges")
|
||||
defer span.End()
|
||||
|
||||
// The length of the nodes can not be different than length of the delta.
|
||||
if len(s.nodes) != len(delta) {
|
||||
return errInvalidDeltaLength
|
||||
}
|
||||
|
||||
// Update the justified / finalized epochs in store if necessary.
|
||||
if s.justifiedEpoch != justifiedEpoch || s.finalizedEpoch != finalizedEpoch {
|
||||
s.justifiedEpoch = justifiedEpoch
|
||||
s.finalizedEpoch = finalizedEpoch
|
||||
}
|
||||
|
||||
// Proposer score defaults to 0.
|
||||
proposerScore := uint64(0)
|
||||
|
||||
// Iterate backwards through all index to node in store.
|
||||
var err error
|
||||
for i := len(s.nodes) - 1; i >= 0; i-- {
|
||||
n := s.nodes[i]
|
||||
|
||||
// There is no need to adjust the balances or manage parent of the zero hash, it
|
||||
// is an alias to the genesis block.
|
||||
if n.root == params.BeaconConfig().ZeroHash {
|
||||
continue
|
||||
}
|
||||
|
||||
nodeDelta := delta[i]
|
||||
|
||||
// If we have a node where the proposer boost was previously applied,
|
||||
// we then decrease the delta by the required score amount.
|
||||
s.proposerBoostLock.Lock()
|
||||
if s.previousProposerBoostRoot != params.BeaconConfig().ZeroHash && s.previousProposerBoostRoot == n.root {
|
||||
nodeDelta -= int(s.previousProposerBoostScore)
|
||||
}
|
||||
|
||||
if s.proposerBoostRoot != params.BeaconConfig().ZeroHash && s.proposerBoostRoot == n.root {
|
||||
proposerScore, err = computeProposerBoostScore(newBalances)
|
||||
if err != nil {
|
||||
s.proposerBoostLock.Unlock()
|
||||
return err
|
||||
}
|
||||
nodeDelta = nodeDelta + int(proposerScore)
|
||||
}
|
||||
s.proposerBoostLock.Unlock()
|
||||
|
||||
// A node's weight can not be negative but the delta can be negative.
|
||||
if nodeDelta < 0 {
|
||||
d := uint64(-nodeDelta)
|
||||
if n.weight < d {
|
||||
n.weight = 0
|
||||
} else {
|
||||
n.weight -= d
|
||||
}
|
||||
} else {
|
||||
n.weight += uint64(nodeDelta)
|
||||
}
|
||||
|
||||
// Update parent's best child and descendant if the node has a known parent.
|
||||
if n.parent != NonExistentNode {
|
||||
// Protection against node parent index out of bound. This should not happen.
|
||||
if int(n.parent) >= len(delta) {
|
||||
return errInvalidParentDelta
|
||||
}
|
||||
// Back propagate the nodes' delta to its parent.
|
||||
delta[n.parent] += nodeDelta
|
||||
}
|
||||
}
|
||||
|
||||
// Set the previous boosted root and score.
|
||||
s.proposerBoostLock.Lock()
|
||||
s.previousProposerBoostRoot = s.proposerBoostRoot
|
||||
s.previousProposerBoostScore = proposerScore
|
||||
s.proposerBoostLock.Unlock()
|
||||
|
||||
for i := len(s.nodes) - 1; i >= 0; i-- {
|
||||
n := s.nodes[i]
|
||||
if n.parent != NonExistentNode {
|
||||
if int(n.parent) >= len(delta) {
|
||||
return errInvalidParentDelta
|
||||
}
|
||||
if err := s.updateBestChildAndDescendant(n.parent, uint64(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateBestChildAndDescendant updates parent node's best child and descendant.
|
||||
// It looks at input parent node and input child node and potentially modifies parent's best
|
||||
// child and best descendant indices.
|
||||
// There are four outcomes:
|
||||
// 1.) The child is already the best child, but it's now invalid due to a FFG change and should be removed.
|
||||
// 2.) The child is already the best child and the parent is updated with the new best descendant.
|
||||
// 3.) The child is not the best child but becomes the best child.
|
||||
// 4.) The child is not the best child and does not become the best child.
|
||||
func (s *Store) updateBestChildAndDescendant(parentIndex, childIndex uint64) error {
|
||||
|
||||
// Protection against parent index out of bound, this should not happen.
|
||||
if parentIndex >= uint64(len(s.nodes)) {
|
||||
return errInvalidNodeIndex
|
||||
}
|
||||
parent := s.nodes[parentIndex]
|
||||
|
||||
// Protection against child index out of bound, again this should not happen.
|
||||
if childIndex >= uint64(len(s.nodes)) {
|
||||
return errInvalidNodeIndex
|
||||
}
|
||||
child := s.nodes[childIndex]
|
||||
|
||||
// Is the child viable to become head? Based on justification and finalization rules.
|
||||
childLeadsToViableHead, err := s.leadsToViableHead(child)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Define 3 variables for the 3 outcomes mentioned above. This is to
|
||||
// set `parent.bestChild` and `parent.bestDescendant` to. These
|
||||
// aliases are to assist readability.
|
||||
changeToNone := []uint64{NonExistentNode, NonExistentNode}
|
||||
bestDescendant := child.bestDescendant
|
||||
if bestDescendant == NonExistentNode {
|
||||
bestDescendant = childIndex
|
||||
}
|
||||
changeToChild := []uint64{childIndex, bestDescendant}
|
||||
noChange := []uint64{parent.bestChild, parent.bestDescendant}
|
||||
var newParentChild []uint64
|
||||
|
||||
if parent.bestChild != NonExistentNode {
|
||||
if parent.bestChild == childIndex && !childLeadsToViableHead {
|
||||
// If the child is already the best child of the parent, but it's not viable for head,
|
||||
// we should remove it. (Outcome 1)
|
||||
newParentChild = changeToNone
|
||||
} else if parent.bestChild == childIndex {
|
||||
// If the child is already the best child of the parent, set it again to ensure the best
|
||||
// descendant of the parent is updated. (Outcome 2)
|
||||
newParentChild = changeToChild
|
||||
} else {
|
||||
// Protection against parent's best child going out of bound.
|
||||
if parent.bestChild > uint64(len(s.nodes)) {
|
||||
return errInvalidBestDescendantIndex
|
||||
}
|
||||
bestChild := s.nodes[parent.bestChild]
|
||||
// Is current parent's best child viable to be head? Based on justification and finalization rules.
|
||||
bestChildLeadsToViableHead, err := s.leadsToViableHead(bestChild)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if childLeadsToViableHead && !bestChildLeadsToViableHead {
|
||||
// The child leads to a viable head, but the current parent's best child doesn't.
|
||||
newParentChild = changeToChild
|
||||
} else if !childLeadsToViableHead && bestChildLeadsToViableHead {
|
||||
// The child doesn't lead to a viable head, the current parent's best child does.
|
||||
newParentChild = noChange
|
||||
} else if child.weight == bestChild.weight {
|
||||
// If both are viable, compare their weights.
|
||||
// Tie-breaker of equal weights by root.
|
||||
if bytes.Compare(child.root[:], bestChild.root[:]) > 0 {
|
||||
newParentChild = changeToChild
|
||||
} else {
|
||||
newParentChild = noChange
|
||||
}
|
||||
} else {
|
||||
// Choose winner by weight.
|
||||
if child.weight > bestChild.weight {
|
||||
newParentChild = changeToChild
|
||||
} else {
|
||||
newParentChild = noChange
|
||||
}
|
||||
}
|
||||
if !optimistic {
|
||||
if err := n.setNodeAndParentValidated(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if childLeadsToViableHead {
|
||||
// If parent doesn't have a best child and the child is viable.
|
||||
newParentChild = changeToChild
|
||||
} else {
|
||||
// If parent doesn't have a best child and the child is not viable.
|
||||
newParentChild = noChange
|
||||
}
|
||||
optimisticCount.Inc()
|
||||
}
|
||||
|
||||
// Update parent with the outcome.
|
||||
parent.bestChild = newParentChild[0]
|
||||
parent.bestDescendant = newParentChild[1]
|
||||
s.nodes[parentIndex] = parent
|
||||
// Set the node as root if the store was empty
|
||||
if s.treeRootNode == nil {
|
||||
s.treeRootNode = n
|
||||
s.headNode = n
|
||||
}
|
||||
|
||||
// Update metrics.
|
||||
processedBlockCount.Inc()
|
||||
nodeCount.Set(float64(len(s.nodeByRoot)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// prune prunes the store with the new finalized root. The tree is only
|
||||
// pruned if the input finalized root are different than the one in stored and
|
||||
// the number of the nodes in store has met prune threshold.
|
||||
func (s *Store) prune(ctx context.Context, finalizedRoot [32]byte, syncedTips *optimisticStore) error {
|
||||
_, span := trace.StartSpan(ctx, "protoArrayForkChoice.prune")
|
||||
// updateCheckpoints Update the justified / finalized epochs in store if necessary.
|
||||
func (s *Store) updateCheckpoints(justifiedEpoch, finalizedEpoch types.Epoch) {
|
||||
s.justifiedEpoch = justifiedEpoch
|
||||
s.finalizedEpoch = finalizedEpoch
|
||||
}
|
||||
|
||||
// pruneMaps prunes the `nodeByRoot` map
|
||||
// starting from `node` down to the finalized Node or to a leaf of the Fork
|
||||
// choice store. This method assumes a lock on nodesLock.
|
||||
func (s *Store) pruneMaps(ctx context.Context, node, finalizedNode *Node) error {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
if node == finalizedNode {
|
||||
return nil
|
||||
}
|
||||
for _, child := range node.children {
|
||||
if err := s.pruneMaps(ctx, child, finalizedNode); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
delete(s.nodeByRoot, node.root)
|
||||
return nil
|
||||
}
|
||||
|
||||
// prune prunes the fork choice store with the new finalized root. The store is only pruned if the input
|
||||
// root is different than the current store finalized root, and the number of the store has met prune threshold.
|
||||
func (s *Store) prune(ctx context.Context, finalizedRoot [32]byte) error {
|
||||
_, span := trace.StartSpan(ctx, "protoArrayForkChoice.Prune")
|
||||
defer span.End()
|
||||
|
||||
s.nodesLock.Lock()
|
||||
defer s.nodesLock.Unlock()
|
||||
|
||||
// The node would have seen finalized root or else it
|
||||
// wouldn't be able to prune it.
|
||||
finalizedIndex, ok := s.nodesIndices[finalizedRoot]
|
||||
if !ok {
|
||||
finalizedNode, ok := s.nodeByRoot[finalizedRoot]
|
||||
if !ok || finalizedNode == nil {
|
||||
return errUnknownFinalizedRoot
|
||||
}
|
||||
|
||||
// The number of the nodes has not met the prune threshold.
|
||||
// Pruning at small numbers incurs more cost than benefit.
|
||||
if finalizedIndex < s.pruneThreshold {
|
||||
if finalizedNode.depth() < s.pruneThreshold {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Traverse through the node list starting from the finalized node at index 0.
|
||||
// Nodes that are not branching off from the finalized node will be removed.
|
||||
syncedTips.Lock()
|
||||
defer syncedTips.Unlock()
|
||||
|
||||
canonicalNodesMap := make(map[uint64]uint64, uint64(len(s.nodes))-finalizedIndex)
|
||||
canonicalNodes := make([]*Node, 1, uint64(len(s.nodes))-finalizedIndex)
|
||||
finalizedNode := s.nodes[finalizedIndex]
|
||||
finalizedTipIndex, err := s.findSyncedTip(ctx, finalizedNode, syncedTips)
|
||||
if err != nil {
|
||||
// Prune nodeByRoot starting from root
|
||||
if err := s.pruneMaps(ctx, s.treeRootNode, finalizedNode); err != nil {
|
||||
return err
|
||||
}
|
||||
finalizedNode.parent = NonExistentNode
|
||||
canonicalNodes[0] = finalizedNode
|
||||
canonicalNodesMap[finalizedIndex] = uint64(0)
|
||||
|
||||
for idx := uint64(0); idx < uint64(len(s.nodes)); idx++ {
|
||||
node := copyNode(s.nodes[idx])
|
||||
parentIdx, ok := canonicalNodesMap[node.parent]
|
||||
if ok {
|
||||
s.nodesIndices[node.root] = uint64(len(canonicalNodes))
|
||||
canonicalNodesMap[idx] = uint64(len(canonicalNodes))
|
||||
node.parent = parentIdx
|
||||
canonicalNodes = append(canonicalNodes, node)
|
||||
} else {
|
||||
// Remove node and synced tip that is not part of finalized branch.
|
||||
delete(s.nodesIndices, node.root)
|
||||
_, ok := syncedTips.validatedTips[node.root]
|
||||
if ok && idx != finalizedTipIndex {
|
||||
delete(syncedTips.validatedTips, node.root)
|
||||
}
|
||||
}
|
||||
}
|
||||
s.nodesIndices[finalizedRoot] = uint64(0)
|
||||
finalizedNode.parent = nil
|
||||
s.treeRootNode = finalizedNode
|
||||
|
||||
// Recompute the best child and descendant for each canonical nodes.
|
||||
for _, node := range canonicalNodes {
|
||||
if node.bestChild != NonExistentNode {
|
||||
node.bestChild = canonicalNodesMap[node.bestChild]
|
||||
}
|
||||
if node.bestDescendant != NonExistentNode {
|
||||
node.bestDescendant = canonicalNodesMap[node.bestDescendant]
|
||||
}
|
||||
}
|
||||
|
||||
s.nodes = canonicalNodes
|
||||
prunedCount.Inc()
|
||||
syncedTipsCount.Set(float64(len(syncedTips.validatedTips)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// leadsToViableHead returns true if the node or the best descendant of the node is viable for head.
|
||||
// Any node with diff finalized or justified epoch than the ones in fork choice store
|
||||
// should not be viable to head.
|
||||
func (s *Store) leadsToViableHead(node *Node) (bool, error) {
|
||||
var bestDescendantViable bool
|
||||
bestDescendantIndex := node.bestDescendant
|
||||
|
||||
// If the best descendant is not part of the leaves.
|
||||
if bestDescendantIndex != NonExistentNode {
|
||||
// Protection against out of bound, the best descendant index can not be
|
||||
// exceeds length of nodes list.
|
||||
if bestDescendantIndex >= uint64(len(s.nodes)) {
|
||||
return false, errInvalidBestDescendantIndex
|
||||
}
|
||||
|
||||
bestDescendantNode := s.nodes[bestDescendantIndex]
|
||||
bestDescendantViable = s.viableForHead(bestDescendantNode)
|
||||
}
|
||||
|
||||
// The node is viable as long as the best descendant is viable.
|
||||
return bestDescendantViable || s.viableForHead(node), nil
|
||||
}
|
||||
|
||||
// viableForHead returns true if the node is viable to head.
|
||||
// Any node with diff finalized or justified epoch than the ones in fork choice store
|
||||
// should not be viable to head.
|
||||
func (s *Store) viableForHead(node *Node) bool {
|
||||
// `node` is viable if its justified epoch and finalized epoch are the same as the one in `Store`.
|
||||
// It's also viable if we are in genesis epoch.
|
||||
justified := s.justifiedEpoch == node.justifiedEpoch || s.justifiedEpoch == 0
|
||||
finalized := s.finalizedEpoch == node.finalizedEpoch || s.finalizedEpoch == 0
|
||||
|
||||
return justified && finalized
|
||||
}
|
||||
|
||||
// Returns the list of leaves in the Fork Choice store.
|
||||
// These are all the nodes that have NonExistentNode as best child.
|
||||
// This internal method assumes that the caller holds a lock in s.nodesLock.
|
||||
func (s *Store) leaves() ([]uint64, error) {
|
||||
var leaves []uint64
|
||||
for i := uint64(0); i < uint64(len(s.nodes)); i++ {
|
||||
node := s.nodes[i]
|
||||
if node.bestChild == NonExistentNode {
|
||||
leaves = append(leaves, i)
|
||||
// tips returns a list of possible heads from fork choice store, it returns the
|
||||
// roots and the slots of the leaf nodes.
|
||||
func (s *Store) tips() ([][32]byte, []types.Slot) {
|
||||
var roots [][32]byte
|
||||
var slots []types.Slot
|
||||
for root, node := range s.nodeByRoot {
|
||||
if len(node.children) == 0 {
|
||||
roots = append(roots, root)
|
||||
slots = append(slots, node.slot)
|
||||
}
|
||||
}
|
||||
return leaves, nil
|
||||
return roots, slots
|
||||
}
|
||||
|
||||
//TreeRoot returns the current root Node of the Store
|
||||
func (s *Store) TreeRoot() *Node {
|
||||
s.nodesLock.RLock()
|
||||
defer s.nodesLock.RUnlock()
|
||||
return s.treeRootNode
|
||||
}
|
||||
|
||||
// Safe returns whether the block is considered safe with respect to
|
||||
// synchronicity assumptions on the network and how much an attacker is willing
|
||||
// to spend
|
||||
func (s *Store) Safe(ctx context.Context, root [32]byte, committeeWeight uint64) (bool, error) {
|
||||
node, ok := s.nodeByRoot[root]
|
||||
if !ok || node == nil {
|
||||
return false, errNilNode
|
||||
}
|
||||
|
||||
if node.bestDescendant != nil && node.bestDescendant != s.headNode {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
potentialVotes := uint64(s.headNode.slot-node.slot+1) * committeeWeight
|
||||
actualVotes := node.weight
|
||||
|
||||
tailWeight := uint64(0)
|
||||
for node.parent != nil {
|
||||
if ctx.Err() != nil {
|
||||
return false, ctx.Err()
|
||||
}
|
||||
node = node.parent
|
||||
tailWeight += node.balance
|
||||
}
|
||||
|
||||
s.proposerBoostLock.RLock()
|
||||
defer s.proposerBoostLock.RUnlock()
|
||||
return tailWeight+2*actualVotes > potentialVotes+safeHeadAttackersWeight+s.previousProposerBoostScore, nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
@@ -40,445 +39,159 @@ func TestStore_FinalizedEpoch(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_Nodes(t *testing.T) {
|
||||
nodes := []*Node{
|
||||
{slot: 100},
|
||||
{slot: 101},
|
||||
}
|
||||
s := &Store{
|
||||
nodes: nodes,
|
||||
}
|
||||
require.DeepEqual(t, nodes, s.Nodes())
|
||||
func TestStore_NodeNumber(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
require.Equal(t, 2, f.store.NodeNumber())
|
||||
}
|
||||
|
||||
func TestStore_NodesIndices(t *testing.T) {
|
||||
nodeIndices := map[[32]byte]uint64{
|
||||
{'a'}: 1,
|
||||
{'b'}: 2,
|
||||
func TestStore_NodeByRoot(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 2, indexToHash(2), indexToHash(1), 0, 0, false))
|
||||
node0 := f.store.treeRootNode
|
||||
node1 := node0.children[0]
|
||||
node2 := node1.children[0]
|
||||
|
||||
expectedRoots := map[[32]byte]*Node{
|
||||
params.BeaconConfig().ZeroHash: node0,
|
||||
indexToHash(1): node1,
|
||||
indexToHash(2): node2,
|
||||
}
|
||||
s := &Store{
|
||||
nodesIndices: nodeIndices,
|
||||
|
||||
require.Equal(t, 3, f.store.NodeNumber())
|
||||
for root, node := range f.store.nodeByRoot {
|
||||
v, ok := expectedRoots[root]
|
||||
require.Equal(t, ok, true)
|
||||
require.Equal(t, v, node)
|
||||
}
|
||||
require.DeepEqual(t, nodeIndices, s.NodesIndices())
|
||||
}
|
||||
|
||||
func TestForkChoice_HasNode(t *testing.T) {
|
||||
nodeIndices := map[[32]byte]uint64{
|
||||
{'a'}: 1,
|
||||
{'b'}: 2,
|
||||
}
|
||||
s := &Store{
|
||||
nodesIndices: nodeIndices,
|
||||
}
|
||||
f := &ForkChoice{store: s}
|
||||
require.Equal(t, true, f.HasNode([32]byte{'a'}))
|
||||
}
|
||||
|
||||
func TestForkChoice_Store(t *testing.T) {
|
||||
nodeIndices := map[[32]byte]uint64{
|
||||
{'a'}: 1,
|
||||
{'b'}: 2,
|
||||
}
|
||||
s := &Store{
|
||||
nodesIndices: nodeIndices,
|
||||
}
|
||||
f := &ForkChoice{store: s}
|
||||
require.DeepEqual(t, s, f.Store())
|
||||
}
|
||||
|
||||
func TestForkChoice_Nodes(t *testing.T) {
|
||||
nodes := []*Node{
|
||||
{slot: 100},
|
||||
{slot: 101},
|
||||
}
|
||||
s := &Store{
|
||||
nodes: nodes,
|
||||
}
|
||||
f := &ForkChoice{store: s}
|
||||
require.DeepEqual(t, s.nodes, f.Nodes())
|
||||
f := setup(0, 0)
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
require.Equal(t, true, f.HasNode(indexToHash(1)))
|
||||
}
|
||||
|
||||
func TestStore_Head_UnknownJustifiedRoot(t *testing.T) {
|
||||
s := &Store{nodesIndices: make(map[[32]byte]uint64)}
|
||||
f := setup(0, 0)
|
||||
|
||||
_, err := s.head(context.Background(), [32]byte{})
|
||||
_, err := f.store.head(context.Background(), [32]byte{'a'})
|
||||
assert.ErrorContains(t, errUnknownJustifiedRoot.Error(), err)
|
||||
}
|
||||
|
||||
func TestStore_Head_UnknownJustifiedIndex(t *testing.T) {
|
||||
r := [32]byte{'A'}
|
||||
indices := make(map[[32]byte]uint64)
|
||||
indices[r] = 1
|
||||
s := &Store{nodesIndices: indices}
|
||||
|
||||
_, err := s.head(context.Background(), r)
|
||||
assert.ErrorContains(t, errInvalidJustifiedIndex.Error(), err)
|
||||
}
|
||||
|
||||
func TestStore_Head_Itself(t *testing.T) {
|
||||
r := [32]byte{'A'}
|
||||
indices := map[[32]byte]uint64{r: 0}
|
||||
f := setup(0, 0)
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
|
||||
// Since the justified node does not have a best descendant so the best node
|
||||
// is itself.
|
||||
s := &Store{nodesIndices: indices, nodes: []*Node{{root: r, parent: NonExistentNode, bestDescendant: NonExistentNode}}, canonicalNodes: make(map[[32]byte]bool)}
|
||||
h, err := s.head(context.Background(), r)
|
||||
h, err := f.store.head(context.Background(), indexToHash(1))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, r, h)
|
||||
assert.Equal(t, indexToHash(1), h)
|
||||
}
|
||||
|
||||
func TestStore_Head_BestDescendant(t *testing.T) {
|
||||
r := [32]byte{'A'}
|
||||
best := [32]byte{'B'}
|
||||
indices := map[[32]byte]uint64{r: 0, best: 1}
|
||||
|
||||
// Since the justified node's best descendant is at index 1, and its root is `best`,
|
||||
// the head should be `best`.
|
||||
s := &Store{nodesIndices: indices, nodes: []*Node{{root: r, bestDescendant: 1, parent: NonExistentNode}, {root: best, parent: 0}}, canonicalNodes: make(map[[32]byte]bool)}
|
||||
h, err := s.head(context.Background(), r)
|
||||
f := setup(0, 0)
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 2, indexToHash(2), indexToHash(1), 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 3, indexToHash(3), indexToHash(1), 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 4, indexToHash(4), indexToHash(2), 0, 0, false))
|
||||
h, err := f.store.head(context.Background(), indexToHash(1))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, best, h)
|
||||
require.Equal(t, h, indexToHash(4))
|
||||
}
|
||||
|
||||
func TestStore_Head_ContextCancelled(t *testing.T) {
|
||||
func TestStore_UpdateBestDescendant_ContextCancelled(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
r := [32]byte{'A'}
|
||||
best := [32]byte{'B'}
|
||||
indices := map[[32]byte]uint64{r: 0, best: 1}
|
||||
|
||||
s := &Store{nodesIndices: indices, nodes: []*Node{{root: r, parent: NonExistentNode, bestDescendant: 1}, {root: best, parent: 0}}, canonicalNodes: make(map[[32]byte]bool)}
|
||||
f := setup(0, 0)
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
cancel()
|
||||
_, err := s.head(ctx, r)
|
||||
err := f.ProcessBlock(ctx, 2, indexToHash(2), indexToHash(1), 0, 0, false)
|
||||
require.ErrorContains(t, "context canceled", err)
|
||||
}
|
||||
|
||||
func TestStore_Insert_UnknownParent(t *testing.T) {
|
||||
func TestStore_Insert(t *testing.T) {
|
||||
// The new node does not have a parent.
|
||||
s := &Store{nodesIndices: make(map[[32]byte]uint64)}
|
||||
require.NoError(t, s.insert(context.Background(), 100, [32]byte{'A'}, [32]byte{'B'}, [32]byte{}, 1, 1))
|
||||
assert.Equal(t, 1, len(s.nodes), "Did not insert block")
|
||||
assert.Equal(t, 1, len(s.nodesIndices), "Did not insert block")
|
||||
assert.Equal(t, NonExistentNode, s.nodes[0].parent, "Incorrect parent")
|
||||
assert.Equal(t, types.Epoch(1), s.nodes[0].justifiedEpoch, "Incorrect justification")
|
||||
assert.Equal(t, types.Epoch(1), s.nodes[0].finalizedEpoch, "Incorrect finalization")
|
||||
assert.Equal(t, [32]byte{'A'}, s.nodes[0].root, "Incorrect root")
|
||||
treeRootNode := &Node{slot: 0, root: indexToHash(0)}
|
||||
nodeByRoot := map[[32]byte]*Node{indexToHash(0): treeRootNode}
|
||||
s := &Store{nodeByRoot: nodeByRoot, treeRootNode: treeRootNode}
|
||||
require.NoError(t, s.insert(context.Background(), 100, indexToHash(100), indexToHash(0), 1, 1, false))
|
||||
assert.Equal(t, 2, len(s.nodeByRoot), "Did not insert block")
|
||||
assert.Equal(t, (*Node)(nil), treeRootNode.parent, "Incorrect parent")
|
||||
assert.Equal(t, 1, len(treeRootNode.children), "Incorrect children number")
|
||||
child := treeRootNode.children[0]
|
||||
assert.Equal(t, types.Epoch(1), child.justifiedEpoch, "Incorrect justification")
|
||||
assert.Equal(t, types.Epoch(1), child.finalizedEpoch, "Incorrect finalization")
|
||||
assert.Equal(t, indexToHash(100), child.root, "Incorrect root")
|
||||
}
|
||||
|
||||
func TestStore_Insert_KnownParent(t *testing.T) {
|
||||
// Similar to UnknownParent test, but this time the new node has a valid parent already in store.
|
||||
// The new node builds on top of the parent.
|
||||
s := &Store{nodesIndices: make(map[[32]byte]uint64)}
|
||||
s.nodes = []*Node{{}}
|
||||
p := [32]byte{'B'}
|
||||
s.nodesIndices[p] = 0
|
||||
require.NoError(t, s.insert(context.Background(), 100, [32]byte{'A'}, p, [32]byte{}, 1, 1))
|
||||
assert.Equal(t, 2, len(s.nodes), "Did not insert block")
|
||||
assert.Equal(t, 2, len(s.nodesIndices), "Did not insert block")
|
||||
assert.Equal(t, uint64(0), s.nodes[1].parent, "Incorrect parent")
|
||||
assert.Equal(t, types.Epoch(1), s.nodes[1].justifiedEpoch, "Incorrect justification")
|
||||
assert.Equal(t, types.Epoch(1), s.nodes[1].finalizedEpoch, "Incorrect finalization")
|
||||
assert.Equal(t, [32]byte{'A'}, s.nodes[1].root, "Incorrect root")
|
||||
}
|
||||
func TestStore_updateCheckpoints(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
s := f.store
|
||||
|
||||
func TestStore_ApplyScoreChanges_InvalidDeltaLength(t *testing.T) {
|
||||
s := &Store{}
|
||||
|
||||
// This will fail because node indices has length of 0, and delta list has a length of 1.
|
||||
err := s.applyWeightChanges(context.Background(), 0, 0, []uint64{}, []int{1})
|
||||
assert.ErrorContains(t, errInvalidDeltaLength.Error(), err)
|
||||
}
|
||||
|
||||
func TestStore_ApplyScoreChanges_UpdateEpochs(t *testing.T) {
|
||||
s := &Store{}
|
||||
|
||||
// The justified and finalized epochs in Store should be updated to 1 and 1 given the following input.
|
||||
require.NoError(t, s.applyWeightChanges(context.Background(), 1, 1, []uint64{}, []int{}))
|
||||
s.updateCheckpoints(1, 1)
|
||||
assert.Equal(t, types.Epoch(1), s.justifiedEpoch, "Did not update justified epoch")
|
||||
assert.Equal(t, types.Epoch(1), s.finalizedEpoch, "Did not update finalized epoch")
|
||||
}
|
||||
|
||||
func TestStore_ApplyScoreChanges_UpdateWeightsPositiveDelta(t *testing.T) {
|
||||
// Construct 3 nodes with weight 100 on each node. The 3 nodes linked to each other.
|
||||
s := &Store{nodes: []*Node{
|
||||
{root: [32]byte{'A'}, weight: 100},
|
||||
{root: [32]byte{'A'}, weight: 100},
|
||||
{parent: 1, root: [32]byte{'A'}, weight: 100}}}
|
||||
|
||||
// Each node gets one unique vote. The weight should look like 103 <- 102 <- 101 because
|
||||
// they get propagated back.
|
||||
require.NoError(t, s.applyWeightChanges(context.Background(), 0, 0, []uint64{}, []int{1, 1, 1}))
|
||||
assert.Equal(t, uint64(103), s.nodes[0].weight)
|
||||
assert.Equal(t, uint64(102), s.nodes[1].weight)
|
||||
assert.Equal(t, uint64(101), s.nodes[2].weight)
|
||||
}
|
||||
|
||||
func TestStore_ApplyScoreChanges_UpdateWeightsNegativeDelta(t *testing.T) {
|
||||
// Construct 3 nodes with weight 100 on each node. The 3 nodes linked to each other.
|
||||
s := &Store{nodes: []*Node{
|
||||
{root: [32]byte{'A'}, weight: 100},
|
||||
{root: [32]byte{'A'}, weight: 100},
|
||||
{parent: 1, root: [32]byte{'A'}, weight: 100}}}
|
||||
|
||||
// Each node gets one unique vote which contributes to negative delta.
|
||||
// The weight should look like 97 <- 98 <- 99 because they get propagated back.
|
||||
require.NoError(t, s.applyWeightChanges(context.Background(), 0, 0, []uint64{}, []int{-1, -1, -1}))
|
||||
assert.Equal(t, uint64(97), s.nodes[0].weight)
|
||||
assert.Equal(t, uint64(98), s.nodes[1].weight)
|
||||
assert.Equal(t, uint64(99), s.nodes[2].weight)
|
||||
}
|
||||
|
||||
func TestStore_ApplyScoreChanges_UpdateWeightsMixedDelta(t *testing.T) {
|
||||
// Construct 3 nodes with weight 100 on each node. The 3 nodes linked to each other.
|
||||
s := &Store{nodes: []*Node{
|
||||
{root: [32]byte{'A'}, weight: 100},
|
||||
{root: [32]byte{'A'}, weight: 100},
|
||||
{parent: 1, root: [32]byte{'A'}, weight: 100}}}
|
||||
|
||||
// Each node gets one mixed vote. The weight should look like 100 <- 200 <- 250.
|
||||
require.NoError(t, s.applyWeightChanges(context.Background(), 0, 0, []uint64{}, []int{-100, -50, 150}))
|
||||
assert.Equal(t, uint64(100), s.nodes[0].weight)
|
||||
assert.Equal(t, uint64(200), s.nodes[1].weight)
|
||||
assert.Equal(t, uint64(250), s.nodes[2].weight)
|
||||
}
|
||||
|
||||
func TestStore_UpdateBestChildAndDescendant_RemoveChild(t *testing.T) {
|
||||
// Make parent's best child equal's to input child index and child is not viable.
|
||||
s := &Store{nodes: []*Node{{bestChild: 1}, {}}, justifiedEpoch: 1, finalizedEpoch: 1}
|
||||
require.NoError(t, s.updateBestChildAndDescendant(0, 1))
|
||||
|
||||
// Verify parent's best child and best descendant are `none`.
|
||||
assert.Equal(t, NonExistentNode, s.nodes[0].bestChild, "Did not get correct best child index")
|
||||
assert.Equal(t, NonExistentNode, s.nodes[0].bestDescendant, "Did not get correct best descendant index")
|
||||
}
|
||||
|
||||
func TestStore_UpdateBestChildAndDescendant_UpdateDescendant(t *testing.T) {
|
||||
// Make parent's best child equal to child index and child is viable.
|
||||
s := &Store{nodes: []*Node{{bestChild: 1}, {bestDescendant: NonExistentNode}}}
|
||||
require.NoError(t, s.updateBestChildAndDescendant(0, 1))
|
||||
|
||||
// Verify parent's best child is the same and best descendant is not set to child index.
|
||||
assert.Equal(t, uint64(1), s.nodes[0].bestChild, "Did not get correct best child index")
|
||||
assert.Equal(t, uint64(1), s.nodes[0].bestDescendant, "Did not get correct best descendant index")
|
||||
}
|
||||
|
||||
func TestStore_UpdateBestChildAndDescendant_ChangeChildByViability(t *testing.T) {
|
||||
// Make parent's best child not equal to child index, child leads to viable index and
|
||||
// parent's best child doesn't lead to viable index.
|
||||
s := &Store{
|
||||
justifiedEpoch: 1,
|
||||
finalizedEpoch: 1,
|
||||
nodes: []*Node{{bestChild: 1, justifiedEpoch: 1, finalizedEpoch: 1},
|
||||
{bestDescendant: NonExistentNode},
|
||||
{bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}}}
|
||||
require.NoError(t, s.updateBestChildAndDescendant(0, 2))
|
||||
|
||||
// Verify parent's best child and best descendant are set to child index.
|
||||
assert.Equal(t, uint64(2), s.nodes[0].bestChild, "Did not get correct best child index")
|
||||
assert.Equal(t, uint64(2), s.nodes[0].bestDescendant, "Did not get correct best descendant index")
|
||||
}
|
||||
|
||||
func TestStore_UpdateBestChildAndDescendant_ChangeChildByWeight(t *testing.T) {
|
||||
// Make parent's best child not equal to child index, child leads to viable index and
|
||||
// parents best child leads to viable index but child has more weight than parent's best child.
|
||||
s := &Store{
|
||||
justifiedEpoch: 1,
|
||||
finalizedEpoch: 1,
|
||||
nodes: []*Node{{bestChild: 1, justifiedEpoch: 1, finalizedEpoch: 1},
|
||||
{bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1},
|
||||
{bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1, weight: 1}}}
|
||||
require.NoError(t, s.updateBestChildAndDescendant(0, 2))
|
||||
|
||||
// Verify parent's best child and best descendant are set to child index.
|
||||
assert.Equal(t, uint64(2), s.nodes[0].bestChild, "Did not get correct best child index")
|
||||
assert.Equal(t, uint64(2), s.nodes[0].bestDescendant, "Did not get correct best descendant index")
|
||||
}
|
||||
|
||||
func TestStore_UpdateBestChildAndDescendant_ChangeChildAtLeaf(t *testing.T) {
|
||||
// Make parent's best child to none and input child leads to viable index.
|
||||
s := &Store{
|
||||
justifiedEpoch: 1,
|
||||
finalizedEpoch: 1,
|
||||
nodes: []*Node{{bestChild: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1},
|
||||
{bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1},
|
||||
{bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}}}
|
||||
require.NoError(t, s.updateBestChildAndDescendant(0, 2))
|
||||
|
||||
// Verify parent's best child and best descendant are set to child index.
|
||||
assert.Equal(t, uint64(2), s.nodes[0].bestChild, "Did not get correct best child index")
|
||||
assert.Equal(t, uint64(2), s.nodes[0].bestDescendant, "Did not get correct best descendant index")
|
||||
}
|
||||
|
||||
func TestStore_UpdateBestChildAndDescendant_NoChangeByViability(t *testing.T) {
|
||||
// Make parent's best child not equal to child index, child leads to not viable index and
|
||||
// parents best child leads to viable index.
|
||||
s := &Store{
|
||||
justifiedEpoch: 1,
|
||||
finalizedEpoch: 1,
|
||||
nodes: []*Node{{bestChild: 1, justifiedEpoch: 1, finalizedEpoch: 1},
|
||||
{bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1},
|
||||
{bestDescendant: NonExistentNode}}}
|
||||
require.NoError(t, s.updateBestChildAndDescendant(0, 2))
|
||||
|
||||
// Verify parent's best child and best descendant are not changed.
|
||||
assert.Equal(t, uint64(1), s.nodes[0].bestChild, "Did not get correct best child index")
|
||||
assert.Equal(t, uint64(0), s.nodes[0].bestDescendant, "Did not get correct best descendant index")
|
||||
}
|
||||
|
||||
func TestStore_UpdateBestChildAndDescendant_NoChangeByWeight(t *testing.T) {
|
||||
// Make parent's best child not equal to child index, child leads to viable index and
|
||||
// parents best child leads to viable index but parent's best child has more weight.
|
||||
s := &Store{
|
||||
justifiedEpoch: 1,
|
||||
finalizedEpoch: 1,
|
||||
nodes: []*Node{{bestChild: 1, justifiedEpoch: 1, finalizedEpoch: 1},
|
||||
{bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1, weight: 1},
|
||||
{bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}}}
|
||||
require.NoError(t, s.updateBestChildAndDescendant(0, 2))
|
||||
|
||||
// Verify parent's best child and best descendant are not changed.
|
||||
assert.Equal(t, uint64(1), s.nodes[0].bestChild, "Did not get correct best child index")
|
||||
assert.Equal(t, uint64(0), s.nodes[0].bestDescendant, "Did not get correct best descendant index")
|
||||
}
|
||||
|
||||
func TestStore_UpdateBestChildAndDescendant_NoChangeAtLeaf(t *testing.T) {
|
||||
// Make parent's best child to none and input child does not lead to viable index.
|
||||
s := &Store{
|
||||
justifiedEpoch: 1,
|
||||
finalizedEpoch: 1,
|
||||
nodes: []*Node{{bestChild: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1},
|
||||
{bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1},
|
||||
{bestDescendant: NonExistentNode}}}
|
||||
require.NoError(t, s.updateBestChildAndDescendant(0, 2))
|
||||
|
||||
// Verify parent's best child and best descendant are not changed.
|
||||
assert.Equal(t, NonExistentNode, s.nodes[0].bestChild, "Did not get correct best child index")
|
||||
assert.Equal(t, uint64(0), s.nodes[0].bestDescendant, "Did not get correct best descendant index")
|
||||
}
|
||||
|
||||
func TestStore_Prune_LessThanThreshold(t *testing.T) {
|
||||
// Define 100 nodes in store.
|
||||
numOfNodes := 100
|
||||
indices := make(map[[32]byte]uint64)
|
||||
nodes := make([]*Node, 0)
|
||||
indices[indexToHash(uint64(0))] = uint64(0)
|
||||
nodes = append(nodes, &Node{
|
||||
slot: types.Slot(0),
|
||||
root: indexToHash(uint64(0)),
|
||||
bestDescendant: uint64(numOfNodes - 1),
|
||||
bestChild: uint64(1),
|
||||
parent: NonExistentNode,
|
||||
})
|
||||
for i := 1; i < numOfNodes-1; i++ {
|
||||
indices[indexToHash(uint64(i))] = uint64(i)
|
||||
nodes = append(nodes, &Node{
|
||||
slot: types.Slot(i),
|
||||
root: indexToHash(uint64(i)),
|
||||
bestDescendant: uint64(numOfNodes - 1),
|
||||
bestChild: uint64(i + 1),
|
||||
parent: uint64(i) - 1,
|
||||
})
|
||||
numOfNodes := uint64(100)
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
for i := uint64(2); i < numOfNodes; i++ {
|
||||
require.NoError(t, f.ProcessBlock(ctx, types.Slot(i), indexToHash(i), indexToHash(i-1), 0, 0, false))
|
||||
}
|
||||
indices[indexToHash(uint64(numOfNodes-1))] = uint64(numOfNodes - 1)
|
||||
nodes = append(nodes, &Node{
|
||||
slot: types.Slot(numOfNodes - 1),
|
||||
root: indexToHash(uint64(numOfNodes - 1)),
|
||||
bestDescendant: NonExistentNode,
|
||||
bestChild: NonExistentNode,
|
||||
parent: uint64(numOfNodes - 2),
|
||||
})
|
||||
|
||||
s := &Store{nodes: nodes, nodesIndices: indices, pruneThreshold: 100}
|
||||
syncedTips := &optimisticStore{}
|
||||
s := f.store
|
||||
s.pruneThreshold = 100
|
||||
|
||||
// Finalized root is at index 99 so everything before 99 should be pruned,
|
||||
// Finalized root has depth 99 so everything before it should be pruned,
|
||||
// but PruneThreshold is at 100 so nothing will be pruned.
|
||||
require.NoError(t, s.prune(context.Background(), indexToHash(99), syncedTips))
|
||||
assert.Equal(t, 100, len(s.nodes), "Incorrect nodes count")
|
||||
assert.Equal(t, 100, len(s.nodesIndices), "Incorrect node indices count")
|
||||
require.NoError(t, s.prune(context.Background(), indexToHash(99)))
|
||||
assert.Equal(t, 100, len(s.nodeByRoot), "Incorrect nodes count")
|
||||
}
|
||||
|
||||
func TestStore_Prune_MoreThanThreshold(t *testing.T) {
|
||||
// Define 100 nodes in store.
|
||||
numOfNodes := 100
|
||||
indices := make(map[[32]byte]uint64)
|
||||
nodes := make([]*Node, 0)
|
||||
indices[indexToHash(uint64(0))] = uint64(0)
|
||||
nodes = append(nodes, &Node{
|
||||
slot: types.Slot(0),
|
||||
root: indexToHash(uint64(0)),
|
||||
bestDescendant: uint64(numOfNodes - 1),
|
||||
bestChild: uint64(1),
|
||||
parent: NonExistentNode,
|
||||
})
|
||||
for i := 1; i < numOfNodes-1; i++ {
|
||||
indices[indexToHash(uint64(i))] = uint64(i)
|
||||
nodes = append(nodes, &Node{
|
||||
slot: types.Slot(i),
|
||||
root: indexToHash(uint64(i)),
|
||||
bestDescendant: uint64(numOfNodes - 1),
|
||||
bestChild: uint64(i + 1),
|
||||
parent: uint64(i) - 1,
|
||||
})
|
||||
numOfNodes := uint64(100)
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
for i := uint64(2); i < numOfNodes; i++ {
|
||||
require.NoError(t, f.ProcessBlock(ctx, types.Slot(i), indexToHash(i), indexToHash(i-1), 0, 0, false))
|
||||
}
|
||||
nodes = append(nodes, &Node{
|
||||
slot: types.Slot(numOfNodes - 1),
|
||||
root: indexToHash(uint64(numOfNodes - 1)),
|
||||
bestDescendant: NonExistentNode,
|
||||
bestChild: NonExistentNode,
|
||||
parent: uint64(numOfNodes - 2),
|
||||
})
|
||||
indices[indexToHash(uint64(numOfNodes-1))] = uint64(numOfNodes - 1)
|
||||
s := &Store{nodes: nodes, nodesIndices: indices}
|
||||
syncedTips := &optimisticStore{}
|
||||
|
||||
s := f.store
|
||||
s.pruneThreshold = 0
|
||||
|
||||
// Finalized root is at index 99 so everything before 99 should be pruned.
|
||||
require.NoError(t, s.prune(context.Background(), indexToHash(99), syncedTips))
|
||||
assert.Equal(t, 1, len(s.nodes), "Incorrect nodes count")
|
||||
assert.Equal(t, 1, len(s.nodesIndices), "Incorrect node indices count")
|
||||
require.NoError(t, s.prune(context.Background(), indexToHash(99)))
|
||||
assert.Equal(t, 1, len(s.nodeByRoot), "Incorrect nodes count")
|
||||
}
|
||||
|
||||
func TestStore_Prune_MoreThanOnce(t *testing.T) {
|
||||
// Define 100 nodes in store.
|
||||
numOfNodes := 100
|
||||
indices := make(map[[32]byte]uint64)
|
||||
nodes := make([]*Node, 0)
|
||||
nodes = append(nodes, &Node{
|
||||
slot: types.Slot(0),
|
||||
root: indexToHash(uint64(0)),
|
||||
bestDescendant: uint64(numOfNodes - 1),
|
||||
bestChild: uint64(1),
|
||||
parent: NonExistentNode,
|
||||
})
|
||||
for i := 1; i < numOfNodes-1; i++ {
|
||||
indices[indexToHash(uint64(i))] = uint64(i)
|
||||
nodes = append(nodes, &Node{
|
||||
slot: types.Slot(i),
|
||||
root: indexToHash(uint64(i)),
|
||||
bestDescendant: uint64(numOfNodes - 1),
|
||||
bestChild: uint64(i + 1),
|
||||
parent: uint64(i) - 1,
|
||||
})
|
||||
numOfNodes := uint64(100)
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
for i := uint64(2); i < numOfNodes; i++ {
|
||||
require.NoError(t, f.ProcessBlock(ctx, types.Slot(i), indexToHash(i), indexToHash(i-1), 0, 0, false))
|
||||
}
|
||||
nodes = append(nodes, &Node{
|
||||
slot: types.Slot(numOfNodes - 1),
|
||||
root: indexToHash(uint64(numOfNodes - 1)),
|
||||
bestDescendant: NonExistentNode,
|
||||
bestChild: NonExistentNode,
|
||||
parent: uint64(numOfNodes - 2),
|
||||
})
|
||||
|
||||
s := &Store{nodes: nodes, nodesIndices: indices}
|
||||
syncedTips := &optimisticStore{}
|
||||
s := f.store
|
||||
s.pruneThreshold = 0
|
||||
|
||||
// Finalized root is at index 11 so everything before 11 should be pruned.
|
||||
require.NoError(t, s.prune(context.Background(), indexToHash(10), syncedTips))
|
||||
assert.Equal(t, 90, len(s.nodes), "Incorrect nodes count")
|
||||
assert.Equal(t, 90, len(s.nodesIndices), "Incorrect node indices count")
|
||||
require.NoError(t, s.prune(context.Background(), indexToHash(10)))
|
||||
assert.Equal(t, 90, len(s.nodeByRoot), "Incorrect nodes count")
|
||||
|
||||
// One more time.
|
||||
require.NoError(t, s.prune(context.Background(), indexToHash(20), syncedTips))
|
||||
assert.Equal(t, 80, len(s.nodes), "Incorrect nodes count")
|
||||
assert.Equal(t, 80, len(s.nodesIndices), "Incorrect node indices count")
|
||||
require.NoError(t, s.prune(context.Background(), indexToHash(20)))
|
||||
assert.Equal(t, 80, len(s.nodeByRoot), "Incorrect nodes count")
|
||||
}
|
||||
|
||||
// This unit tests starts with a simple branch like this
|
||||
@@ -489,41 +202,15 @@ func TestStore_Prune_MoreThanOnce(t *testing.T) {
|
||||
//
|
||||
// And we finalize 1. As a result only 1 should survive
|
||||
func TestStore_Prune_NoDanglingBranch(t *testing.T) {
|
||||
nodes := []*Node{
|
||||
{
|
||||
slot: 100,
|
||||
bestChild: 1,
|
||||
bestDescendant: 1,
|
||||
root: indexToHash(uint64(0)),
|
||||
parent: NonExistentNode,
|
||||
},
|
||||
{
|
||||
slot: 101,
|
||||
root: indexToHash(uint64(1)),
|
||||
bestChild: NonExistentNode,
|
||||
bestDescendant: NonExistentNode,
|
||||
parent: 0,
|
||||
},
|
||||
{
|
||||
slot: 101,
|
||||
root: indexToHash(uint64(2)),
|
||||
parent: 0,
|
||||
bestChild: NonExistentNode,
|
||||
bestDescendant: NonExistentNode,
|
||||
},
|
||||
}
|
||||
syncedTips := &optimisticStore{}
|
||||
s := &Store{
|
||||
pruneThreshold: 0,
|
||||
nodes: nodes,
|
||||
nodesIndices: map[[32]byte]uint64{
|
||||
indexToHash(uint64(0)): 0,
|
||||
indexToHash(uint64(1)): 1,
|
||||
indexToHash(uint64(2)): 2,
|
||||
},
|
||||
}
|
||||
require.NoError(t, s.prune(context.Background(), indexToHash(uint64(1)), syncedTips))
|
||||
require.Equal(t, len(s.nodes), 1)
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
f.store.pruneThreshold = 0
|
||||
|
||||
s := f.store
|
||||
require.NoError(t, s.prune(context.Background(), indexToHash(1)))
|
||||
require.Equal(t, len(s.nodeByRoot), 1)
|
||||
}
|
||||
|
||||
// This test starts with the following branching diagram
|
||||
@@ -538,254 +225,58 @@ func TestStore_Prune_NoDanglingBranch(t *testing.T) {
|
||||
// J -- K -- L
|
||||
//
|
||||
//
|
||||
// Synced tips are B, D and E. And we finalize F. All that is left in fork
|
||||
// choice is F, and the only synced tip left is E which is now away from Fork
|
||||
// Choice.
|
||||
func TestStore_PruneSyncedTips(t *testing.T) {
|
||||
func TestStore_tips(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
f := setup(1, 1)
|
||||
|
||||
require.NoError(t, f.ProcessBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, [32]byte{}, 1, 1))
|
||||
syncedTips := &optimisticStore{
|
||||
validatedTips: map[[32]byte]types.Slot{
|
||||
[32]byte{'b'}: 101,
|
||||
[32]byte{'d'}: 103,
|
||||
[32]byte{'e'}: 104,
|
||||
},
|
||||
require.NoError(t, f.ProcessBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, 1, 1, true))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, 1, 1, true))
|
||||
expectedMap := map[[32]byte]types.Slot{
|
||||
[32]byte{'f'}: 105,
|
||||
[32]byte{'i'}: 106,
|
||||
[32]byte{'l'}: 106,
|
||||
[32]byte{'j'}: 102,
|
||||
}
|
||||
f.syncedTips = syncedTips
|
||||
f.store.pruneThreshold = 0
|
||||
require.NoError(t, f.Prune(ctx, [32]byte{'f'}))
|
||||
require.Equal(t, 1, len(f.syncedTips.validatedTips))
|
||||
_, ok := f.syncedTips.validatedTips[[32]byte{'e'}]
|
||||
require.Equal(t, true, ok)
|
||||
}
|
||||
|
||||
func TestStore_LeadsToViableHead(t *testing.T) {
|
||||
tests := []struct {
|
||||
n *Node
|
||||
justifiedEpoch types.Epoch
|
||||
finalizedEpoch types.Epoch
|
||||
want bool
|
||||
}{
|
||||
{&Node{}, 0, 0, true},
|
||||
{&Node{}, 1, 0, false},
|
||||
{&Node{}, 0, 1, false},
|
||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 1, 1, true},
|
||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 2, 2, false},
|
||||
{&Node{finalizedEpoch: 3, justifiedEpoch: 4}, 4, 3, true},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
s := &Store{
|
||||
justifiedEpoch: tc.justifiedEpoch,
|
||||
finalizedEpoch: tc.finalizedEpoch,
|
||||
nodes: []*Node{tc.n},
|
||||
}
|
||||
got, err := s.leadsToViableHead(tc.n)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.want, got)
|
||||
roots, slots := f.store.tips()
|
||||
for i, r := range roots {
|
||||
expectedSlot, ok := expectedMap[r]
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, slots[i], expectedSlot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_SetSyncedTips(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
tips := make(map[[32]byte]types.Slot)
|
||||
require.ErrorIs(t, errInvalidSyncedTips, f.SetSyncedTips(tips))
|
||||
tips[bytesutil.ToBytes32([]byte{'a'})] = 1
|
||||
require.NoError(t, f.SetSyncedTips(tips))
|
||||
f.syncedTips.RLock()
|
||||
defer f.syncedTips.RUnlock()
|
||||
require.Equal(t, 1, len(f.syncedTips.validatedTips))
|
||||
slot, ok := f.syncedTips.validatedTips[bytesutil.ToBytes32([]byte{'a'})]
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, types.Slot(1), slot)
|
||||
}
|
||||
func TestStore_PruneMapsNodes(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0, false))
|
||||
|
||||
s := f.store
|
||||
s.pruneThreshold = 0
|
||||
require.NoError(t, s.prune(context.Background(), indexToHash(uint64(1))))
|
||||
require.Equal(t, len(s.nodeByRoot), 1)
|
||||
|
||||
func TestStore_ViableForHead(t *testing.T) {
|
||||
tests := []struct {
|
||||
n *Node
|
||||
justifiedEpoch types.Epoch
|
||||
finalizedEpoch types.Epoch
|
||||
want bool
|
||||
}{
|
||||
{&Node{}, 0, 0, true},
|
||||
{&Node{}, 1, 0, false},
|
||||
{&Node{}, 0, 1, false},
|
||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 1, 1, true},
|
||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 2, 2, false},
|
||||
{&Node{finalizedEpoch: 3, justifiedEpoch: 4}, 4, 3, true},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
s := &Store{
|
||||
justifiedEpoch: tc.justifiedEpoch,
|
||||
finalizedEpoch: tc.finalizedEpoch,
|
||||
}
|
||||
assert.Equal(t, tc.want, s.viableForHead(tc.n))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_HasParent(t *testing.T) {
|
||||
tests := []struct {
|
||||
m map[[32]byte]uint64
|
||||
n []*Node
|
||||
r [32]byte
|
||||
want bool
|
||||
}{
|
||||
{r: [32]byte{'a'}, want: false},
|
||||
{m: map[[32]byte]uint64{{'a'}: 0}, r: [32]byte{'a'}, want: false},
|
||||
{m: map[[32]byte]uint64{{'a'}: 0}, r: [32]byte{'a'},
|
||||
n: []*Node{{parent: NonExistentNode}}, want: false},
|
||||
{m: map[[32]byte]uint64{{'a'}: 0},
|
||||
n: []*Node{{parent: 0}}, r: [32]byte{'a'},
|
||||
want: true},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
f := &ForkChoice{store: &Store{
|
||||
nodesIndices: tc.m,
|
||||
nodes: tc.n,
|
||||
}}
|
||||
assert.Equal(t, tc.want, f.HasParent(tc.r))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_AncestorRoot(t *testing.T) {
|
||||
f := setup(1, 1)
|
||||
ctx := context.Background()
|
||||
f := &ForkChoice{store: &Store{}}
|
||||
f.store.nodesIndices = map[[32]byte]uint64{}
|
||||
_, err := f.AncestorRoot(ctx, [32]byte{'a'}, 0)
|
||||
assert.ErrorContains(t, "node does not exist", err)
|
||||
f.store.nodesIndices[[32]byte{'a'}] = 0
|
||||
_, err = f.AncestorRoot(ctx, [32]byte{'a'}, 0)
|
||||
assert.ErrorContains(t, "node index out of range", err)
|
||||
f.store.nodesIndices[[32]byte{'b'}] = 1
|
||||
f.store.nodesIndices[[32]byte{'c'}] = 2
|
||||
f.store.nodes = []*Node{
|
||||
{slot: 1, root: [32]byte{'a'}, parent: NonExistentNode},
|
||||
{slot: 2, root: [32]byte{'b'}, parent: 0},
|
||||
{slot: 3, root: [32]byte{'c'}, parent: 1},
|
||||
}
|
||||
|
||||
r, err := f.AncestorRoot(ctx, [32]byte{'c'}, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, bytesutil.ToBytes32(r), [32]byte{'a'})
|
||||
r, err = f.AncestorRoot(ctx, [32]byte{'c'}, 2)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, bytesutil.ToBytes32(r), [32]byte{'b'})
|
||||
}
|
||||
|
||||
func TestStore_AncestorRootOutOfBound(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
f := &ForkChoice{store: &Store{}}
|
||||
f.store.nodesIndices = map[[32]byte]uint64{}
|
||||
_, err := f.AncestorRoot(ctx, [32]byte{'a'}, 0)
|
||||
assert.ErrorContains(t, "node does not exist", err)
|
||||
f.store.nodesIndices[[32]byte{'a'}] = 0
|
||||
_, err = f.AncestorRoot(ctx, [32]byte{'a'}, 0)
|
||||
assert.ErrorContains(t, "node index out of range", err)
|
||||
f.store.nodesIndices[[32]byte{'b'}] = 1
|
||||
f.store.nodesIndices[[32]byte{'c'}] = 2
|
||||
f.store.nodes = []*Node{
|
||||
{slot: 1, root: [32]byte{'a'}, parent: NonExistentNode},
|
||||
{slot: 2, root: [32]byte{'b'}, parent: 100}, // Out of bound parent.
|
||||
{slot: 3, root: [32]byte{'c'}, parent: 1},
|
||||
}
|
||||
|
||||
_, err = f.AncestorRoot(ctx, [32]byte{'c'}, 1)
|
||||
require.ErrorContains(t, "node index out of range", err)
|
||||
}
|
||||
|
||||
func TestStore_UpdateCanonicalNodes_WholeList(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
f := &ForkChoice{store: &Store{}}
|
||||
f.store.canonicalNodes = map[[32]byte]bool{}
|
||||
f.store.nodesIndices = map[[32]byte]uint64{}
|
||||
f.store.nodes = []*Node{
|
||||
{slot: 1, root: [32]byte{'a'}, parent: NonExistentNode},
|
||||
{slot: 2, root: [32]byte{'b'}, parent: 0},
|
||||
{slot: 3, root: [32]byte{'c'}, parent: 1},
|
||||
}
|
||||
f.store.nodesIndices[[32]byte{'c'}] = 2
|
||||
require.NoError(t, f.store.updateCanonicalNodes(ctx, [32]byte{'c'}))
|
||||
require.Equal(t, len(f.store.nodes), len(f.store.canonicalNodes))
|
||||
require.Equal(t, true, f.IsCanonical([32]byte{'a'}))
|
||||
require.Equal(t, true, f.IsCanonical([32]byte{'b'}))
|
||||
require.Equal(t, true, f.IsCanonical([32]byte{'c'}))
|
||||
require.DeepEqual(t, f.Node([32]byte{'c'}), f.store.nodes[2])
|
||||
require.Equal(t, f.Node([32]byte{'d'}), (*Node)(nil))
|
||||
}
|
||||
|
||||
func TestStore_UpdateCanonicalNodes_ParentAlreadyIn(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
f := &ForkChoice{store: &Store{}}
|
||||
f.store.canonicalNodes = map[[32]byte]bool{}
|
||||
f.store.nodesIndices = map[[32]byte]uint64{}
|
||||
f.store.nodes = []*Node{
|
||||
{},
|
||||
{slot: 2, root: [32]byte{'b'}, parent: 0},
|
||||
{slot: 3, root: [32]byte{'c'}, parent: 1},
|
||||
}
|
||||
f.store.nodesIndices[[32]byte{'c'}] = 2
|
||||
f.store.canonicalNodes[[32]byte{'b'}] = true
|
||||
require.NoError(t, f.store.updateCanonicalNodes(ctx, [32]byte{'c'}))
|
||||
require.Equal(t, len(f.store.nodes)-1, len(f.store.canonicalNodes))
|
||||
|
||||
require.Equal(t, true, f.IsCanonical([32]byte{'c'}))
|
||||
require.Equal(t, true, f.IsCanonical([32]byte{'b'}))
|
||||
}
|
||||
|
||||
func TestStore_UpdateCanonicalNodes_ContextCancelled(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
f := &ForkChoice{store: &Store{}}
|
||||
f.store.canonicalNodes = map[[32]byte]bool{}
|
||||
f.store.nodesIndices = map[[32]byte]uint64{}
|
||||
f.store.nodes = []*Node{
|
||||
{slot: 1, root: [32]byte{'a'}, parent: NonExistentNode},
|
||||
{slot: 2, root: [32]byte{'b'}, parent: 0},
|
||||
{slot: 3, root: [32]byte{'c'}, parent: 1},
|
||||
}
|
||||
f.store.nodesIndices[[32]byte{'c'}] = 2
|
||||
cancel()
|
||||
require.ErrorContains(t, "context canceled", f.store.updateCanonicalNodes(ctx, [32]byte{'c'}))
|
||||
}
|
||||
|
||||
func TestStore_UpdateCanonicalNodes_RemoveOldCanonical(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
f := &ForkChoice{store: &Store{}}
|
||||
f.store.canonicalNodes = map[[32]byte]bool{}
|
||||
f.store.nodesIndices = map[[32]byte]uint64{
|
||||
[32]byte{'a'}: 0,
|
||||
[32]byte{'b'}: 1,
|
||||
[32]byte{'c'}: 2,
|
||||
[32]byte{'d'}: 3,
|
||||
[32]byte{'e'}: 4,
|
||||
}
|
||||
|
||||
f.store.nodes = []*Node{
|
||||
{slot: 1, root: [32]byte{'a'}, parent: NonExistentNode},
|
||||
{slot: 2, root: [32]byte{'b'}, parent: 0},
|
||||
{slot: 3, root: [32]byte{'c'}, parent: 1},
|
||||
{slot: 4, root: [32]byte{'d'}, parent: 1},
|
||||
{slot: 5, root: [32]byte{'e'}, parent: 3},
|
||||
}
|
||||
require.NoError(t, f.store.updateCanonicalNodes(ctx, [32]byte{'c'}))
|
||||
require.Equal(t, 3, len(f.store.canonicalNodes))
|
||||
require.NoError(t, f.store.updateCanonicalNodes(ctx, [32]byte{'e'}))
|
||||
require.Equal(t, 4, len(f.store.canonicalNodes))
|
||||
require.Equal(t, true, f.IsCanonical([32]byte{'a'}))
|
||||
require.Equal(t, true, f.IsCanonical([32]byte{'b'}))
|
||||
require.Equal(t, true, f.IsCanonical([32]byte{'d'}))
|
||||
require.Equal(t, true, f.IsCanonical([32]byte{'e'}))
|
||||
_, ok := f.store.canonicalNodes[[32]byte{'c'}]
|
||||
require.Equal(t, false, ok)
|
||||
require.NoError(t, f.ProcessBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 2, indexToHash(2), indexToHash(1), 1, 1, false))
|
||||
require.NoError(t, f.ProcessBlock(ctx, 3, indexToHash(3), indexToHash(2), 1, 1, false))
|
||||
require.Equal(t, false, f.HasParent(params.BeaconConfig().ZeroHash))
|
||||
require.Equal(t, true, f.HasParent(indexToHash(1)))
|
||||
require.Equal(t, true, f.HasParent(indexToHash(2)))
|
||||
require.Equal(t, true, f.HasParent(indexToHash(3)))
|
||||
require.Equal(t, false, f.HasParent(indexToHash(4)))
|
||||
}
|
||||
|
||||
@@ -9,25 +9,23 @@ import (
|
||||
|
||||
// ForkChoice defines the overall fork choice store which includes all block nodes, validator's latest votes and balances.
|
||||
type ForkChoice struct {
|
||||
store *Store
|
||||
votes []Vote // tracks individual validator's last vote.
|
||||
votesLock sync.RWMutex
|
||||
balances []uint64 // tracks individual validator's last justified balances.
|
||||
syncedTips *optimisticStore
|
||||
store *Store
|
||||
votes []Vote // tracks individual validator's last vote.
|
||||
votesLock sync.RWMutex
|
||||
balances []uint64 // tracks individual validator's last justified balances.
|
||||
}
|
||||
|
||||
// Store defines the fork choice store which includes block nodes and the last view of checkpoint information.
|
||||
type Store struct {
|
||||
pruneThreshold uint64 // do not prune tree unless threshold is reached.
|
||||
justifiedEpoch types.Epoch // latest justified epoch in store.
|
||||
finalizedEpoch types.Epoch // latest finalized epoch in store.
|
||||
finalizedRoot [fieldparams.RootLength]byte // latest finalized root in store.
|
||||
proposerBoostRoot [fieldparams.RootLength]byte // latest block root that was boosted after being received in a timely manner.
|
||||
previousProposerBoostRoot [fieldparams.RootLength]byte // previous block root that was boosted after being received in a timely manner.
|
||||
previousProposerBoostScore uint64 // previous proposer boosted root score.
|
||||
nodes []*Node // list of block nodes, each node is a representation of one block.
|
||||
nodesIndices map[[fieldparams.RootLength]byte]uint64 // the root of block node and the nodes index in the list.
|
||||
canonicalNodes map[[fieldparams.RootLength]byte]bool // the canonical block nodes.
|
||||
justifiedEpoch types.Epoch // latest justified epoch in store.
|
||||
finalizedEpoch types.Epoch // latest finalized epoch in store.
|
||||
pruneThreshold uint64 // do not prune tree unless threshold is reached.
|
||||
proposerBoostRoot [fieldparams.RootLength]byte // latest block root that was boosted after being received in a timely manner.
|
||||
previousProposerBoostRoot [fieldparams.RootLength]byte // previous block root that was boosted after being received in a timely manner.
|
||||
previousProposerBoostScore uint64 // previous proposer boosted root score.
|
||||
treeRootNode *Node // the root node of the store tree.
|
||||
headNode *Node // last head Node
|
||||
nodeByRoot map[[fieldparams.RootLength]byte]*Node // nodes indexed by roots.
|
||||
nodesLock sync.RWMutex
|
||||
proposerBoostLock sync.RWMutex
|
||||
}
|
||||
@@ -37,20 +35,14 @@ type Store struct {
|
||||
type Node struct {
|
||||
slot types.Slot // slot of the block converted to the node.
|
||||
root [fieldparams.RootLength]byte // root of the block converted to the node.
|
||||
parent uint64 // parent index of this node.
|
||||
parent *Node // parent index of this node.
|
||||
children []*Node // the list of direct children of this Node
|
||||
justifiedEpoch types.Epoch // justifiedEpoch of this node.
|
||||
finalizedEpoch types.Epoch // finalizedEpoch of this node.
|
||||
weight uint64 // weight of this node.
|
||||
bestChild uint64 // bestChild index of this node.
|
||||
bestDescendant uint64 // bestDescendant of this node.
|
||||
graffiti [fieldparams.RootLength]byte // graffiti of the block node.
|
||||
}
|
||||
|
||||
// optimisticStore defines a structure that tracks the tips of the fully
|
||||
// validated blocks tree.
|
||||
type optimisticStore struct {
|
||||
validatedTips map[[32]byte]types.Slot
|
||||
sync.RWMutex
|
||||
balance uint64 // the balance that voted for this node directly
|
||||
weight uint64 // weight of this node: the total balance including children
|
||||
bestDescendant *Node // bestDescendant node of this node.
|
||||
optimistic bool // whether the block has been fully validated or not
|
||||
}
|
||||
|
||||
// Vote defines an individual validator's vote.
|
||||
@@ -59,6 +51,3 @@ type Vote struct {
|
||||
nextRoot [fieldparams.RootLength]byte // next voting root.
|
||||
nextEpoch types.Epoch // epoch of next voting period.
|
||||
}
|
||||
|
||||
// NonExistentNode defines an unknown node which is used for the array based stateful DAG.
|
||||
const NonExistentNode = ^uint64(0)
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
func TestVotes_CanFindHead(t *testing.T) {
|
||||
balances := []uint64{1, 1}
|
||||
f := setup(1, 1)
|
||||
syncedTips := &optimisticStore{}
|
||||
|
||||
// The head should always start at the finalized block.
|
||||
r, err := f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
@@ -23,7 +22,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 0
|
||||
// /
|
||||
// 2 <- head
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1, true))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -33,7 +32,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 0
|
||||
// / \
|
||||
// head -> 2 1
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1, true))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -63,7 +62,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// head -> 2 1
|
||||
// |
|
||||
// 3
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(3), indexToHash(1), [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(3), indexToHash(1), 1, 1, true))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -99,7 +98,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 3
|
||||
// |
|
||||
// 4 <- head
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(4), indexToHash(3), [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(4), indexToHash(3), 1, 1, true))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -115,7 +114,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 4 <- head
|
||||
// /
|
||||
// 5 <- justified epoch = 2
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(5), indexToHash(4), [32]byte{}, 2, 2))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(5), indexToHash(4), 2, 2, true))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -131,7 +130,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 4 <- head
|
||||
// / \
|
||||
// 5 6 <- justified epoch = 0
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(6), indexToHash(4), [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(6), indexToHash(4), 1, 1, true))
|
||||
|
||||
// Moved 2 votes to block 5:
|
||||
// 0
|
||||
@@ -143,7 +142,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 4
|
||||
// / \
|
||||
// 2 votes-> 5 6
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(6), indexToHash(4), [32]byte{}, 1, 1))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(6), indexToHash(4), 1, 1, true))
|
||||
|
||||
f.ProcessAttestation(context.Background(), []uint64{0, 1}, indexToHash(5), 4)
|
||||
|
||||
@@ -164,9 +163,9 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 8
|
||||
// |
|
||||
// 9
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(7), indexToHash(5), [32]byte{}, 2, 2))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(8), indexToHash(7), [32]byte{}, 2, 2))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(9), indexToHash(8), [32]byte{}, 2, 2))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(7), indexToHash(5), 2, 2, true))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(8), indexToHash(7), 2, 2, true))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(9), indexToHash(8), 2, 2, true))
|
||||
|
||||
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
|
||||
require.NoError(t, err)
|
||||
@@ -211,7 +210,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// / \
|
||||
// 2 votes->9 10
|
||||
f.ProcessAttestation(context.Background(), []uint64{0, 1}, indexToHash(9), 5)
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(10), indexToHash(8), [32]byte{}, 2, 2))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(10), indexToHash(8), 2, 2, true))
|
||||
|
||||
r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
|
||||
require.NoError(t, err)
|
||||
@@ -249,8 +248,8 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
|
||||
// Verify pruning below the prune threshold does not affect head.
|
||||
f.store.pruneThreshold = 1000
|
||||
require.NoError(t, f.store.prune(context.Background(), indexToHash(5), syncedTips))
|
||||
assert.Equal(t, 11, len(f.store.nodes), "Incorrect nodes length after prune")
|
||||
require.NoError(t, f.store.prune(context.Background(), indexToHash(5)))
|
||||
assert.Equal(t, 11, len(f.store.nodeByRoot), "Incorrect nodes length after prune")
|
||||
|
||||
r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
|
||||
require.NoError(t, err)
|
||||
@@ -273,8 +272,8 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// / \
|
||||
// 9 10
|
||||
f.store.pruneThreshold = 1
|
||||
require.NoError(t, f.store.prune(context.Background(), indexToHash(5), syncedTips))
|
||||
assert.Equal(t, 5, len(f.store.nodes), "Incorrect nodes length after prune")
|
||||
require.NoError(t, f.store.prune(context.Background(), indexToHash(5)))
|
||||
assert.Equal(t, 5, len(f.store.nodeByRoot), "Incorrect nodes length after prune")
|
||||
|
||||
r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
|
||||
require.NoError(t, err)
|
||||
@@ -290,7 +289,7 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 9 10
|
||||
// |
|
||||
// head-> 11
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(11), indexToHash(9), [32]byte{}, 2, 2))
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, indexToHash(11), indexToHash(9), 2, 2, true))
|
||||
|
||||
r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -310,7 +310,7 @@ func (b *BeaconNode) Close() {
|
||||
}
|
||||
|
||||
func (b *BeaconNode) startForkChoice() {
|
||||
f := protoarray.New(0, 0, params.BeaconConfig().ZeroHash)
|
||||
f := protoarray.New(0, 0)
|
||||
b.forkChoiceStore = f
|
||||
}
|
||||
|
||||
@@ -808,8 +808,6 @@ func (b *BeaconNode) registerPrometheusService(cliCtx *cli.Context) error {
|
||||
)
|
||||
}
|
||||
|
||||
additionalHandlers = append(additionalHandlers, prometheus.Handler{Path: "/tree", Handler: c.TreeHandler})
|
||||
|
||||
service := prometheus.NewService(
|
||||
fmt.Sprintf("%s:%d", b.cliCtx.String(cmd.MonitoringHostFlag.Name), b.cliCtx.Int(flags.MonitoringPortFlag.Name)),
|
||||
b.services,
|
||||
|
||||
@@ -16,6 +16,7 @@ go_library(
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/db/filters:go_default_library",
|
||||
"//beacon-chain/forkchoice/protoarray:go_default_library",
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
|
||||
@@ -2,43 +2,39 @@ package debug
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray"
|
||||
pbrpc "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
func protoArrayNode(node *protoarray.Node) *pbrpc.ProtoArrayNode {
|
||||
var pbChildren []*pbrpc.ProtoArrayNode
|
||||
for _, child := range node.Children() {
|
||||
pbChildren = append(pbChildren, protoArrayNode(child))
|
||||
}
|
||||
root := node.Root()
|
||||
return &pbrpc.ProtoArrayNode{
|
||||
Slot: node.Slot(),
|
||||
Root: root[:],
|
||||
Children: pbChildren,
|
||||
JustifiedEpoch: node.JustifiedEpoch(),
|
||||
FinalizedEpoch: node.FinalizedEpoch(),
|
||||
Balance: node.Balance(),
|
||||
Optimistic: node.Optimistic(),
|
||||
}
|
||||
}
|
||||
|
||||
// GetProtoArrayForkChoice returns proto array fork choice store.
|
||||
func (ds *Server) GetProtoArrayForkChoice(_ context.Context, _ *empty.Empty) (*pbrpc.ProtoArrayForkChoiceResponse, error) {
|
||||
store := ds.HeadFetcher.ProtoArrayStore()
|
||||
|
||||
nodes := store.Nodes()
|
||||
returnedNodes := make([]*pbrpc.ProtoArrayNode, len(nodes))
|
||||
|
||||
for i := 0; i < len(returnedNodes); i++ {
|
||||
r := nodes[i].Root()
|
||||
returnedNodes[i] = &pbrpc.ProtoArrayNode{
|
||||
Slot: nodes[i].Slot(),
|
||||
Root: r[:],
|
||||
Parent: nodes[i].Parent(),
|
||||
JustifiedEpoch: nodes[i].JustifiedEpoch(),
|
||||
FinalizedEpoch: nodes[i].FinalizedEpoch(),
|
||||
Weight: nodes[i].Weight(),
|
||||
BestChild: nodes[i].BestChild(),
|
||||
BestDescendant: nodes[i].BestDescendant(),
|
||||
}
|
||||
}
|
||||
|
||||
indices := make(map[string]uint64, len(store.NodesIndices()))
|
||||
for k, v := range store.NodesIndices() {
|
||||
indices[hex.EncodeToString(k[:])] = v
|
||||
}
|
||||
treeRoot := store.TreeRoot()
|
||||
pbTreeRoot := protoArrayNode(treeRoot)
|
||||
|
||||
return &pbrpc.ProtoArrayForkChoiceResponse{
|
||||
PruneThreshold: store.PruneThreshold(),
|
||||
JustifiedEpoch: store.JustifiedEpoch(),
|
||||
FinalizedEpoch: store.FinalizedEpoch(),
|
||||
ProtoArrayNodes: returnedNodes,
|
||||
Indices: indices,
|
||||
PruneThreshold: store.PruneThreshold(),
|
||||
JustifiedEpoch: store.JustifiedEpoch(),
|
||||
FinalizedEpoch: store.FinalizedEpoch(),
|
||||
TreeRoot: pbTreeRoot,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@ import (
|
||||
)
|
||||
|
||||
func TestServer_GetForkChoice(t *testing.T) {
|
||||
store := &protoarray.Store{}
|
||||
f := protoarray.New(0, 0)
|
||||
require.NoError(t, f.ProcessBlock(context.Background(), 0, [32]byte{}, [32]byte{}, 0, 0, false))
|
||||
store := f.Store()
|
||||
bs := &Server{HeadFetcher: &mock.ChainService{ForkChoiceStore: store}}
|
||||
res, err := bs.GetProtoArrayForkChoice(context.Background(), &empty.Empty{})
|
||||
require.NoError(t, err)
|
||||
|
||||
577
proto/prysm/v1alpha1/debug.pb.go
generated
577
proto/prysm/v1alpha1/debug.pb.go
generated
@@ -406,11 +406,10 @@ type ProtoArrayForkChoiceResponse struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
PruneThreshold uint64 `protobuf:"varint,1,opt,name=prune_threshold,json=pruneThreshold,proto3" json:"prune_threshold,omitempty"`
|
||||
JustifiedEpoch github_com_prysmaticlabs_eth2_types.Epoch `protobuf:"varint,2,opt,name=justified_epoch,json=justifiedEpoch,proto3" json:"justified_epoch,omitempty" cast-type:"github.com/prysmaticlabs/eth2-types.Epoch"`
|
||||
FinalizedEpoch github_com_prysmaticlabs_eth2_types.Epoch `protobuf:"varint,3,opt,name=finalized_epoch,json=finalizedEpoch,proto3" json:"finalized_epoch,omitempty" cast-type:"github.com/prysmaticlabs/eth2-types.Epoch"`
|
||||
ProtoArrayNodes []*ProtoArrayNode `protobuf:"bytes,4,rep,name=proto_array_nodes,json=protoArrayNodes,proto3" json:"proto_array_nodes,omitempty"`
|
||||
Indices map[string]uint64 `protobuf:"bytes,5,rep,name=indices,proto3" json:"indices,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
|
||||
PruneThreshold uint64 `protobuf:"varint,1,opt,name=prune_threshold,json=pruneThreshold,proto3" json:"prune_threshold,omitempty"`
|
||||
JustifiedEpoch github_com_prysmaticlabs_eth2_types.Epoch `protobuf:"varint,2,opt,name=justified_epoch,json=justifiedEpoch,proto3" json:"justified_epoch,omitempty" cast-type:"github.com/prysmaticlabs/eth2-types.Epoch"`
|
||||
FinalizedEpoch github_com_prysmaticlabs_eth2_types.Epoch `protobuf:"varint,3,opt,name=finalized_epoch,json=finalizedEpoch,proto3" json:"finalized_epoch,omitempty" cast-type:"github.com/prysmaticlabs/eth2-types.Epoch"`
|
||||
TreeRoot *ProtoArrayNode `protobuf:"bytes,4,opt,name=tree_root,json=treeRoot,proto3" json:"tree_root,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ProtoArrayForkChoiceResponse) Reset() {
|
||||
@@ -466,16 +465,9 @@ func (x *ProtoArrayForkChoiceResponse) GetFinalizedEpoch() github_com_prysmaticl
|
||||
return github_com_prysmaticlabs_eth2_types.Epoch(0)
|
||||
}
|
||||
|
||||
func (x *ProtoArrayForkChoiceResponse) GetProtoArrayNodes() []*ProtoArrayNode {
|
||||
func (x *ProtoArrayForkChoiceResponse) GetTreeRoot() *ProtoArrayNode {
|
||||
if x != nil {
|
||||
return x.ProtoArrayNodes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ProtoArrayForkChoiceResponse) GetIndices() map[string]uint64 {
|
||||
if x != nil {
|
||||
return x.Indices
|
||||
return x.TreeRoot
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -487,12 +479,14 @@ type ProtoArrayNode struct {
|
||||
|
||||
Slot github_com_prysmaticlabs_eth2_types.Slot `protobuf:"varint,1,opt,name=slot,proto3" json:"slot,omitempty" cast-type:"github.com/prysmaticlabs/eth2-types.Slot"`
|
||||
Root []byte `protobuf:"bytes,2,opt,name=root,proto3" json:"root,omitempty"`
|
||||
Parent uint64 `protobuf:"varint,3,opt,name=parent,proto3" json:"parent,omitempty"`
|
||||
JustifiedEpoch github_com_prysmaticlabs_eth2_types.Epoch `protobuf:"varint,4,opt,name=justified_epoch,json=justifiedEpoch,proto3" json:"justified_epoch,omitempty" cast-type:"github.com/prysmaticlabs/eth2-types.Epoch"`
|
||||
FinalizedEpoch github_com_prysmaticlabs_eth2_types.Epoch `protobuf:"varint,5,opt,name=finalized_epoch,json=finalizedEpoch,proto3" json:"finalized_epoch,omitempty" cast-type:"github.com/prysmaticlabs/eth2-types.Epoch"`
|
||||
Weight uint64 `protobuf:"varint,6,opt,name=weight,proto3" json:"weight,omitempty"`
|
||||
BestChild uint64 `protobuf:"varint,7,opt,name=best_child,json=bestChild,proto3" json:"best_child,omitempty"`
|
||||
BestDescendant uint64 `protobuf:"varint,8,opt,name=best_descendant,json=bestDescendant,proto3" json:"best_descendant,omitempty"`
|
||||
Parent *ProtoArrayNode `protobuf:"bytes,3,opt,name=parent,proto3" json:"parent,omitempty"`
|
||||
Children []*ProtoArrayNode `protobuf:"bytes,4,rep,name=children,proto3" json:"children,omitempty"`
|
||||
JustifiedEpoch github_com_prysmaticlabs_eth2_types.Epoch `protobuf:"varint,5,opt,name=justified_epoch,json=justifiedEpoch,proto3" json:"justified_epoch,omitempty" cast-type:"github.com/prysmaticlabs/eth2-types.Epoch"`
|
||||
FinalizedEpoch github_com_prysmaticlabs_eth2_types.Epoch `protobuf:"varint,6,opt,name=finalized_epoch,json=finalizedEpoch,proto3" json:"finalized_epoch,omitempty" cast-type:"github.com/prysmaticlabs/eth2-types.Epoch"`
|
||||
Balance uint64 `protobuf:"varint,7,opt,name=balance,proto3" json:"balance,omitempty"`
|
||||
Weight uint64 `protobuf:"varint,8,opt,name=weight,proto3" json:"weight,omitempty"`
|
||||
BestDescendant *ProtoArrayNode `protobuf:"bytes,9,opt,name=best_descendant,json=bestDescendant,proto3" json:"best_descendant,omitempty"`
|
||||
Optimistic bool `protobuf:"varint,10,opt,name=optimistic,proto3" json:"optimistic,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ProtoArrayNode) Reset() {
|
||||
@@ -541,11 +535,18 @@ func (x *ProtoArrayNode) GetRoot() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ProtoArrayNode) GetParent() uint64 {
|
||||
func (x *ProtoArrayNode) GetParent() *ProtoArrayNode {
|
||||
if x != nil {
|
||||
return x.Parent
|
||||
}
|
||||
return 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ProtoArrayNode) GetChildren() []*ProtoArrayNode {
|
||||
if x != nil {
|
||||
return x.Children
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ProtoArrayNode) GetJustifiedEpoch() github_com_prysmaticlabs_eth2_types.Epoch {
|
||||
@@ -562,6 +563,13 @@ func (x *ProtoArrayNode) GetFinalizedEpoch() github_com_prysmaticlabs_eth2_types
|
||||
return github_com_prysmaticlabs_eth2_types.Epoch(0)
|
||||
}
|
||||
|
||||
func (x *ProtoArrayNode) GetBalance() uint64 {
|
||||
if x != nil {
|
||||
return x.Balance
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ProtoArrayNode) GetWeight() uint64 {
|
||||
if x != nil {
|
||||
return x.Weight
|
||||
@@ -569,18 +577,18 @@ func (x *ProtoArrayNode) GetWeight() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ProtoArrayNode) GetBestChild() uint64 {
|
||||
if x != nil {
|
||||
return x.BestChild
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ProtoArrayNode) GetBestDescendant() uint64 {
|
||||
func (x *ProtoArrayNode) GetBestDescendant() *ProtoArrayNode {
|
||||
if x != nil {
|
||||
return x.BestDescendant
|
||||
}
|
||||
return 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ProtoArrayNode) GetOptimistic() bool {
|
||||
if x != nil {
|
||||
return x.Optimistic
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type DebugPeerResponses struct {
|
||||
@@ -924,7 +932,7 @@ type DebugPeerResponse_PeerInfo struct {
|
||||
func (x *DebugPeerResponse_PeerInfo) Reset() {
|
||||
*x = DebugPeerResponse_PeerInfo{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_prysm_v1alpha1_debug_proto_msgTypes[13]
|
||||
mi := &file_proto_prysm_v1alpha1_debug_proto_msgTypes[12]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -937,7 +945,7 @@ func (x *DebugPeerResponse_PeerInfo) String() string {
|
||||
func (*DebugPeerResponse_PeerInfo) ProtoMessage() {}
|
||||
|
||||
func (x *DebugPeerResponse_PeerInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_prysm_v1alpha1_debug_proto_msgTypes[13]
|
||||
mi := &file_proto_prysm_v1alpha1_debug_proto_msgTypes[12]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1054,7 +1062,7 @@ var file_proto_prysm_v1alpha1_debug_proto_rawDesc = []byte{
|
||||
0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x27, 0x0a, 0x05, 0x4c, 0x65,
|
||||
0x76, 0x65, 0x6c, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x00, 0x12, 0x09, 0x0a,
|
||||
0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43,
|
||||
0x45, 0x10, 0x02, 0x22, 0xe2, 0x03, 0x0a, 0x1c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x72, 0x72,
|
||||
0x45, 0x10, 0x02, 0x22, 0xbb, 0x02, 0x0a, 0x1c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x72, 0x72,
|
||||
0x61, 0x79, 0x46, 0x6f, 0x72, 0x6b, 0x43, 0x68, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70,
|
||||
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x5f, 0x74, 0x68,
|
||||
0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x70,
|
||||
@@ -1069,216 +1077,216 @@ var file_proto_prysm_v1alpha1_debug_proto_rawDesc = []byte{
|
||||
0x82, 0xb5, 0x18, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70,
|
||||
0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68,
|
||||
0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x0e, 0x66,
|
||||
0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x51, 0x0a,
|
||||
0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5f, 0x6e, 0x6f, 0x64,
|
||||
0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
|
||||
0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
|
||||
0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x72, 0x72, 0x61, 0x79, 0x4e, 0x6f, 0x64, 0x65, 0x52,
|
||||
0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x72, 0x72, 0x61, 0x79, 0x4e, 0x6f, 0x64, 0x65, 0x73,
|
||||
0x12, 0x5a, 0x0a, 0x07, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x40, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41,
|
||||
0x72, 0x72, 0x61, 0x79, 0x46, 0x6f, 0x72, 0x6b, 0x43, 0x68, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x45, 0x6e,
|
||||
0x74, 0x72, 0x79, 0x52, 0x07, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x1a, 0x3a, 0x0a, 0x0c,
|
||||
0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
|
||||
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14,
|
||||
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8e, 0x03, 0x0a, 0x0e, 0x50, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x41, 0x72, 0x72, 0x61, 0x79, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x40, 0x0a, 0x04, 0x73,
|
||||
0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x2c, 0x82, 0xb5, 0x18, 0x28, 0x67,
|
||||
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61,
|
||||
0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70,
|
||||
0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x72, 0x6f, 0x6f,
|
||||
0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x04, 0x52, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x56, 0x0a, 0x0f, 0x6a, 0x75, 0x73,
|
||||
0x74, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x04, 0x20, 0x01,
|
||||
0x28, 0x04, 0x42, 0x2d, 0x82, 0xb5, 0x18, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
|
||||
0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x42, 0x0a,
|
||||
0x09, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e,
|
||||
0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x72,
|
||||
0x72, 0x61, 0x79, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x08, 0x74, 0x72, 0x65, 0x65, 0x52, 0x6f, 0x6f,
|
||||
0x74, 0x22, 0xba, 0x04, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x72, 0x72, 0x61, 0x79,
|
||||
0x4e, 0x6f, 0x64, 0x65, 0x12, 0x40, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x04, 0x42, 0x2c, 0x82, 0xb5, 0x18, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73,
|
||||
0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63,
|
||||
0x68, 0x52, 0x0e, 0x6a, 0x75, 0x73, 0x74, 0x69, 0x66, 0x69, 0x65, 0x64, 0x45, 0x70, 0x6f, 0x63,
|
||||
0x68, 0x12, 0x56, 0x0a, 0x0f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x65,
|
||||
0x70, 0x6f, 0x63, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x2d, 0x82, 0xb5, 0x18, 0x29,
|
||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d,
|
||||
0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79,
|
||||
0x70, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x0e, 0x66, 0x69, 0x6e, 0x61, 0x6c,
|
||||
0x69, 0x7a, 0x65, 0x64, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x65, 0x69,
|
||||
0x67, 0x68, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68,
|
||||
0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x18,
|
||||
0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x62, 0x65, 0x73, 0x74, 0x43, 0x68, 0x69, 0x6c, 0x64,
|
||||
0x12, 0x27, 0x0a, 0x0f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x65, 0x6e, 0x64,
|
||||
0x61, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x62, 0x65, 0x73, 0x74, 0x44,
|
||||
0x65, 0x73, 0x63, 0x65, 0x6e, 0x64, 0x61, 0x6e, 0x74, 0x22, 0x5c, 0x0a, 0x12, 0x44, 0x65, 0x62,
|
||||
0x75, 0x67, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x12,
|
||||
0x46, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74,
|
||||
0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67,
|
||||
0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x09, 0x72, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x22, 0xbf, 0x06, 0x0a, 0x11, 0x44, 0x65, 0x62, 0x75,
|
||||
0x67, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a,
|
||||
0x13, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x6c, 0x69, 0x73, 0x74,
|
||||
0x65, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x42,
|
||||
0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x0e, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x44, 0x69,
|
||||
0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x12, 0x51, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x65,
|
||||
0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c,
|
||||
0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53,
|
||||
0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10,
|
||||
0x0a, 0x03, 0x65, 0x6e, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x72,
|
||||
0x12, 0x4e, 0x0a, 0x09, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65,
|
||||
0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75,
|
||||
0x67, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x50, 0x65,
|
||||
0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x70, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f,
|
||||
0x12, 0x3e, 0x0a, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18,
|
||||
0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d,
|
||||
0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x74,
|
||||
0x61, 0x74, 0x75, 0x73, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
|
||||
0x12, 0x21, 0x0a, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64,
|
||||
0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61,
|
||||
0x74, 0x65, 0x64, 0x12, 0x3f, 0x0a, 0x0a, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x69, 0x6e, 0x66,
|
||||
0x6f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65,
|
||||
0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
|
||||
0x53, 0x63, 0x6f, 0x72, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x73, 0x63, 0x6f, 0x72, 0x65,
|
||||
0x49, 0x6e, 0x66, 0x6f, 0x1a, 0xc2, 0x02, 0x0a, 0x08, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66,
|
||||
0x6f, 0x12, 0x41, 0x0a, 0x0a, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x56, 0x30, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d,
|
||||
0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4d, 0x65,
|
||||
0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x56, 0x30, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
||||
0x74, 0x61, 0x56, 0x30, 0x12, 0x41, 0x0a, 0x0a, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
|
||||
0x56, 0x31, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
|
||||
0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
|
||||
0x2e, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x56, 0x31, 0x52, 0x0a, 0x6d, 0x65, 0x74,
|
||||
0x61, 0x64, 0x61, 0x74, 0x61, 0x56, 0x31, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x63, 0x6f, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x66, 0x61, 0x75, 0x6c,
|
||||
0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
|
||||
0x6f, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x56,
|
||||
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x6c,
|
||||
0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x70, 0x65,
|
||||
0x65, 0x72, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x22, 0xc9, 0x03, 0x0a, 0x09, 0x53, 0x63,
|
||||
0x6f, 0x72, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x76, 0x65, 0x72, 0x61,
|
||||
0x6c, 0x6c, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0c,
|
||||
0x6f, 0x76, 0x65, 0x72, 0x61, 0x6c, 0x6c, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x29, 0x0a, 0x10,
|
||||
0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65,
|
||||
0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
|
||||
0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x12, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x50, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x54, 0x0a, 0x0c, 0x74, 0x6f, 0x70,
|
||||
0x69, 0x63, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76,
|
||||
0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x49, 0x6e, 0x66,
|
||||
0x6f, 0x2e, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74,
|
||||
0x72, 0x79, 0x52, 0x0b, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x12,
|
||||
0x21, 0x0a, 0x0c, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18,
|
||||
0x05, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0b, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x53, 0x63, 0x6f,
|
||||
0x72, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x75, 0x72, 0x5f,
|
||||
0x70, 0x65, 0x6e, 0x61, 0x6c, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x52, 0x10, 0x62,
|
||||
0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x75, 0x72, 0x50, 0x65, 0x6e, 0x61, 0x6c, 0x74, 0x79, 0x12,
|
||||
0x29, 0x0a, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72,
|
||||
0x72, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x69, 0x0a, 0x10, 0x54, 0x6f,
|
||||
0x70, 0x69, 0x63, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
|
||||
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
|
||||
0x12, 0x3f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76,
|
||||
0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x53, 0x63, 0x6f,
|
||||
0x72, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe6, 0x01, 0x0a, 0x12, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x53,
|
||||
0x63, 0x6f, 0x72, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x20, 0x0a, 0x0c,
|
||||
0x74, 0x69, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x5f, 0x6d, 0x65, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x04, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x4d, 0x65, 0x73, 0x68, 0x12, 0x38,
|
||||
0x0a, 0x18, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f,
|
||||
0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02,
|
||||
0x52, 0x16, 0x66, 0x69, 0x72, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x44, 0x65,
|
||||
0x6c, 0x69, 0x76, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x6d, 0x65, 0x73, 0x68,
|
||||
0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72,
|
||||
0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x15, 0x6d, 0x65, 0x73, 0x68, 0x4d,
|
||||
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x69, 0x65, 0x73,
|
||||
0x12, 0x3c, 0x0a, 0x1a, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x04,
|
||||
0x20, 0x01, 0x28, 0x02, 0x52, 0x18, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x4d, 0x65, 0x73,
|
||||
0x73, 0x61, 0x67, 0x65, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x69, 0x65, 0x73, 0x32, 0x9c,
|
||||
0x07, 0x0a, 0x05, 0x44, 0x65, 0x62, 0x75, 0x67, 0x12, 0x82, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74,
|
||||
0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x29, 0x2e, 0x65, 0x74,
|
||||
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
|
||||
0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75,
|
||||
0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53,
|
||||
0x53, 0x5a, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93,
|
||||
0x02, 0x1b, 0x12, 0x19, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
|
||||
0x31, 0x2f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x7c, 0x0a,
|
||||
0x08, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65,
|
||||
0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
|
||||
0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x79,
|
||||
0x52, 0x6f, 0x6f, 0x74, 0x1a, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x53, 0x5a,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b,
|
||||
0x12, 0x19, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f,
|
||||
0x64, 0x65, 0x62, 0x75, 0x67, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x7a, 0x0a, 0x0f, 0x53,
|
||||
0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x2a,
|
||||
0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31,
|
||||
0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65,
|
||||
0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
|
||||
0x74, 0x79, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x1b, 0x2f, 0x65, 0x74, 0x68,
|
||||
0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2f,
|
||||
0x6c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x12, 0x8e, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x50,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x41, 0x72, 0x72, 0x61, 0x79, 0x46, 0x6f, 0x72, 0x6b, 0x43, 0x68, 0x6f,
|
||||
0x69, 0x63, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x33, 0x2e, 0x65, 0x74,
|
||||
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
|
||||
0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x72, 0x72, 0x61, 0x79, 0x46, 0x6f,
|
||||
0x72, 0x6b, 0x43, 0x68, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76,
|
||||
0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2f, 0x66, 0x6f,
|
||||
0x72, 0x6b, 0x63, 0x68, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x71, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74,
|
||||
0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x29, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61,
|
||||
0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x50, 0x65, 0x65, 0x72, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b,
|
||||
0x12, 0x19, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f,
|
||||
0x64, 0x65, 0x62, 0x75, 0x67, 0x2f, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12, 0x79, 0x0a, 0x07, 0x47,
|
||||
0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75,
|
||||
0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50,
|
||||
0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74,
|
||||
0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x12, 0x3d, 0x0a, 0x06, 0x70, 0x61,
|
||||
0x72, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
|
||||
0x61, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70,
|
||||
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x65,
|
||||
0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x64, 0x65, 0x62, 0x75,
|
||||
0x67, 0x2f, 0x70, 0x65, 0x65, 0x72, 0x12, 0x94, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x49, 0x6e,
|
||||
0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x6c, 0x6f, 0x74, 0x12, 0x2b, 0x2e, 0x65, 0x74,
|
||||
0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x72, 0x72, 0x61, 0x79, 0x4e, 0x6f, 0x64,
|
||||
0x65, 0x52, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x41, 0x0a, 0x08, 0x63, 0x68, 0x69,
|
||||
0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74,
|
||||
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
|
||||
0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x6c, 0x6f,
|
||||
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
|
||||
0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
|
||||
0x2e, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d,
|
||||
0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x64, 0x65,
|
||||
0x62, 0x75, 0x67, 0x2f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x92, 0x01,
|
||||
0x0a, 0x19, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65,
|
||||
0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x0a, 0x44, 0x65, 0x62,
|
||||
0x75, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||
0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x72, 0x72, 0x61, 0x79, 0x4e, 0x6f,
|
||||
0x64, 0x65, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x12, 0x56, 0x0a, 0x0f,
|
||||
0x6a, 0x75, 0x73, 0x74, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18,
|
||||
0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x2d, 0x82, 0xb5, 0x18, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c,
|
||||
0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
|
||||
0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x65,
|
||||
0x74, 0x68, 0xaa, 0x02, 0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x74,
|
||||
0x68, 0x2e, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, 0x15, 0x45, 0x74, 0x68,
|
||||
0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
|
||||
0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x45,
|
||||
0x70, 0x6f, 0x63, 0x68, 0x52, 0x0e, 0x6a, 0x75, 0x73, 0x74, 0x69, 0x66, 0x69, 0x65, 0x64, 0x45,
|
||||
0x70, 0x6f, 0x63, 0x68, 0x12, 0x56, 0x0a, 0x0f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65,
|
||||
0x64, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x42, 0x2d, 0x82,
|
||||
0xb5, 0x18, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72,
|
||||
0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32,
|
||||
0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x0e, 0x66, 0x69,
|
||||
0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x18, 0x0a, 0x07,
|
||||
0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62,
|
||||
0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74,
|
||||
0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x4e,
|
||||
0x0a, 0x0f, 0x62, 0x65, 0x73, 0x74, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x65, 0x6e, 0x64, 0x61, 0x6e,
|
||||
0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65,
|
||||
0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
|
||||
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x72, 0x72, 0x61, 0x79, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0e,
|
||||
0x62, 0x65, 0x73, 0x74, 0x44, 0x65, 0x73, 0x63, 0x65, 0x6e, 0x64, 0x61, 0x6e, 0x74, 0x12, 0x1e,
|
||||
0x0a, 0x0a, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x18, 0x0a, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x0a, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x22, 0x5c,
|
||||
0x0a, 0x12, 0x44, 0x65, 0x62, 0x75, 0x67, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x73, 0x12, 0x46, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65,
|
||||
0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
|
||||
0x44, 0x65, 0x62, 0x75, 0x67, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x22, 0xbf, 0x06, 0x0a,
|
||||
0x11, 0x44, 0x65, 0x62, 0x75, 0x67, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x69, 0x6e, 0x67, 0x5f,
|
||||
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52,
|
||||
0x12, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75,
|
||||
0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50,
|
||||
0x65, 0x65, 0x72, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x64, 0x69,
|
||||
0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x51, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x0e, 0x32, 0x26, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65,
|
||||
0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x65, 0x65,
|
||||
0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x03, 0x65, 0x6e, 0x72, 0x12, 0x4e, 0x0a, 0x09, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x6e,
|
||||
0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
|
||||
0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
|
||||
0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x70, 0x65, 0x65,
|
||||
0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3e, 0x0a, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x73, 0x74,
|
||||
0x61, 0x74, 0x75, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
|
||||
0x61, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x53,
|
||||
0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70,
|
||||
0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6c, 0x61, 0x73,
|
||||
0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x3f, 0x0a, 0x0a, 0x73, 0x63, 0x6f, 0x72,
|
||||
0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65,
|
||||
0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c,
|
||||
0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09,
|
||||
0x73, 0x63, 0x6f, 0x72, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0xc2, 0x02, 0x0a, 0x08, 0x50, 0x65,
|
||||
0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x41, 0x0a, 0x0a, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
||||
0x74, 0x61, 0x56, 0x30, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x74, 0x68,
|
||||
0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
|
||||
0x61, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x56, 0x30, 0x52, 0x0a, 0x6d,
|
||||
0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x56, 0x30, 0x12, 0x41, 0x0a, 0x0a, 0x6d, 0x65, 0x74,
|
||||
0x61, 0x64, 0x61, 0x74, 0x61, 0x56, 0x31, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61,
|
||||
0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x56, 0x31,
|
||||
0x52, 0x0a, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x56, 0x31, 0x12, 0x1c, 0x0a, 0x09,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52,
|
||||
0x09, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x61,
|
||||
0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52,
|
||||
0x0a, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18,
|
||||
0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56,
|
||||
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f,
|
||||
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61,
|
||||
0x67, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x70,
|
||||
0x65, 0x65, 0x72, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28,
|
||||
0x04, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x22, 0xc9,
|
||||
0x03, 0x0a, 0x09, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x23, 0x0a, 0x0d,
|
||||
0x6f, 0x76, 0x65, 0x72, 0x61, 0x6c, 0x6c, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x02, 0x52, 0x0c, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x6c, 0x6c, 0x53, 0x63, 0x6f, 0x72,
|
||||
0x65, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x5f, 0x62,
|
||||
0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x70, 0x72, 0x6f,
|
||||
0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x30, 0x0a, 0x14,
|
||||
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x73,
|
||||
0x63, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x12, 0x62, 0x6c, 0x6f, 0x63,
|
||||
0x6b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x54,
|
||||
0x0a, 0x0c, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x04,
|
||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x63, 0x6f,
|
||||
0x72, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x53, 0x63, 0x6f, 0x72,
|
||||
0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x53, 0x63,
|
||||
0x6f, 0x72, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x5f, 0x73,
|
||||
0x63, 0x6f, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0b, 0x67, 0x6f, 0x73, 0x73,
|
||||
0x69, 0x70, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x62, 0x65, 0x68, 0x61, 0x76,
|
||||
0x69, 0x6f, 0x75, 0x72, 0x5f, 0x70, 0x65, 0x6e, 0x61, 0x6c, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01,
|
||||
0x28, 0x02, 0x52, 0x10, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x75, 0x72, 0x50, 0x65, 0x6e,
|
||||
0x61, 0x6c, 0x74, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f,
|
||||
0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x1a,
|
||||
0x69, 0x0a, 0x10, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x45, 0x6e,
|
||||
0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x6f, 0x70,
|
||||
0x69, 0x63, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52,
|
||||
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe6, 0x01, 0x0a, 0x12, 0x54,
|
||||
0x6f, 0x70, 0x69, 0x63, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f,
|
||||
0x74, 0x12, 0x20, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x5f, 0x6d, 0x65, 0x73,
|
||||
0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x4d,
|
||||
0x65, 0x73, 0x68, 0x12, 0x38, 0x0a, 0x18, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x6d, 0x65, 0x73,
|
||||
0x73, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x16, 0x66, 0x69, 0x72, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x36, 0x0a,
|
||||
0x17, 0x6d, 0x65, 0x73, 0x68, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x65,
|
||||
0x6c, 0x69, 0x76, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x15,
|
||||
0x6d, 0x65, 0x73, 0x68, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x44, 0x65, 0x6c, 0x69, 0x76,
|
||||
0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x1a, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64,
|
||||
0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72,
|
||||
0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x02, 0x52, 0x18, 0x69, 0x6e, 0x76, 0x61, 0x6c,
|
||||
0x69, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72,
|
||||
0x69, 0x65, 0x73, 0x32, 0x9c, 0x07, 0x0a, 0x05, 0x44, 0x65, 0x62, 0x75, 0x67, 0x12, 0x82, 0x01,
|
||||
0x0a, 0x0e, 0x47, 0x65, 0x74, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65,
|
||||
0x12, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e,
|
||||
0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53,
|
||||
0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x65, 0x74,
|
||||
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
|
||||
0x68, 0x61, 0x31, 0x2e, 0x53, 0x53, 0x5a, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||
0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31,
|
||||
0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2f, 0x73, 0x74, 0x61,
|
||||
0x74, 0x65, 0x12, 0x7c, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x29,
|
||||
0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31,
|
||||
0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x42, 0x79, 0x52, 0x6f, 0x6f, 0x74, 0x1a, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65,
|
||||
0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
|
||||
0x31, 0x2e, 0x53, 0x53, 0x5a, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82,
|
||||
0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c,
|
||||
0x70, 0x68, 0x61, 0x31, 0x2f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
|
||||
0x12, 0x7a, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65,
|
||||
0x76, 0x65, 0x6c, 0x12, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65,
|
||||
0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x67,
|
||||
0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22,
|
||||
0x1b, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x64,
|
||||
0x65, 0x62, 0x75, 0x67, 0x2f, 0x6c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x12, 0x8e, 0x01, 0x0a,
|
||||
0x17, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x72, 0x72, 0x61, 0x79, 0x46, 0x6f,
|
||||
0x72, 0x6b, 0x43, 0x68, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||
0x1a, 0x33, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e,
|
||||
0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x72,
|
||||
0x72, 0x61, 0x79, 0x46, 0x6f, 0x72, 0x6b, 0x43, 0x68, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f,
|
||||
0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x64, 0x65, 0x62,
|
||||
0x75, 0x67, 0x2f, 0x66, 0x6f, 0x72, 0x6b, 0x63, 0x68, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x71, 0x0a,
|
||||
0x09, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
|
||||
0x74, 0x79, 0x1a, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74,
|
||||
0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67,
|
||||
0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x22, 0x21, 0x82,
|
||||
0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c,
|
||||
0x70, 0x68, 0x61, 0x31, 0x2f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2f, 0x70, 0x65, 0x65, 0x72, 0x73,
|
||||
0x12, 0x79, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x22, 0x2e, 0x65, 0x74,
|
||||
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
|
||||
0x68, 0x61, 0x31, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76,
|
||||
0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x50, 0x65, 0x65,
|
||||
0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02,
|
||||
0x1a, 0x12, 0x18, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
|
||||
0x2f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2f, 0x70, 0x65, 0x65, 0x72, 0x12, 0x94, 0x01, 0x0a, 0x10,
|
||||
0x47, 0x65, 0x74, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x6c, 0x6f, 0x74,
|
||||
0x12, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e,
|
||||
0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61,
|
||||
0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x53,
|
||||
0x6c, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4,
|
||||
0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
|
||||
0x61, 0x31, 0x2f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x42, 0x92, 0x01, 0x0a, 0x19, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
|
||||
0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
|
||||
0x42, 0x0a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x37,
|
||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d,
|
||||
0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70,
|
||||
0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02, 0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65,
|
||||
0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xca,
|
||||
0x02, 0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76,
|
||||
0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -1294,7 +1302,7 @@ func file_proto_prysm_v1alpha1_debug_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_proto_prysm_v1alpha1_debug_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_proto_prysm_v1alpha1_debug_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
|
||||
var file_proto_prysm_v1alpha1_debug_proto_msgTypes = make([]protoimpl.MessageInfo, 14)
|
||||
var file_proto_prysm_v1alpha1_debug_proto_goTypes = []interface{}{
|
||||
(LoggingLevelRequest_Level)(0), // 0: ethereum.eth.v1alpha1.LoggingLevelRequest.Level
|
||||
(*InclusionSlotRequest)(nil), // 1: ethereum.eth.v1alpha1.InclusionSlotRequest
|
||||
@@ -1309,50 +1317,51 @@ var file_proto_prysm_v1alpha1_debug_proto_goTypes = []interface{}{
|
||||
(*DebugPeerResponse)(nil), // 10: ethereum.eth.v1alpha1.DebugPeerResponse
|
||||
(*ScoreInfo)(nil), // 11: ethereum.eth.v1alpha1.ScoreInfo
|
||||
(*TopicScoreSnapshot)(nil), // 12: ethereum.eth.v1alpha1.TopicScoreSnapshot
|
||||
nil, // 13: ethereum.eth.v1alpha1.ProtoArrayForkChoiceResponse.IndicesEntry
|
||||
(*DebugPeerResponse_PeerInfo)(nil), // 14: ethereum.eth.v1alpha1.DebugPeerResponse.PeerInfo
|
||||
nil, // 15: ethereum.eth.v1alpha1.ScoreInfo.TopicScoresEntry
|
||||
(PeerDirection)(0), // 16: ethereum.eth.v1alpha1.PeerDirection
|
||||
(ConnectionState)(0), // 17: ethereum.eth.v1alpha1.ConnectionState
|
||||
(*Status)(nil), // 18: ethereum.eth.v1alpha1.Status
|
||||
(*MetaDataV0)(nil), // 19: ethereum.eth.v1alpha1.MetaDataV0
|
||||
(*MetaDataV1)(nil), // 20: ethereum.eth.v1alpha1.MetaDataV1
|
||||
(*empty.Empty)(nil), // 21: google.protobuf.Empty
|
||||
(*PeerRequest)(nil), // 22: ethereum.eth.v1alpha1.PeerRequest
|
||||
(*DebugPeerResponse_PeerInfo)(nil), // 13: ethereum.eth.v1alpha1.DebugPeerResponse.PeerInfo
|
||||
nil, // 14: ethereum.eth.v1alpha1.ScoreInfo.TopicScoresEntry
|
||||
(PeerDirection)(0), // 15: ethereum.eth.v1alpha1.PeerDirection
|
||||
(ConnectionState)(0), // 16: ethereum.eth.v1alpha1.ConnectionState
|
||||
(*Status)(nil), // 17: ethereum.eth.v1alpha1.Status
|
||||
(*MetaDataV0)(nil), // 18: ethereum.eth.v1alpha1.MetaDataV0
|
||||
(*MetaDataV1)(nil), // 19: ethereum.eth.v1alpha1.MetaDataV1
|
||||
(*empty.Empty)(nil), // 20: google.protobuf.Empty
|
||||
(*PeerRequest)(nil), // 21: ethereum.eth.v1alpha1.PeerRequest
|
||||
}
|
||||
var file_proto_prysm_v1alpha1_debug_proto_depIdxs = []int32{
|
||||
0, // 0: ethereum.eth.v1alpha1.LoggingLevelRequest.level:type_name -> ethereum.eth.v1alpha1.LoggingLevelRequest.Level
|
||||
8, // 1: ethereum.eth.v1alpha1.ProtoArrayForkChoiceResponse.proto_array_nodes:type_name -> ethereum.eth.v1alpha1.ProtoArrayNode
|
||||
13, // 2: ethereum.eth.v1alpha1.ProtoArrayForkChoiceResponse.indices:type_name -> ethereum.eth.v1alpha1.ProtoArrayForkChoiceResponse.IndicesEntry
|
||||
10, // 3: ethereum.eth.v1alpha1.DebugPeerResponses.responses:type_name -> ethereum.eth.v1alpha1.DebugPeerResponse
|
||||
16, // 4: ethereum.eth.v1alpha1.DebugPeerResponse.direction:type_name -> ethereum.eth.v1alpha1.PeerDirection
|
||||
17, // 5: ethereum.eth.v1alpha1.DebugPeerResponse.connection_state:type_name -> ethereum.eth.v1alpha1.ConnectionState
|
||||
14, // 6: ethereum.eth.v1alpha1.DebugPeerResponse.peer_info:type_name -> ethereum.eth.v1alpha1.DebugPeerResponse.PeerInfo
|
||||
18, // 7: ethereum.eth.v1alpha1.DebugPeerResponse.peer_status:type_name -> ethereum.eth.v1alpha1.Status
|
||||
11, // 8: ethereum.eth.v1alpha1.DebugPeerResponse.score_info:type_name -> ethereum.eth.v1alpha1.ScoreInfo
|
||||
15, // 9: ethereum.eth.v1alpha1.ScoreInfo.topic_scores:type_name -> ethereum.eth.v1alpha1.ScoreInfo.TopicScoresEntry
|
||||
19, // 10: ethereum.eth.v1alpha1.DebugPeerResponse.PeerInfo.metadataV0:type_name -> ethereum.eth.v1alpha1.MetaDataV0
|
||||
20, // 11: ethereum.eth.v1alpha1.DebugPeerResponse.PeerInfo.metadataV1:type_name -> ethereum.eth.v1alpha1.MetaDataV1
|
||||
12, // 12: ethereum.eth.v1alpha1.ScoreInfo.TopicScoresEntry.value:type_name -> ethereum.eth.v1alpha1.TopicScoreSnapshot
|
||||
3, // 13: ethereum.eth.v1alpha1.Debug.GetBeaconState:input_type -> ethereum.eth.v1alpha1.BeaconStateRequest
|
||||
4, // 14: ethereum.eth.v1alpha1.Debug.GetBlock:input_type -> ethereum.eth.v1alpha1.BlockRequestByRoot
|
||||
6, // 15: ethereum.eth.v1alpha1.Debug.SetLoggingLevel:input_type -> ethereum.eth.v1alpha1.LoggingLevelRequest
|
||||
21, // 16: ethereum.eth.v1alpha1.Debug.GetProtoArrayForkChoice:input_type -> google.protobuf.Empty
|
||||
21, // 17: ethereum.eth.v1alpha1.Debug.ListPeers:input_type -> google.protobuf.Empty
|
||||
22, // 18: ethereum.eth.v1alpha1.Debug.GetPeer:input_type -> ethereum.eth.v1alpha1.PeerRequest
|
||||
1, // 19: ethereum.eth.v1alpha1.Debug.GetInclusionSlot:input_type -> ethereum.eth.v1alpha1.InclusionSlotRequest
|
||||
5, // 20: ethereum.eth.v1alpha1.Debug.GetBeaconState:output_type -> ethereum.eth.v1alpha1.SSZResponse
|
||||
5, // 21: ethereum.eth.v1alpha1.Debug.GetBlock:output_type -> ethereum.eth.v1alpha1.SSZResponse
|
||||
21, // 22: ethereum.eth.v1alpha1.Debug.SetLoggingLevel:output_type -> google.protobuf.Empty
|
||||
7, // 23: ethereum.eth.v1alpha1.Debug.GetProtoArrayForkChoice:output_type -> ethereum.eth.v1alpha1.ProtoArrayForkChoiceResponse
|
||||
9, // 24: ethereum.eth.v1alpha1.Debug.ListPeers:output_type -> ethereum.eth.v1alpha1.DebugPeerResponses
|
||||
10, // 25: ethereum.eth.v1alpha1.Debug.GetPeer:output_type -> ethereum.eth.v1alpha1.DebugPeerResponse
|
||||
2, // 26: ethereum.eth.v1alpha1.Debug.GetInclusionSlot:output_type -> ethereum.eth.v1alpha1.InclusionSlotResponse
|
||||
20, // [20:27] is the sub-list for method output_type
|
||||
13, // [13:20] is the sub-list for method input_type
|
||||
13, // [13:13] is the sub-list for extension type_name
|
||||
13, // [13:13] is the sub-list for extension extendee
|
||||
0, // [0:13] is the sub-list for field type_name
|
||||
8, // 1: ethereum.eth.v1alpha1.ProtoArrayForkChoiceResponse.tree_root:type_name -> ethereum.eth.v1alpha1.ProtoArrayNode
|
||||
8, // 2: ethereum.eth.v1alpha1.ProtoArrayNode.parent:type_name -> ethereum.eth.v1alpha1.ProtoArrayNode
|
||||
8, // 3: ethereum.eth.v1alpha1.ProtoArrayNode.children:type_name -> ethereum.eth.v1alpha1.ProtoArrayNode
|
||||
8, // 4: ethereum.eth.v1alpha1.ProtoArrayNode.best_descendant:type_name -> ethereum.eth.v1alpha1.ProtoArrayNode
|
||||
10, // 5: ethereum.eth.v1alpha1.DebugPeerResponses.responses:type_name -> ethereum.eth.v1alpha1.DebugPeerResponse
|
||||
15, // 6: ethereum.eth.v1alpha1.DebugPeerResponse.direction:type_name -> ethereum.eth.v1alpha1.PeerDirection
|
||||
16, // 7: ethereum.eth.v1alpha1.DebugPeerResponse.connection_state:type_name -> ethereum.eth.v1alpha1.ConnectionState
|
||||
13, // 8: ethereum.eth.v1alpha1.DebugPeerResponse.peer_info:type_name -> ethereum.eth.v1alpha1.DebugPeerResponse.PeerInfo
|
||||
17, // 9: ethereum.eth.v1alpha1.DebugPeerResponse.peer_status:type_name -> ethereum.eth.v1alpha1.Status
|
||||
11, // 10: ethereum.eth.v1alpha1.DebugPeerResponse.score_info:type_name -> ethereum.eth.v1alpha1.ScoreInfo
|
||||
14, // 11: ethereum.eth.v1alpha1.ScoreInfo.topic_scores:type_name -> ethereum.eth.v1alpha1.ScoreInfo.TopicScoresEntry
|
||||
18, // 12: ethereum.eth.v1alpha1.DebugPeerResponse.PeerInfo.metadataV0:type_name -> ethereum.eth.v1alpha1.MetaDataV0
|
||||
19, // 13: ethereum.eth.v1alpha1.DebugPeerResponse.PeerInfo.metadataV1:type_name -> ethereum.eth.v1alpha1.MetaDataV1
|
||||
12, // 14: ethereum.eth.v1alpha1.ScoreInfo.TopicScoresEntry.value:type_name -> ethereum.eth.v1alpha1.TopicScoreSnapshot
|
||||
3, // 15: ethereum.eth.v1alpha1.Debug.GetBeaconState:input_type -> ethereum.eth.v1alpha1.BeaconStateRequest
|
||||
4, // 16: ethereum.eth.v1alpha1.Debug.GetBlock:input_type -> ethereum.eth.v1alpha1.BlockRequestByRoot
|
||||
6, // 17: ethereum.eth.v1alpha1.Debug.SetLoggingLevel:input_type -> ethereum.eth.v1alpha1.LoggingLevelRequest
|
||||
20, // 18: ethereum.eth.v1alpha1.Debug.GetProtoArrayForkChoice:input_type -> google.protobuf.Empty
|
||||
20, // 19: ethereum.eth.v1alpha1.Debug.ListPeers:input_type -> google.protobuf.Empty
|
||||
21, // 20: ethereum.eth.v1alpha1.Debug.GetPeer:input_type -> ethereum.eth.v1alpha1.PeerRequest
|
||||
1, // 21: ethereum.eth.v1alpha1.Debug.GetInclusionSlot:input_type -> ethereum.eth.v1alpha1.InclusionSlotRequest
|
||||
5, // 22: ethereum.eth.v1alpha1.Debug.GetBeaconState:output_type -> ethereum.eth.v1alpha1.SSZResponse
|
||||
5, // 23: ethereum.eth.v1alpha1.Debug.GetBlock:output_type -> ethereum.eth.v1alpha1.SSZResponse
|
||||
20, // 24: ethereum.eth.v1alpha1.Debug.SetLoggingLevel:output_type -> google.protobuf.Empty
|
||||
7, // 25: ethereum.eth.v1alpha1.Debug.GetProtoArrayForkChoice:output_type -> ethereum.eth.v1alpha1.ProtoArrayForkChoiceResponse
|
||||
9, // 26: ethereum.eth.v1alpha1.Debug.ListPeers:output_type -> ethereum.eth.v1alpha1.DebugPeerResponses
|
||||
10, // 27: ethereum.eth.v1alpha1.Debug.GetPeer:output_type -> ethereum.eth.v1alpha1.DebugPeerResponse
|
||||
2, // 28: ethereum.eth.v1alpha1.Debug.GetInclusionSlot:output_type -> ethereum.eth.v1alpha1.InclusionSlotResponse
|
||||
22, // [22:29] is the sub-list for method output_type
|
||||
15, // [15:22] is the sub-list for method input_type
|
||||
15, // [15:15] is the sub-list for extension type_name
|
||||
15, // [15:15] is the sub-list for extension extendee
|
||||
0, // [0:15] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_proto_prysm_v1alpha1_debug_proto_init() }
|
||||
@@ -1507,7 +1516,7 @@ func file_proto_prysm_v1alpha1_debug_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_prysm_v1alpha1_debug_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
|
||||
file_proto_prysm_v1alpha1_debug_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*DebugPeerResponse_PeerInfo); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -1530,7 +1539,7 @@ func file_proto_prysm_v1alpha1_debug_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_proto_prysm_v1alpha1_debug_proto_rawDesc,
|
||||
NumEnums: 1,
|
||||
NumMessages: 15,
|
||||
NumMessages: 14,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -111,9 +111,7 @@ message ProtoArrayForkChoiceResponse {
|
||||
// Latest finalized epoch in proto array store.
|
||||
uint64 finalized_epoch = 3 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/eth2-types.Epoch"];
|
||||
// The list of the proto array nodes in store.
|
||||
repeated ProtoArrayNode proto_array_nodes = 4;
|
||||
// Root to indices mapping of the proto array nodes in store.
|
||||
map<string, uint64> indices = 5;
|
||||
ProtoArrayNode tree_root = 4;
|
||||
}
|
||||
|
||||
message ProtoArrayNode {
|
||||
@@ -122,17 +120,21 @@ message ProtoArrayNode {
|
||||
// Root of the proto array node.
|
||||
bytes root = 2;
|
||||
// Parent of the proto array node.
|
||||
uint64 parent = 3;
|
||||
ProtoArrayNode parent = 3;
|
||||
// children of this node
|
||||
repeated ProtoArrayNode children = 4;
|
||||
// Justified epoch of the current proto array node.
|
||||
uint64 justified_epoch = 4 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/eth2-types.Epoch"];
|
||||
uint64 justified_epoch = 5 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/eth2-types.Epoch"];
|
||||
// finalized epoch of the current proto array node.
|
||||
uint64 finalized_epoch = 5 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/eth2-types.Epoch"];
|
||||
uint64 finalized_epoch = 6 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/eth2-types.Epoch"];
|
||||
// Current balance of the current proto array node.
|
||||
uint64 balance = 7;
|
||||
// Current weight of the current proto array node.
|
||||
uint64 weight = 6;
|
||||
// Best child of the current proto array node.
|
||||
uint64 best_child = 7;
|
||||
uint64 weight = 8;
|
||||
// Best descendant of the proto array node.
|
||||
uint64 best_descendant = 8;
|
||||
ProtoArrayNode best_descendant = 9;
|
||||
// optimistic sync status of the node
|
||||
bool optimistic = 10;
|
||||
}
|
||||
|
||||
message DebugPeerResponses {
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
@@ -45,7 +44,7 @@ func startChainService(t *testing.T, st state.BeaconState, block block.SignedBea
|
||||
blockchain.WithFinalizedStateAtStartUp(st),
|
||||
blockchain.WithDatabase(db),
|
||||
blockchain.WithAttestationService(attPool),
|
||||
blockchain.WithForkChoiceStore(protoarray.New(0, 0, params.BeaconConfig().ZeroHash)),
|
||||
blockchain.WithForkChoiceStore(protoarray.New(0, 0)),
|
||||
blockchain.WithStateGen(stategen.New(db)),
|
||||
blockchain.WithStateNotifier(&mock.MockStateNotifier{}),
|
||||
blockchain.WithAttestationPool(attestations.NewPool()),
|
||||
|
||||
Reference in New Issue
Block a user