Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Part 1 of Aligning Core Blockchain Package with Latest 2.1 Spec #371

Merged
merged 29 commits into from
Aug 14, 2018
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2af62ad
sync functions for active and crystallized states
terencechain Jul 31, 2018
b98a8dd
merge with master
terencechain Aug 1, 2018
d353026
Merge branch 'master' of https://github.com/prysmaticlabs/prysm
terencechain Aug 2, 2018
b418efd
Merge branch 'master' of https://github.com/prysmaticlabs/prysm
terencechain Aug 2, 2018
f436b85
Merge branch 'master' of https://github.com/prysmaticlabs/prysm
terencechain Aug 2, 2018
774a43c
first commit to sync 2.1 spec
terencechain Aug 3, 2018
2c93f8e
Replace Cutoff with SplitIndicies function
terencechain Aug 3, 2018
a88411c
new function to select validators by height and shard
terencechain Aug 3, 2018
33cdbc9
merged with master
terencechain Aug 6, 2018
21c0d8c
queued/exicted validator pools no longer exist in 2.1
terencechain Aug 8, 2018
70ef131
just one last function calculateRewardsFFG to fix!
terencechain Aug 9, 2018
a7fb434
just one last function calculateRewardsFFG to fix!
terencechain Aug 9, 2018
d22cc83
fixed tests and lint
terencechain Aug 10, 2018
1aa0cd1
bazel
terencechain Aug 10, 2018
692fd2e
merge 2.1 with Raul's attester fixes
terencechain Aug 11, 2018
186f4cc
aligned state and block types with 2.1
terencechain Aug 11, 2018
d146f46
bazel
terencechain Aug 11, 2018
bc7809a
fixed protoc
terencechain Aug 11, 2018
06233f4
fixed params
terencechain Aug 11, 2018
e4f8d3b
2.1 configs
terencechain Aug 11, 2018
77fdae4
fixed tests
terencechain Aug 12, 2018
12ca291
cross check with pyevm implementations
terencechain Aug 12, 2018
c5e20e9
merged with Raul's proposer code
terencechain Aug 12, 2018
9b8c9ac
merged with master
terencechain Aug 12, 2018
961a1df
merged with master
terencechain Aug 13, 2018
b24e2d9
fixed test
terencechain Aug 13, 2018
f36a349
Merge branch 'master' into new-2.1-changes
terencechain Aug 13, 2018
c43259d
comments and punchuations
terencechain Aug 13, 2018
21bf3ed
Merge branch 'new-2.1-changes' of github.com:terenc3t/prysm into new-…
terencechain Aug 13, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 138 additions & 77 deletions beacon-chain/blockchain/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ type beaconState struct {
CrystallizedState *types.CrystallizedState
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rauljordan thanks for the review! it's ready again!

type beaconCommittee struct {
shardID int
committee []int
}

// NewBeaconChain initializes a beacon chain using genesis state parameters if
// none provided.
func NewBeaconChain(db ethdb.Database) (*BeaconChain, error) {
Expand Down Expand Up @@ -168,17 +173,14 @@ func (b *BeaconChain) PersistCrystallizedState() error {
return b.db.Put([]byte(crystallizedStateLookupKey), encodedState)
}

// IsEpochTransition checks if the current slotNumber divided
// by the epoch length (64 slots) is greater than the current epoch.
// IsEpochTransition checks if it's epoch transition time.
func (b *BeaconChain) IsEpochTransition(slotNumber uint64) bool {
currentEpoch := b.state.CrystallizedState.CurrentEpoch()
isTransition := (slotNumber / params.EpochLength) > currentEpoch
return isTransition
return slotNumber >= b.CrystallizedState().LastStateRecalc()+params.CycleLength
}

// CanProcessBlock decides if an incoming p2p block can be processed into the chain's block trie.
func (b *BeaconChain) CanProcessBlock(fetcher types.POWBlockFetcher, block *types.Block) (bool, error) {
if _, err := fetcher.BlockByHash(context.Background(), block.MainChainRef()); err != nil {
if _, err := fetcher.BlockByHash(context.Background(), block.PowChainRef()); err != nil {
return false, fmt.Errorf("fetching PoW block corresponding to mainchain reference failed: %v", err)
}

Expand Down Expand Up @@ -249,99 +251,106 @@ func (b *BeaconChain) computeNewActiveState(seed common.Hash) (*types.ActiveStat
// TODO: Verify randao reveal from validator's hash pre image.

return types.NewActiveState(&pb.ActiveState{
TotalAttesterDeposits: 0,
AttesterBitfield: []byte{},
PendingAttestations: []*pb.AttestationRecord{},
RecentBlockHashes: [][]byte{},
}), nil
}

// rotateValidatorSet is called every dynasty transition. It's primary function is
// to go through queued validators and induct them to be active, and remove bad
// active validator whose balance is below threshold to the exit set. It also cross checks
// every validator's switch dynasty before induct or remove.
func (b *BeaconChain) rotateValidatorSet() ([]*pb.ValidatorRecord, []*pb.ValidatorRecord, []*pb.ValidatorRecord) {

var newExitedValidators = b.CrystallizedState().ExitedValidators()
var newActiveValidators []*pb.ValidatorRecord
upperbound := b.CrystallizedState().ActiveValidatorsLength()/30 + 1
exitCount := 0

// Loop through active validator set, remove validator whose balance is below 50% and switch dynasty > current dynasty.
for _, validator := range b.CrystallizedState().ActiveValidators() {
if validator.Balance < params.DefaultBalance/2 {
newExitedValidators = append(newExitedValidators, validator)
} else if validator.SwitchDynasty == b.CrystallizedState().CurrentDynasty()+1 && exitCount < upperbound {
newExitedValidators = append(newExitedValidators, validator)
exitCount++
} else {
newActiveValidators = append(newActiveValidators, validator)
// rotateValidatorSet is called every dynasty transition. The primary functions are:
// 1.) Go through queued validator indices and induct them to be active by setting start
// dynasty to current epoch.
// 2.) Remove bad active validator whose balance is below threshold to the exit set by
// setting end dynasty to current epoch.
func (b *BeaconChain) rotateValidatorSet() {

validators := b.CrystallizedState().Validators()
upperbound := len(b.activeValidatorIndices())/30 + 1

// Loop through active validator set, remove validator whose balance is below 50%.
for _, index := range b.activeValidatorIndices() {
if validators[index].Balance < params.DefaultBalance/2 {
validators[index].EndDynasty = b.CrystallizedState().CurrentDynasty()
}
}
// Get the total number of validator we can induct.
inductNum := upperbound
if b.CrystallizedState().QueuedValidatorsLength() < inductNum {
inductNum = b.CrystallizedState().QueuedValidatorsLength()
if len(b.queuedValidatorIndices()) < inductNum {
inductNum = len(b.queuedValidatorIndices())
}

// Induct queued validator to active validator set until the switch dynasty is greater than current number.
for i := 0; i < inductNum; i++ {
if b.CrystallizedState().QueuedValidators()[i].SwitchDynasty > b.CrystallizedState().CurrentDynasty()+1 {
inductNum = i
for _, index := range b.queuedValidatorIndices() {
validators[index].StartDynasty = b.CrystallizedState().CurrentDynasty()
inductNum--
if inductNum == 0 {
break
}
newActiveValidators = append(newActiveValidators, b.CrystallizedState().QueuedValidators()[i])
}
newQueuedValidators := b.CrystallizedState().QueuedValidators()[inductNum:]

return newQueuedValidators, newActiveValidators, newExitedValidators
}

// getAttestersProposer returns lists of random sampled attesters and proposer indices.
func (b *BeaconChain) getAttestersProposer(seed common.Hash) ([]int, int, error) {
attesterCount := math.Min(params.AttesterCount, float64(b.CrystallizedState().ActiveValidatorsLength()))
attesterCount := math.Min(params.MinCommiteeSize, float64(b.CrystallizedState().ValidatorsLength()))

indices, err := utils.ShuffleIndices(seed, b.CrystallizedState().ActiveValidatorsLength())
indices, err := utils.ShuffleIndices(seed, b.activeValidatorIndices())
if err != nil {
return nil, -1, err
}
return indices[:int(attesterCount)], indices[len(indices)-1], nil
}

// getAttestersTotalDeposit returns the total deposit combined by attesters.
// TODO: Consider slashing condition
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

punctuation

func (b *BeaconChain) getAttestersTotalDeposit() (uint64, error) {
var numOfBits int
for _, attestation := range b.ActiveState().PendingAttestations() {
for _, byte := range attestation.AttesterBitfield {
numOfBits += int(utils.BitSetCount(byte))
}
}
// Assume there's no slashing condition, the following logic will change later phase.
return uint64(numOfBits) * params.DefaultBalance, nil
}

// calculateRewardsFFG adjusts validators balances by applying rewards or penalties
// based on FFG incentive structure.
func (b *BeaconChain) calculateRewardsFFG() error {
func (b *BeaconChain) calculateRewardsFFG(block *types.Block) error {
b.lock.Lock()
defer b.lock.Unlock()
activeValidators := b.state.CrystallizedState.ActiveValidators()
attesterDeposits := b.state.ActiveState.TotalAttesterDeposits()
validators := b.CrystallizedState().Validators()
activeValidators := b.activeValidatorIndices()
attesterDeposits, err := b.getAttestersTotalDeposit()
if err != nil {
return err
}
totalDeposit := b.state.CrystallizedState.TotalDeposits()

attesterFactor := attesterDeposits * 3
totalFactor := uint64(totalDeposit * 2)

println(attesterFactor)
println(totalFactor)
if attesterFactor >= totalFactor {
log.Info("Setting justified epoch to current epoch: %v", b.CrystallizedState().CurrentEpoch())
b.state.CrystallizedState.UpdateJustifiedEpoch()
log.Info("Setting justified epoch to current slot number: %v", block.SlotNumber())
b.state.CrystallizedState.UpdateJustifiedSlot(block.SlotNumber())

log.Info("Applying rewards and penalties for the validators from last epoch")
for i := range activeValidators {
voted, err := b.hasAttesterAtIndexVoted(i)

for i, attesterIndex := range activeValidators {
voted, err := utils.CheckBit(b.state.ActiveState.LatestPendingAttestation().AttesterBitfield, attesterIndex)
if err != nil {
return fmt.Errorf("exiting calculate rewards FFG due to %v", err)
}
if voted {
activeValidators[i].Balance += params.AttesterReward
validators[i].Balance += params.AttesterReward
} else {
activeValidators[i].Balance -= params.AttesterReward
validators[i].Balance -= params.AttesterReward
}
}

log.Info("Resetting attester bit field to all zeros")
b.resetAttesterBitfield()

log.Info("Resetting total attester deposit to zero")
b.ActiveState().SetTotalAttesterDeposits(0)
b.ActiveState().ClearPendingAttestations()

b.CrystallizedState().SetActiveValidators(activeValidators)
b.CrystallizedState().SetValidators(validators)
err := b.PersistActiveState()
if err != nil {
return err
Expand All @@ -354,33 +363,88 @@ func (b *BeaconChain) calculateRewardsFFG() error {
return nil
}

// hasAttesterAtIndexVoted checks if the attester at the passed index has voted by comparing its bit field.
func (b *BeaconChain) hasAttesterAtIndexVoted(index int) (bool, error) {
bitfield := b.state.ActiveState.AttesterBitfield()
attesterBlock := (index + 1) / 8
attesterFieldIndex := (index + 1) % 8
if attesterFieldIndex == 0 {
attesterFieldIndex = 8
} else {
attesterBlock++
// activeValidatorIndices filters out active validators based on start and end dynasty
// and returns their indices in a list.
func (b *BeaconChain) activeValidatorIndices() []int {
var indices []int
validators := b.CrystallizedState().Validators()
dynasty := b.CrystallizedState().CurrentDynasty()
for i := 0; i < len(validators); i++ {
if validators[i].StartDynasty <= dynasty && dynasty < validators[i].EndDynasty {
indices = append(indices, i)
}
}
return indices
}

if len(bitfield) < attesterBlock {
return false, errors.New("attester index does not exist")
// exitedValidatorIndices filters out exited validators based on start and end dynasty
// and returns their indices in a list.
func (b *BeaconChain) exitedValidatorIndices() []int {
var indices []int
validators := b.CrystallizedState().Validators()
dynasty := b.CrystallizedState().CurrentDynasty()
for i := 0; i < len(validators); i++ {
if validators[i].StartDynasty < dynasty && validators[i].EndDynasty < dynasty {
indices = append(indices, i)
}
}
return indices
}

field := bitfield[attesterBlock-1] >> (8 - uint(attesterFieldIndex))
if field%2 != 0 {
return true, nil
// queuedValidatorIndices filters out queued validators based on start and end dynasty
// and returns their indices in a list.
func (b *BeaconChain) queuedValidatorIndices() []int {
var indices []int
validators := b.CrystallizedState().Validators()
dynasty := b.CrystallizedState().CurrentDynasty()
for i := 0; i < len(validators); i++ {
if validators[i].StartDynasty > dynasty {
indices = append(indices, i)
}
}

return false, nil
return indices
}

// resetAttesterBitfield resets the attester bit field of active state to zeros.
func (b *BeaconChain) resetAttesterBitfield() {
newbitfields := make([]byte, b.CrystallizedState().ActiveValidatorsLength()/8)
b.state.ActiveState.SetAttesterBitfield(newbitfields)
// validatorsByHeightShard splits a shuffled validator list by height and by shard,
// it ensures there's enough validators per height and per shard, if not, it'll skip
// some heights and shards.
func (b *BeaconChain) validatorsByHeightShard() ([]*beaconCommittee, error) {
indices := b.activeValidatorIndices()
var committeesPerSlot int
var slotsPerCommittee int
var committees []*beaconCommittee

if len(indices) >= params.CycleLength*params.MinCommiteeSize {
committeesPerSlot = len(indices)/params.CycleLength/(params.MinCommiteeSize*2) + 1
slotsPerCommittee = 1
} else {
committeesPerSlot = 1
slotsPerCommittee = 1
for len(indices)*slotsPerCommittee < params.MinCommiteeSize && slotsPerCommittee < params.CycleLength {
slotsPerCommittee *= 2
}
}

// split the shuffled list for heights
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

punctuation

shuffledList, err := utils.ShuffleIndices(b.state.CrystallizedState.DynastySeed(), indices)
if err != nil {
return nil, err
}

heightList := utils.SplitIndices(shuffledList, params.CycleLength)

// split the shuffled height list for shards
for i, subList := range heightList {
shardList := utils.SplitIndices(subList, params.MinCommiteeSize)
for _, shardIndex := range shardList {
shardID := int(b.CrystallizedState().CrosslinkingStartShard()) + i*committeesPerSlot/slotsPerCommittee
committees = append(committees, &beaconCommittee{
shardID: shardID,
committee: shardIndex,
})
}
}
return committees, nil
}

// saveBlock puts the passed block into the beacon chain db.
Expand All @@ -396,6 +460,3 @@ func (b *BeaconChain) saveBlock(block *types.Block) error {

return b.db.Put(hash[:], encodedState)
}

// Slashing Condtions
// TODO: Implement all the conditions and add in the methods once the spec is updated
Loading