mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 13:28:01 -05:00
Process justification with precompute (#3792)
This commit is contained in:
@@ -4,6 +4,7 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"attestation.go",
|
||||
"justification_finalization.go",
|
||||
"new.go",
|
||||
"type.go",
|
||||
],
|
||||
@@ -12,6 +13,7 @@ go_library(
|
||||
deps = [
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/traceutil:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
@@ -22,6 +24,7 @@ go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"attestation_test.go",
|
||||
"justification_finalization_test.go",
|
||||
"new_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
@@ -32,5 +35,6 @@ go_test(
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package precompute
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
)
|
||||
|
||||
// ProcessJustificationAndFinalizationPreCompute processes justification and finalization during
|
||||
// epoch processing. This is where a beacon node can justify and finalize a new epoch.
|
||||
// Note: this is an optimized version by passing in precomputed total and attesting balances.
|
||||
func ProcessJustificationAndFinalizationPreCompute(state *pb.BeaconState, p *Balance) (*pb.BeaconState, error) {
|
||||
if state.Slot <= helpers.StartSlot(2) {
|
||||
return state, nil
|
||||
}
|
||||
|
||||
prevEpoch := helpers.PrevEpoch(state)
|
||||
currentEpoch := helpers.CurrentEpoch(state)
|
||||
oldPrevJustifiedCheckpoint := state.PreviousJustifiedCheckpoint
|
||||
oldCurrJustifiedCheckpoint := state.CurrentJustifiedCheckpoint
|
||||
|
||||
// Process justifications
|
||||
state.PreviousJustifiedCheckpoint = state.CurrentJustifiedCheckpoint
|
||||
state.JustificationBits.Shift(1)
|
||||
|
||||
// Note: the spec refers to the bit index position starting at 1 instead of starting at zero.
|
||||
// We will use that paradigm here for consistency with the godoc spec definition.
|
||||
|
||||
// If 2/3 or more of total balance attested in the previous epoch.
|
||||
if 3*p.PrevEpochTargetAttesters >= 2*p.CurrentEpoch {
|
||||
blockRoot, err := helpers.BlockRoot(state, prevEpoch)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not get block root for previous epoch %d", prevEpoch)
|
||||
}
|
||||
state.CurrentJustifiedCheckpoint = ðpb.Checkpoint{Epoch: prevEpoch, Root: blockRoot}
|
||||
state.JustificationBits.SetBitAt(1, true)
|
||||
}
|
||||
|
||||
// If 2/3 or more of the total balance attested in the current epoch.
|
||||
if 3*p.CurrentEpochTargetAttesters >= 2*p.CurrentEpoch {
|
||||
blockRoot, err := helpers.BlockRoot(state, currentEpoch)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not get block root for current epoch %d", prevEpoch)
|
||||
}
|
||||
state.CurrentJustifiedCheckpoint = ðpb.Checkpoint{Epoch: currentEpoch, Root: blockRoot}
|
||||
state.JustificationBits.SetBitAt(0, true)
|
||||
}
|
||||
|
||||
// Process finalization according to ETH2.0 specifications.
|
||||
justification := state.JustificationBits.Bytes()[0]
|
||||
|
||||
// 2nd/3rd/4th (0b1110) most recent epochs are justified, the 2nd using the 4th as source.
|
||||
if justification&0x0E == 0x0E && (oldPrevJustifiedCheckpoint.Epoch+3) == currentEpoch {
|
||||
state.FinalizedCheckpoint = oldPrevJustifiedCheckpoint
|
||||
}
|
||||
|
||||
// 2nd/3rd (0b0110) most recent epochs are justified, the 2nd using the 3rd as source.
|
||||
if justification&0x06 == 0x06 && (oldPrevJustifiedCheckpoint.Epoch+2) == currentEpoch {
|
||||
state.FinalizedCheckpoint = oldPrevJustifiedCheckpoint
|
||||
}
|
||||
|
||||
// 1st/2nd/3rd (0b0111) most recent epochs are justified, the 1st using the 3rd as source.
|
||||
if justification&0x07 == 0x07 && (oldCurrJustifiedCheckpoint.Epoch+2) == currentEpoch {
|
||||
state.FinalizedCheckpoint = oldCurrJustifiedCheckpoint
|
||||
}
|
||||
|
||||
// The 1st/2nd (0b0011) most recent epochs are justified, the 1st using the 2nd as source
|
||||
if justification&0x03 == 0x03 && (oldCurrJustifiedCheckpoint.Epoch+1) == currentEpoch {
|
||||
state.FinalizedCheckpoint = oldCurrJustifiedCheckpoint
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package precompute_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch/precompute"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
func TestProcessJustificationAndFinalizationPreCompute_ConsecutiveEpochs(t *testing.T) {
|
||||
e := params.BeaconConfig().FarFutureEpoch
|
||||
a := params.BeaconConfig().MaxEffectiveBalance
|
||||
blockRoots := make([][]byte, params.BeaconConfig().SlotsPerEpoch*2+1)
|
||||
for i := 0; i < len(blockRoots); i++ {
|
||||
blockRoots[i] = []byte{byte(i)}
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
Slot: params.BeaconConfig().SlotsPerEpoch*2 + 1,
|
||||
PreviousJustifiedCheckpoint: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
Root: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
CurrentJustifiedCheckpoint: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
Root: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
JustificationBits: bitfield.Bitvector4{0x0F}, // 0b1111
|
||||
Validators: []*ethpb.Validator{{ExitEpoch: e}, {ExitEpoch: e}, {ExitEpoch: e}, {ExitEpoch: e}},
|
||||
Balances: []uint64{a, a, a, a}, // validator total balance should be 128000000000
|
||||
BlockRoots: blockRoots,
|
||||
}
|
||||
attestedBalance := 4 * e * 3 / 2
|
||||
b := &precompute.Balance{PrevEpochTargetAttesters:attestedBalance}
|
||||
newState, err := precompute.ProcessJustificationAndFinalizationPreCompute(state, b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(128)}) {
|
||||
t.Errorf("Wanted current justified root: %v, got: %v",
|
||||
[]byte{byte(128)}, newState.CurrentJustifiedCheckpoint.Root)
|
||||
}
|
||||
if newState.CurrentJustifiedCheckpoint.Epoch != 2 {
|
||||
t.Errorf("Wanted justified epoch: %d, got: %d",
|
||||
2, newState.CurrentJustifiedCheckpoint.Epoch)
|
||||
}
|
||||
if newState.PreviousJustifiedCheckpoint.Epoch != 0 {
|
||||
t.Errorf("Wanted previous justified epoch: %d, got: %d",
|
||||
0, newState.PreviousJustifiedCheckpoint.Epoch)
|
||||
}
|
||||
if !bytes.Equal(newState.FinalizedCheckpoint.Root, params.BeaconConfig().ZeroHash[:]) {
|
||||
t.Errorf("Wanted current finalized root: %v, got: %v",
|
||||
params.BeaconConfig().ZeroHash, newState.FinalizedCheckpoint.Root)
|
||||
}
|
||||
if newState.FinalizedCheckpoint.Epoch != 0 {
|
||||
t.Errorf("Wanted finalized epoch: 0, got: %d", newState.FinalizedCheckpoint.Epoch)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessJustificationAndFinalizationPreCompute_JustifyCurrentEpoch(t *testing.T) {
|
||||
e := params.BeaconConfig().FarFutureEpoch
|
||||
a := params.BeaconConfig().MaxEffectiveBalance
|
||||
blockRoots := make([][]byte, params.BeaconConfig().SlotsPerEpoch*2+1)
|
||||
for i := 0; i < len(blockRoots); i++ {
|
||||
blockRoots[i] = []byte{byte(i)}
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
Slot: params.BeaconConfig().SlotsPerEpoch*2 + 1,
|
||||
PreviousJustifiedCheckpoint: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
Root: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
CurrentJustifiedCheckpoint: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
Root: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
JustificationBits: bitfield.Bitvector4{0x03}, // 0b0011
|
||||
Validators: []*ethpb.Validator{{ExitEpoch: e}, {ExitEpoch: e}, {ExitEpoch: e}, {ExitEpoch: e}},
|
||||
Balances: []uint64{a, a, a, a}, // validator total balance should be 128000000000
|
||||
BlockRoots: blockRoots,
|
||||
}
|
||||
attestedBalance := 4 * e * 3 / 2
|
||||
b := &precompute.Balance{PrevEpochTargetAttesters:attestedBalance}
|
||||
newState, err := precompute.ProcessJustificationAndFinalizationPreCompute(state, b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(128)}) {
|
||||
t.Errorf("Wanted current justified root: %v, got: %v",
|
||||
[]byte{byte(128)}, newState.CurrentJustifiedCheckpoint.Root)
|
||||
}
|
||||
if newState.CurrentJustifiedCheckpoint.Epoch != 2 {
|
||||
t.Errorf("Wanted justified epoch: %d, got: %d",
|
||||
2, newState.CurrentJustifiedCheckpoint.Epoch)
|
||||
}
|
||||
if newState.PreviousJustifiedCheckpoint.Epoch != 0 {
|
||||
t.Errorf("Wanted previous justified epoch: %d, got: %d",
|
||||
0, newState.PreviousJustifiedCheckpoint.Epoch)
|
||||
}
|
||||
if !bytes.Equal(newState.FinalizedCheckpoint.Root, params.BeaconConfig().ZeroHash[:]) {
|
||||
t.Errorf("Wanted current finalized root: %v, got: %v",
|
||||
params.BeaconConfig().ZeroHash, newState.FinalizedCheckpoint.Root)
|
||||
}
|
||||
if newState.FinalizedCheckpoint.Epoch != 0 {
|
||||
t.Errorf("Wanted finalized epoch: 0, got: %d", newState.FinalizedCheckpoint.Epoch)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessJustificationAndFinalizationPreCompute_JustifyPrevEpoch(t *testing.T) {
|
||||
e := params.BeaconConfig().FarFutureEpoch
|
||||
a := params.BeaconConfig().MaxEffectiveBalance
|
||||
blockRoots := make([][]byte, params.BeaconConfig().SlotsPerEpoch*2+1)
|
||||
for i := 0; i < len(blockRoots); i++ {
|
||||
blockRoots[i] = []byte{byte(i)}
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
Slot: params.BeaconConfig().SlotsPerEpoch*2 + 1,
|
||||
PreviousJustifiedCheckpoint: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
Root: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
CurrentJustifiedCheckpoint: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
Root: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
JustificationBits: bitfield.Bitvector4{0x03}, // 0b0011
|
||||
Validators: []*ethpb.Validator{{ExitEpoch: e}, {ExitEpoch: e}, {ExitEpoch: e}, {ExitEpoch: e}},
|
||||
Balances: []uint64{a, a, a, a}, // validator total balance should be 128000000000
|
||||
BlockRoots: blockRoots, FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
}
|
||||
attestedBalance := 4 * e * 3 / 2
|
||||
b := &precompute.Balance{PrevEpochTargetAttesters:attestedBalance}
|
||||
newState, err := precompute.ProcessJustificationAndFinalizationPreCompute(state, b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(128)}) {
|
||||
t.Errorf("Wanted current justified root: %v, got: %v",
|
||||
[]byte{byte(128)}, newState.CurrentJustifiedCheckpoint.Root)
|
||||
}
|
||||
if newState.PreviousJustifiedCheckpoint.Epoch != 0 {
|
||||
t.Errorf("Wanted previous justified epoch: %d, got: %d",
|
||||
0, newState.PreviousJustifiedCheckpoint.Epoch)
|
||||
}
|
||||
if newState.CurrentJustifiedCheckpoint.Epoch != 2 {
|
||||
t.Errorf("Wanted justified epoch: %d, got: %d",
|
||||
2, newState.CurrentJustifiedCheckpoint.Epoch)
|
||||
}
|
||||
if !bytes.Equal(newState.FinalizedCheckpoint.Root, params.BeaconConfig().ZeroHash[:]) {
|
||||
t.Errorf("Wanted current finalized root: %v, got: %v",
|
||||
params.BeaconConfig().ZeroHash, newState.FinalizedCheckpoint.Root)
|
||||
}
|
||||
if newState.FinalizedCheckpoint.Epoch != 0 {
|
||||
t.Errorf("Wanted finalized epoch: 0, got: %d", newState.FinalizedCheckpoint.Epoch)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user