Skip to content

Commit

Permalink
Part 2 of State Sync - Active State Proto Struct (#355)
Browse files Browse the repository at this point in the history
  • Loading branch information
terencechain authored Jul 31, 2018
1 parent 33c5609 commit 9b19663
Show file tree
Hide file tree
Showing 6 changed files with 332 additions and 167 deletions.
2 changes: 0 additions & 2 deletions beacon-chain/blockchain/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ go_library(
"//shared/database:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//ethdb:go_default_library",
"@com_github_ethereum_go_ethereum//rlp:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@org_golang_x_crypto//blake2b:go_default_library",
],
)

Expand Down
53 changes: 19 additions & 34 deletions beacon-chain/blockchain/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp"
"github.com/gogo/protobuf/proto"
"github.com/prysmaticlabs/prysm/beacon-chain/params"
"github.com/prysmaticlabs/prysm/beacon-chain/types"
"github.com/prysmaticlabs/prysm/beacon-chain/utils"
pb "github.com/prysmaticlabs/prysm/proto/sharding/v1"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/blake2b"
)

var activeStateLookupKey = "beacon-active-state"
Expand Down Expand Up @@ -64,10 +62,12 @@ func NewBeaconChain(db ethdb.Database) (*BeaconChain, error) {
if err != nil {
return nil, err
}
// Deserializes the encoded object into a beacon chain.
if err := rlp.DecodeBytes(enc, &beaconChain.state); err != nil {
return nil, fmt.Errorf("could not deserialize chainstate from disk: %v", err)
activeData := &pb.ActiveStateResponse{}
err = proto.Unmarshal(enc, activeData)
if err != nil {
return nil, err
}
beaconChain.state.ActiveState = types.NewActiveState(activeData)
}
if hasCrystallized {
enc, err := db.Get([]byte(crystallizedStateLookupKey))
Expand Down Expand Up @@ -152,26 +152,22 @@ func (b *BeaconChain) CanProcessBlock(fetcher types.POWBlockFetcher, block *type
}

// Verify state hashes from the block are correct
hash, err := hashActiveState(b.ActiveState())
hash, err := b.ActiveState().Hash()
if err != nil {
return false, err
}

blockActiveStateHash := block.ActiveStateHash()

if blockActiveStateHash != hash {
return false, fmt.Errorf("active state hash mismatched, wanted: %v, got: %v", blockActiveStateHash, hash)
if block.ActiveStateHash() != hash {
return false, fmt.Errorf("active state hash mismatched, wanted: %v, got: %v", block.ActiveStateHash(), hash)
}

hash, err = b.CrystallizedState().Hash()
if err != nil {
return false, err
}

blockCrystallizedStateHash := block.CrystallizedStateHash()

if blockCrystallizedStateHash != hash {
return false, fmt.Errorf("crystallized state hash mismatched, wanted: %v, got: %v", blockCrystallizedStateHash, hash)
if block.CrystallizedStateHash() != hash {
return false, fmt.Errorf("crystallized state hash mismatched, wanted: %v, got: %v", block.CrystallizedStateHash(), hash)
}

return true, nil
Expand Down Expand Up @@ -218,10 +214,9 @@ func (b *BeaconChain) RotateValidatorSet() ([]*pb.ValidatorRecord, []*pb.Validat
return newQueuedValidators, newActiveValidators, newExitedValidators
}

// TODO: Use proto marshal instead of RLP when we change activeState to proto
// persistActiveState stores the RLP encoding of the latest beacon chain active state into the db.
// persistActiveState stores proto encoding of the latest beacon chain active state into the db.
func (b *BeaconChain) persistActiveState() error {
encodedState, err := rlp.EncodeToBytes(b.state)
encodedState, err := b.ActiveState().Marshal()
if err != nil {
return err
}
Expand Down Expand Up @@ -255,10 +250,10 @@ func (b *BeaconChain) computeNewActiveState(seed common.Hash) (*types.ActiveStat

// TODO: Verify randao reveal from validator's hash pre image.

return &types.ActiveState{
return types.NewActiveState(&pb.ActiveStateResponse{
TotalAttesterDeposits: 0,
AttesterBitfields: []byte{},
}, nil
AttesterBitfield: []byte{},
}), nil
}

// getAttestersProposer returns lists of random sampled attesters and proposer indices.
Expand All @@ -272,16 +267,6 @@ func (b *BeaconChain) getAttestersProposer(seed common.Hash) ([]int, int, error)
return indices[:int(attesterCount)], indices[len(indices)-1], nil
}

// hashActiveState serializes the active state object then uses
// blake2b to hash the serialized object.
func hashActiveState(state *types.ActiveState) ([32]byte, error) {
serializedState, err := rlp.EncodeToBytes(state)
if err != nil {
return [32]byte{}, err
}
return blake2b.Sum256(serializedState), nil
}

// GetCutoffs is used to split up validators into groups at the start
// of every epoch. It determines at what height validators can make
// attestations and crosslinks. It returns lists of cutoff indices.
Expand Down Expand Up @@ -370,7 +355,7 @@ func (b *BeaconChain) resetAttesterBitfields() error {
defer b.lock.Unlock()

newbitfields := make([]byte, length)
b.state.ActiveState.AttesterBitfields = newbitfields
b.state.ActiveState.SetAttesterBitfield(newbitfields)

return b.persistCrystallizedState()
}
Expand All @@ -379,7 +364,7 @@ func (b *BeaconChain) resetAttesterBitfields() error {
func (b *BeaconChain) resetTotalAttesterDeposit() error {
b.lock.Lock()
defer b.lock.Unlock()
b.state.ActiveState.TotalAttesterDeposits = 0
b.state.ActiveState.SetTotalAttesterDeposits(0)

return b.persistCrystallizedState()
}
Expand All @@ -402,7 +387,7 @@ func (b *BeaconChain) updateJustifiedEpoch() error {
// updateRewardsAndPenalties checks if the attester has voted and then applies the
// rewards and penalties for them.
func (b *BeaconChain) updateRewardsAndPenalties(index int) error {
bitfields := b.state.ActiveState.AttesterBitfields
bitfields := b.state.ActiveState.AttesterBitfield()
attesterBlock := (index + 1) / 8
attesterFieldIndex := (index + 1) % 8
if attesterFieldIndex == 0 {
Expand All @@ -427,7 +412,7 @@ func (b *BeaconChain) updateRewardsAndPenalties(index int) error {
// rewards and penalties, resets the bitfield and deposits and also applies the slashing conditions.
func (b *BeaconChain) computeValidatorRewardsAndPenalties() error {
activeValidatorSet := b.state.CrystallizedState.ActiveValidators()
attesterDeposits := b.state.ActiveState.TotalAttesterDeposits
attesterDeposits := b.state.ActiveState.TotalAttesterDeposits()
totalDeposit := b.state.CrystallizedState.TotalDeposits()

attesterFactor := attesterDeposits * 3
Expand Down
65 changes: 32 additions & 33 deletions beacon-chain/blockchain/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,12 @@ func TestMutateActiveState(t *testing.T) {
beaconChain, db := startInMemoryBeaconChain(t)
defer db.Close()

active := &types.ActiveState{
data := &pb.ActiveStateResponse{
TotalAttesterDeposits: 4096,
AttesterBitfields: []byte{'A', 'B', 'C'},
AttesterBitfield: []byte{'A', 'B', 'C'},
}
active := types.NewActiveState(data)

if err := beaconChain.MutateActiveState(active); err != nil {
t.Fatalf("unable to mutate active state: %v", err)
}
Expand All @@ -89,11 +91,11 @@ func TestMutateActiveState(t *testing.T) {
}

// The active state should still be the one we mutated and persited earlier.
if active.TotalAttesterDeposits != newBeaconChain.state.ActiveState.TotalAttesterDeposits {
t.Errorf("active state height incorrect. wanted %v, got %v", active.TotalAttesterDeposits, newBeaconChain.state.ActiveState.TotalAttesterDeposits)
if active.TotalAttesterDeposits() != newBeaconChain.state.ActiveState.TotalAttesterDeposits() {
t.Errorf("active state height incorrect. wanted %v, got %v", active.TotalAttesterDeposits(), newBeaconChain.state.ActiveState.TotalAttesterDeposits())
}
if !bytes.Equal(active.AttesterBitfields, newBeaconChain.state.ActiveState.AttesterBitfields) {
t.Errorf("active state randao incorrect. wanted %v, got %v", active.AttesterBitfields, newBeaconChain.state.ActiveState.AttesterBitfields)
if !bytes.Equal(active.AttesterBitfield(), newBeaconChain.state.ActiveState.AttesterBitfield()) {
t.Errorf("active state randao incorrect. wanted %v, got %v", active.AttesterBitfield(), newBeaconChain.state.ActiveState.AttesterBitfield())
}
}

Expand Down Expand Up @@ -172,10 +174,10 @@ func TestCanProcessBlock(t *testing.T) {
if _, err := beaconChain.CanProcessBlock(&faultyFetcher{}, block); err == nil {
t.Errorf("Using a faulty fetcher should throw an error, received nil")
}
activeState := &types.ActiveState{TotalAttesterDeposits: 10000}
activeState := types.NewActiveState(&pb.ActiveStateResponse{TotalAttesterDeposits: 10000})
beaconChain.state.ActiveState = activeState

activeHash, err := hashActiveState(activeState)
activeHash, err := activeState.Hash()
if err != nil {
t.Fatalf("Cannot hash active state: %v", err)
}
Expand Down Expand Up @@ -213,8 +215,8 @@ func TestProcessBlockWithBadHashes(t *testing.T) {
beaconChain, db := startInMemoryBeaconChain(t)
defer db.Close()

activeState := &types.ActiveState{TotalAttesterDeposits: 10000}
activeStateHash, err := hashActiveState(activeState)
active := types.NewActiveState(&pb.ActiveStateResponse{TotalAttesterDeposits: 10000})
activeStateHash, err := active.Hash()
if err != nil {
t.Fatalf("Cannot hash active state: %v", err)
}
Expand All @@ -226,7 +228,7 @@ func TestProcessBlockWithBadHashes(t *testing.T) {
block := NewBlock(t, 1, activeStateHash, crystallizedStateHash)

// Test negative scenario where active state hash is different than node's compute
beaconChain.state.ActiveState = &types.ActiveState{TotalAttesterDeposits: 9999}
beaconChain.state.ActiveState = types.NewActiveState(&pb.ActiveStateResponse{TotalAttesterDeposits: 9999})

canProcess, err := beaconChain.CanProcessBlock(&mockFetcher{}, block)
if err == nil {
Expand Down Expand Up @@ -304,7 +306,6 @@ func TestRotateValidatorSet(t *testing.T) {
}

func TestCutOffValidatorSet(t *testing.T) {

// Test scenario #1: Assume there's enough validators to fill in all the heights.
validatorCount := params.EpochLength * params.MinCommiteeSize
cutoffsValidators := GetCutoffs(validatorCount)
Expand Down Expand Up @@ -441,23 +442,22 @@ func TestResetAttesterBitfields(t *testing.T) {
beaconChain.CrystallizedState().UpdateActiveValidators(validators)

testAttesterBitfield := []byte{2, 4, 6, 9}

if err := beaconChain.MutateActiveState(&types.ActiveState{AttesterBitfields: testAttesterBitfield}); err != nil {
if err := beaconChain.MutateActiveState(types.NewActiveState(&pb.ActiveStateResponse{AttesterBitfield: testAttesterBitfield})); err != nil {
t.Fatal("unable to mutate active state")
}
if err := beaconChain.resetAttesterBitfields(); err != nil {
t.Fatalf("unable to reset Attester Bitfields")
}
if bytes.Equal(testAttesterBitfield, beaconChain.state.ActiveState.AttesterBitfields) {
if bytes.Equal(testAttesterBitfield, beaconChain.state.ActiveState.AttesterBitfield()) {
t.Fatalf("attester bitfields have not been able to be reset: %v", testAttesterBitfield)
}

bitfieldLength := j / 8
if j%8 != 0 {
bitfieldLength++
}
if !bytes.Equal(beaconChain.state.ActiveState.AttesterBitfields, make([]byte, bitfieldLength)) {
t.Fatalf("attester bitfields are not zeroed out: %v", beaconChain.state.ActiveState.AttesterBitfields)
if !bytes.Equal(beaconChain.state.ActiveState.AttesterBitfield(), make([]byte, bitfieldLength)) {
t.Fatalf("attester bitfields are not zeroed out: %v", beaconChain.state.ActiveState.AttesterBitfield())
}
}
}
Expand All @@ -466,18 +466,18 @@ func TestResetTotalAttesterDeposit(t *testing.T) {
beaconChain, db := startInMemoryBeaconChain(t)
defer db.Close()

ActiveState := &types.ActiveState{TotalAttesterDeposits: 10000}
if err := beaconChain.MutateActiveState(ActiveState); err != nil {
active := types.NewActiveState(&pb.ActiveStateResponse{TotalAttesterDeposits: 10000})
if err := beaconChain.MutateActiveState(active); err != nil {
t.Fatalf("unable to Mutate Active state: %v", err)
}
if beaconChain.state.ActiveState.TotalAttesterDeposits != uint64(10000) {
t.Fatalf("attester deposit was not saved: %d", beaconChain.state.ActiveState.TotalAttesterDeposits)
if beaconChain.state.ActiveState.TotalAttesterDeposits() != uint64(10000) {
t.Fatalf("attester deposit was not saved: %d", beaconChain.state.ActiveState.TotalAttesterDeposits())
}
if err := beaconChain.resetTotalAttesterDeposit(); err != nil {
t.Fatalf("unable to reset total attester deposit: %v", err)
}
if beaconChain.state.ActiveState.TotalAttesterDeposits != uint64(0) {
t.Fatalf("attester deposit was not able to be reset: %d", beaconChain.state.ActiveState.TotalAttesterDeposits)
if beaconChain.state.ActiveState.TotalAttesterDeposits() != uint64(0) {
t.Fatalf("attester deposit was not able to be reset: %d", beaconChain.state.ActiveState.TotalAttesterDeposits())
}
}

Expand Down Expand Up @@ -538,8 +538,7 @@ func TestUpdateRewardsAndPenalties(t *testing.T) {

//Binary Representation of Bitfield: 00010110 00101011 00101110 01001111 01010000
testAttesterBitfield := []byte{22, 43, 46, 79, 80}

if err := beaconChain.MutateActiveState(&types.ActiveState{AttesterBitfields: testAttesterBitfield}); err != nil {
if err := beaconChain.MutateActiveState(types.NewActiveState(&pb.ActiveStateResponse{AttesterBitfield: testAttesterBitfield})); err != nil {
t.Fatalf("unable to mutate active state: %v", err)
}
// Test Validator with index 10 would refer to the 11th bit in the bitfield
Expand Down Expand Up @@ -601,19 +600,19 @@ func TestComputeValidatorRewardsAndPenalties(t *testing.T) {

//Binary representation of bitfield: 11001000 10010100 10010010 10110011 00110001
testAttesterBitfield := []byte{200, 148, 146, 179, 49}

ActiveState := &types.ActiveState{TotalAttesterDeposits: 8000, AttesterBitfields: testAttesterBitfield}
types.NewActiveState(&pb.ActiveStateResponse{AttesterBitfield: testAttesterBitfield})
ActiveState := types.NewActiveState(&pb.ActiveStateResponse{TotalAttesterDeposits: 8000, AttesterBitfield: testAttesterBitfield})
if err := beaconChain.MutateActiveState(ActiveState); err != nil {
t.Fatalf("unable to Mutate Active state: %v", err)
}
if err := beaconChain.computeValidatorRewardsAndPenalties(); err != nil {
t.Fatalf("could not compute validator rewards and penalties: %v", err)
}
if beaconChain.state.CrystallizedState.LastJustifiedEpoch() != uint64(5) {
t.Fatalf("unable to update last justified epoch: %d", beaconChain.state.CrystallizedState.LastJustifiedEpoch)
t.Fatalf("unable to update last justified epoch: %d", beaconChain.state.CrystallizedState.LastJustifiedEpoch())
}
if beaconChain.state.CrystallizedState.LastFinalizedEpoch() != uint64(4) {
t.Fatalf("unable to update last finalized epoch: %d", beaconChain.state.CrystallizedState.LastFinalizedEpoch)
t.Fatalf("unable to update last finalized epoch: %d", beaconChain.state.CrystallizedState.LastFinalizedEpoch())
}
if beaconChain.CrystallizedState().ActiveValidators()[0].Balance != uint64(1001) {
t.Fatalf("validator balance not updated: %d", beaconChain.CrystallizedState().ActiveValidators()[1].Balance)
Expand All @@ -624,11 +623,11 @@ func TestComputeValidatorRewardsAndPenalties(t *testing.T) {
if beaconChain.CrystallizedState().ActiveValidators()[29].Balance != uint64(999) {
t.Fatalf("validator balance not updated: %d", beaconChain.CrystallizedState().ActiveValidators()[1].Balance)
}
if beaconChain.state.ActiveState.TotalAttesterDeposits != uint64(0) {
t.Fatalf("attester deposit was not able to be reset: %d", beaconChain.state.ActiveState.TotalAttesterDeposits)
if beaconChain.state.ActiveState.TotalAttesterDeposits() != uint64(0) {
t.Fatalf("attester deposit was not able to be reset: %d", beaconChain.state.ActiveState.TotalAttesterDeposits())
}
if !bytes.Equal(beaconChain.state.ActiveState.AttesterBitfields, make([]byte, 5)) {
t.Fatalf("attester bitfields are not zeroed out: %v", beaconChain.state.ActiveState.AttesterBitfields)
if !bytes.Equal(beaconChain.state.ActiveState.AttesterBitfield(), make([]byte, 5)) {
t.Fatalf("attester bitfields are not zeroed out: %v", beaconChain.state.ActiveState.AttesterBitfield())
}
}

Expand Down
Loading

0 comments on commit 9b19663

Please sign in to comment.