mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-24 12:27:18 +00:00
764 lines
25 KiB
Go
764 lines
25 KiB
Go
package blockchain
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/ethdb"
|
|
"github.com/gogo/protobuf/proto"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/casper"
|
|
"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/beacon/p2p/v1"
|
|
"golang.org/x/crypto/blake2b"
|
|
)
|
|
|
|
var clock utils.Clock = &utils.RealClock{}
|
|
|
|
// BeaconChain represents the core PoS blockchain object containing
|
|
// both a crystallized and active state.
|
|
type BeaconChain struct {
|
|
state *beaconState
|
|
lock sync.Mutex
|
|
db ethdb.Database
|
|
}
|
|
|
|
type beaconState struct {
|
|
// ActiveState captures the beacon state at block processing level,
|
|
// it focuses on verifying aggregated signatures and pending attestations.
|
|
ActiveState *types.ActiveState
|
|
// CrystallizedState captures the beacon state at cycle transition level,
|
|
// it focuses on changes to the validator set, processing cross links and
|
|
// setting up FFG checkpoints.
|
|
CrystallizedState *types.CrystallizedState
|
|
}
|
|
|
|
// NewBeaconChain initializes a beacon chain using genesis state parameters if
|
|
// none provided.
|
|
func NewBeaconChain(db ethdb.Database) (*BeaconChain, error) {
|
|
beaconChain := &BeaconChain{
|
|
db: db,
|
|
state: &beaconState{},
|
|
}
|
|
hasCrystallized, err := db.Has(crystallizedStateLookupKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hasGenesis, err := db.Has(genesisLookupKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
active, crystallized, err := types.NewGenesisStates()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
beaconChain.state.ActiveState = active
|
|
|
|
if !hasGenesis {
|
|
log.Info("No genesis block found on disk, initializing genesis block")
|
|
genesisBlock, err := types.NewGenesisBlock()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
genesisMarshall, err := proto.Marshal(genesisBlock.Proto())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := beaconChain.db.Put(genesisLookupKey, genesisMarshall); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if !hasCrystallized {
|
|
log.Info("No chainstate found on disk, initializing beacon from genesis")
|
|
beaconChain.state.CrystallizedState = crystallized
|
|
return beaconChain, nil
|
|
}
|
|
|
|
enc, err := db.Get(crystallizedStateLookupKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
crystallizedData := &pb.CrystallizedState{}
|
|
err = proto.Unmarshal(enc, crystallizedData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
beaconChain.state.CrystallizedState = types.NewCrystallizedState(crystallizedData)
|
|
|
|
return beaconChain, nil
|
|
}
|
|
|
|
// GenesisBlock returns the canonical, genesis block.
|
|
func (b *BeaconChain) GenesisBlock() (*types.Block, error) {
|
|
genesisExists, err := b.db.Has(genesisLookupKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if genesisExists {
|
|
bytes, err := b.db.Get(genesisLookupKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
block := &pb.BeaconBlock{}
|
|
if err := proto.Unmarshal(bytes, block); err != nil {
|
|
return nil, err
|
|
}
|
|
return types.NewBlock(block), nil
|
|
}
|
|
return types.NewGenesisBlock()
|
|
}
|
|
|
|
// CanonicalHead fetches the latest head stored in persistent storage.
|
|
func (b *BeaconChain) CanonicalHead() (*types.Block, error) {
|
|
|
|
bytes, err := b.db.Get(canonicalHeadLookupKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
block := &pb.BeaconBlock{}
|
|
if err := proto.Unmarshal(bytes, block); err != nil {
|
|
return nil, fmt.Errorf("cannot unmarshal proto: %v", err)
|
|
}
|
|
return types.NewBlock(block), nil
|
|
|
|
}
|
|
|
|
// ActiveState contains the current state of attestations and changes every block.
|
|
func (b *BeaconChain) ActiveState() *types.ActiveState {
|
|
return b.state.ActiveState
|
|
}
|
|
|
|
// CrystallizedState contains cycle dependent validator information, changes every cycle.
|
|
func (b *BeaconChain) CrystallizedState() *types.CrystallizedState {
|
|
return b.state.CrystallizedState
|
|
}
|
|
|
|
// SetActiveState is a convenience method which sets and persists the active state on the beacon chain.
|
|
func (b *BeaconChain) SetActiveState(activeState *types.ActiveState) error {
|
|
b.lock.Lock()
|
|
defer b.lock.Unlock()
|
|
b.state.ActiveState = activeState
|
|
return b.PersistActiveState()
|
|
}
|
|
|
|
// SetCrystallizedState is a convenience method which sets and persists the crystallized state on the beacon chain.
|
|
func (b *BeaconChain) SetCrystallizedState(crystallizedState *types.CrystallizedState) error {
|
|
b.lock.Lock()
|
|
defer b.lock.Unlock()
|
|
b.state.CrystallizedState = crystallizedState
|
|
return b.PersistCrystallizedState()
|
|
}
|
|
|
|
// PersistActiveState stores proto encoding of the current beacon chain active state into the db.
|
|
func (b *BeaconChain) PersistActiveState() error {
|
|
encodedState, err := b.ActiveState().Marshal()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return b.db.Put(activeStateLookupKey, encodedState)
|
|
}
|
|
|
|
// PersistCrystallizedState stores proto encoding of the current beacon chain crystallized state into the db.
|
|
func (b *BeaconChain) PersistCrystallizedState() error {
|
|
encodedState, err := b.CrystallizedState().Marshal()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return b.db.Put(crystallizedStateLookupKey, encodedState)
|
|
}
|
|
|
|
// IsCycleTransition checks if a new cycle has been reached. At that point,
|
|
// a new crystallized state and active state transition will occur.
|
|
func (b *BeaconChain) IsCycleTransition(slotNumber uint64) bool {
|
|
return slotNumber >= b.CrystallizedState().LastStateRecalc()+params.CycleLength
|
|
}
|
|
|
|
// CanProcessBlock is called to decide if an incoming p2p block can be processed into the chain's block trie,
|
|
// it checks time stamp, beacon chain parent block hash. It also checks pow chain reference hash if it's a validator.
|
|
func (b *BeaconChain) CanProcessBlock(fetcher types.POWBlockFetcher, block *types.Block, isValidator bool) (bool, error) {
|
|
if isValidator {
|
|
if _, err := fetcher.BlockByHash(context.Background(), block.PowChainRef()); err != nil {
|
|
return false, fmt.Errorf("fetching PoW block corresponding to mainchain reference failed: %v", err)
|
|
}
|
|
}
|
|
|
|
canProcess, err := b.verifyBlockTimeStamp(block)
|
|
if err != nil {
|
|
return false, fmt.Errorf("unable to process block: %v", err)
|
|
}
|
|
if !canProcess {
|
|
return false, fmt.Errorf("time stamp verification for beacon block %v failed", block.SlotNumber())
|
|
}
|
|
return canProcess, nil
|
|
}
|
|
|
|
// verifyBlockTimeStamp verifies node's local time is greater than or equal to
|
|
// min timestamp as computed by GENESIS_TIME + slot_number * SLOT_DURATION.
|
|
func (b *BeaconChain) verifyBlockTimeStamp(block *types.Block) (bool, error) {
|
|
slotDuration := time.Duration(block.SlotNumber()*params.SlotDuration) * time.Second
|
|
genesis, err := b.GenesisBlock()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
genesisTime, err := genesis.Timestamp()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if clock.Now().Before(genesisTime.Add(slotDuration)) {
|
|
return false, nil
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// computeNewActiveState for every newly processed beacon block.
|
|
func (b *BeaconChain) computeNewActiveState(attestations []*pb.AttestationRecord, activeState *types.ActiveState, blockVoteCache map[[32]byte]*types.VoteCache, blockHash [32]byte) (*types.ActiveState, error) {
|
|
// TODO: Insert recent block hash.
|
|
activeState.SetBlockVoteCache(blockVoteCache)
|
|
activeState.NewPendingAttestation(attestations)
|
|
blockHashes := activeState.RecentBlockHashes()
|
|
blockHashes = append(blockHashes, blockHash)
|
|
|
|
for len(blockHashes) > 2*params.CycleLength {
|
|
blockHashes = blockHashes[1:]
|
|
}
|
|
|
|
activeState.ReplaceBlockHashes(blockHashes)
|
|
|
|
return activeState, nil
|
|
}
|
|
|
|
// processAttestation processes the attestations for one shard in an incoming block.
|
|
func (b *BeaconChain) processAttestation(attestationIndex int, block *types.Block) error {
|
|
// Validate attestation's slot number has is within range of incoming block number.
|
|
slotNumber := int(block.SlotNumber())
|
|
attestation := block.Attestations()[attestationIndex]
|
|
if int(attestation.Slot) > slotNumber {
|
|
return fmt.Errorf("attestation slot number can't be higher than block slot number. Found: %d, Needed lower than: %d",
|
|
attestation.Slot,
|
|
slotNumber)
|
|
}
|
|
if int(attestation.Slot) < slotNumber-params.CycleLength {
|
|
return fmt.Errorf("attestation slot number can't be lower than block slot number by one CycleLength. Found: %v, Needed greater than: %v",
|
|
attestation.Slot,
|
|
slotNumber-params.CycleLength)
|
|
}
|
|
|
|
if attestation.JustifiedSlot != b.CrystallizedState().LastJustifiedSlot() {
|
|
return fmt.Errorf("attestation's last justified slot has to match crystallied state's last justified slot. Found: %d. Want: %d",
|
|
attestation.JustifiedSlot,
|
|
b.CrystallizedState().LastJustifiedSlot())
|
|
}
|
|
|
|
// TODO: Validate last justified block hash matches in the crystallizedState.
|
|
|
|
// Get all the block hashes up to cycle length.
|
|
parentHashes := b.getSignedParentHashes(block, attestation)
|
|
attesterIndices, err := b.getAttesterIndices(attestation)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to get validator committee: %v", attesterIndices)
|
|
}
|
|
|
|
// Verify attester bitfields matches crystallized state's prev computed bitfield.
|
|
if err := b.validateAttesterBitfields(attestation, attesterIndices); err != nil {
|
|
return err
|
|
}
|
|
|
|
// TODO: Generate validators aggregated pub key.
|
|
|
|
// Hash parentHashes + shardID + slotNumber + shardBlockHash into a message to use to
|
|
// to verify with aggregated public key and aggregated attestation signature.
|
|
msg := make([]byte, binary.MaxVarintLen64)
|
|
var signedHashesStr []byte
|
|
for _, parentHash := range parentHashes {
|
|
signedHashesStr = append(signedHashesStr, parentHash[:]...)
|
|
signedHashesStr = append(signedHashesStr, byte(' '))
|
|
}
|
|
binary.PutUvarint(msg, attestation.Slot%params.CycleLength)
|
|
msg = append(msg, signedHashesStr...)
|
|
binary.PutUvarint(msg, attestation.ShardId)
|
|
msg = append(msg, attestation.ShardBlockHash...)
|
|
|
|
msgHash := blake2b.Sum512(msg)
|
|
|
|
log.Debugf("Attestation message for shard: %v, slot %v, block hash %v is: %v",
|
|
attestation.ShardId, attestation.Slot, attestation.ShardBlockHash, msgHash)
|
|
|
|
// TODO: Verify msgHash against aggregated pub key and aggregated signature.
|
|
return nil
|
|
}
|
|
|
|
// calculateBlockVoteCache calculates and updates active state's block vote cache.
|
|
func (b *BeaconChain) calculateBlockVoteCache(attestationIndex int, block *types.Block) (map[[32]byte]*types.VoteCache, error) {
|
|
attestation := block.Attestations()[attestationIndex]
|
|
newVoteCache := b.ActiveState().GetBlockVoteCache()
|
|
parentHashes := b.getSignedParentHashes(block, attestation)
|
|
attesterIndices, err := b.getAttesterIndices(attestation)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, h := range parentHashes {
|
|
// Skip calculating for this hash if the hash is part of oblique parent hashes.
|
|
var skip bool
|
|
for _, obliqueParentHash := range attestation.ObliqueParentHashes {
|
|
if bytes.Equal(h[:], obliqueParentHash) {
|
|
skip = true
|
|
}
|
|
}
|
|
if skip {
|
|
continue
|
|
}
|
|
|
|
// Initialize vote cache of a given block hash if it doesn't exist already.
|
|
if !b.ActiveState().IsVoteCacheEmpty(h) {
|
|
newVoteCache[h] = &types.VoteCache{VoterIndices: []uint32{}, VoteTotalDeposit: 0}
|
|
}
|
|
|
|
// Loop through attester indices, if the attester has voted but was not accounted for
|
|
// in the cache, then we add attester's index and balance to the block cache.
|
|
for i, attesterIndex := range attesterIndices {
|
|
var existingAttester bool
|
|
if !utils.CheckBit(attestation.AttesterBitfield, i) {
|
|
continue
|
|
}
|
|
for _, indexInCache := range newVoteCache[h].VoterIndices {
|
|
if attesterIndex == indexInCache {
|
|
existingAttester = true
|
|
}
|
|
}
|
|
if !existingAttester {
|
|
newVoteCache[h].VoterIndices = append(newVoteCache[h].VoterIndices, attesterIndex)
|
|
newVoteCache[h].VoteTotalDeposit += b.CrystallizedState().Validators()[attesterIndex].Balance
|
|
}
|
|
}
|
|
}
|
|
return newVoteCache, nil
|
|
}
|
|
|
|
// getSignedParentHashes returns all the parent hashes stored in active state up to last cycle length.
|
|
func (b *BeaconChain) getSignedParentHashes(block *types.Block, attestation *pb.AttestationRecord) [][32]byte {
|
|
var signedParentHashes [][32]byte
|
|
start := block.SlotNumber() - attestation.Slot
|
|
end := block.SlotNumber() - attestation.Slot - uint64(len(attestation.ObliqueParentHashes)) + params.CycleLength
|
|
|
|
signedParentHashes = append(signedParentHashes, b.ActiveState().RecentBlockHashes()[start:end]...)
|
|
|
|
for _, obliqueParentHashes := range attestation.ObliqueParentHashes {
|
|
hashes := common.BytesToHash(obliqueParentHashes)
|
|
signedParentHashes = append(signedParentHashes, hashes)
|
|
}
|
|
return signedParentHashes
|
|
}
|
|
|
|
// getAttesterIndices returns the attester committee of based from attestation's shard ID and slot number.
|
|
func (b *BeaconChain) getAttesterIndices(attestation *pb.AttestationRecord) ([]uint32, error) {
|
|
lastStateRecalc := b.CrystallizedState().LastStateRecalc()
|
|
// TODO: IndicesForSlots will return default value because the spec for dynasty transition is not finalized.
|
|
shardCommitteeArray := b.CrystallizedState().IndicesForSlots()
|
|
shardCommittee := shardCommitteeArray[attestation.Slot-lastStateRecalc].ArrayShardAndCommittee
|
|
for i := 0; i < len(shardCommittee); i++ {
|
|
if attestation.ShardId == shardCommittee[i].ShardId {
|
|
return shardCommittee[i].Committee, nil
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("unable to find attestation based on slot: %v, shardID: %v", attestation.Slot, attestation.ShardId)
|
|
}
|
|
|
|
// validateAttesterBitfields validates the attester bitfields are equal between attestation and crystallized state's calculation.
|
|
func (b *BeaconChain) validateAttesterBitfields(attestation *pb.AttestationRecord, attesterIndices []uint32) error {
|
|
// Validate attester bit field has the correct length.
|
|
if utils.BitLength(len(attesterIndices)) != len(attestation.AttesterBitfield) {
|
|
return fmt.Errorf("attestation has incorrect bitfield length. Found %v, expected %v",
|
|
len(attestation.AttesterBitfield), utils.BitLength(len(attesterIndices)))
|
|
}
|
|
|
|
// Valid attestation can not have non-zero trailing bits.
|
|
lastBit := len(attesterIndices)
|
|
if lastBit%8 != 0 {
|
|
for i := 0; i < 8-lastBit%8; i++ {
|
|
if utils.CheckBit(attestation.AttesterBitfield, lastBit+i) {
|
|
return errors.New("attestation has non-zero trailing bits")
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// stateRecalc is called when a new cycle has been reached, beacon node
|
|
// will re-compute active state and crystallized state during init cycle transition.
|
|
func (b *BeaconChain) stateRecalc(
|
|
cState *types.CrystallizedState,
|
|
aState *types.ActiveState,
|
|
block *types.Block) (*types.CrystallizedState, *types.ActiveState, error) {
|
|
|
|
var blockVoteBalance uint64
|
|
justifiedStreak := cState.JustifiedStreak()
|
|
justifiedSlot := cState.LastJustifiedSlot()
|
|
finalizedSlot := cState.LastFinalizedSlot()
|
|
lastStateRecalc := cState.LastStateRecalc()
|
|
blockVoteCache := aState.GetBlockVoteCache()
|
|
|
|
// walk through all the slots from LastStateRecalc - cycleLength to LastStateRecalc - 1.
|
|
for i := uint64(0); i < params.CycleLength; i++ {
|
|
slot := lastStateRecalc - params.CycleLength + i
|
|
blockHash := aState.RecentBlockHashes()[i]
|
|
if _, ok := blockVoteCache[blockHash]; ok {
|
|
blockVoteBalance = blockVoteCache[blockHash].VoteTotalDeposit
|
|
} else {
|
|
blockVoteBalance = 0
|
|
}
|
|
if 3*blockVoteBalance >= 2*cState.TotalDeposits() {
|
|
if slot > justifiedSlot {
|
|
justifiedSlot = slot
|
|
}
|
|
justifiedStreak++
|
|
} else {
|
|
justifiedStreak = 0
|
|
}
|
|
|
|
if justifiedStreak >= params.CycleLength+1 && slot-params.CycleLength > finalizedSlot {
|
|
finalizedSlot = slot - params.CycleLength
|
|
}
|
|
}
|
|
|
|
newCrossLinkRecords, err := b.processCrosslinks(
|
|
cState.CrosslinkRecords(),
|
|
cState.Validators(),
|
|
aState.PendingAttestations(),
|
|
cState.CurrentDynasty(),
|
|
block.SlotNumber(),
|
|
)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// Remove attestations older than LastStateRecalc.
|
|
var newPendingAttestations []*pb.AttestationRecord
|
|
for _, attestation := range aState.PendingAttestations() {
|
|
if attestation.Slot > lastStateRecalc {
|
|
newPendingAttestations = append(newPendingAttestations, attestation)
|
|
}
|
|
}
|
|
|
|
// TODO: Full rewards and penalties design is not finalized according to the spec.
|
|
rewardedValidators, _ := casper.CalculateRewards(
|
|
aState.PendingAttestations(),
|
|
cState.Validators(),
|
|
cState.CurrentDynasty(),
|
|
cState.TotalDeposits())
|
|
|
|
// Get all active validators and calculate total balance for next cycle.
|
|
var nextCycleBalance uint64
|
|
nextCycleValidators := casper.ActiveValidatorIndices(cState.Validators(), cState.CurrentDynasty())
|
|
for _, index := range nextCycleValidators {
|
|
nextCycleBalance += cState.Validators()[index].Balance
|
|
}
|
|
|
|
// Construct new crystallized state for cycle transition.
|
|
newCrystallizedState := types.NewCrystallizedState(&pb.CrystallizedState{
|
|
Validators: rewardedValidators, // TODO: Stub. Static validator set because dynasty transition is not finalized according to the spec.
|
|
LastStateRecalc: lastStateRecalc + params.CycleLength,
|
|
IndicesForSlots: cState.IndicesForSlots(), // TODO: Stub. This will be addresses by shuffling during dynasty transition.
|
|
LastJustifiedSlot: justifiedSlot,
|
|
JustifiedStreak: justifiedStreak,
|
|
LastFinalizedSlot: finalizedSlot,
|
|
CrosslinkingStartShard: 0, // TODO: Stub. Need to see where this epoch left off.
|
|
CrosslinkRecords: newCrossLinkRecords,
|
|
DynastySeedLastReset: cState.DynastySeedLastReset(), // TODO: Stub. Dynasty transition is not finalized according to the spec.
|
|
TotalDeposits: nextCycleBalance,
|
|
})
|
|
|
|
var recentBlockHashes [][]byte
|
|
for _, blockHashes := range aState.RecentBlockHashes() {
|
|
recentBlockHashes = append(recentBlockHashes, blockHashes[:])
|
|
// Drop the oldest block hash if the recent block hashes length is more than 2 * cycle length.
|
|
for len(recentBlockHashes) > 2*params.CycleLength {
|
|
// Delete attestation hash list that's corresponding to the block hash.
|
|
recentBlockHashes = recentBlockHashes[1:]
|
|
}
|
|
}
|
|
|
|
// Construct new active state for cycle transition.
|
|
newActiveState := types.NewActiveState(&pb.ActiveState{
|
|
PendingAttestations: newPendingAttestations,
|
|
RecentBlockHashes: recentBlockHashes,
|
|
}, aState.GetBlockVoteCache())
|
|
|
|
return newCrystallizedState, newActiveState, nil
|
|
}
|
|
|
|
// processCrosslinks checks if the proposed shard block has recevied
|
|
// 2/3 of the votes. If yes, we update crosslink record to point to
|
|
// the proposed shard block with latest dynasty and slot numbers.
|
|
func (b *BeaconChain) processCrosslinks(
|
|
crosslinkRecords []*pb.CrosslinkRecord,
|
|
validators []*pb.ValidatorRecord,
|
|
pendingAttestations []*pb.AttestationRecord,
|
|
dynasty uint64,
|
|
slot uint64) ([]*pb.CrosslinkRecord, error) {
|
|
|
|
type shardBlockHash struct {
|
|
id uint64
|
|
blockHash []byte
|
|
}
|
|
shardBlockHashBalance := make(map[*shardBlockHash]uint64)
|
|
|
|
for _, attestation := range pendingAttestations {
|
|
sbh := shardBlockHash{
|
|
id: attestation.ShardId,
|
|
blockHash: attestation.ShardBlockHash,
|
|
}
|
|
var keyExists bool
|
|
if _, ok := shardBlockHashBalance[&sbh]; ok {
|
|
keyExists = true
|
|
}
|
|
if !keyExists {
|
|
shardBlockHashBalance[&sbh] = 0
|
|
}
|
|
|
|
indices, err := b.getAttesterIndices(attestation)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// find the total balance of the shard committee.
|
|
var totalBalances uint64
|
|
for _, attesterIndex := range indices {
|
|
totalBalances += validators[attesterIndex].Balance
|
|
}
|
|
|
|
// find the balance of votes cast in shard attestation.
|
|
var voteBalances uint64
|
|
for i, attesterIndex := range indices {
|
|
if utils.CheckBit(attestation.AttesterBitfield, i) {
|
|
voteBalances += validators[attesterIndex].Balance
|
|
}
|
|
}
|
|
|
|
// if 2/3 of committee voted on this crosslink, update the crosslink
|
|
// with latest dynasty number, shard block hash, and slot number.
|
|
if 3*voteBalances >= 2*totalBalances && dynasty > crosslinkRecords[attestation.ShardId].Dynasty {
|
|
crosslinkRecords[attestation.ShardId] = &pb.CrosslinkRecord{
|
|
Dynasty: dynasty,
|
|
Blockhash: attestation.ShardBlockHash,
|
|
Slot: slot,
|
|
}
|
|
}
|
|
}
|
|
return crosslinkRecords, nil
|
|
}
|
|
|
|
func (b *BeaconChain) hasBlock(blockhash [32]byte) (bool, error) {
|
|
return b.db.Has(blockKey(blockhash))
|
|
}
|
|
|
|
// saveBlock puts the passed block into the beacon chain db.
|
|
func (b *BeaconChain) saveBlock(block *types.Block) error {
|
|
hash, err := block.Hash()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
key := blockKey(hash)
|
|
encodedState, err := block.Marshal()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return b.db.Put(key, encodedState)
|
|
}
|
|
|
|
// saveCanonicalSlotNumber saves the slotnumber and blockhash of a canonical block
|
|
// saved in the db. This will alow for canonical blocks to be retrieved from the db
|
|
// by using their slotnumber as a key, and then using the retrieved blockhash to get
|
|
// the block from the db.
|
|
// prefix + slotnumber -> blockhash
|
|
// prefix + blockhash -> block
|
|
func (b *BeaconChain) saveCanonicalSlotNumber(slotnumber uint64, hash [32]byte) error {
|
|
return b.db.Put(canonicalBlockKey(slotnumber), hash[:])
|
|
}
|
|
|
|
// saveCanonicalBlock puts the passed block into the beacon chain db
|
|
// and also saves a "latest-head" key mapping to the block in the db.
|
|
func (b *BeaconChain) saveCanonicalBlock(block *types.Block) error {
|
|
if err := b.saveBlock(block); err != nil {
|
|
return err
|
|
}
|
|
enc, err := block.Marshal()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return b.db.Put(canonicalHeadLookupKey, enc)
|
|
}
|
|
|
|
// getBlock retrieves a block from the db using its hash.
|
|
func (b *BeaconChain) getBlock(hash [32]byte) (*types.Block, error) {
|
|
key := blockKey(hash)
|
|
enc, err := b.db.Get(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
block := &pb.BeaconBlock{}
|
|
|
|
err = proto.Unmarshal(enc, block)
|
|
|
|
return types.NewBlock(block), err
|
|
}
|
|
|
|
// removeBlock removes the block from the db.
|
|
func (b *BeaconChain) removeBlock(hash [32]byte) error {
|
|
return b.db.Delete(blockKey(hash))
|
|
}
|
|
|
|
// hasCanonicalBlockForSlot checks the db if the canonical block for
|
|
// this slot exists.
|
|
func (b *BeaconChain) hasCanonicalBlockForSlot(slotnumber uint64) (bool, error) {
|
|
return b.db.Has(canonicalBlockKey(slotnumber))
|
|
}
|
|
|
|
// getCanonicalBlockForSlot retrieves the canonical block which is saved in the db
|
|
// for that required slot number.
|
|
func (b *BeaconChain) getCanonicalBlockForSlot(slotNumber uint64) (*types.Block, error) {
|
|
enc, err := b.db.Get(canonicalBlockKey(slotNumber))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var blockhash [32]byte
|
|
copy(blockhash[:], enc)
|
|
|
|
block, err := b.getBlock(blockhash)
|
|
|
|
return block, err
|
|
}
|
|
|
|
func (b *BeaconChain) hasAttestation(attestationHash [32]byte) (bool, error) {
|
|
return b.db.Has(attestationKey(attestationHash))
|
|
}
|
|
|
|
// saveAttestation puts the attestation record into the beacon chain db.
|
|
func (b *BeaconChain) saveAttestation(attestation *types.Attestation) error {
|
|
hash := attestation.Key()
|
|
key := attestationKey(hash)
|
|
encodedState, err := attestation.Marshal()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return b.db.Put(key, encodedState)
|
|
}
|
|
|
|
// getAttestation retrieves an attestation record from the db using its hash.
|
|
func (b *BeaconChain) getAttestation(hash [32]byte) (*types.Attestation, error) {
|
|
key := attestationKey(hash)
|
|
enc, err := b.db.Get(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
attestation := &pb.AttestationRecord{}
|
|
|
|
err = proto.Unmarshal(enc, attestation)
|
|
|
|
return types.NewAttestation(attestation), err
|
|
}
|
|
|
|
// removeAttestation removes the attestation from the db.
|
|
func (b *BeaconChain) removeAttestation(blockHash [32]byte) error {
|
|
return b.db.Delete(attestationKey(blockHash))
|
|
}
|
|
|
|
// hasAttestationHash checks if the beacon block has the attestation.
|
|
func (b *BeaconChain) hasAttestationHash(blockHash [32]byte, attestationHash [32]byte) (bool, error) {
|
|
enc, err := b.db.Get(attestationHashListKey(blockHash))
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
attestationHashes := &pb.AttestationHashes{}
|
|
if err := proto.Unmarshal(enc, attestationHashes); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
for _, hash := range attestationHashes.AttestationHash {
|
|
if bytes.Equal(hash, attestationHash[:]) {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
// hasAttestationHashList checks if the attestation hash list is available.
|
|
func (b *BeaconChain) hasAttestationHashList(blockHash [32]byte) (bool, error) {
|
|
key := attestationHashListKey(blockHash)
|
|
|
|
hasKey, err := b.db.Has(key)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if !hasKey {
|
|
return false, nil
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// getAttestationHashList gets the attestation hash list of the beacon block from the db.
|
|
func (b *BeaconChain) getAttestationHashList(blockHash [32]byte) ([][]byte, error) {
|
|
key := attestationHashListKey(blockHash)
|
|
|
|
hasList, err := b.hasAttestationHashList(blockHash)
|
|
if err != nil {
|
|
return [][]byte{}, err
|
|
}
|
|
if !hasList {
|
|
if err := b.db.Put(key, []byte{}); err != nil {
|
|
return [][]byte{}, err
|
|
}
|
|
}
|
|
enc, err := b.db.Get(key)
|
|
if err != nil {
|
|
return [][]byte{}, err
|
|
}
|
|
|
|
attestationHashes := &pb.AttestationHashes{}
|
|
if err := proto.Unmarshal(enc, attestationHashes); err != nil {
|
|
return [][]byte{}, err
|
|
}
|
|
return attestationHashes.AttestationHash, nil
|
|
}
|
|
|
|
// removeAttestationHashList removes the attestation hash list of the beacon block from the db.
|
|
func (b *BeaconChain) removeAttestationHashList(blockHash [32]byte) error {
|
|
return b.db.Delete(attestationHashListKey(blockHash))
|
|
}
|
|
|
|
// saveAttestationHash saves the attestation hash into the attestation hash list of the corresponding beacon block.
|
|
func (b *BeaconChain) saveAttestationHash(blockHash [32]byte, attestationHash [32]byte) error {
|
|
key := attestationHashListKey(blockHash)
|
|
|
|
hashes, err := b.getAttestationHashList(blockHash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
hashes = append(hashes, attestationHash[:])
|
|
|
|
attestationHashes := &pb.AttestationHashes{}
|
|
attestationHashes.AttestationHash = hashes
|
|
|
|
encodedState, err := proto.Marshal(attestationHashes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return b.db.Put(key, encodedState)
|
|
}
|