Skip to content

Commit a19f08f

Browse files
terencechainrauljordan
authored andcommitted
Attestation Using Head State instead of Latest State (#2156)
* headRoot is a better name * yay merged with master * fixed attester server returning incorrect attestation field values * revert stupid mock changes * fixed and updated all the tests * uncomment * head start should handle skip blocks * Raul's feedback
1 parent 9abefc3 commit a19f08f

31 files changed

+241
-196
lines changed

beacon-chain/attestation/service.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func (a *Service) IncomingAttestationFeed() *event.Feed {
9191
// Attestation` be the attestation with the highest slot number in `store`
9292
// from the validator with the given `validator_index`
9393
func (a *Service) LatestAttestation(ctx context.Context, index uint64) (*pb.Attestation, error) {
94-
state, err := a.beaconDB.State(ctx)
94+
state, err := a.beaconDB.HeadState(ctx)
9595
if err != nil {
9696
return nil, err
9797
}
@@ -173,7 +173,7 @@ func (a *Service) handleAttestation(ctx context.Context, msg proto.Message) erro
173173
func (a *Service) UpdateLatestAttestation(ctx context.Context, attestation *pb.Attestation) error {
174174
// Potential improvement, instead of getting the state,
175175
// we could get a mapping of validator index to public key.
176-
state, err := a.beaconDB.State(ctx)
176+
state, err := a.beaconDB.HeadState(ctx)
177177
if err != nil {
178178
return err
179179
}

beacon-chain/blockchain/block_processing_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ func TestIsBlockReadyForProcessing_ValidBlock(t *testing.T) {
424424
if err := db.InitializeState(unixTime, deposits, &pb.Eth1Data{}); err != nil {
425425
t.Fatalf("Could not initialize beacon state to disk: %v", err)
426426
}
427-
beaconState, err := db.State(ctx)
427+
beaconState, err := db.HeadState(ctx)
428428
if err != nil {
429429
t.Fatalf("Can't get genesis state: %v", err)
430430
}

beacon-chain/blockchain/service.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func NewChainService(ctx context.Context, cfg *Config) (*ChainService, error) {
7777

7878
// Start a blockchain service's main event loop.
7979
func (c *ChainService) Start() {
80-
beaconState, err := c.beaconDB.State(c.ctx)
80+
beaconState, err := c.beaconDB.HeadState(c.ctx)
8181
if err != nil {
8282
log.Fatalf("Could not fetch beacon state: %v", err)
8383
}
@@ -138,7 +138,7 @@ func (c *ChainService) initializeBeaconChain(genesisTime time.Time, deposits []*
138138
if err := c.beaconDB.InitializeState(unixTime, deposits, eth1data); err != nil {
139139
return nil, fmt.Errorf("could not initialize beacon state to disk: %v", err)
140140
}
141-
beaconState, err := c.beaconDB.State(c.ctx)
141+
beaconState, err := c.beaconDB.HeadState(c.ctx)
142142
if err != nil {
143143
return nil, fmt.Errorf("could not attempt fetch beacon state: %v", err)
144144
}

beacon-chain/blockchain/service_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ func setupBeaconChain(t *testing.T, beaconDB *db.BeaconDB, attsService *attestat
251251
}
252252

253253
func SetSlotInState(service *ChainService, slot uint64) error {
254-
bState, err := service.beaconDB.State(context.Background())
254+
bState, err := service.beaconDB.HeadState(context.Background())
255255
if err != nil {
256256
return err
257257
}
@@ -281,7 +281,7 @@ func TestChainStartStop_Uninitialized(t *testing.T) {
281281
)
282282
}
283283

284-
beaconState, err := db.State(context.Background())
284+
beaconState, err := db.HeadState(context.Background())
285285
if err != nil {
286286
t.Fatal(err)
287287
}
@@ -312,7 +312,7 @@ func TestChainStartStop_Initialized(t *testing.T) {
312312
if err := db.InitializeState(unixTime, deposits, &pb.Eth1Data{}); err != nil {
313313
t.Fatalf("Could not initialize beacon state to disk: %v", err)
314314
}
315-
beaconState, err := db.State(ctx)
315+
beaconState, err := db.HeadState(ctx)
316316
if err != nil {
317317
t.Fatalf("Could not fetch beacon state: %v", err)
318318
}

beacon-chain/db/block_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func TestUpdateChainHead_NoBlock(t *testing.T) {
9595
if err != nil {
9696
t.Fatalf("failed to initialize state: %v", err)
9797
}
98-
beaconState, err := db.State(ctx)
98+
beaconState, err := db.HeadState(ctx)
9999
if err != nil {
100100
t.Fatalf("failed to get beacon state: %v", err)
101101
}
@@ -127,7 +127,7 @@ func TestUpdateChainHead_OK(t *testing.T) {
127127
t.Fatalf("failed to get hash of b: %v", err)
128128
}
129129

130-
beaconState, err := db.State(ctx)
130+
beaconState, err := db.HeadState(ctx)
131131
if err != nil {
132132
t.Fatalf("failed to get beacon state: %v", err)
133133
}
@@ -185,7 +185,7 @@ func TestChainProgress_OK(t *testing.T) {
185185
t.Fatalf("failed to initialize state: %v", err)
186186
}
187187

188-
beaconState, err := db.State(ctx)
188+
beaconState, err := db.HeadState(ctx)
189189
if err != nil {
190190
t.Fatalf("Failed to get beacon state: %v", err)
191191
}

beacon-chain/db/state.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ func (db *BeaconDB) InitializeState(genesisTime uint64, deposits []*pb.Deposit,
8686
})
8787
}
8888

89-
// State fetches the canonical beacon chain's state from the DB.
90-
func (db *BeaconDB) State(ctx context.Context) (*pb.BeaconState, error) {
89+
// HeadState fetches the canonical beacon chain's head state from the DB.
90+
func (db *BeaconDB) HeadState(ctx context.Context) (*pb.BeaconState, error) {
9191
ctx, span := trace.StartSpan(ctx, "BeaconDB.State")
9292
defer span.End()
9393

@@ -358,7 +358,7 @@ func createState(enc []byte) (*pb.BeaconState, error) {
358358

359359
// GenesisTime returns the genesis timestamp for the state.
360360
func (db *BeaconDB) GenesisTime(ctx context.Context) (time.Time, error) {
361-
state, err := db.State(ctx)
361+
state, err := db.HeadState(ctx)
362362
if err != nil {
363363
return time.Time{}, fmt.Errorf("could not retrieve state: %v", err)
364364
}

beacon-chain/db/state_test.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func TestInitializeState_OK(t *testing.T) {
5555
t.Fatalf("Expected block height to equal 1. Got %d", b.GetSlot())
5656
}
5757

58-
beaconState, err := db.State(ctx)
58+
beaconState, err := db.HeadState(ctx)
5959
if err != nil {
6060
t.Fatalf("Failed to get state: %v", err)
6161
}
@@ -67,7 +67,7 @@ func TestInitializeState_OK(t *testing.T) {
6767
t.Fatalf("Failed to encode state: %v", err)
6868
}
6969

70-
statePrime, err := db.State(ctx)
70+
statePrime, err := db.HeadState(ctx)
7171
if err != nil {
7272
t.Fatalf("Failed to get state: %v", err)
7373
}
@@ -120,7 +120,7 @@ func TestFinalizeState_OK(t *testing.T) {
120120
t.Fatalf("Failed to initialize state: %v", err)
121121
}
122122

123-
state, err := db.State(context.Background())
123+
state, err := db.HeadState(context.Background())
124124
if err != nil {
125125
t.Fatalf("Failed to retrieve state: %v", err)
126126
}
@@ -150,7 +150,7 @@ func TestCurrentAndFinalizeState_OK(t *testing.T) {
150150
t.Fatalf("Failed to initialize state: %v", err)
151151
}
152152

153-
state, err := db.State(context.Background())
153+
state, err := db.HeadState(context.Background())
154154
if err != nil {
155155
t.Fatalf("Failed to retrieve state: %v", err)
156156
}
@@ -167,7 +167,7 @@ func TestCurrentAndFinalizeState_OK(t *testing.T) {
167167
t.Fatalf("Unable to retrieve finalized state")
168168
}
169169

170-
cState, err := db.State(context.Background())
170+
cState, err := db.HeadState(context.Background())
171171
if err != nil {
172172
t.Fatalf("Unable to retrieve state")
173173
}
@@ -192,7 +192,7 @@ func BenchmarkState_ReadingFromCache(b *testing.B) {
192192
b.Fatalf("Failed to initialize state: %v", err)
193193
}
194194

195-
state, err := db.State(ctx)
195+
state, err := db.HeadState(ctx)
196196
if err != nil {
197197
b.Fatalf("Could not read DV beacon state from DB: %v", err)
198198
}
@@ -210,7 +210,7 @@ func BenchmarkState_ReadingFromCache(b *testing.B) {
210210
b.ResetTimer()
211211

212212
for i := 0; i < b.N; i++ {
213-
_, err := db.State(ctx)
213+
_, err := db.HeadState(ctx)
214214
if err != nil {
215215
b.Fatalf("Could not read beacon state from cache: %v", err)
216216
}

beacon-chain/internal/beacon_service_mock.go

+1-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

beacon-chain/internal/validator_service_mock.go

+1-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

beacon-chain/rpc/attester_server.go

+28-27
Original file line numberDiff line numberDiff line change
@@ -46,22 +46,26 @@ func (as *AttesterServer) AttestationDataAtSlot(ctx context.Context, req *pb.Att
4646
if err != nil {
4747
return nil, fmt.Errorf("failed to retrieve chain head: %v", err)
4848
}
49-
blockRoot, err := hashutil.HashBeaconBlock(head)
49+
headRoot, err := hashutil.HashBeaconBlock(head)
5050
if err != nil {
5151
return nil, fmt.Errorf("could not tree hash beacon block: %v", err)
5252
}
53-
beaconState, err := as.beaconDB.State(ctx)
53+
54+
// Let head state be the state of head block processed through empty slots up to assigned slot.
55+
headState, err := as.beaconDB.HeadState(ctx)
5456
if err != nil {
55-
return nil, fmt.Errorf("could not fetch beacon state: %v", err)
57+
return nil, fmt.Errorf("could not fetch head state: %v", err)
5658
}
57-
for beaconState.Slot < req.Slot {
58-
beaconState, err = state.ExecuteStateTransition(
59-
ctx, beaconState, nil /* block */, blockRoot, state.DefaultConfig(),
59+
60+
for headState.Slot < req.Slot {
61+
headState, err = state.ExecuteStateTransition(
62+
ctx, headState, nil /* block */, headRoot, state.DefaultConfig(),
6063
)
6164
if err != nil {
6265
return nil, fmt.Errorf("could not execute head transition: %v", err)
6366
}
6467
}
68+
6569
// Fetch the epoch boundary root = hash_tree_root(epoch_boundary)
6670
// where epoch_boundary is the block at the most recent epoch boundary in the
6771
// chain defined by head -- i.e. the BeaconBlock where block.slot == get_epoch_start_slot(head.slot).
@@ -70,15 +74,12 @@ func (as *AttesterServer) AttestationDataAtSlot(ctx context.Context, req *pb.Att
7074
epochBoundaryRoot := make([]byte, 32)
7175
epochStartSlot := helpers.StartSlot(helpers.SlotToEpoch(head.Slot))
7276
if epochStartSlot == head.Slot {
73-
hash, err := hashutil.HashBeaconBlock(head)
74-
if err != nil {
75-
return nil, fmt.Errorf("could not tree hash head block: %v", err)
76-
}
77-
epochBoundaryRoot = hash[:]
77+
epochBoundaryRoot = headRoot[:]
7878
} else {
79-
epochBoundaryRoot, err = blocks.BlockRoot(beaconState, epochStartSlot)
79+
epochBoundaryRoot, err = blocks.BlockRoot(headState, epochStartSlot)
8080
if err != nil {
81-
return nil, fmt.Errorf("could not get epoch boundary block: %v", err)
81+
return nil, fmt.Errorf("could not get epoch boundary block for slot %d: %v",
82+
epochStartSlot, err)
8283
}
8384
}
8485
// epoch_start_slot = get_epoch_start_slot(slot_to_epoch(head.slot))
@@ -87,15 +88,12 @@ func (as *AttesterServer) AttestationDataAtSlot(ctx context.Context, req *pb.Att
8788
// On the server side, this is fetched by calling get_block_root(state, justified_epoch).
8889
// If the last justified boundary slot is the same as state current slot (ex: slot 0),
8990
// we set justified block root to an empty root.
90-
lastJustifiedSlot := helpers.StartSlot(beaconState.JustifiedEpoch)
91+
lastJustifiedSlot := helpers.StartSlot(headState.JustifiedEpoch)
9192
justifiedBlockRoot := make([]byte, 32)
92-
if lastJustifiedSlot != beaconState.Slot {
93-
var justifiedBlock *pbp2p.BeaconBlock
94-
for i := uint64(0); justifiedBlock == nil && i < params.BeaconConfig().SlotsPerEpoch; i++ {
95-
justifiedBlock, err = as.beaconDB.BlockBySlot(lastJustifiedSlot - i)
96-
if err != nil {
97-
return nil, fmt.Errorf("could not get justified block: %v", err)
98-
}
93+
if lastJustifiedSlot != headState.Slot {
94+
justifiedBlock, err := as.beaconDB.BlockBySlot(lastJustifiedSlot)
95+
if err != nil {
96+
return nil, fmt.Errorf("could not get justified block: %v", err)
9997
}
10098

10199
justifiedBlockRoot32, err := hashutil.HashBeaconBlock(justifiedBlock)
@@ -105,15 +103,18 @@ func (as *AttesterServer) AttestationDataAtSlot(ctx context.Context, req *pb.Att
105103
justifiedBlockRoot = justifiedBlockRoot32[:]
106104
}
107105

108-
if beaconState.Slot == params.BeaconConfig().GenesisSlot {
109-
epochBoundaryRoot = blockRoot[:]
110-
justifiedBlockRoot = blockRoot[:]
106+
// If an attester has to attest for gensis block.
107+
if headState.Slot == params.BeaconConfig().GenesisSlot {
108+
epochBoundaryRoot = headRoot[:]
109+
justifiedBlockRoot = headRoot[:]
111110
}
111+
112112
return &pb.AttestationDataResponse{
113-
BeaconBlockRootHash32: blockRoot[:],
113+
HeadSlot: headState.Slot,
114+
BeaconBlockRootHash32: headRoot[:],
114115
EpochBoundaryRootHash32: epochBoundaryRoot,
115-
JustifiedEpoch: beaconState.JustifiedEpoch,
116+
JustifiedEpoch: headState.JustifiedEpoch,
116117
JustifiedBlockRootHash32: justifiedBlockRoot,
117-
LatestCrosslink: beaconState.LatestCrosslinks[req.Shard],
118+
LatestCrosslink: headState.LatestCrosslinks[req.Shard],
118119
}, nil
119120
}

beacon-chain/rpc/attester_server_test.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ func TestAttestationDataAtSlot_JustifiedBlockFailure(t *testing.T) {
6565
defer internal.TeardownDB(t, db)
6666
ctx := context.Background()
6767

68+
finalizedState := &pbp2p.BeaconState{
69+
Slot: params.BeaconConfig().GenesisSlot + 1,
70+
LatestBlockRootHash32S: make([][]byte, params.BeaconConfig().LatestBlockRootsLength),
71+
}
6872
beaconState := &pbp2p.BeaconState{
6973
Slot: params.BeaconConfig().GenesisSlot + params.BeaconConfig().SlotsPerEpoch + 2,
7074
LatestBlockRootHash32S: make([][]byte, params.BeaconConfig().LatestBlockRootsLength),
@@ -81,6 +85,9 @@ func TestAttestationDataAtSlot_JustifiedBlockFailure(t *testing.T) {
8185
if err := attesterServer.beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
8286
t.Fatalf("Could not update chain head in test db: %v", err)
8387
}
88+
if err := attesterServer.beaconDB.SaveHistoricalState(finalizedState); err != nil {
89+
t.Fatalf("Could not save historical state in test db: %v", err)
90+
}
8491
epochBoundaryBlock := &pbp2p.BeaconBlock{
8592
Slot: params.BeaconConfig().GenesisSlot + 1,
8693
}
@@ -123,6 +130,7 @@ func TestAttestationDataAtSlot_OK(t *testing.T) {
123130
if err != nil {
124131
t.Fatalf("Could not hash justified block: %v", err)
125132
}
133+
126134
beaconState := &pbp2p.BeaconState{
127135
Slot: 3*params.BeaconConfig().SlotsPerEpoch + params.BeaconConfig().GenesisSlot + 1,
128136
JustifiedEpoch: 2 + params.BeaconConfig().GenesisEpoch,
@@ -165,6 +173,7 @@ func TestAttestationDataAtSlot_OK(t *testing.T) {
165173
t.Fatalf("Could not get attestation info at slot: %v", err)
166174
}
167175
expectedInfo := &pb.AttestationDataResponse{
176+
HeadSlot: beaconState.Slot,
168177
BeaconBlockRootHash32: blockRoot[:],
169178
JustifiedEpoch: 2 + params.BeaconConfig().GenesisEpoch,
170179
JustifiedBlockRootHash32: justifiedBlockRoot[:],
@@ -202,7 +211,7 @@ func TestAttestationDataAtSlot_handlesFarAwayJustifiedEpoch(t *testing.T) {
202211
Slot: helpers.StartSlot(helpers.SlotToEpoch(10000 + params.BeaconConfig().GenesisSlot)),
203212
}
204213
justifiedBlock := &pbp2p.BeaconBlock{
205-
Slot: helpers.StartSlot(helpers.SlotToEpoch(1500+params.BeaconConfig().GenesisSlot)) - 2, // Imagine two skip blocks
214+
Slot: helpers.StartSlot(helpers.SlotToEpoch(1500 + params.BeaconConfig().GenesisSlot)),
206215
}
207216
blockRoot, err := hashutil.HashBeaconBlock(block)
208217
if err != nil {
@@ -258,6 +267,7 @@ func TestAttestationDataAtSlot_handlesFarAwayJustifiedEpoch(t *testing.T) {
258267
t.Fatalf("Could not get attestation info at slot: %v", err)
259268
}
260269
expectedInfo := &pb.AttestationDataResponse{
270+
HeadSlot: 10000 + params.BeaconConfig().GenesisSlot,
261271
BeaconBlockRootHash32: blockRoot[:],
262272
JustifiedEpoch: helpers.SlotToEpoch(1500 + params.BeaconConfig().GenesisSlot),
263273
JustifiedBlockRootHash32: justifiedBlockRoot[:],

beacon-chain/rpc/beacon_server.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func (bs *BeaconServer) LatestAttestation(req *ptypes.Empty, stream pb.BeaconSer
9999

100100
// ForkData fetches the current fork information from the beacon state.
101101
func (bs *BeaconServer) ForkData(ctx context.Context, _ *ptypes.Empty) (*pbp2p.Fork, error) {
102-
state, err := bs.beaconDB.State(ctx)
102+
state, err := bs.beaconDB.HeadState(ctx)
103103
if err != nil {
104104
return nil, fmt.Errorf("could not retrieve beacon state: %v", err)
105105
}
@@ -112,7 +112,7 @@ func (bs *BeaconServer) ForkData(ctx context.Context, _ *ptypes.Empty) (*pbp2p.F
112112
// The deposit root can be calculated by calling the get_deposit_root() function of
113113
// the deposit contract using the post-state of the block hash.
114114
func (bs *BeaconServer) Eth1Data(ctx context.Context, _ *ptypes.Empty) (*pb.Eth1DataResponse, error) {
115-
beaconState, err := bs.beaconDB.State(ctx)
115+
beaconState, err := bs.beaconDB.HeadState(ctx)
116116
if err != nil {
117117
return nil, fmt.Errorf("could not fetch beacon state: %v", err)
118118
}
@@ -217,7 +217,7 @@ func (bs *BeaconServer) PendingDeposits(ctx context.Context, _ *ptypes.Empty) (*
217217
pendingDeps := bs.beaconDB.PendingDeposits(ctx, bNum)
218218
// Need to fetch if the deposits up to the state's latest eth 1 data matches
219219
// the number of all deposits in this RPC call. If not, then we return nil.
220-
beaconState, err := bs.beaconDB.State(ctx)
220+
beaconState, err := bs.beaconDB.HeadState(ctx)
221221
if err != nil {
222222
return nil, fmt.Errorf("could not fetch beacon state: %v", err)
223223
}

0 commit comments

Comments
 (0)