Add epoch number in shard state for use of beacon epoch sync

pull/1927/head
Rongjian Lan 5 years ago
parent 283a8f48b4
commit f59469b644
  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. 41
      core/blockchain.go
  11. 2
      core/chain_makers.go
  12. 4
      core/genesis.go
  13. 18
      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. 2
      internal/hmyapi/backend.go
  19. 2
      internal/hmyapi/blockchain.go
  20. 15
      node/node_cross_shard.go
  21. 2
      node/node_explorer.go
  22. 10
      node/node_genesis.go
  23. 2
      node/node_handler.go
  24. 2
      node/node_resharding.go
  25. 15
      node/worker/worker.go
  26. 38
      shard/committee/assignment.go
  27. 92
      shard/shard_state.go
  28. 8
      shard/shard_state_test.go
  29. 26
      staking/types/validator.go

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

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

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

@ -418,7 +418,11 @@ func (h *Header) Logger(logger *zerolog.Logger) *zerolog.Logger {
// GetShardState returns the deserialized shard state object.
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.

@ -406,7 +406,11 @@ func (h *Header) Logger(logger *zerolog.Logger) *zerolog.Logger {
// GetShardState returns the deserialized shard state object.
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.

@ -402,7 +402,11 @@ func (h *Header) Logger(logger *zerolog.Logger) *zerolog.Logger {
// GetShardState returns the deserialized shard state object.
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.

@ -406,7 +406,11 @@ func (h *Header) Logger(logger *zerolog.Logger) *zerolog.Logger {
// GetShardState returns the deserialized shard state object.
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.

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

@ -40,7 +40,7 @@ type ChainReader interface {
// ReadShardState retrieves sharding state given the epoch number.
// 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.
ReadShardState(epoch *big.Int) (shard.State, error)
ReadShardState(epoch *big.Int) (*shard.State, error)
// ReadActiveValidatorList retrieves the list of active validators
ReadActiveValidatorList() ([]common.Address, error)

@ -1181,8 +1181,8 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
// Find all the active validator addresses and store them in db
allActiveValidators := []common.Address{}
processed := make(map[common.Address]struct{})
for i := range *newShardState {
shard := (*newShardState)[i]
for i := range newShardState.Shards {
shard := newShardState.Shards[i]
for j := range shard.Slots {
slot := shard.Slots[j]
if slot.TotalStake != nil { // For external validator
@ -1219,8 +1219,9 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
// Update voting power of validators for all shards
if block.ShardID() == 0 && len(block.Header().ShardState()) > 0 {
shardState := shard.State{}
if err := rlp.DecodeBytes(block.Header().ShardState(), &shardState); err == nil {
shardState := new(shard.State)
if shardState, err = shard.DecodeWrapper(block.Header().ShardState()); err == nil {
if err = bc.UpdateValidatorVotingPower(shardState); err != nil {
utils.Logger().Err(err)
}
@ -2007,10 +2008,10 @@ func (bc *BlockChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscript
}
// 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())
if cached, ok := bc.shardStateCache.Get(cacheKey); ok {
shardState := cached.(shard.State)
shardState := cached.(*shard.State)
return shardState, nil
}
shardState, err := rawdb.ReadShardState(bc.db, epoch)
@ -2021,22 +2022,6 @@ func (bc *BlockChain) ReadShardState(epoch *big.Int) (shard.State, error) {
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.
func (bc *BlockChain) WriteShardStateBytes(db rawdb.DatabaseWriter,
epoch *big.Int, shardState []byte,
@ -2051,7 +2036,7 @@ func (bc *BlockChain) WriteShardStateBytes(db rawdb.DatabaseWriter,
}
cacheKey := string(epoch.Bytes())
bc.shardStateCache.Add(cacheKey, decodeShardState)
return &decodeShardState, nil
return decodeShardState, nil
}
// ReadLastCommits retrieves last commits.
@ -2661,11 +2646,15 @@ func (bc *BlockChain) UpdateValidatorUptime(slots shard.SlotList, mask *bls.Mask
}
// 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)
addrToEffectiveStakes := make(map[common.Address]map[uint32]numeric.Dec)
for _, committee := range state {
for _, committee := range state.Shards {
for _, slot := range committee.Slots {
if slot.TotalStake != nil {
if _, ok := addrToEffectiveStakes[slot.EcdsaAddress]; !ok {
@ -2698,7 +2687,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.AvgVotingPower = addrTotalVotingPower.Quo(numeric.NewDec(int64(len(state))))
stats.AvgVotingPower = addrTotalVotingPower.Quo(numeric.NewDec(int64(len(state.Shards))))
stats.TotalEffectiveStake = addrTotalEffectiveStake
err = rawdb.WriteValidatorStats(batch, addr, stats)

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

@ -417,7 +417,7 @@ func FindCommonAncestor(db DatabaseReader, a, b *block.Header) *block.Header {
// ReadShardState retrieves sharding state.
func ReadShardState(
db DatabaseReader, epoch *big.Int,
) (shard.State, error) {
) (*shard.State, error) {
data, err := db.Get(shardStateKey(epoch))
if err != nil {
return nil, ctxerror.New(MsgNoShardStateFromDB,
@ -433,20 +433,6 @@ func ReadShardState(
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.
func WriteShardStateBytes(db DatabaseWriter, epoch *big.Int, data []byte) (err error) {
if err = db.Put(shardStateKey(epoch), data); err != nil {
@ -767,7 +753,7 @@ func ReadDelegationsByDelegator(db DatabaseReader, delegator common.Address) ([]
return addrs, nil
}
// writeDelegationsByDelegator stores the list of validators delegated by a delegator
// WriteDelegationsByDelegator stores the list of validators delegated by a delegator
func WriteDelegationsByDelegator(db DatabaseWriter, delegator common.Address, indices []staking.DelegationIndex) error {
bytes, err := rlp.EncodeToBytes(indices)
if err != nil {

@ -419,7 +419,7 @@ func (st *StateTransition) applyEditValidatorTx(editValidator *staking.EditValid
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 nil

@ -38,7 +38,6 @@ import (
v3 "github.com/harmony-one/harmony/block/v3"
"github.com/harmony-one/harmony/crypto/hash"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/shard"
staking "github.com/harmony-one/harmony/staking/types"
"github.com/harmony-one/taggedrlp"
"github.com/pkg/errors"
@ -593,20 +592,6 @@ func (b *Block) AddVdf(vdf []byte) {
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.
func (b *Block) Logger(logger *zerolog.Logger) *zerolog.Logger {
return b.header.Logger(logger)

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

@ -235,7 +235,7 @@ func (e *engineImpl) Finalize(
func QuorumForBlock(
chain engine.ChainReader, h *block.Header, reCalculate bool,
) (quorum int, err error) {
var ss shard.State
ss := new(shard.State)
if reCalculate {
ss, _ = committee.WithStakingEnabled.Compute(h.Epoch(), chain)
} 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
func GetPublicKeys(chain engine.ChainReader, header *block.Header, reCalculate bool) ([]*bls.PublicKey, error) {
var shardState shard.State
shardState := new(shard.State)
var err error
if reCalculate {
shardState, _ = committee.WithStakingEnabled.Compute(header.Epoch(), chain)

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

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

@ -156,10 +156,10 @@ func (node *Node) ProcessCrossLinkMessage(msgPayload []byte) {
utils.Logger().Debug().
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)
Msgf("[ProcessingCrossLink] Crosslink blockNum %d epochNum %d shard %d skipped: %v", cl.BlockNum(), cl.Epoch().Uint64(), cl.ShardID(), cl)
continue
}
exist, err := node.Blockchain().ReadCrossLink(cl.ShardID(), cl.Number().Uint64())
@ -228,8 +228,13 @@ func (node *Node) verifyIncomingReceipts(block *types.Block) error {
// VerifyCrossLink verifies the header is valid
func (node *Node) VerifyCrossLink(cl types.CrossLink) error {
if cl.BlockNum() <= 1 {
return ctxerror.New("CrossLink BlockNumber should greater than 1")
}
// TODO: add fork choice rule
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
// TODO: check whether to recalculate shard state
@ -255,10 +260,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)
}
if cl.BlockNum() <= 1 {
return ctxerror.New("CrossLink BlockNumber should greater than 1")
}
mask, err := bls_cosi.NewMask(committerKeys, nil)
if err != nil {
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")
continue
}
for _, committee := range state {
for _, committee := range state.Shards {
if committee.ShardID == curBlock.ShardID() {
utils.Logger().Debug().Msg("[Explorer] Dumping 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(
big.NewInt(core.GenesisEpoch), nil,
)
if shardState == nil {
return errors.New("failed to create genesis shard state")
}
if shardID != shard.BeaconChainShardID {
// store only the local shard for shard chains
c := shardState.FindCommitteeByID(shardID)
if c == nil {
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)
return nil
}
// 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")
if shardID == node.NodeConfig.ShardID {
node.isFirstTime = true
@ -102,7 +106,7 @@ func (node *Node) SetupGenesisBlock(db ethdb.Database, shardID uint32, myShardSt
ShardID: shardID,
GasLimit: params.GenesisGasLimit,
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
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 {
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.BroadcastCXReceipts(newBlock, commitSigAndBitmap)

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

@ -289,9 +289,9 @@ func (w *Worker) IncomingReceipts() []*types.CXReceiptsProof {
func (w *Worker) SuperCommitteeForNextEpoch(
shardID uint32,
beacon *core.BlockChain,
) (shard.State, error) {
) (*shard.State, error) {
var (
nextCommittee shard.State
nextCommittee = new(shard.State)
err error
)
switch shardID {
@ -362,7 +362,7 @@ func (w *Worker) SuperCommitteeForNextEpoch(
}
// 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 {
sig2 := w.current.header.LastCommitSignature()
copy(sig2[:], sig[:])
@ -391,9 +391,14 @@ func (w *Worker) FinalizeNewBlock(sig []byte, signers []byte, viewID uint64, coi
}
// Shard State
if shardState != nil && len(shardState) != 0 {
if shardState != nil && len(shardState.Shards) != 0 {
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 {
w.current.header.SetShardState(shardStateData)
} else {

@ -19,8 +19,8 @@ import (
type ValidatorListProvider interface {
Compute(
epoch *big.Int, reader DataProvider,
) (shard.State, error)
ReadFromDB(epoch *big.Int, reader DataProvider) (shard.State, error)
) (*shard.State, error)
ReadFromDB(epoch *big.Int, reader DataProvider) (*shard.State, error)
GetCommitteePublicKeys(committee *shard.Committee) []*bls.PublicKey
}
@ -41,7 +41,7 @@ type ChainReader interface {
// ReadShardState retrieves sharding state given the epoch number.
// 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.
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.
GetHeaderByHash(common.Hash) *block.Header
// Config retrieves the blockchain's chain configuration.
@ -61,13 +61,13 @@ var (
WithStakingEnabled Reader = partialStakingEnabled{}
)
func preStakingEnabledCommittee(s shardingconfig.Instance) shard.State {
func preStakingEnabledCommittee(s shardingconfig.Instance) *shard.State {
shardNum := int(s.NumShards())
shardHarmonyNodes := s.NumHarmonyOperatedNodesPerShard()
shardSize := s.NumNodesPerShard()
hmyAccounts := s.HmyAccounts()
fnAccounts := s.FnAccounts()
shardState := shard.State{}
shardState := &shard.State{}
// Shard state needs to be sorted by shard ID
for i := 0; i < shardNum; i++ {
com := shard.Committee{ShardID: uint32(i)}
@ -100,14 +100,14 @@ func preStakingEnabledCommittee(s shardingconfig.Instance) shard.State {
}
com.Slots = append(com.Slots, curNodeID)
}
shardState = append(shardState, com)
shardState.Shards = append(shardState.Shards, com)
}
return shardState
}
func eposStakedCommittee(
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
candidates := stakerReader.ValidatorCandidates()
essentials := map[common.Address]effective.SlotOrder{}
@ -134,21 +134,23 @@ func eposStakedCommittee(
}
shardCount := int(s.NumShards())
superComm := make(shard.State, shardCount)
shardState := &shard.State{}
shardState.Shards = make([]shard.Committee, shardCount)
hAccounts := s.HmyAccounts()
// Shard state needs to be sorted by shard ID
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 {
spot := i % shardCount
shardID := i % shardCount
pub := &bls.PublicKey{}
pub.DeserializeHexStr(hAccounts[i].BlsPublicKey)
pubKey := shard.BlsPublicKey{}
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),
pubKey,
nil,
@ -158,7 +160,7 @@ func eposStakedCommittee(
if stakedSlotsCount == 0 {
utils.Logger().Info().Int("slots-for-epos", stakedSlotsCount).
Msg("committe composed only of harmony node")
return superComm, nil
return shardState, nil
}
staked := effective.Apply(essentials, stakedSlotsCount)
@ -170,9 +172,9 @@ func eposStakedCommittee(
}
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]
superComm[bucket].Slots = append(superComm[bucket].Slots, shard.Slot{
shardState.Shards[shardID].Slots = append(shardState.Shards[shardID].Slots, shard.Slot{
slot.Address,
staked[i].BlsPublicKey,
&slot.Dec,
@ -180,10 +182,10 @@ func eposStakedCommittee(
}
if c := len(candidates); c != 0 {
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")
}
return superComm, nil
return shardState, nil
}
// GetCommitteePublicKeys returns the public keys of a shard
@ -201,14 +203,14 @@ func (def partialStakingEnabled) GetCommitteePublicKeys(committee *shard.Committ
func (def partialStakingEnabled) ReadFromDB(
epoch *big.Int, reader DataProvider,
) (newSuperComm shard.State, err error) {
) (newSuperComm *shard.State, err error) {
return reader.ReadShardState(epoch)
}
// ReadFromComputation is single entry point for reading the State of the network
func (def partialStakingEnabled) Compute(
epoch *big.Int, stakerReader DataProvider,
) (newSuperComm shard.State, err error) {
) (newSuperComm *shard.State, err error) {
preStaking := true
if stakerReader != nil {
config := stakerReader.Config()

@ -24,7 +24,10 @@ var (
const PublicKeySizeInBytes = 48
// 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
type BlsPublicKey [PublicKeySizeInBytes]byte
@ -70,7 +73,7 @@ type CommitteeLegacy struct {
}
// DecodeWrapper ..
func DecodeWrapper(shardState []byte) (State, error) {
func DecodeWrapper(shardState []byte) (*State, error) {
oldSS := StateLegacy{}
newSS := State{}
var (
@ -79,20 +82,21 @@ func DecodeWrapper(shardState []byte) (State, error) {
)
err1 = rlp.DecodeBytes(shardState, &newSS)
if err1 == nil {
return newSS, nil
return &newSS, nil
}
err2 = rlp.DecodeBytes(shardState, &oldSS)
if err2 == nil {
newSS = make(State, len(oldSS))
newSS := State{}
newSS.Shards = make([]Committee, len(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 {
newSS[i].Slots = append(newSS[i].Slots, Slot{
newSS.Shards[i].Slots = append(newSS.Shards[i].Slots, Slot{
slot.EcdsaAddress, slot.BlsPublicKey, nil,
})
}
}
return newSS, nil
return &newSS, nil
}
return nil, err2
}
@ -106,12 +110,12 @@ func EncodeWrapper(shardState State, isStaking bool) ([]byte, error) {
if isStaking {
data, err = rlp.EncodeToBytes(shardState)
} else {
shardStateLegacy := make(StateLegacy, len(shardState))
for i := range shardState {
shardStateLegacy := make(StateLegacy, len(shardState.Shards))
for i := range shardState.Shards {
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{
slot.EcdsaAddress, slot.BlsPublicKey,
})
@ -125,7 +129,7 @@ func EncodeWrapper(shardState State, isStaking bool) ([]byte, error) {
}
// JSON produces a non-pretty printed JSON string of the SuperCommittee
func (ss State) JSON() string {
func (ss *State) JSON() string {
type t struct {
Slot
EcdsaAddress string `json:"ecdsa-address"`
@ -135,14 +139,14 @@ func (ss State) JSON() string {
Count int `json:"member-count"`
NodeList []t `json:"subcommittee"`
}
dump := make([]v, len(ss))
for i := range ss {
c := len(ss[i].Slots)
dump[i].ShardID = ss[i].ShardID
dump := make([]v, len(ss.Shards))
for i := range ss.Shards {
c := len(ss.Shards[i].Slots)
dump[i].ShardID = ss.Shards[i].ShardID
dump[i].NodeList = make([]t, c)
dump[i].Count = c
for j := range ss[i].Slots {
n := ss[i].Slots[j]
for j := range ss.Shards[i].Slots {
n := ss.Shards[i].Slots[j]
dump[i].NodeList[j].BlsPublicKey = n.BlsPublicKey
dump[i].NodeList[j].TotalStake = n.TotalStake
dump[i].NodeList[j].EcdsaAddress = common2.MustAddressToBech32(n.EcdsaAddress)
@ -154,42 +158,25 @@ func (ss State) JSON() string {
// FindCommitteeByID returns the committee configuration for the given shard,
// or nil if the given shard is not found.
func (ss State) FindCommitteeByID(shardID uint32) *Committee {
for committee := range ss {
if ss[committee].ShardID == shardID {
return &ss[committee]
func (ss *State) FindCommitteeByID(shardID uint32) *Committee {
for committee := range ss.Shards {
if ss.Shards[committee].ShardID == shardID {
return &ss.Shards[committee]
}
}
return nil
}
// DeepCopy returns a deep copy of the receiver.
func (ss State) DeepCopy() State {
func (ss *State) DeepCopy() *State {
var r State
for _, c := range ss {
r = append(r, c.DeepCopy())
}
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++ {
if c := CompareCommittee(&s1[idx], &s2[idx]); c != 0 {
return c
}
if ss.Epoch != nil {
r.Epoch = big.NewInt(0).Set(ss.Epoch)
}
switch {
case len(s1) < len(s2):
return -1
case len(s1) > len(s2):
return +1
for _, c := range ss.Shards {
r.Shards = append(r.Shards, c.DeepCopy())
}
return 0
return &r
}
// Big ..
@ -312,27 +299,22 @@ func GetHashFromNodeList(nodeList []Slot) []byte {
}
// 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
// be made an explicit invariant to be maintained and, if needed, checked.
copy := ss.DeepCopy()
sort.Slice(copy, func(i, j int) bool {
return copy[i].ShardID < copy[j].ShardID
sort.Slice(copy.Shards, func(i, j int) bool {
return copy.Shards[i].ShardID < copy.Shards[j].ShardID
})
d := sha3.NewLegacyKeccak256()
for i := range copy {
hash := GetHashFromNodeList(copy[i].Slots)
for i := range copy.Shards {
hash := GetHashFromNodeList(copy.Shards[i].Slots)
d.Write(hash)
}
d.Sum(h[:0])
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
func (n Slot) Serialize() []byte {
return append(n.EcdsaAddress[:], n.BlsPublicKey[:]...)

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

@ -271,19 +271,6 @@ func UpdateValidatorFromEditMsg(validator *Validator, edit *EditValidator) error
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 {
index := -1
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:]...)
}
}
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
}

Loading…
Cancel
Save