Merge pull request #1927 from rlan35/staking_devnet_fixes

Add epoch number in shard state for use of beacon epoch sync
pull/1931/head
Rongjian Lan 5 years ago committed by GitHub
commit 1f5ca9c661
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      api/proto/node/node.go
  2. 4
      api/service/explorer/service.go
  3. 6
      api/service/syncing/syncing.go
  4. 6
      block/v0/header.go
  5. 6
      block/v1/header.go
  6. 6
      block/v2/header.go
  7. 6
      block/v3/header.go
  8. 9
      consensus/consensus_v2.go
  9. 2
      consensus/engine/consensus_engine.go
  10. 94
      core/blockchain.go
  11. 2
      core/chain_makers.go
  12. 4
      core/genesis.go
  13. 20
      core/rawdb/accessors_chain.go
  14. 2
      core/state_transition.go
  15. 15
      core/types/block.go
  16. 10
      hmy/api_backend.go
  17. 4
      internal/chain/engine.go
  18. 5
      internal/chain/reward.go
  19. 2
      internal/hmyapi/backend.go
  20. 2
      internal/hmyapi/blockchain.go
  21. 31
      node/node_cross_shard.go
  22. 2
      node/node_explorer.go
  23. 10
      node/node_genesis.go
  24. 2
      node/node_handler.go
  25. 18
      node/node_newblock.go
  26. 2
      node/node_resharding.go
  27. 47
      node/worker/worker.go
  28. 56
      shard/committee/assignment.go
  29. 93
      shard/shard_state.go
  30. 8
      shard/shard_state_test.go
  31. 26
      staking/types/validator.go

@ -174,6 +174,9 @@ func ConstructCrossLinkMessage(bc engine.ChainReader, headers []*block.Header) [
crosslinks := []types.CrossLink{} crosslinks := []types.CrossLink{}
for _, header := range headers { for _, header := range headers {
if header.Number().Uint64() <= 1 || !bc.Config().IsCrossLink(header.Epoch()) {
continue
}
parentHeader := bc.GetHeaderByHash(header.ParentHash()) parentHeader := bc.GetHeaderByHash(header.ParentHash())
if parentHeader == nil { if parentHeader == nil {
continue continue

@ -269,7 +269,7 @@ func (s *Service) GetExplorerBlocks(w http.ResponseWriter, r *http.Request) {
if accountBlocks[id-1] != nil { if accountBlocks[id-1] != nil {
state, err := accountBlocks[id-1].Header().GetShardState() state, err := accountBlocks[id-1].Header().GetShardState()
if err == nil { if err == nil {
for _, shardCommittee := range state { for _, shardCommittee := range state.Shards {
if shardCommittee.ShardID == accountBlock.ShardID() { if shardCommittee.ShardID == accountBlock.ShardID() {
committee = &shardCommittee committee = &shardCommittee
break break
@ -392,7 +392,7 @@ func (s *ServiceAPI) GetExplorerBlocks(ctx context.Context, from, to, page, offs
if accountBlocks[id-1] != nil { if accountBlocks[id-1] != nil {
state, err := accountBlocks[id-1].Header().GetShardState() state, err := accountBlocks[id-1].Header().GetShardState()
if err == nil { if err == nil {
for _, shardCommittee := range state { for _, shardCommittee := range state.Shards {
if shardCommittee.ShardID == accountBlock.ShardID() { if shardCommittee.ShardID == accountBlock.ShardID() {
committee = &shardCommittee committee = &shardCommittee
break break

@ -114,10 +114,14 @@ type StateSync struct {
} }
func (ss *StateSync) purgeAllBlocksFromCache() { func (ss *StateSync) purgeAllBlocksFromCache() {
ss.lastMileMux.Lock()
ss.lastMileBlocks = nil
ss.lastMileMux.Unlock()
ss.syncMux.Lock() ss.syncMux.Lock()
defer ss.syncMux.Unlock() defer ss.syncMux.Unlock()
ss.commonBlocks = make(map[int]*types.Block) ss.commonBlocks = make(map[int]*types.Block)
ss.lastMileBlocks = nil
ss.syncConfig.ForEachPeer(func(configPeer *SyncPeerConfig) (brk bool) { ss.syncConfig.ForEachPeer(func(configPeer *SyncPeerConfig) (brk bool) {
configPeer.blockHashes = nil configPeer.blockHashes = nil
configPeer.newBlocks = nil configPeer.newBlocks = nil

@ -418,7 +418,11 @@ func (h *Header) Logger(logger *zerolog.Logger) *zerolog.Logger {
// GetShardState returns the deserialized shard state object. // GetShardState returns the deserialized shard state object.
func (h *Header) GetShardState() (shard.State, error) { func (h *Header) GetShardState() (shard.State, error) {
return shard.DecodeWrapper(h.ShardState()) state, err := shard.DecodeWrapper(h.ShardState())
if err != nil {
return shard.State{}, err
}
return *state, nil
} }
// Copy returns a copy of the given header. // Copy returns a copy of the given header.

@ -406,7 +406,11 @@ func (h *Header) Logger(logger *zerolog.Logger) *zerolog.Logger {
// GetShardState returns the deserialized shard state object. // GetShardState returns the deserialized shard state object.
func (h *Header) GetShardState() (shard.State, error) { func (h *Header) GetShardState() (shard.State, error) {
return shard.DecodeWrapper(h.ShardState()) state, err := shard.DecodeWrapper(h.ShardState())
if err != nil {
return shard.State{}, err
}
return *state, nil
} }
// Copy returns a copy of the given header. // Copy returns a copy of the given header.

@ -402,7 +402,11 @@ func (h *Header) Logger(logger *zerolog.Logger) *zerolog.Logger {
// GetShardState returns the deserialized shard state object. // GetShardState returns the deserialized shard state object.
func (h *Header) GetShardState() (shard.State, error) { func (h *Header) GetShardState() (shard.State, error) {
return shard.DecodeWrapper(h.ShardState()) state, err := shard.DecodeWrapper(h.ShardState())
if err != nil {
return shard.State{}, err
}
return *state, nil
} }
// Copy returns a copy of the given header. // Copy returns a copy of the given header.

@ -406,7 +406,11 @@ func (h *Header) Logger(logger *zerolog.Logger) *zerolog.Logger {
// GetShardState returns the deserialized shard state object. // GetShardState returns the deserialized shard state object.
func (h *Header) GetShardState() (shard.State, error) { func (h *Header) GetShardState() (shard.State, error) {
return shard.DecodeWrapper(h.ShardState()) state, err := shard.DecodeWrapper(h.ShardState())
if err != nil {
return shard.State{}, err
}
return *state, nil
} }
// Copy returns a copy of the given header. // Copy returns a copy of the given header.

@ -215,8 +215,8 @@ func (consensus *Consensus) onAnnounce(msg *msg_pb.Message) {
if recvMsg.BlockNum < consensus.blockNum || recvMsg.BlockNum != header.Number().Uint64() { if recvMsg.BlockNum < consensus.blockNum || recvMsg.BlockNum != header.Number().Uint64() {
utils.Logger().Debug(). utils.Logger().Debug().
Uint64("MsgBlockNum", recvMsg.BlockNum). Uint64("MsgBlockNum", recvMsg.BlockNum).
Uint64("blockNum", consensus.blockNum).
Uint64("hdrBlockNum", header.Number().Uint64()). Uint64("hdrBlockNum", header.Number().Uint64()).
Uint64("consensuBlockNum", consensus.blockNum).
Msg("[OnAnnounce] BlockNum does not match") Msg("[OnAnnounce] BlockNum does not match")
return return
} }
@ -789,10 +789,11 @@ func (consensus *Consensus) finalizeCommits() {
consensus.ChainReader.WriteLastCommits(pbftMsg.Payload) consensus.ChainReader.WriteLastCommits(pbftMsg.Payload)
// find correct block content // find correct block content
block := consensus.FBFTLog.GetBlockByHash(consensus.blockHash) curBlockHash := consensus.blockHash
block := consensus.FBFTLog.GetBlockByHash(curBlockHash)
if block == nil { if block == nil {
utils.Logger().Warn(). utils.Logger().Warn().
Str("blockHash", hex.EncodeToString(consensus.blockHash[:])). Str("blockHash", hex.EncodeToString(curBlockHash[:])).
Msg("[FinalizeCommits] Cannot find block by hash") Msg("[FinalizeCommits] Cannot find block by hash")
return return
} }
@ -814,7 +815,7 @@ func (consensus *Consensus) finalizeCommits() {
utils.Logger().Warn().Err(err).Msg("[Finalizing] Cannot send committed message") utils.Logger().Warn().Err(err).Msg("[Finalizing] Cannot send committed message")
} else { } else {
utils.Logger().Info(). utils.Logger().Info().
Hex("blockHash", consensus.blockHash[:]). Hex("blockHash", curBlockHash[:]).
Uint64("blockNum", consensus.blockNum). Uint64("blockNum", consensus.blockNum).
Msg("[Finalizing] Sent Committed Message") Msg("[Finalizing] Sent Committed Message")
} }

@ -40,7 +40,7 @@ type ChainReader interface {
// ReadShardState retrieves sharding state given the epoch number. // ReadShardState retrieves sharding state given the epoch number.
// This api reads the shard state cached or saved on the chaindb. // This api reads the shard state cached or saved on the chaindb.
// Thus, only should be used to read the shard state of the current chain. // Thus, only should be used to read the shard state of the current chain.
ReadShardState(epoch *big.Int) (shard.State, error) ReadShardState(epoch *big.Int) (*shard.State, error)
// ReadActiveValidatorList retrieves the list of active validators // ReadActiveValidatorList retrieves the list of active validators
ReadActiveValidatorList() ([]common.Address, error) ReadActiveValidatorList() ([]common.Address, error)

@ -68,7 +68,7 @@ const (
maxTimeFutureBlocks = 30 maxTimeFutureBlocks = 30
badBlockLimit = 10 badBlockLimit = 10
triesInMemory = 128 triesInMemory = 128
shardCacheLimit = 4 shardCacheLimit = 10
commitsCacheLimit = 10 commitsCacheLimit = 10
epochCacheLimit = 10 epochCacheLimit = 10
randomnessCacheLimit = 10 randomnessCacheLimit = 10
@ -76,7 +76,7 @@ const (
validatorStatsCacheLimit = 1024 validatorStatsCacheLimit = 1024
validatorListCacheLimit = 10 validatorListCacheLimit = 10
validatorListByDelegatorCacheLimit = 1024 validatorListByDelegatorCacheLimit = 1024
pendingCrossLinksCacheLimit = 10 pendingCrossLinksCacheLimit = 2
// BlockChainVersion ensures that an incompatible database forces a resync from scratch. // BlockChainVersion ensures that an incompatible database forces a resync from scratch.
BlockChainVersion = 3 BlockChainVersion = 3
@ -1156,33 +1156,11 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
return NonStatTy, err return NonStatTy, err
} }
// Staking epoch migration. Only Execute once:
// Normally the last block of an epoch should have the same epoch as this block
// In the case beacon chain catch up, it may have a epoch larger than the current epoch
// We need to write the same shard state for this one-off epoch so other readers doesn't break
parentHeader := bc.GetHeaderByHash(header.ParentHash())
if parentHeader != nil && parentHeader.Epoch().Cmp(header.Epoch()) != 0 {
curShardState, err := bc.ReadShardState(parentHeader.Epoch())
if err != nil {
header.Logger(utils.Logger()).Warn().Err(err).Msg("cannot read current shard state")
return NonStatTy, err
}
data, err := rlp.EncodeToBytes(curShardState)
if err != nil {
return NonStatTy, err
}
_, err = bc.WriteShardStateBytes(batch, header.Epoch(), data)
if err != nil {
header.Logger(utils.Logger()).Warn().Err(err).Msg("cannot store shard state")
return NonStatTy, err
}
}
// Find all the active validator addresses and store them in db // Find all the active validator addresses and store them in db
allActiveValidators := []common.Address{} allActiveValidators := []common.Address{}
processed := make(map[common.Address]struct{}) processed := make(map[common.Address]struct{})
for i := range *newShardState { for i := range newShardState.Shards {
shard := (*newShardState)[i] shard := newShardState.Shards[i]
for j := range shard.Slots { for j := range shard.Slots {
slot := shard.Slots[j] slot := shard.Slots[j]
if slot.TotalStake != nil { // For external validator if slot.TotalStake != nil { // For external validator
@ -1219,13 +1197,14 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
// Update voting power of validators for all shards // Update voting power of validators for all shards
if block.ShardID() == 0 && len(block.Header().ShardState()) > 0 { if block.ShardID() == 0 && len(block.Header().ShardState()) > 0 {
shardState := shard.State{} shardState := new(shard.State)
if err := rlp.DecodeBytes(block.Header().ShardState(), &shardState); err == nil {
if shardState, err = shard.DecodeWrapper(block.Header().ShardState()); err == nil {
if err = bc.UpdateValidatorVotingPower(shardState); err != nil { if err = bc.UpdateValidatorVotingPower(shardState); err != nil {
utils.Logger().Err(err) utils.Logger().Err(err).Msg("[UpdateValidatorVotingPower] Failed to update voting power")
} }
} else { } else {
utils.Logger().Err(err) utils.Logger().Err(err).Msg("[UpdateValidatorVotingPower] Failed to decode shard state")
} }
} }
@ -2007,10 +1986,10 @@ func (bc *BlockChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscript
} }
// ReadShardState retrieves sharding state given the epoch number. // ReadShardState retrieves sharding state given the epoch number.
func (bc *BlockChain) ReadShardState(epoch *big.Int) (shard.State, error) { func (bc *BlockChain) ReadShardState(epoch *big.Int) (*shard.State, error) {
cacheKey := string(epoch.Bytes()) cacheKey := string(epoch.Bytes())
if cached, ok := bc.shardStateCache.Get(cacheKey); ok { if cached, ok := bc.shardStateCache.Get(cacheKey); ok {
shardState := cached.(shard.State) shardState := cached.(*shard.State)
return shardState, nil return shardState, nil
} }
shardState, err := rawdb.ReadShardState(bc.db, epoch) shardState, err := rawdb.ReadShardState(bc.db, epoch)
@ -2021,22 +2000,6 @@ func (bc *BlockChain) ReadShardState(epoch *big.Int) (shard.State, error) {
return shardState, nil return shardState, nil
} }
// WriteShardState saves the given sharding state under the given epoch number.
func (bc *BlockChain) WriteShardState(
epoch *big.Int, shardState shard.State,
) error {
shardState = shardState.DeepCopy()
err := rawdb.WriteShardState(
bc.db, epoch, shardState, bc.Config().IsStaking(epoch),
)
if err != nil {
return err
}
cacheKey := string(epoch.Bytes())
bc.shardStateCache.Add(cacheKey, shardState)
return nil
}
// WriteShardStateBytes saves the given sharding state under the given epoch number. // WriteShardStateBytes saves the given sharding state under the given epoch number.
func (bc *BlockChain) WriteShardStateBytes(db rawdb.DatabaseWriter, func (bc *BlockChain) WriteShardStateBytes(db rawdb.DatabaseWriter,
epoch *big.Int, shardState []byte, epoch *big.Int, shardState []byte,
@ -2051,7 +2014,7 @@ func (bc *BlockChain) WriteShardStateBytes(db rawdb.DatabaseWriter,
} }
cacheKey := string(epoch.Bytes()) cacheKey := string(epoch.Bytes())
bc.shardStateCache.Add(cacheKey, decodeShardState) bc.shardStateCache.Add(cacheKey, decodeShardState)
return &decodeShardState, nil return decodeShardState, nil
} }
// ReadLastCommits retrieves last commits. // ReadLastCommits retrieves last commits.
@ -2574,9 +2537,8 @@ func (bc *BlockChain) ReadValidatorSnapshot(addr common.Address) (*staking.Valid
return rawdb.ReadValidatorSnapshot(bc.db, addr) return rawdb.ReadValidatorSnapshot(bc.db, addr)
} }
// WriteValidatorSnapshots writes the snapshot of provided list of validators // writeValidatorSnapshots writes the snapshot of provided list of validators
// Note: this should only be called within the blockchain insertBlock process. func (bc *BlockChain) writeValidatorSnapshots(addrs []common.Address) error {
func (bc *BlockChain) WriteValidatorSnapshots(addrs []common.Address) error {
// Read all validator's current data // Read all validator's current data
validators := []*staking.ValidatorWrapper{} validators := []*staking.ValidatorWrapper{}
for _, addr := range addrs { for _, addr := range addrs {
@ -2662,11 +2624,15 @@ func (bc *BlockChain) UpdateValidatorUptime(slots shard.SlotList, mask *bls.Mask
} }
// UpdateValidatorVotingPower writes the voting power for the committees // UpdateValidatorVotingPower writes the voting power for the committees
func (bc *BlockChain) UpdateValidatorVotingPower(state shard.State) error { func (bc *BlockChain) UpdateValidatorVotingPower(state *shard.State) error {
if state == nil {
return errors.New("[UpdateValidatorVotingPower] Nil shard state")
}
totalEffectiveStake := make(map[uint32]numeric.Dec) totalEffectiveStake := make(map[uint32]numeric.Dec)
addrToEffectiveStakes := make(map[common.Address]map[uint32]numeric.Dec) addrToEffectiveStakes := make(map[common.Address]map[uint32]numeric.Dec)
for _, committee := range state { for _, committee := range state.Shards {
for _, slot := range committee.Slots { for _, slot := range committee.Slots {
if slot.TotalStake != nil { if slot.TotalStake != nil {
if _, ok := addrToEffectiveStakes[slot.EcdsaAddress]; !ok { if _, ok := addrToEffectiveStakes[slot.EcdsaAddress]; !ok {
@ -2699,7 +2665,7 @@ func (bc *BlockChain) UpdateValidatorVotingPower(state shard.State) error {
stats = &staking.ValidatorStats{big.NewInt(0), big.NewInt(0), big.NewInt(0), numeric.NewDec(0), numeric.NewDec(0)} stats = &staking.ValidatorStats{big.NewInt(0), big.NewInt(0), big.NewInt(0), numeric.NewDec(0), numeric.NewDec(0)}
} }
stats.AvgVotingPower = addrTotalVotingPower.Quo(numeric.NewDec(int64(len(state)))) stats.AvgVotingPower = addrTotalVotingPower.Quo(numeric.NewDec(int64(len(state.Shards))))
stats.TotalEffectiveStake = addrTotalEffectiveStake stats.TotalEffectiveStake = addrTotalEffectiveStake
err = rawdb.WriteValidatorStats(batch, addr, stats) err = rawdb.WriteValidatorStats(batch, addr, stats)
@ -2714,9 +2680,8 @@ func (bc *BlockChain) UpdateValidatorVotingPower(state shard.State) error {
return nil return nil
} }
// DeleteValidatorSnapshots deletes the snapshot staking information of given validator address // deleteValidatorSnapshots deletes the snapshot staking information of given validator address
// Note: this should only be called within the blockchain insertBlock process. func (bc *BlockChain) deleteValidatorSnapshots(addrs []common.Address) error {
func (bc *BlockChain) DeleteValidatorSnapshots(addrs []common.Address) error {
batch := bc.db.NewBatch() batch := bc.db.NewBatch()
for i := range addrs { for i := range addrs {
rawdb.DeleteValidatorSnapshot(batch, addrs[i]) rawdb.DeleteValidatorSnapshot(batch, addrs[i])
@ -2739,12 +2704,12 @@ func (bc *BlockChain) UpdateValidatorSnapshots() error {
} }
// TODO: enable this once we allow validator to delete itself. // TODO: enable this once we allow validator to delete itself.
//err = bc.DeleteValidatorSnapshots(allValidators) //err = bc.deleteValidatorSnapshots(allValidators)
//if err != nil { //if err != nil {
// return err // return err
//} //}
if err := bc.WriteValidatorSnapshots(allValidators); err != nil { if err := bc.writeValidatorSnapshots(allValidators); err != nil {
return err return err
} }
return nil return nil
@ -2817,9 +2782,8 @@ func (bc *BlockChain) ReadDelegationsByDelegator(delegator common.Address) ([]st
return rawdb.ReadDelegationsByDelegator(bc.db, delegator) return rawdb.ReadDelegationsByDelegator(bc.db, delegator)
} }
// WriteDelegationsByDelegator writes the list of validator addresses to database // writeDelegationsByDelegator writes the list of validator addresses to database
// Note: this should only be called within the blockchain insertBlock process. func (bc *BlockChain) writeDelegationsByDelegator(delegator common.Address, indices []staking.DelegationIndex) error {
func (bc *BlockChain) WriteDelegationsByDelegator(delegator common.Address, indices []staking.DelegationIndex) error {
err := rawdb.WriteDelegationsByDelegator(bc.db, delegator, indices) err := rawdb.WriteDelegationsByDelegator(bc.db, delegator, indices)
if err != nil { if err != nil {
return err return err
@ -2893,7 +2857,7 @@ func (bc *BlockChain) BlockRewardAccumulator() (*big.Int, error) {
} }
// WriteBlockRewardAccumulator directly writes the BlockRewardAccumulator value // WriteBlockRewardAccumulator directly writes the BlockRewardAccumulator value
// Note: this should only be called within the blockchain insertBlock process. // Note: this should only be called once during staking launch.
func (bc *BlockChain) WriteBlockRewardAccumulator(reward *big.Int) error { func (bc *BlockChain) WriteBlockRewardAccumulator(reward *big.Int) error {
return rawdb.WriteBlockRewardAccumulator(bc.db, reward) return rawdb.WriteBlockRewardAccumulator(bc.db, reward)
} }
@ -2938,7 +2902,7 @@ func (bc *BlockChain) addDelegationIndex(delegatorAddress, validatorAddress comm
}) })
} }
} }
return bc.WriteDelegationsByDelegator(delegatorAddress, delegations) return bc.writeDelegationsByDelegator(delegatorAddress, delegations)
} }
// ValidatorCandidates returns the up to date validator candidates for next epoch // ValidatorCandidates returns the up to date validator candidates for next epoch

@ -269,7 +269,7 @@ func (cr *fakeChainReader) GetHeaderByNumber(number uint64) *block.Header
func (cr *fakeChainReader) GetHeaderByHash(hash common.Hash) *block.Header { return nil } func (cr *fakeChainReader) GetHeaderByHash(hash common.Hash) *block.Header { return nil }
func (cr *fakeChainReader) GetHeader(hash common.Hash, number uint64) *block.Header { return nil } func (cr *fakeChainReader) GetHeader(hash common.Hash, number uint64) *block.Header { return nil }
func (cr *fakeChainReader) GetBlock(hash common.Hash, number uint64) *types.Block { return nil } func (cr *fakeChainReader) GetBlock(hash common.Hash, number uint64) *types.Block { return nil }
func (cr *fakeChainReader) ReadShardState(epoch *big.Int) (shard.State, error) { return nil, nil } func (cr *fakeChainReader) ReadShardState(epoch *big.Int) (*shard.State, error) { return nil, nil }
func (cr *fakeChainReader) ReadActiveValidatorList() ([]common.Address, error) { return nil, nil } func (cr *fakeChainReader) ReadActiveValidatorList() ([]common.Address, error) { return nil, nil }
func (cr *fakeChainReader) ValidatorCandidates() []common.Address { return nil } func (cr *fakeChainReader) ValidatorCandidates() []common.Address { return nil }
func (cr *fakeChainReader) ReadValidatorInformation(addr common.Address) (*staking.ValidatorWrapper, error) { func (cr *fakeChainReader) ReadValidatorInformation(addr common.Address) (*staking.ValidatorWrapper, error) {

@ -29,8 +29,6 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp"
blockfactory "github.com/harmony-one/harmony/block/factory" blockfactory "github.com/harmony-one/harmony/block/factory"
"github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/params"
@ -249,7 +247,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
} }
} }
root := statedb.IntermediateRoot(false) root := statedb.IntermediateRoot(false)
shardStateBytes, err := rlp.EncodeToBytes(g.ShardState) shardStateBytes, err := shard.EncodeWrapper(g.ShardState, false)
if err != nil { if err != nil {
utils.Logger().Error().Msg("failed to rlp-serialize genesis shard state") utils.Logger().Error().Msg("failed to rlp-serialize genesis shard state")
os.Exit(1) os.Exit(1)

@ -417,7 +417,7 @@ func FindCommonAncestor(db DatabaseReader, a, b *block.Header) *block.Header {
// ReadShardState retrieves sharding state. // ReadShardState retrieves sharding state.
func ReadShardState( func ReadShardState(
db DatabaseReader, epoch *big.Int, db DatabaseReader, epoch *big.Int,
) (shard.State, error) { ) (*shard.State, error) {
data, err := db.Get(shardStateKey(epoch)) data, err := db.Get(shardStateKey(epoch))
if err != nil { if err != nil {
return nil, ctxerror.New(MsgNoShardStateFromDB, return nil, ctxerror.New(MsgNoShardStateFromDB,
@ -433,20 +433,6 @@ func ReadShardState(
return ss, nil return ss, nil
} }
// WriteShardState stores sharding state into database.
func WriteShardState(
db DatabaseWriter, epoch *big.Int,
shardState shard.State, isStaking bool,
) error {
data, err := shard.EncodeWrapper(shardState, isStaking)
if err != nil {
return ctxerror.New("cannot encode sharding state",
"epoch", epoch,
).WithCause(err)
}
return WriteShardStateBytes(db, epoch, data)
}
// WriteShardStateBytes stores sharding state into database. // WriteShardStateBytes stores sharding state into database.
func WriteShardStateBytes(db DatabaseWriter, epoch *big.Int, data []byte) (err error) { func WriteShardStateBytes(db DatabaseWriter, epoch *big.Int, data []byte) (err error) {
if err = db.Put(shardStateKey(epoch), data); err != nil { if err = db.Put(shardStateKey(epoch), data); err != nil {
@ -771,10 +757,10 @@ func ReadDelegationsByDelegator(db DatabaseReader, delegator common.Address) ([]
func WriteDelegationsByDelegator(db DatabaseWriter, delegator common.Address, indices []staking.DelegationIndex) error { func WriteDelegationsByDelegator(db DatabaseWriter, delegator common.Address, indices []staking.DelegationIndex) error {
bytes, err := rlp.EncodeToBytes(indices) bytes, err := rlp.EncodeToBytes(indices)
if err != nil { if err != nil {
utils.Logger().Error().Msg("[WriteDelegationsByDelegator] Failed to encode") utils.Logger().Error().Msg("[writeDelegationsByDelegator] Failed to encode")
} }
if err := db.Put(delegatorValidatorListKey(delegator), bytes); err != nil { if err := db.Put(delegatorValidatorListKey(delegator), bytes); err != nil {
utils.Logger().Error().Msg("[WriteDelegationsByDelegator] Failed to store to database") utils.Logger().Error().Msg("[writeDelegationsByDelegator] Failed to store to database")
} }
return err return err
} }

@ -419,7 +419,7 @@ func (st *StateTransition) applyEditValidatorTx(editValidator *staking.EditValid
return errCommissionRateChangeTooHigh return errCommissionRateChangeTooHigh
} }
if err := st.state.UpdateStakingInfo(editValidator.ValidatorAddress, wrapper); err != nil { if err := st.state.UpdateStakingInfo(wrapper.Address, wrapper); err != nil {
return err return err
} }
return nil return nil

@ -38,7 +38,6 @@ import (
v3 "github.com/harmony-one/harmony/block/v3" v3 "github.com/harmony-one/harmony/block/v3"
"github.com/harmony-one/harmony/crypto/hash" "github.com/harmony-one/harmony/crypto/hash"
"github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/shard"
staking "github.com/harmony-one/harmony/staking/types" staking "github.com/harmony-one/harmony/staking/types"
"github.com/harmony-one/taggedrlp" "github.com/harmony-one/taggedrlp"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -593,20 +592,6 @@ func (b *Block) AddVdf(vdf []byte) {
b.header.SetVdf(vdf) b.header.SetVdf(vdf)
} }
// AddShardState add shardState into block header
func (b *Block) AddShardState(shardState shard.State) error {
// Make a copy because State.Hash() internally sorts entries.
// Store the sorted copy.
shardState = append(shardState[:0:0], shardState...)
b.header.SetShardStateHash(shardState.Hash())
data, err := rlp.EncodeToBytes(shardState)
if err != nil {
return err
}
b.header.SetShardState(data)
return nil
}
// Logger returns a sub-logger with block contexts added. // Logger returns a sub-logger with block contexts added.
func (b *Block) Logger(logger *zerolog.Logger) *zerolog.Logger { func (b *Block) Logger(logger *zerolog.Logger) *zerolog.Logger {
return b.header.Logger(logger) return b.header.Logger(logger)

@ -247,7 +247,7 @@ func (b *APIBackend) GetValidators(epoch *big.Int) (*shard.Committee, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, committee := range state { for _, committee := range state.Shards {
if committee.ShardID == b.GetShardID() { if committee.ShardID == b.GetShardID() {
return &committee, nil return &committee, nil
} }
@ -364,10 +364,6 @@ func (b *APIBackend) GetValidatorSelfDelegation(addr common.Address) *big.Int {
} }
// GetShardState ... // GetShardState ...
func (b *APIBackend) GetShardState() (shard.State, error) { func (b *APIBackend) GetShardState() (*shard.State, error) {
state, err := b.hmy.BlockChain().ReadShardState(b.hmy.BlockChain().CurrentHeader().Epoch()) return b.hmy.BlockChain().ReadShardState(b.hmy.BlockChain().CurrentHeader().Epoch())
if err != nil {
return nil, err
}
return state, nil
} }

@ -235,7 +235,7 @@ func (e *engineImpl) Finalize(
func QuorumForBlock( func QuorumForBlock(
chain engine.ChainReader, h *block.Header, reCalculate bool, chain engine.ChainReader, h *block.Header, reCalculate bool,
) (quorum int, err error) { ) (quorum int, err error) {
var ss shard.State ss := new(shard.State)
if reCalculate { if reCalculate {
ss, _ = committee.WithStakingEnabled.Compute(h.Epoch(), chain) ss, _ = committee.WithStakingEnabled.Compute(h.Epoch(), chain)
} else { } else {
@ -297,7 +297,7 @@ func (e *engineImpl) VerifyHeaderWithSignature(chain engine.ChainReader, header
// GetPublicKeys finds the public keys of the committee that signed the block header // GetPublicKeys finds the public keys of the committee that signed the block header
func GetPublicKeys(chain engine.ChainReader, header *block.Header, reCalculate bool) ([]*bls.PublicKey, error) { func GetPublicKeys(chain engine.ChainReader, header *block.Header, reCalculate bool) ([]*bls.PublicKey, error) {
var shardState shard.State shardState := new(shard.State)
var err error var err error
if reCalculate { if reCalculate {
shardState, _ = committee.WithStakingEnabled.Compute(header.Epoch(), chain) shardState, _ = committee.WithStakingEnabled.Compute(header.Epoch(), chain)

@ -4,8 +4,6 @@ import (
"math/big" "math/big"
"sort" "sort"
"github.com/harmony-one/harmony/shard/committee"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/bls/ffi/go/bls" "github.com/harmony-one/bls/ffi/go/bls"
@ -258,9 +256,6 @@ func AccumulateRewards(
for i := range crossLinks { for i := range crossLinks {
cxLink := crossLinks[i] cxLink := crossLinks[i]
shardState, err := bc.ReadShardState(cxLink.Epoch()) shardState, err := bc.ReadShardState(cxLink.Epoch())
if !bc.Config().IsStaking(cxLink.Epoch()) {
shardState, err = committee.WithStakingEnabled.Compute(cxLink.Epoch(), bc)
}
if err != nil { if err != nil {
return err return err

@ -80,7 +80,7 @@ type Backend interface {
GetDelegationsByValidator(validator common.Address) []*staking.Delegation GetDelegationsByValidator(validator common.Address) []*staking.Delegation
GetDelegationsByDelegator(delegator common.Address) ([]common.Address, []*staking.Delegation) GetDelegationsByDelegator(delegator common.Address) ([]common.Address, []*staking.Delegation)
GetValidatorSelfDelegation(addr common.Address) *big.Int GetValidatorSelfDelegation(addr common.Address) *big.Int
GetShardState() (shard.State, error) GetShardState() (*shard.State, error)
} }
// GetAPIs returns all the APIs. // GetAPIs returns all the APIs.

@ -523,7 +523,7 @@ func (s *PublicBlockChainAPI) GetValidatorInformation(ctx context.Context, addre
if err == nil { if err == nil {
blsKeyToShardID := make(map[shard.BlsPublicKey]uint32) blsKeyToShardID := make(map[shard.BlsPublicKey]uint32)
for _, committee := range shardState { for _, committee := range shardState.Shards {
for _, slot := range committee.Slots { for _, slot := range committee.Slots {
blsKeyToShardID[slot.BlsPublicKey] = committee.ShardID blsKeyToShardID[slot.BlsPublicKey] = committee.ShardID
} }

@ -156,12 +156,7 @@ func (node *Node) ProcessCrossLinkMessage(msgPayload []byte) {
utils.Logger().Debug(). utils.Logger().Debug().
Msgf("[ProcessingCrossLink] Crosslink going to propose: %d", len(crosslinks)) Msgf("[ProcessingCrossLink] Crosslink going to propose: %d", len(crosslinks))
for i, cl := range crosslinks { for _, cl := range crosslinks {
if cl.Number() == nil || cl.Epoch().Cmp(node.Blockchain().Config().CrossLinkEpoch) < 0 {
utils.Logger().Debug().
Msgf("[ProcessingCrossLink] Crosslink %d skipped: %v", i, cl)
continue
}
exist, err := node.Blockchain().ReadCrossLink(cl.ShardID(), cl.Number().Uint64()) exist, err := node.Blockchain().ReadCrossLink(cl.ShardID(), cl.Number().Uint64())
if err == nil && exist != nil { if err == nil && exist != nil {
// TODO: leader add double sign checking // TODO: leader add double sign checking
@ -170,13 +165,12 @@ func (node *Node) ProcessCrossLinkMessage(msgPayload []byte) {
continue continue
} }
err = node.VerifyCrossLink(cl) if err = node.VerifyCrossLink(cl); err != nil {
if err != nil { utils.Logger().Debug().
utils.Logger().Error(). Msgf("[ProcessingCrossLink] Failed to verify new cross link for blockNum %d epochNum %d shard %d skipped: %v", cl.BlockNum(), cl.Epoch().Uint64(), cl.ShardID(), cl)
Err(err).
Msgf("[ProcessingCrossLink] Failed to verify new cross link for shardID %d, blockNum %d", cl.ShardID(), cl.Number())
continue continue
} }
candidates = append(candidates, cl) candidates = append(candidates, cl)
utils.Logger().Debug(). utils.Logger().Debug().
Msgf("[ProcessingCrossLink] committing for shardID %d, blockNum %d", cl.ShardID(), cl.Number().Uint64()) Msgf("[ProcessingCrossLink] committing for shardID %d, blockNum %d", cl.ShardID(), cl.Number().Uint64())
@ -228,8 +222,17 @@ func (node *Node) verifyIncomingReceipts(block *types.Block) error {
// VerifyCrossLink verifies the header is valid // VerifyCrossLink verifies the header is valid
func (node *Node) VerifyCrossLink(cl types.CrossLink) error { func (node *Node) VerifyCrossLink(cl types.CrossLink) error {
if node.Blockchain().ShardID() != shard.BeaconChainShardID {
return ctxerror.New("Shard chains should not verify cross links")
}
// TODO: add fork choice rule if cl.BlockNum() <= 1 {
return ctxerror.New("CrossLink BlockNumber should greater than 1")
}
if node.Blockchain().Config().IsCrossLink(cl.Epoch()) {
return ctxerror.New("CrossLink Epoch should >= crosslink epoch", "crossLinkEpoch", node.Blockchain().Config().CrossLinkEpoch)
}
// Verify signature of the new cross link header // Verify signature of the new cross link header
// TODO: check whether to recalculate shard state // TODO: check whether to recalculate shard state
@ -255,10 +258,6 @@ func (node *Node) VerifyCrossLink(cl types.CrossLink) error {
return ctxerror.New("[CrossLink] cannot convert BLS public key", "shardID", cl.ShardID(), "blockNum", cl.BlockNum()).WithCause(err) return ctxerror.New("[CrossLink] cannot convert BLS public key", "shardID", cl.ShardID(), "blockNum", cl.BlockNum()).WithCause(err)
} }
if cl.BlockNum() <= 1 {
return ctxerror.New("CrossLink BlockNumber should greater than 1")
}
mask, err := bls_cosi.NewMask(committerKeys, nil) mask, err := bls_cosi.NewMask(committerKeys, nil)
if err != nil { if err != nil {
return ctxerror.New("cannot create group sig mask", "shardID", cl.ShardID(), "blockNum", cl.BlockNum()).WithCause(err) return ctxerror.New("cannot create group sig mask", "shardID", cl.ShardID(), "blockNum", cl.BlockNum()).WithCause(err)

@ -192,7 +192,7 @@ func (node *Node) CommitCommittee() {
utils.Logger().Error().Err(err).Msg("[Explorer] Error reading shard state") utils.Logger().Error().Err(err).Msg("[Explorer] Error reading shard state")
continue continue
} }
for _, committee := range state { for _, committee := range state.Shards {
if committee.ShardID == curBlock.ShardID() { if committee.ShardID == curBlock.ShardID() {
utils.Logger().Debug().Msg("[Explorer] Dumping committee") utils.Logger().Debug().Msg("[Explorer] Dumping committee")
err := explorer.GetStorageInstance(node.SelfPeer.IP, node.SelfPeer.Port, false).DumpCommittee(curBlock.ShardID(), curBlock.Epoch().Uint64(), committee) err := explorer.GetStorageInstance(node.SelfPeer.IP, node.SelfPeer.Port, false).DumpCommittee(curBlock.ShardID(), curBlock.Epoch().Uint64(), committee)

@ -48,20 +48,24 @@ func (gi *genesisInitializer) InitChainDB(db ethdb.Database, shardID uint32) err
shardState, _ := committee.WithStakingEnabled.Compute( shardState, _ := committee.WithStakingEnabled.Compute(
big.NewInt(core.GenesisEpoch), nil, big.NewInt(core.GenesisEpoch), nil,
) )
if shardState == nil {
return errors.New("failed to create genesis shard state")
}
if shardID != shard.BeaconChainShardID { if shardID != shard.BeaconChainShardID {
// store only the local shard for shard chains // store only the local shard for shard chains
c := shardState.FindCommitteeByID(shardID) c := shardState.FindCommitteeByID(shardID)
if c == nil { if c == nil {
return errors.New("cannot find local shard in genesis") return errors.New("cannot find local shard in genesis")
} }
shardState = shard.State{*c} shardState = &shard.State{nil, []shard.Committee{*c}}
} }
gi.node.SetupGenesisBlock(db, shardID, shardState) gi.node.SetupGenesisBlock(db, shardID, shardState)
return nil return nil
} }
// SetupGenesisBlock sets up a genesis blockchain. // SetupGenesisBlock sets up a genesis blockchain.
func (node *Node) SetupGenesisBlock(db ethdb.Database, shardID uint32, myShardState shard.State) { func (node *Node) SetupGenesisBlock(db ethdb.Database, shardID uint32, myShardState *shard.State) {
utils.Logger().Info().Interface("shardID", shardID).Msg("setting up a brand new chain database") utils.Logger().Info().Interface("shardID", shardID).Msg("setting up a brand new chain database")
if shardID == node.NodeConfig.ShardID { if shardID == node.NodeConfig.ShardID {
node.isFirstTime = true node.isFirstTime = true
@ -102,7 +106,7 @@ func (node *Node) SetupGenesisBlock(db ethdb.Database, shardID uint32, myShardSt
ShardID: shardID, ShardID: shardID,
GasLimit: params.GenesisGasLimit, GasLimit: params.GenesisGasLimit,
ShardStateHash: myShardState.Hash(), ShardStateHash: myShardState.Hash(),
ShardState: myShardState.DeepCopy(), ShardState: *myShardState.DeepCopy(),
Timestamp: 1561734000, // GMT: Friday, June 28, 2019 3:00:00 PM. PST: Friday, June 28, 2019 8:00:00 AM Timestamp: 1561734000, // GMT: Friday, June 28, 2019 3:00:00 PM. PST: Friday, June 28, 2019 8:00:00 AM
ExtraData: []byte("Harmony for One and All. Open Consensus for 10B."), ExtraData: []byte("Harmony for One and All. Open Consensus for 10B."),
} }

@ -346,7 +346,7 @@ func (node *Node) PostConsensusProcessing(newBlock *types.Block, commitSigAndBit
if node.NodeConfig.ShardID == 0 { if node.NodeConfig.ShardID == 0 {
node.BroadcastNewBlock(newBlock) node.BroadcastNewBlock(newBlock)
} }
if node.NodeConfig.ShardID != shard.BeaconChainShardID && newBlock.Epoch().Cmp(node.Blockchain().Config().CrossLinkEpoch) >= 0 { if node.NodeConfig.ShardID != shard.BeaconChainShardID && node.Blockchain().Config().IsCrossLink(newBlock.Epoch()) {
node.BroadcastCrossLink(newBlock) node.BroadcastCrossLink(newBlock)
} }
node.BroadcastCXReceipts(newBlock, commitSigAndBitmap) node.BroadcastCXReceipts(newBlock, commitSigAndBitmap)

@ -5,6 +5,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/core/rawdb" "github.com/harmony-one/harmony/core/rawdb"
types2 "github.com/harmony-one/harmony/staking/types" types2 "github.com/harmony-one/harmony/staking/types"
@ -80,13 +82,6 @@ func (node *Node) WaitForConsensusReadyV2(readySignal chan struct{}, stopChan ch
func (node *Node) proposeNewBlock() (*types.Block, error) { func (node *Node) proposeNewBlock() (*types.Block, error) {
node.Worker.UpdateCurrent() node.Worker.UpdateCurrent()
// Prepare shard state
// NOTE: this will potentially override shard chain's epoch to beacon chain's epoch during staking migration period.
// So this needs to be executed early on.
shardState, err := node.Worker.SuperCommitteeForNextEpoch(
node.Consensus.ShardID, node.Beaconchain(),
)
// Update worker's current header and state data in preparation to propose/process new transactions // Update worker's current header and state data in preparation to propose/process new transactions
coinbase := node.Consensus.SelfAddress coinbase := node.Consensus.SelfAddress
@ -139,6 +134,9 @@ func (node *Node) proposeNewBlock() (*types.Block, error) {
if err == nil { if err == nil {
for _, pending := range allPending { for _, pending := range allPending {
if err = node.VerifyCrossLink(pending); err != nil {
continue
}
exist, err := node.Blockchain().ReadCrossLink(pending.ShardID(), pending.BlockNum()) exist, err := node.Blockchain().ReadCrossLink(pending.ShardID(), pending.BlockNum())
if err == nil || exist != nil { if err == nil || exist != nil {
continue continue
@ -151,7 +149,11 @@ func (node *Node) proposeNewBlock() (*types.Block, error) {
} }
} }
if err != nil { // Prepare shard state
shardState := new(shard.State)
if shardState, err = node.Worker.SuperCommitteeForNextEpoch(
node.Consensus.ShardID, node.Beaconchain(),
); err != nil {
return nil, err return nil, err
} }

@ -69,7 +69,7 @@ func findRoleInShardState(
key *bls.PublicKey, state shard.State, key *bls.PublicKey, state shard.State,
) (shardID uint32, isLeader bool) { ) (shardID uint32, isLeader bool) {
keyBytes := key.Serialize() keyBytes := key.Serialize()
for idx, shard := range state { for idx, shard := range state.Shards {
for nodeIdx, nodeID := range shard.Slots { for nodeIdx, nodeID := range shard.Slots {
if bytes.Compare(nodeID.BlsPublicKey[:], keyBytes) == 0 { if bytes.Compare(nodeID.BlsPublicKey[:], keyBytes) == 0 {
return uint32(idx), nodeIdx == 0 return uint32(idx), nodeIdx == 0

@ -262,10 +262,15 @@ func (w *Worker) GetNewEpoch() *big.Int {
parent := w.chain.CurrentBlock() parent := w.chain.CurrentBlock()
epoch := new(big.Int).Set(parent.Header().Epoch()) epoch := new(big.Int).Set(parent.Header().Epoch())
// TODO: Don't depend on sharding state for epoch change. shardState, err := parent.Header().GetShardState()
if len(parent.Header().ShardState()) > 0 && parent.NumberU64() != 0 { if err == nil && shardState.Epoch != nil {
// ... except if parent has a resharding assignment it increases by 1. // For shard state of staking epochs, the shard state will have an epoch and it will decide the next epoch for following blocks
epoch = epoch.Add(epoch, common.Big1) epoch = new(big.Int).Set(shardState.Epoch)
} else {
if len(parent.Header().ShardState()) > 0 && parent.NumberU64() != 0 {
// if parent has proposed a new shard state it increases by 1, except for genesis block.
epoch = epoch.Add(epoch, common.Big1)
}
} }
return epoch return epoch
} }
@ -289,9 +294,9 @@ func (w *Worker) IncomingReceipts() []*types.CXReceiptsProof {
func (w *Worker) SuperCommitteeForNextEpoch( func (w *Worker) SuperCommitteeForNextEpoch(
shardID uint32, shardID uint32,
beacon *core.BlockChain, beacon *core.BlockChain,
) (shard.State, error) { ) (*shard.State, error) {
var ( var (
nextCommittee shard.State nextCommittee = new(shard.State)
err error err error
) )
switch shardID { switch shardID {
@ -315,15 +320,11 @@ func (w *Worker) SuperCommitteeForNextEpoch(
beaconEpoch, beacon, beaconEpoch, beacon,
) )
// Set this block's epoch to be beaconEpoch - 1, so the next block will have beaconEpoch
// This shouldn't be exactly beaconEpoch because the next block will have beaconEpoch + 1
blockEpoch := big.NewInt(0).Set(beaconEpoch).Sub(beaconEpoch, big.NewInt(1))
utils.Logger().Debug(). utils.Logger().Debug().
Uint64("blockNum", w.current.header.Number().Uint64()). Uint64("blockNum", w.current.header.Number().Uint64()).
Uint64("myPrevEpoch", w.current.header.Epoch().Uint64()). Uint64("myCurEpoch", w.current.header.Epoch().Uint64()).
Uint64("myCurEpoch", blockEpoch.Uint64()). Uint64("beaconEpoch", beaconEpoch.Uint64()).
Msg("Propose new epoch as beacon chain's epoch") Msg("Propose new epoch as beacon chain's epoch")
w.current.header.SetEpoch(blockEpoch)
case 0: case 0:
// If it's same epoch, no need to propose new shard state (new epoch change) // If it's same epoch, no need to propose new shard state (new epoch change)
case -1: case -1:
@ -337,20 +338,17 @@ func (w *Worker) SuperCommitteeForNextEpoch(
beaconEpoch, beacon, beaconEpoch, beacon,
) )
blockEpoch := big.NewInt(0).Set(beaconEpoch).Sub(beaconEpoch, big.NewInt(1))
utils.Logger().Debug(). utils.Logger().Debug().
Uint64("blockNum", w.current.header.Number().Uint64()). Uint64("blockNum", w.current.header.Number().Uint64()).
Uint64("myPrevEpoch", w.current.header.Epoch().Uint64()). Uint64("myCurEpoch", w.current.header.Epoch().Uint64()).
Uint64("myCurEpoch", blockEpoch.Uint64()). Uint64("beaconEpoch", beaconEpoch.Uint64()).
Msg("Propose one-time catch up with beacon chain's epoch") Msg("Propose entering staking along with beacon chain's epoch")
// Set this block's epoch to be beaconEpoch - 1, so the next block will have beaconEpoch
w.current.header.SetEpoch(blockEpoch)
} else { } else {
// If I are not in staking nor has beacon chain proposed a staking-based shard state, // If I are not in staking nor has beacon chain proposed a staking-based shard state,
// do pre-staking committee calculation // do pre-staking committee calculation
if shard.Schedule.IsLastBlock(w.current.header.Number().Uint64()) { if shard.Schedule.IsLastBlock(w.current.header.Number().Uint64()) {
nextCommittee, err = committee.WithStakingEnabled.Compute( nextCommittee, err = committee.WithStakingEnabled.Compute(
new(big.Int).Add(w.current.header.Epoch(), common.Big1), nextEpoch,
w.chain, w.chain,
) )
} }
@ -362,7 +360,7 @@ func (w *Worker) SuperCommitteeForNextEpoch(
} }
// FinalizeNewBlock generate a new block for the next consensus round. // FinalizeNewBlock generate a new block for the next consensus round.
func (w *Worker) FinalizeNewBlock(sig []byte, signers []byte, viewID uint64, coinbase common.Address, crossLinks types.CrossLinks, shardState shard.State) (*types.Block, error) { func (w *Worker) FinalizeNewBlock(sig []byte, signers []byte, viewID uint64, coinbase common.Address, crossLinks types.CrossLinks, shardState *shard.State) (*types.Block, error) {
if len(sig) > 0 && len(signers) > 0 { if len(sig) > 0 && len(signers) > 0 {
sig2 := w.current.header.LastCommitSignature() sig2 := w.current.header.LastCommitSignature()
copy(sig2[:], sig[:]) copy(sig2[:], sig[:])
@ -391,9 +389,14 @@ func (w *Worker) FinalizeNewBlock(sig []byte, signers []byte, viewID uint64, coi
} }
// Shard State // Shard State
if shardState != nil && len(shardState) != 0 { if shardState != nil && len(shardState.Shards) != 0 {
w.current.header.SetShardStateHash(shardState.Hash()) w.current.header.SetShardStateHash(shardState.Hash())
shardStateData, err := rlp.EncodeToBytes(shardState) isStaking := false
if shardState.Epoch != nil && w.config.IsStaking(shardState.Epoch) {
isStaking = true
}
// NOTE: Besides genesis, this is the only place where the shard state is encoded.
shardStateData, err := shard.EncodeWrapper(*shardState, isStaking)
if err == nil { if err == nil {
w.current.header.SetShardState(shardStateData) w.current.header.SetShardState(shardStateData)
} else { } else {

@ -19,8 +19,8 @@ import (
type ValidatorListProvider interface { type ValidatorListProvider interface {
Compute( Compute(
epoch *big.Int, reader DataProvider, epoch *big.Int, reader DataProvider,
) (shard.State, error) ) (*shard.State, error)
ReadFromDB(epoch *big.Int, reader DataProvider) (shard.State, error) ReadFromDB(epoch *big.Int, reader DataProvider) (*shard.State, error)
GetCommitteePublicKeys(committee *shard.Committee) []*bls.PublicKey GetCommitteePublicKeys(committee *shard.Committee) []*bls.PublicKey
} }
@ -41,7 +41,7 @@ type ChainReader interface {
// ReadShardState retrieves sharding state given the epoch number. // ReadShardState retrieves sharding state given the epoch number.
// This api reads the shard state cached or saved on the chaindb. // This api reads the shard state cached or saved on the chaindb.
// Thus, only should be used to read the shard state of the current chain. // Thus, only should be used to read the shard state of the current chain.
ReadShardState(epoch *big.Int) (shard.State, error) ReadShardState(epoch *big.Int) (*shard.State, error)
// GetHeader retrieves a block header from the database by hash and number. // GetHeader retrieves a block header from the database by hash and number.
GetHeaderByHash(common.Hash) *block.Header GetHeaderByHash(common.Hash) *block.Header
// Config retrieves the blockchain's chain configuration. // Config retrieves the blockchain's chain configuration.
@ -61,13 +61,13 @@ var (
WithStakingEnabled Reader = partialStakingEnabled{} WithStakingEnabled Reader = partialStakingEnabled{}
) )
func preStakingEnabledCommittee(s shardingconfig.Instance) shard.State { func preStakingEnabledCommittee(s shardingconfig.Instance) *shard.State {
shardNum := int(s.NumShards()) shardNum := int(s.NumShards())
shardHarmonyNodes := s.NumHarmonyOperatedNodesPerShard() shardHarmonyNodes := s.NumHarmonyOperatedNodesPerShard()
shardSize := s.NumNodesPerShard() shardSize := s.NumNodesPerShard()
hmyAccounts := s.HmyAccounts() hmyAccounts := s.HmyAccounts()
fnAccounts := s.FnAccounts() fnAccounts := s.FnAccounts()
shardState := shard.State{} shardState := &shard.State{}
// Shard state needs to be sorted by shard ID // Shard state needs to be sorted by shard ID
for i := 0; i < shardNum; i++ { for i := 0; i < shardNum; i++ {
com := shard.Committee{ShardID: uint32(i)} com := shard.Committee{ShardID: uint32(i)}
@ -100,14 +100,14 @@ func preStakingEnabledCommittee(s shardingconfig.Instance) shard.State {
} }
com.Slots = append(com.Slots, curNodeID) com.Slots = append(com.Slots, curNodeID)
} }
shardState = append(shardState, com) shardState.Shards = append(shardState.Shards, com)
} }
return shardState return shardState
} }
func eposStakedCommittee( func eposStakedCommittee(
s shardingconfig.Instance, stakerReader DataProvider, stakedSlotsCount int, s shardingconfig.Instance, stakerReader DataProvider, stakedSlotsCount int,
) (shard.State, error) { ) (*shard.State, error) {
// TODO Nervous about this because overtime the list will become quite large // TODO Nervous about this because overtime the list will become quite large
candidates := stakerReader.ValidatorCandidates() candidates := stakerReader.ValidatorCandidates()
essentials := map[common.Address]effective.SlotOrder{} essentials := map[common.Address]effective.SlotOrder{}
@ -117,6 +117,11 @@ func eposStakedCommittee(
// TODO benchmark difference if went with data structure that sorts on insert // TODO benchmark difference if went with data structure that sorts on insert
for i := range candidates { for i := range candidates {
validator, err := stakerReader.ReadValidatorInformation(candidates[i]) validator, err := stakerReader.ReadValidatorInformation(candidates[i])
if err != nil {
return nil, err
}
if err := validator.SanityCheck(); err != nil { if err := validator.SanityCheck(); err != nil {
continue continue
} }
@ -124,9 +129,6 @@ func eposStakedCommittee(
for _, delegation := range validator.Delegations { for _, delegation := range validator.Delegations {
validatorStake.Add(validatorStake, delegation.Amount) validatorStake.Add(validatorStake, delegation.Amount)
} }
if err != nil {
return nil, err
}
essentials[validator.Address] = effective.SlotOrder{ essentials[validator.Address] = effective.SlotOrder{
validatorStake, validatorStake,
validator.SlotPubKeys, validator.SlotPubKeys,
@ -134,21 +136,23 @@ func eposStakedCommittee(
} }
shardCount := int(s.NumShards()) shardCount := int(s.NumShards())
superComm := make(shard.State, shardCount)
shardState := &shard.State{}
shardState.Shards = make([]shard.Committee, shardCount)
hAccounts := s.HmyAccounts() hAccounts := s.HmyAccounts()
// Shard state needs to be sorted by shard ID // Shard state needs to be sorted by shard ID
for i := 0; i < shardCount; i++ { for i := 0; i < shardCount; i++ {
superComm[i] = shard.Committee{uint32(i), shard.SlotList{}} shardState.Shards[i] = shard.Committee{uint32(i), shard.SlotList{}}
} }
for i := range hAccounts { for i := range hAccounts {
spot := i % shardCount shardID := i % shardCount
pub := &bls.PublicKey{} pub := &bls.PublicKey{}
pub.DeserializeHexStr(hAccounts[i].BlsPublicKey) pub.DeserializeHexStr(hAccounts[i].BlsPublicKey)
pubKey := shard.BlsPublicKey{} pubKey := shard.BlsPublicKey{}
pubKey.FromLibBLSPublicKey(pub) pubKey.FromLibBLSPublicKey(pub)
superComm[spot].Slots = append(superComm[spot].Slots, shard.Slot{ shardState.Shards[shardID].Slots = append(shardState.Shards[shardID].Slots, shard.Slot{
common2.ParseAddr(hAccounts[i].Address), common2.ParseAddr(hAccounts[i].Address),
pubKey, pubKey,
nil, nil,
@ -158,7 +162,7 @@ func eposStakedCommittee(
if stakedSlotsCount == 0 { if stakedSlotsCount == 0 {
utils.Logger().Info().Int("slots-for-epos", stakedSlotsCount). utils.Logger().Info().Int("slots-for-epos", stakedSlotsCount).
Msg("committe composed only of harmony node") Msg("committe composed only of harmony node")
return superComm, nil return shardState, nil
} }
staked := effective.Apply(essentials, stakedSlotsCount) staked := effective.Apply(essentials, stakedSlotsCount)
@ -170,9 +174,9 @@ func eposStakedCommittee(
} }
for i := 0; i < stakedSlotsCount; i++ { for i := 0; i < stakedSlotsCount; i++ {
bucket := int(new(big.Int).Mod(staked[i].BlsPublicKey.Big(), shardBig).Int64()) shardID := int(new(big.Int).Mod(staked[i].BlsPublicKey.Big(), shardBig).Int64())
slot := staked[i] slot := staked[i]
superComm[bucket].Slots = append(superComm[bucket].Slots, shard.Slot{ shardState.Shards[shardID].Slots = append(shardState.Shards[shardID].Slots, shard.Slot{
slot.Address, slot.Address,
staked[i].BlsPublicKey, staked[i].BlsPublicKey,
&slot.Dec, &slot.Dec,
@ -180,10 +184,10 @@ func eposStakedCommittee(
} }
if c := len(candidates); c != 0 { if c := len(candidates); c != 0 {
utils.Logger().Info().Int("staked-candidates", c). utils.Logger().Info().Int("staked-candidates", c).
RawJSON("staked-super-committee", []byte(superComm.JSON())). RawJSON("staked-super-committee", []byte(shardState.JSON())).
Msg("EPoS based super-committe") Msg("EPoS based super-committe")
} }
return superComm, nil return shardState, nil
} }
// GetCommitteePublicKeys returns the public keys of a shard // GetCommitteePublicKeys returns the public keys of a shard
@ -201,14 +205,14 @@ func (def partialStakingEnabled) GetCommitteePublicKeys(committee *shard.Committ
func (def partialStakingEnabled) ReadFromDB( func (def partialStakingEnabled) ReadFromDB(
epoch *big.Int, reader DataProvider, epoch *big.Int, reader DataProvider,
) (newSuperComm shard.State, err error) { ) (newSuperComm *shard.State, err error) {
return reader.ReadShardState(epoch) return reader.ReadShardState(epoch)
} }
// ReadFromComputation is single entry point for reading the State of the network // ReadFromComputation is single entry point for reading the State of the network
func (def partialStakingEnabled) Compute( func (def partialStakingEnabled) Compute(
epoch *big.Int, stakerReader DataProvider, epoch *big.Int, stakerReader DataProvider,
) (newSuperComm shard.State, err error) { ) (newSuperComm *shard.State, err error) {
preStaking := true preStaking := true
if stakerReader != nil { if stakerReader != nil {
config := stakerReader.Config() config := stakerReader.Config()
@ -219,10 +223,18 @@ func (def partialStakingEnabled) Compute(
instance := shard.Schedule.InstanceForEpoch(epoch) instance := shard.Schedule.InstanceForEpoch(epoch)
if preStaking { if preStaking {
// Pre-staking shard state doesn't need to set epoch (backward compatible)
return preStakingEnabledCommittee(instance), nil return preStakingEnabledCommittee(instance), nil
} }
stakedSlots := stakedSlots :=
(instance.NumNodesPerShard() - instance.NumHarmonyOperatedNodesPerShard()) * (instance.NumNodesPerShard() - instance.NumHarmonyOperatedNodesPerShard()) *
int(instance.NumShards()) int(instance.NumShards())
return eposStakedCommittee(instance, stakerReader, stakedSlots) shardState, err := eposStakedCommittee(instance, stakerReader, stakedSlots)
if err != nil {
return nil, err
}
// Set the epoch of shard state
shardState.Epoch = big.NewInt(0).Set(epoch)
return shardState, nil
} }

@ -24,7 +24,10 @@ var (
const PublicKeySizeInBytes = 48 const PublicKeySizeInBytes = 48
// State is the collection of all committees // State is the collection of all committees
type State []Committee type State struct {
Epoch *big.Int `json:"epoch"`
Shards []Committee `json:"shards"`
}
// BlsPublicKey defines the bls public key // BlsPublicKey defines the bls public key
type BlsPublicKey [PublicKeySizeInBytes]byte type BlsPublicKey [PublicKeySizeInBytes]byte
@ -70,7 +73,7 @@ type CommitteeLegacy struct {
} }
// DecodeWrapper .. // DecodeWrapper ..
func DecodeWrapper(shardState []byte) (State, error) { func DecodeWrapper(shardState []byte) (*State, error) {
oldSS := StateLegacy{} oldSS := StateLegacy{}
newSS := State{} newSS := State{}
var ( var (
@ -79,20 +82,22 @@ func DecodeWrapper(shardState []byte) (State, error) {
) )
err1 = rlp.DecodeBytes(shardState, &newSS) err1 = rlp.DecodeBytes(shardState, &newSS)
if err1 == nil { if err1 == nil {
return newSS, nil return &newSS, nil
} }
err2 = rlp.DecodeBytes(shardState, &oldSS) err2 = rlp.DecodeBytes(shardState, &oldSS)
if err2 == nil { if err2 == nil {
newSS = make(State, len(oldSS)) newSS := State{}
newSS.Shards = make([]Committee, len(oldSS))
for i := range oldSS { for i := range oldSS {
newSS[i] = Committee{ShardID: oldSS[i].ShardID, Slots: SlotList{}} newSS.Shards[i] = Committee{ShardID: oldSS[i].ShardID, Slots: SlotList{}}
for _, slot := range oldSS[i].Slots { for _, slot := range oldSS[i].Slots {
newSS[i].Slots = append(newSS[i].Slots, Slot{ newSS.Shards[i].Slots = append(newSS.Shards[i].Slots, Slot{
slot.EcdsaAddress, slot.BlsPublicKey, nil, slot.EcdsaAddress, slot.BlsPublicKey, nil,
}) })
} }
} }
return newSS, nil newSS.Epoch = nil // Make sure for legacy state, the epoch is nil
return &newSS, nil
} }
return nil, err2 return nil, err2
} }
@ -106,12 +111,12 @@ func EncodeWrapper(shardState State, isStaking bool) ([]byte, error) {
if isStaking { if isStaking {
data, err = rlp.EncodeToBytes(shardState) data, err = rlp.EncodeToBytes(shardState)
} else { } else {
shardStateLegacy := make(StateLegacy, len(shardState)) shardStateLegacy := make(StateLegacy, len(shardState.Shards))
for i := range shardState { for i := range shardState.Shards {
shardStateLegacy[i] = CommitteeLegacy{ shardStateLegacy[i] = CommitteeLegacy{
ShardID: shardState[i].ShardID, Slots: SlotListLegacy{}, ShardID: shardState.Shards[i].ShardID, Slots: SlotListLegacy{},
} }
for _, slot := range shardState[i].Slots { for _, slot := range shardState.Shards[i].Slots {
shardStateLegacy[i].Slots = append(shardStateLegacy[i].Slots, SlotLegacy{ shardStateLegacy[i].Slots = append(shardStateLegacy[i].Slots, SlotLegacy{
slot.EcdsaAddress, slot.BlsPublicKey, slot.EcdsaAddress, slot.BlsPublicKey,
}) })
@ -125,7 +130,7 @@ func EncodeWrapper(shardState State, isStaking bool) ([]byte, error) {
} }
// JSON produces a non-pretty printed JSON string of the SuperCommittee // JSON produces a non-pretty printed JSON string of the SuperCommittee
func (ss State) JSON() string { func (ss *State) JSON() string {
type t struct { type t struct {
Slot Slot
EcdsaAddress string `json:"ecdsa-address"` EcdsaAddress string `json:"ecdsa-address"`
@ -135,14 +140,14 @@ func (ss State) JSON() string {
Count int `json:"member-count"` Count int `json:"member-count"`
NodeList []t `json:"subcommittee"` NodeList []t `json:"subcommittee"`
} }
dump := make([]v, len(ss)) dump := make([]v, len(ss.Shards))
for i := range ss { for i := range ss.Shards {
c := len(ss[i].Slots) c := len(ss.Shards[i].Slots)
dump[i].ShardID = ss[i].ShardID dump[i].ShardID = ss.Shards[i].ShardID
dump[i].NodeList = make([]t, c) dump[i].NodeList = make([]t, c)
dump[i].Count = c dump[i].Count = c
for j := range ss[i].Slots { for j := range ss.Shards[i].Slots {
n := ss[i].Slots[j] n := ss.Shards[i].Slots[j]
dump[i].NodeList[j].BlsPublicKey = n.BlsPublicKey dump[i].NodeList[j].BlsPublicKey = n.BlsPublicKey
dump[i].NodeList[j].TotalStake = n.TotalStake dump[i].NodeList[j].TotalStake = n.TotalStake
dump[i].NodeList[j].EcdsaAddress = common2.MustAddressToBech32(n.EcdsaAddress) dump[i].NodeList[j].EcdsaAddress = common2.MustAddressToBech32(n.EcdsaAddress)
@ -154,42 +159,25 @@ func (ss State) JSON() string {
// FindCommitteeByID returns the committee configuration for the given shard, // FindCommitteeByID returns the committee configuration for the given shard,
// or nil if the given shard is not found. // or nil if the given shard is not found.
func (ss State) FindCommitteeByID(shardID uint32) *Committee { func (ss *State) FindCommitteeByID(shardID uint32) *Committee {
for committee := range ss { for committee := range ss.Shards {
if ss[committee].ShardID == shardID { if ss.Shards[committee].ShardID == shardID {
return &ss[committee] return &ss.Shards[committee]
} }
} }
return nil return nil
} }
// DeepCopy returns a deep copy of the receiver. // DeepCopy returns a deep copy of the receiver.
func (ss State) DeepCopy() State { func (ss *State) DeepCopy() *State {
var r State var r State
for _, c := range ss { if ss.Epoch != nil {
r = append(r, c.DeepCopy()) r.Epoch = big.NewInt(0).Set(ss.Epoch)
}
return r
}
// CompareShardState compares two State instances.
func CompareShardState(s1, s2 State) int {
commonLen := len(s1)
if commonLen > len(s2) {
commonLen = len(s2)
} }
for idx := 0; idx < commonLen; idx++ { for _, c := range ss.Shards {
if c := CompareCommittee(&s1[idx], &s2[idx]); c != 0 { r.Shards = append(r.Shards, c.DeepCopy())
return c
}
} }
switch { return &r
case len(s1) < len(s2):
return -1
case len(s1) > len(s2):
return +1
}
return 0
} }
// Big .. // Big ..
@ -312,27 +300,22 @@ func GetHashFromNodeList(nodeList []Slot) []byte {
} }
// Hash is the root hash of State // Hash is the root hash of State
func (ss State) Hash() (h common.Hash) { func (ss *State) Hash() (h common.Hash) {
// TODO ek – this sorting really doesn't belong here; it should instead // TODO ek – this sorting really doesn't belong here; it should instead
// be made an explicit invariant to be maintained and, if needed, checked. // be made an explicit invariant to be maintained and, if needed, checked.
copy := ss.DeepCopy() copy := ss.DeepCopy()
sort.Slice(copy, func(i, j int) bool { sort.Slice(copy.Shards, func(i, j int) bool {
return copy[i].ShardID < copy[j].ShardID return copy.Shards[i].ShardID < copy.Shards[j].ShardID
}) })
d := sha3.NewLegacyKeccak256() d := sha3.NewLegacyKeccak256()
for i := range copy { for i := range copy.Shards {
hash := GetHashFromNodeList(copy[i].Slots) hash := GetHashFromNodeList(copy.Shards[i].Slots)
d.Write(hash) d.Write(hash)
} }
d.Sum(h[:0]) d.Sum(h[:0])
return h return h
} }
// CompareNodeIDByBLSKey compares two nodes by their ID; used to sort node list
func CompareNodeIDByBLSKey(n1 Slot, n2 Slot) int {
return bytes.Compare(n1.BlsPublicKey[:], n2.BlsPublicKey[:])
}
// Serialize serialize Slot into bytes // Serialize serialize Slot into bytes
func (n Slot) Serialize() []byte { func (n Slot) Serialize() []byte {
return append(n.EcdsaAddress[:], n.BlsPublicKey[:]...) return append(n.EcdsaAddress[:], n.BlsPublicKey[:]...)

@ -73,7 +73,7 @@ func TestHash(t *testing.T) {
{common.Address{0x66}, blsPubKey6, nil}, {common.Address{0x66}, blsPubKey6, nil},
}, },
} }
shardState1 := State{com1, com2} shardState1 := State{nil, []Committee{com1, com2}}
h1 := shardState1.Hash() h1 := shardState1.Hash()
com3 := Committee{ com3 := Committee{
@ -93,7 +93,7 @@ func TestHash(t *testing.T) {
}, },
} }
shardState2 := State{com3, com4} shardState2 := State{nil, []Committee{com3, com4}}
h2 := shardState2.Hash() h2 := shardState2.Hash()
if bytes.Compare(h1[:], h2[:]) != 0 { if bytes.Compare(h1[:], h2[:]) != 0 {
@ -120,7 +120,7 @@ func TestCompatibilityOldShardStateIntoNew(t *testing.T) {
}}, }},
} }
postStakingState := State{ postStakingState := State{nil, []Committee{
Committee{ShardID: 0, Slots: SlotList{ Committee{ShardID: 0, Slots: SlotList{
Slot{junkA, blsPubKey1, nil}, Slot{junkA, blsPubKey1, nil},
Slot{junkA, blsPubKey2, nil}, Slot{junkA, blsPubKey2, nil},
@ -134,7 +134,7 @@ func TestCompatibilityOldShardStateIntoNew(t *testing.T) {
Slot{junkA, blsPubKey4, nil}, Slot{junkA, blsPubKey4, nil},
Slot{junkA, blsPubKey5, &stake2}, Slot{junkA, blsPubKey5, &stake2},
}}, }},
} }}
preStakingStateBytes, _ := rlp.EncodeToBytes(preStakingState) preStakingStateBytes, _ := rlp.EncodeToBytes(preStakingState)
postStakingStateBytes, _ := rlp.EncodeToBytes(postStakingState) postStakingStateBytes, _ := rlp.EncodeToBytes(postStakingState)

@ -271,19 +271,6 @@ func UpdateValidatorFromEditMsg(validator *Validator, edit *EditValidator) error
validator.MaxTotalDelegation = edit.MaxTotalDelegation validator.MaxTotalDelegation = edit.MaxTotalDelegation
} }
if edit.SlotKeyToAdd != nil {
found := false
for _, key := range validator.SlotPubKeys {
if key == *edit.SlotKeyToAdd {
found = true
break
}
}
if !found {
validator.SlotPubKeys = append(validator.SlotPubKeys, *edit.SlotKeyToAdd)
}
}
if edit.SlotKeyToRemove != nil { if edit.SlotKeyToRemove != nil {
index := -1 index := -1
for i, key := range validator.SlotPubKeys { for i, key := range validator.SlotPubKeys {
@ -297,6 +284,19 @@ func UpdateValidatorFromEditMsg(validator *Validator, edit *EditValidator) error
validator.SlotPubKeys = append(validator.SlotPubKeys[:index], validator.SlotPubKeys[index+1:]...) validator.SlotPubKeys = append(validator.SlotPubKeys[:index], validator.SlotPubKeys[index+1:]...)
} }
} }
if edit.SlotKeyToAdd != nil {
found := false
for _, key := range validator.SlotPubKeys {
if key == *edit.SlotKeyToAdd {
found = true
break
}
}
if !found {
validator.SlotPubKeys = append(validator.SlotPubKeys, *edit.SlotKeyToAdd)
}
}
return nil return nil
} }

Loading…
Cancel
Save