Merge remote-tracking branch 'origin/master' into slashing

pull/1850/head
Edgar Aroutiounian 5 years ago
commit cd228e9a6f
  1. 16
      api/service/explorer/service.go
  2. 8
      api/service/explorer/storage_test.go
  3. 15
      cmd/staking/root.go
  4. 2
      consensus/consensus_service.go
  5. 2
      consensus/consensus_v2.go
  6. 4
      consensus/engine/consensus_engine.go
  7. 233
      core/blockchain.go
  8. 2
      core/chain_makers.go
  9. 5
      core/evm.go
  10. 83
      core/rawdb/accessors_chain.go
  11. 14
      core/rawdb/schema.go
  12. 2
      core/state/statedb.go
  13. 11
      core/state_transition.go
  14. 21
      core/types/block.go
  15. 11
      core/types/bodyfieldsetter.go
  16. 14
      core/types/bodyv0.go
  17. 14
      core/types/bodyv1.go
  18. 9
      core/types/bodyv2.go
  19. 11
      hmy/api_backend.go
  20. 25
      internal/chain/engine.go
  21. 26
      internal/chain/reward.go
  22. 4
      internal/hmyapi/backend.go
  23. 60
      internal/hmyapi/blockchain.go
  24. 33
      internal/hmyapi/types.go
  25. 2
      node/node_cross_shard.go
  26. 28
      node/node_handler.go
  27. 1
      node/node_newblock.go
  28. 6
      node/node_resharding.go
  29. 26
      shard/committee/assignment.go
  30. 42
      shard/shard_state.go
  31. 12
      shard/shard_state_test.go
  32. 22
      staking/types/delegation.go
  33. 27
      staking/types/validator.go
  34. 12
      test/deploy.sh

@ -282,8 +282,8 @@ func (s *Service) GetExplorerBlocks(w http.ResponseWriter, r *http.Request) {
curEpoch = int64(block.Epoch) curEpoch = int64(block.Epoch)
} }
if withSigners { if withSigners {
pubkeys := make([]*bls.PublicKey, len(committee.NodeList)) pubkeys := make([]*bls.PublicKey, len(committee.Slots))
for i, validator := range committee.NodeList { for i, validator := range committee.Slots {
pubkeys[i] = new(bls.PublicKey) pubkeys[i] = new(bls.PublicKey)
validator.BlsPublicKey.ToLibBLSPublicKey(pubkeys[i]) validator.BlsPublicKey.ToLibBLSPublicKey(pubkeys[i])
} }
@ -291,7 +291,7 @@ func (s *Service) GetExplorerBlocks(w http.ResponseWriter, r *http.Request) {
if err == nil && accountBlocks[id+1] != nil { if err == nil && accountBlocks[id+1] != nil {
err = mask.SetMask(accountBlocks[id+1].Header().LastCommitBitmap()) err = mask.SetMask(accountBlocks[id+1].Header().LastCommitBitmap())
if err == nil { if err == nil {
for _, validator := range committee.NodeList { for _, validator := range committee.Slots {
oneAddress, err := common2.AddressToBech32(validator.EcdsaAddress) oneAddress, err := common2.AddressToBech32(validator.EcdsaAddress)
if err != nil { if err != nil {
continue continue
@ -403,8 +403,8 @@ func (s *ServiceAPI) GetExplorerBlocks(ctx context.Context, from, to, page, offs
curEpoch = int64(block.Epoch) curEpoch = int64(block.Epoch)
} }
if withSigners { if withSigners {
pubkeys := make([]*bls.PublicKey, len(committee.NodeList)) pubkeys := make([]*bls.PublicKey, len(committee.Slots))
for i, validator := range committee.NodeList { for i, validator := range committee.Slots {
pubkeys[i] = new(bls.PublicKey) pubkeys[i] = new(bls.PublicKey)
validator.BlsPublicKey.ToLibBLSPublicKey(pubkeys[i]) validator.BlsPublicKey.ToLibBLSPublicKey(pubkeys[i])
} }
@ -412,7 +412,7 @@ func (s *ServiceAPI) GetExplorerBlocks(ctx context.Context, from, to, page, offs
if err == nil && accountBlocks[id+1] != nil { if err == nil && accountBlocks[id+1] != nil {
err = mask.SetMask(accountBlocks[id+1].Header().LastCommitBitmap()) err = mask.SetMask(accountBlocks[id+1].Header().LastCommitBitmap())
if err == nil { if err == nil {
for _, validator := range committee.NodeList { for _, validator := range committee.Slots {
oneAddress, err := common2.AddressToBech32(validator.EcdsaAddress) oneAddress, err := common2.AddressToBech32(validator.EcdsaAddress)
if err != nil { if err != nil {
continue continue
@ -592,7 +592,7 @@ func (s *Service) GetExplorerCommittee(w http.ResponseWriter, r *http.Request) {
return return
} }
validators := &Committee{} validators := &Committee{}
for _, validator := range committee.NodeList { for _, validator := range committee.Slots {
validatorBalance := big.NewInt(0) validatorBalance := big.NewInt(0)
validatorBalance, err := s.GetAccountBalance(validator.EcdsaAddress) validatorBalance, err := s.GetAccountBalance(validator.EcdsaAddress)
if err != nil { if err != nil {
@ -645,7 +645,7 @@ func (s *ServiceAPI) GetExplorerCommittee(ctx context.Context, shardID uint32, e
return nil, err return nil, err
} }
validators := &Committee{} validators := &Committee{}
for _, validator := range committee.NodeList { for _, validator := range committee.Slots {
validatorBalance := big.NewInt(0) validatorBalance := big.NewInt(0)
validatorBalance, err := s.Service.GetAccountBalance(validator.EcdsaAddress) validatorBalance, err := s.Service.GetAccountBalance(validator.EcdsaAddress)
if err != nil { if err != nil {

@ -90,10 +90,10 @@ func TestDumpCommittee(t *testing.T) {
BlsPublicKey2 := new(shard.BlsPublicKey) BlsPublicKey2 := new(shard.BlsPublicKey)
BlsPublicKey1.FromLibBLSPublicKey(blsPubKey1) BlsPublicKey1.FromLibBLSPublicKey(blsPubKey1)
BlsPublicKey2.FromLibBLSPublicKey(blsPubKey2) BlsPublicKey2.FromLibBLSPublicKey(blsPubKey2)
nodeID1 := shard.NodeID{EcdsaAddress: common.HexToAddress("52789f18a342da8023cc401e5d2b14a6b710fba9"), BlsPublicKey: *BlsPublicKey1} nodeID1 := shard.Slot{EcdsaAddress: common.HexToAddress("52789f18a342da8023cc401e5d2b14a6b710fba9"), BlsPublicKey: *BlsPublicKey1}
nodeID2 := shard.NodeID{EcdsaAddress: common.HexToAddress("7c41e0668b551f4f902cfaec05b5bdca68b124ce"), BlsPublicKey: *BlsPublicKey2} nodeID2 := shard.Slot{EcdsaAddress: common.HexToAddress("7c41e0668b551f4f902cfaec05b5bdca68b124ce"), BlsPublicKey: *BlsPublicKey2}
nodeIDList := []shard.NodeID{nodeID1, nodeID2} nodeIDList := []shard.Slot{nodeID1, nodeID2}
committee := shard.Committee{ShardID: uint32(0), NodeList: nodeIDList} committee := shard.Committee{ShardID: uint32(0), Slots: nodeIDList}
shardID := uint32(0) shardID := uint32(0)
epoch := uint64(0) epoch := uint64(0)
ins := GetStorageInstance("1.1.1.1", "3333", true) ins := GetStorageInstance("1.1.1.1", "3333", true)

@ -11,6 +11,8 @@ import (
"path" "path"
"strconv" "strconv"
"github.com/harmony-one/harmony/common/denominations"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
@ -75,9 +77,9 @@ func (s *staker) run(cmd *cobra.Command, args []string) error {
pub := shard.BlsPublicKey{} pub := shard.BlsPublicKey{}
pub.FromLibBLSPublicKey(p) pub.FromLibBLSPublicKey(p)
ra, _ := numeric.NewDecFromStr("27.27") ra, _ := numeric.NewDecFromStr("0.2")
maxRate, _ := numeric.NewDecFromStr("150.99") maxRate, _ := numeric.NewDecFromStr("1")
maxChangeRate, _ := numeric.NewDecFromStr("0.5") maxChangeRate, _ := numeric.NewDecFromStr("0.05")
if cmdType == "create" { if cmdType == "create" {
return staking.DirectiveCreateValidator, staking.CreateValidator{ return staking.DirectiveCreateValidator, staking.CreateValidator{
Description: &staking.Description{ Description: &staking.Description{
@ -92,11 +94,11 @@ func (s *staker) run(cmd *cobra.Command, args []string) error {
MaxRate: maxRate, MaxRate: maxRate,
MaxChangeRate: maxChangeRate, MaxChangeRate: maxChangeRate,
}, },
MinSelfDelegation: big.NewInt(10), MinSelfDelegation: big.NewInt(denominations.One),
MaxTotalDelegation: big.NewInt(3000), MaxTotalDelegation: big.NewInt(0).Mul(big.NewInt(denominations.One), big.NewInt(1000)),
ValidatorAddress: common.Address(dAddr), ValidatorAddress: common.Address(dAddr),
SlotPubKeys: []shard.BlsPublicKey{pub}, SlotPubKeys: []shard.BlsPublicKey{pub},
Amount: big.NewInt(100), Amount: big.NewInt(denominations.One),
} }
} }
/* /*
@ -133,6 +135,7 @@ func (s *staker) run(cmd *cobra.Command, args []string) error {
if oops1 != nil { if oops1 != nil {
return oops1 return oops1
} }
tx := new(staking.StakingTransaction) tx := new(staking.StakingTransaction)
if err := rlp.DecodeBytes(enc, tx); err != nil { if err := rlp.DecodeBytes(enc, tx); err != nil {
return err return err

@ -429,7 +429,7 @@ func (consensus *Consensus) getLeaderPubKeyFromCoinbase(header *block.Header) (*
) )
} }
committerKey := new(bls.PublicKey) committerKey := new(bls.PublicKey)
for _, member := range committee.NodeList { for _, member := range committee.Slots {
if member.EcdsaAddress == header.Coinbase() { if member.EcdsaAddress == header.Coinbase() {
err := member.BlsPublicKey.ToLibBLSPublicKey(committerKey) err := member.BlsPublicKey.ToLibBLSPublicKey(committerKey)
if err != nil { if err != nil {

@ -877,6 +877,8 @@ func (consensus *Consensus) finalizeCommits() {
Uint64("ViewId", block.Header().ViewID().Uint64()). Uint64("ViewId", block.Header().ViewID().Uint64()).
Str("blockHash", block.Hash().String()). Str("blockHash", block.Hash().String()).
Int("index", consensus.Decider.IndexOf(consensus.PubKey)). Int("index", consensus.Decider.IndexOf(consensus.PubKey)).
Int("numTxns", len(block.Transactions())).
Int("numStakingTxns", len(block.StakingTransactions())).
Msg("HOORAY!!!!!!! CONSENSUS REACHED!!!!!!!") Msg("HOORAY!!!!!!! CONSENSUS REACHED!!!!!!!")
// Send signal to Node so the new block can be added and new round of consensus can be triggered // Send signal to Node so the new block can be added and new round of consensus can be triggered
consensus.ReadySignal <- struct{}{} consensus.ReadySignal <- struct{}{}

@ -41,8 +41,8 @@ type ChainReader interface {
// 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)
// CurrentValidatorAddresses retrieves the current list of validators // ReadActiveValidatorList retrieves the list of active validators
CurrentValidatorAddresses() []common.Address ReadActiveValidatorList() ([]common.Address, error)
} }
// Engine is an algorithm agnostic consensus engine. // Engine is an algorithm agnostic consensus engine.

@ -70,9 +70,9 @@ const (
commitsCacheLimit = 10 commitsCacheLimit = 10
epochCacheLimit = 10 epochCacheLimit = 10
randomnessCacheLimit = 10 randomnessCacheLimit = 10
stakingCacheLimit = 256 validatorCacheLimit = 1024
validatorListCacheLimit = 2 validatorListCacheLimit = 10
validatorListByDelegatorCacheLimit = 256 validatorListByDelegatorCacheLimit = 1024
// 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
@ -135,7 +135,7 @@ type BlockChain struct {
lastCommitsCache *lru.Cache lastCommitsCache *lru.Cache
epochCache *lru.Cache // Cache epoch number → first block number epochCache *lru.Cache // Cache epoch number → first block number
randomnessCache *lru.Cache // Cache for vrf/vdf randomnessCache *lru.Cache // Cache for vrf/vdf
stakingCache *lru.Cache // Cache for staking validator validatorCache *lru.Cache // Cache for staking validator
validatorListCache *lru.Cache // Cache of validator list validatorListCache *lru.Cache // Cache of validator list
validatorListByDelegatorCache *lru.Cache // Cache of validator list by delegator validatorListByDelegatorCache *lru.Cache // Cache of validator list by delegator
@ -174,7 +174,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
commitsCache, _ := lru.New(commitsCacheLimit) commitsCache, _ := lru.New(commitsCacheLimit)
epochCache, _ := lru.New(epochCacheLimit) epochCache, _ := lru.New(epochCacheLimit)
randomnessCache, _ := lru.New(randomnessCacheLimit) randomnessCache, _ := lru.New(randomnessCacheLimit)
stakingCache, _ := lru.New(stakingCacheLimit) stakingCache, _ := lru.New(validatorCacheLimit)
validatorListCache, _ := lru.New(validatorListCacheLimit) validatorListCache, _ := lru.New(validatorListCacheLimit)
validatorListByDelegatorCache, _ := lru.New(validatorListByDelegatorCacheLimit) validatorListByDelegatorCache, _ := lru.New(validatorListByDelegatorCacheLimit)
@ -195,7 +195,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
lastCommitsCache: commitsCache, lastCommitsCache: commitsCache,
epochCache: epochCache, epochCache: epochCache,
randomnessCache: randomnessCache, randomnessCache: randomnessCache,
stakingCache: stakingCache, validatorCache: stakingCache,
validatorListCache: validatorListCache, validatorListCache: validatorListCache,
validatorListByDelegatorCache: validatorListByDelegatorCache, validatorListByDelegatorCache: validatorListByDelegatorCache,
engine: engine, engine: engine,
@ -1078,6 +1078,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
batch := bc.db.NewBatch() batch := bc.db.NewBatch()
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receipts) rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receipts)
//// Cross-shard txns
epoch := block.Header().Epoch() epoch := block.Header().Epoch()
if bc.chainConfig.IsCrossTx(block.Epoch()) { if bc.chainConfig.IsCrossTx(block.Epoch()) {
shardingConfig := shard.Schedule.InstanceForEpoch(epoch) shardingConfig := shard.Schedule.InstanceForEpoch(epoch)
@ -1097,6 +1098,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
bc.WriteCXReceiptsProofSpent(block.IncomingReceipts()) bc.WriteCXReceiptsProofSpent(block.IncomingReceipts())
} }
//// VRF + VDF
//check non zero VRF field in header and add to local db //check non zero VRF field in header and add to local db
if len(block.Vrf()) > 0 { if len(block.Vrf()) > 0 {
vrfBlockNumbers, _ := bc.ReadEpochVrfBlockNums(block.Header().Epoch()) vrfBlockNumbers, _ := bc.ReadEpochVrfBlockNums(block.Header().Epoch())
@ -1130,16 +1132,58 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
} }
} }
//// Shard State and Validator Update
header := block.Header() header := block.Header()
if header.ShardStateHash() != (common.Hash{}) { if header.ShardStateHash() != (common.Hash{}) {
// Write shard state for the new epoch
epoch := new(big.Int).Add(header.Epoch(), common.Big1) epoch := new(big.Int).Add(header.Epoch(), common.Big1)
err = bc.WriteShardStateBytes(batch, epoch, header.ShardState()) shardState, err := bc.WriteShardStateBytes(batch, epoch, header.ShardState())
if err != nil { if err != nil {
header.Logger(utils.Logger()).Warn().Err(err).Msg("cannot store shard state") header.Logger(utils.Logger()).Warn().Err(err).Msg("cannot store shard state")
return NonStatTy, err return NonStatTy, err
} }
// Find all the active validator addresses and store them in db
allActiveValidators := []common.Address{}
processed := make(map[common.Address]struct{})
for i := range *shardState {
shard := (*shardState)[i]
for j := range shard.Slots {
slot := shard.Slots[j]
if slot.StakeWithDelegationApplied != nil { // For external validator
_, ok := processed[slot.EcdsaAddress]
if !ok {
processed[slot.EcdsaAddress] = struct{}{}
allActiveValidators = append(allActiveValidators, shard.Slots[j].EcdsaAddress)
}
}
}
}
if err := bc.WriteActiveValidatorList(allActiveValidators); err != nil {
return NonStatTy, err
}
// Create snapshot for all validators
if err := bc.UpdateValidatorSnapshots(); err != nil {
return NonStatTy, err
}
} }
// Do bookkeeping for new staking txns
if bc.chainConfig.IsStaking(block.Epoch()) {
for _, tx := range block.StakingTransactions() {
err = bc.UpdateStakingMetaData(tx)
// keep offchain database consistency with onchain we need revert
// but it should not happend unless local database corrupted
if err != nil {
utils.Logger().Debug().Msgf("oops, UpdateStakingMetaData failed, err: %+v", err)
return NonStatTy, err
}
}
}
//// Cross-links
if len(header.CrossLinks()) > 0 { if len(header.CrossLinks()) > 0 {
crossLinks := &types.CrossLinks{} crossLinks := &types.CrossLinks{}
err = rlp.DecodeBytes(header.CrossLinks(), crossLinks) err = rlp.DecodeBytes(header.CrossLinks(), crossLinks)
@ -1159,19 +1203,6 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
bc.WriteShardLastCrossLink(crossLink.ShardID(), crossLink) bc.WriteShardLastCrossLink(crossLink.ShardID(), crossLink)
} }
} }
if bc.chainConfig.IsStaking(block.Epoch()) {
for _, tx := range block.StakingTransactions() {
err = bc.UpdateStakingMetaData(tx)
// keep offchain database consistency with onchain we need revert
// but it should not happend unless local database corrupted
if err != nil {
utils.Logger().Debug().Msgf("oops, UpdateStakingMetaData failed, err: %+v", err)
return NonStatTy, err
}
}
}
/////////////////////////// END /////////////////////////// END
// If the total difficulty is higher than our known, add it to the canonical chain // If the total difficulty is higher than our known, add it to the canonical chain
@ -1870,18 +1901,18 @@ func (bc *BlockChain) WriteShardState(
// 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,
) error { ) (*shard.State, error) {
decodeShardState := shard.State{} decodeShardState := shard.State{}
if err := rlp.DecodeBytes(shardState, &decodeShardState); err != nil { if err := rlp.DecodeBytes(shardState, &decodeShardState); err != nil {
return err return nil, err
} }
err := rawdb.WriteShardStateBytes(db, epoch, shardState) err := rawdb.WriteShardStateBytes(db, epoch, shardState)
if err != nil { if err != nil {
return err return nil, err
} }
cacheKey := string(epoch.Bytes()) cacheKey := string(epoch.Bytes())
bc.shardStateCache.Add(cacheKey, decodeShardState) bc.shardStateCache.Add(cacheKey, decodeShardState)
return nil return &decodeShardState, nil
} }
// ReadLastCommits retrieves last commits. // ReadLastCommits retrieves last commits.
@ -2276,9 +2307,9 @@ func (bc *BlockChain) ReadTxLookupEntry(txID common.Hash) (common.Hash, uint64,
return rawdb.ReadTxLookupEntry(bc.db, txID) return rawdb.ReadTxLookupEntry(bc.db, txID)
} }
// ReadStakingValidator reads staking information of given validatorWrapper // ReadValidatorData reads staking information of given validatorWrapper
func (bc *BlockChain) ReadStakingValidator(addr common.Address) (*staking.ValidatorWrapper, error) { func (bc *BlockChain) ReadValidatorData(addr common.Address) (*staking.ValidatorWrapper, error) {
if cached, ok := bc.stakingCache.Get("staking-" + string(addr.Bytes())); ok { if cached, ok := bc.validatorCache.Get("validator-" + string(addr.Bytes())); ok {
by := cached.([]byte) by := cached.([]byte)
v := staking.ValidatorWrapper{} v := staking.ValidatorWrapper{}
if err := rlp.DecodeBytes(by, &v); err != nil { if err := rlp.DecodeBytes(by, &v); err != nil {
@ -2287,12 +2318,12 @@ func (bc *BlockChain) ReadStakingValidator(addr common.Address) (*staking.Valida
return &v, nil return &v, nil
} }
return rawdb.ReadStakingValidator(bc.db, addr) return rawdb.ReadValidatorData(bc.db, addr)
} }
// WriteStakingValidator reads staking information of given validatorWrapper // WriteValidatorData writes staking information of given validatorWrapper
func (bc *BlockChain) WriteStakingValidator(v *staking.ValidatorWrapper) error { func (bc *BlockChain) WriteValidatorData(v *staking.ValidatorWrapper) error {
err := rawdb.WriteStakingValidator(bc.db, v) err := rawdb.WriteValidatorData(bc.db, v)
if err != nil { if err != nil {
return err return err
} }
@ -2300,7 +2331,90 @@ func (bc *BlockChain) WriteStakingValidator(v *staking.ValidatorWrapper) error {
if err != nil { if err != nil {
return err return err
} }
bc.stakingCache.Add("staking-"+string(v.Address.Bytes()), by) bc.validatorCache.Add("validator-"+string(v.Address.Bytes()), by)
return nil
}
// ReadValidatorSnapshot reads the snapshot staking information of given validator address
// TODO: put epoch number in to snapshot too.
func (bc *BlockChain) ReadValidatorSnapshot(addr common.Address) (*staking.ValidatorWrapper, error) {
if cached, ok := bc.validatorCache.Get("validator-snapshot-" + string(addr.Bytes())); ok {
by := cached.([]byte)
v := staking.ValidatorWrapper{}
if err := rlp.DecodeBytes(by, &v); err != nil {
return nil, err
}
return &v, nil
}
return rawdb.ReadValidatorSnapshot(bc.db, addr)
}
// WriteValidatorSnapshots writes the snapshot of provided list of validators
func (bc *BlockChain) WriteValidatorSnapshots(addrs []common.Address) error {
// Read all validator's current data
validators := []*staking.ValidatorWrapper{}
for _, addr := range addrs {
validator, err := bc.ReadValidatorData(addr)
if err != nil {
return err
}
validators = append(validators, validator)
}
// Batch write the current data as snapshot
batch := bc.db.NewBatch()
for i := range validators {
err := rawdb.WriteValidatorSnapshot(batch, validators[i])
if err != nil {
return err
}
}
if err := batch.Write(); err != nil {
return err
}
// Update cache
for i := range validators {
by, err := rlp.EncodeToBytes(validators[i])
if err == nil {
bc.validatorCache.Add("validator-snapshot-"+string(validators[i].Address.Bytes()), by)
}
}
return nil
}
// DeleteValidatorSnapshots deletes the snapshot staking information of given validator address
func (bc *BlockChain) DeleteValidatorSnapshots(addrs []common.Address) error {
batch := bc.db.NewBatch()
for i := range addrs {
rawdb.DeleteValidatorSnapshot(batch, addrs[i])
}
if err := batch.Write(); err != nil {
return err
}
for i := range addrs {
bc.validatorCache.Remove("validator-snapshot-" + string(addrs[i].Bytes()))
}
return nil
}
// UpdateValidatorSnapshots updates the content snapshot of all validators
func (bc *BlockChain) UpdateValidatorSnapshots() error {
allValidators, err := bc.ReadValidatorList()
if err != nil {
return err
}
// TODO: enable this once we allow validator to delete itself.
//err = bc.DeleteValidatorSnapshots(allValidators)
//if err != nil {
// return err
//}
if err := bc.WriteValidatorSnapshots(allValidators); err != nil {
return err
}
return nil return nil
} }
@ -2314,12 +2428,12 @@ func (bc *BlockChain) ReadValidatorList() ([]common.Address, error) {
} }
return m, nil return m, nil
} }
return rawdb.ReadValidatorList(bc.db) return rawdb.ReadValidatorList(bc.db, false)
} }
// WriteValidatorList writes the list of validator addresses to database // WriteValidatorList writes the list of validator addresses to database
func (bc *BlockChain) WriteValidatorList(addrs []common.Address) error { func (bc *BlockChain) WriteValidatorList(addrs []common.Address) error {
err := rawdb.WriteValidatorList(bc.db, addrs) err := rawdb.WriteValidatorList(bc.db, addrs, false)
if err != nil { if err != nil {
return err return err
} }
@ -2330,6 +2444,32 @@ func (bc *BlockChain) WriteValidatorList(addrs []common.Address) error {
return nil return nil
} }
// ReadActiveValidatorList reads the addresses of active validators
func (bc *BlockChain) ReadActiveValidatorList() ([]common.Address, error) {
if cached, ok := bc.validatorListCache.Get("activeValidatorList"); ok {
by := cached.([]byte)
m := []common.Address{}
if err := rlp.DecodeBytes(by, &m); err != nil {
return nil, err
}
return m, nil
}
return rawdb.ReadValidatorList(bc.db, true)
}
// WriteActiveValidatorList writes the list of active validator addresses to database
func (bc *BlockChain) WriteActiveValidatorList(addrs []common.Address) error {
err := rawdb.WriteValidatorList(bc.db, addrs, true)
if err != nil {
return err
}
bytes, err := rlp.EncodeToBytes(addrs)
if err == nil {
bc.validatorListCache.Add("activeValidatorList", bytes)
}
return nil
}
// ReadValidatorListByDelegator reads the addresses of validators delegated by a delegator // ReadValidatorListByDelegator reads the addresses of validators delegated by a delegator
func (bc *BlockChain) ReadValidatorListByDelegator(delegator common.Address) ([]common.Address, error) { func (bc *BlockChain) ReadValidatorListByDelegator(delegator common.Address) ([]common.Address, error) {
if cached, ok := bc.validatorListByDelegatorCache.Get(delegator.Bytes()); ok { if cached, ok := bc.validatorListByDelegatorCache.Get(delegator.Bytes()); ok {
@ -2413,31 +2553,6 @@ func (bc *BlockChain) UpdateStakingMetaData(tx *staking.StakingTransaction) erro
return nil return nil
} }
// CurrentValidatorAddresses returns the address of active validators for current epoch
func (bc *BlockChain) CurrentValidatorAddresses() []common.Address {
list, err := bc.ReadValidatorList()
if err != nil {
return make([]common.Address, 0)
}
currentEpoch := bc.CurrentBlock().Epoch()
filtered := []common.Address{}
for _, addr := range list {
val, err := bc.ValidatorInformation(addr)
if err != nil {
continue
}
epoch := shard.Schedule.CalcEpochNumber(val.CreationHeight.Uint64())
if epoch.Cmp(currentEpoch) >= 0 {
// wait for next epoch
continue
}
filtered = append(filtered, addr)
}
return filtered
}
// ValidatorCandidates returns the up to date validator candidates for next epoch // ValidatorCandidates returns the up to date validator candidates for next epoch
func (bc *BlockChain) ValidatorCandidates() []common.Address { func (bc *BlockChain) ValidatorCandidates() []common.Address {
list, err := bc.ReadValidatorList() list, err := bc.ReadValidatorList()

@ -270,4 +270,4 @@ func (cr *fakeChainReader) GetHeaderByHash(hash common.Hash) *block.Header
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) CurrentValidatorAddresses() []common.Address { return nil } func (cr *fakeChainReader) ReadActiveValidatorList() ([]common.Address, error) { return nil, nil }

@ -19,6 +19,8 @@ package core
import ( import (
"math/big" "math/big"
types2 "github.com/harmony-one/harmony/staking/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/block" "github.com/harmony-one/harmony/block"
@ -38,6 +40,9 @@ type ChainContext interface {
// ReadValidatorListByDelegator returns the validators list of a delegator // ReadValidatorListByDelegator returns the validators list of a delegator
ReadValidatorListByDelegator(common.Address) ([]common.Address, error) ReadValidatorListByDelegator(common.Address) ([]common.Address, error)
// ReadValidatorSnapshot returns the snapshot of validator at the beginning of current epoch.
ReadValidatorSnapshot(common.Address) (*types2.ValidatorWrapper, error)
} }
// NewEVMContext creates a new context for use in the EVM. // NewEVMContext creates a new context for use in the EVM.

@ -542,7 +542,7 @@ func WriteShardLastCrossLink(db DatabaseWriter, shardID uint32, data []byte) err
// ReadCXReceipts retrieves all the transactions of receipts given destination shardID, number and blockHash // ReadCXReceipts retrieves all the transactions of receipts given destination shardID, number and blockHash
func ReadCXReceipts(db DatabaseReader, shardID uint32, number uint64, hash common.Hash, temp bool) (types.CXReceipts, error) { func ReadCXReceipts(db DatabaseReader, shardID uint32, number uint64, hash common.Hash, temp bool) (types.CXReceipts, error) {
data, err := db.Get(cxReceiptKey(shardID, number, hash, temp)) data, err := db.Get(cxReceiptKey(shardID, number, hash, temp))
if len(data) == 0 || err != nil { if err != nil || len(data) == 0 {
utils.Logger().Info().Err(err).Uint64("number", number).Int("dataLen", len(data)).Msg("ReadCXReceipts") utils.Logger().Info().Err(err).Uint64("number", number).Int("dataLen", len(data)).Msg("ReadCXReceipts")
return nil, err return nil, err
} }
@ -614,11 +614,40 @@ func WriteCXReceiptsProofUnspentCheckpoint(db DatabaseWriter, shardID uint32, bl
return db.Put(cxReceiptUnspentCheckpointKey(shardID), by) return db.Put(cxReceiptUnspentCheckpointKey(shardID), by)
} }
// ReadStakingValidator retrieves staking validator by its address // ReadValidatorData retrieves staking validator by its address
func ReadStakingValidator(db DatabaseReader, addr common.Address) (*staking.ValidatorWrapper, error) { func ReadValidatorData(db DatabaseReader, addr common.Address) (*staking.ValidatorWrapper, error) {
data, err := db.Get(stakingKey(addr)) data, err := db.Get(validatorKey(addr))
if len(data) == 0 || err != nil { if err != nil || len(data) == 0 {
utils.Logger().Info().Err(err).Msg("ReadStakingValidator") utils.Logger().Info().Err(err).Msg("ReadValidatorData")
return nil, err
}
v := staking.ValidatorWrapper{}
if err := rlp.DecodeBytes(data, &v); err != nil {
utils.Logger().Error().Err(err).Str("address", addr.Hex()).Msg("Unable to Decode staking validator from database")
return nil, err
}
return &v, nil
}
// WriteValidatorData stores validator's information by its address
func WriteValidatorData(db DatabaseWriter, v *staking.ValidatorWrapper) error {
bytes, err := rlp.EncodeToBytes(v)
if err != nil {
utils.Logger().Error().Msg("[WriteValidatorData] Failed to encode")
return err
}
if err := db.Put(validatorKey(v.Address), bytes); err != nil {
utils.Logger().Error().Msg("[WriteValidatorData] Failed to store to database")
return err
}
return err
}
// ReadValidatorSnapshot retrieves validator's snapshot by its address
func ReadValidatorSnapshot(db DatabaseReader, addr common.Address) (*staking.ValidatorWrapper, error) {
data, err := db.Get(validatorSnapshotKey(addr))
if err != nil || len(data) == 0 {
utils.Logger().Info().Err(err).Msg("ReadValidatorSnapshot")
return nil, err return nil, err
} }
v := staking.ValidatorWrapper{} v := staking.ValidatorWrapper{}
@ -629,22 +658,36 @@ func ReadStakingValidator(db DatabaseReader, addr common.Address) (*staking.Vali
return &v, nil return &v, nil
} }
// WriteStakingValidator stores staking validator's information by its address // WriteValidatorSnapshot stores validator's snapshot by its address
func WriteStakingValidator(db DatabaseWriter, v *staking.ValidatorWrapper) error { func WriteValidatorSnapshot(db DatabaseWriter, v *staking.ValidatorWrapper) error {
bytes, err := rlp.EncodeToBytes(v) bytes, err := rlp.EncodeToBytes(v)
if err != nil { if err != nil {
utils.Logger().Error().Msg("[WriteStakingValidator] Failed to encode") utils.Logger().Error().Msg("[WriteValidatorSnapshot] Failed to encode")
return err
} }
if err := db.Put(stakingKey(v.Address), bytes); err != nil { if err := db.Put(validatorSnapshotKey(v.Address), bytes); err != nil {
utils.Logger().Error().Msg("[WriteStakingValidator] Failed to store to database") utils.Logger().Error().Msg("[WriteValidatorSnapshot] Failed to store to database")
return err
} }
return err return err
} }
// DeleteValidatorSnapshot removes the validator's snapshot by its address
func DeleteValidatorSnapshot(db DatabaseDeleter, addr common.Address) {
if err := db.Delete(validatorSnapshotKey(addr)); err != nil {
utils.Logger().Error().Msg("Failed to delete snapshot of a validator")
}
}
// ReadValidatorList retrieves staking validator by its address // ReadValidatorList retrieves staking validator by its address
func ReadValidatorList(db DatabaseReader) ([]common.Address, error) { // Return only active validators if activeOnly==true, otherwise, return all validators
data, err := db.Get([]byte("validatorList")) func ReadValidatorList(db DatabaseReader, activeOnly bool) ([]common.Address, error) {
if len(data) == 0 || err != nil { key := validatorListKey
if activeOnly {
key = activeValidatorListKey
}
data, err := db.Get(key)
if err != nil || len(data) == 0 {
return []common.Address{}, nil return []common.Address{}, nil
} }
addrs := []common.Address{} addrs := []common.Address{}
@ -656,12 +699,18 @@ func ReadValidatorList(db DatabaseReader) ([]common.Address, error) {
} }
// WriteValidatorList stores staking validator's information by its address // WriteValidatorList stores staking validator's information by its address
func WriteValidatorList(db DatabaseWriter, addrs []common.Address) error { // Writes only for active validators if activeOnly==true, otherwise, writes for all validators
func WriteValidatorList(db DatabaseWriter, addrs []common.Address, activeOnly bool) error {
key := validatorListKey
if activeOnly {
key = activeValidatorListKey
}
bytes, err := rlp.EncodeToBytes(addrs) bytes, err := rlp.EncodeToBytes(addrs)
if err != nil { if err != nil {
utils.Logger().Error().Msg("[WriteValidatorList] Failed to encode") utils.Logger().Error().Msg("[WriteValidatorList] Failed to encode")
} }
if err := db.Put([]byte("validatorList"), bytes); err != nil { if err := db.Put(key, bytes); err != nil {
utils.Logger().Error().Msg("[WriteValidatorList] Failed to store to database") utils.Logger().Error().Msg("[WriteValidatorList] Failed to store to database")
} }
return err return err
@ -670,7 +719,7 @@ func WriteValidatorList(db DatabaseWriter, addrs []common.Address) error {
// ReadValidatorListByDelegator retrieves the list of validators delegated by a delegator // ReadValidatorListByDelegator retrieves the list of validators delegated by a delegator
func ReadValidatorListByDelegator(db DatabaseReader, delegator common.Address) ([]common.Address, error) { func ReadValidatorListByDelegator(db DatabaseReader, delegator common.Address) ([]common.Address, error) {
data, err := db.Get(delegatorValidatorListKey(delegator)) data, err := db.Get(delegatorValidatorListKey(delegator))
if len(data) == 0 || err != nil { if err != nil || len(data) == 0 {
return []common.Address{}, nil return []common.Address{}, nil
} }
addrs := []common.Address{} addrs := []common.Address{}

@ -74,7 +74,10 @@ var (
cxReceiptSpentPrefix = []byte("cxReceiptSpent") // prefix for indicator of unspent of cxReceiptsProof cxReceiptSpentPrefix = []byte("cxReceiptSpent") // prefix for indicator of unspent of cxReceiptsProof
cxReceiptUnspentCheckpointPrefix = []byte("cxReceiptUnspentCheckpoint") // prefix for cxReceiptsProof unspent checkpoint cxReceiptUnspentCheckpointPrefix = []byte("cxReceiptUnspentCheckpoint") // prefix for cxReceiptsProof unspent checkpoint
stakingPrefix = []byte("staking") // prefix for staking validator information validatorPrefix = []byte("validator-") // prefix for staking validator information
validatorSnapshotPrefix = []byte("validator-snapshot-") // prefix for staking validator's snapshot information
validatorListKey = []byte("validator-list") // key for all validators list
activeValidatorListKey = []byte("active-validator-list") // key for active validators list
// epochBlockNumberPrefix + epoch (big.Int.Bytes()) // epochBlockNumberPrefix + epoch (big.Int.Bytes())
// -> epoch block number (big.Int.Bytes()) // -> epoch block number (big.Int.Bytes())
@ -237,7 +240,12 @@ func cxReceiptUnspentCheckpointKey(shardID uint32) []byte {
return append(prefix, sKey...) return append(prefix, sKey...)
} }
func stakingKey(addr common.Address) []byte { func validatorKey(addr common.Address) []byte {
prefix := stakingPrefix prefix := validatorPrefix
return append(prefix, addr.Bytes()...)
}
func validatorSnapshotKey(addr common.Address) []byte {
prefix := validatorSnapshotPrefix
return append(prefix, addr.Bytes()...) return append(prefix, addr.Bytes()...)
} }

@ -684,7 +684,7 @@ func (db *DB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) {
return root, err return root, err
} }
// GetStakingInfo update staking information of a given validator (including delegation info) // GetStakingInfo returns staking information of a given validator (including delegation info)
func (db *DB) GetStakingInfo(addr common.Address) *stk.ValidatorWrapper { func (db *DB) GetStakingInfo(addr common.Address) *stk.ValidatorWrapper {
by := db.GetCode(addr) by := db.GetCode(addr)
if len(by) == 0 { if len(by) == 0 {

@ -354,7 +354,7 @@ func (st *StateTransition) applyCreateValidatorTx(createValidator *staking.Creat
delegations := []staking.Delegation{} delegations := []staking.Delegation{}
delegations = append(delegations, staking.NewDelegation(v.Address, createValidator.Amount)) delegations = append(delegations, staking.NewDelegation(v.Address, createValidator.Amount))
wrapper := staking.ValidatorWrapper{*v, delegations, nil, nil} wrapper := staking.ValidatorWrapper{*v, delegations}
if err := st.state.UpdateStakingInfo(v.Address, &wrapper); err != nil { if err := st.state.UpdateStakingInfo(v.Address, &wrapper); err != nil {
return err return err
@ -378,8 +378,13 @@ func (st *StateTransition) applyEditValidatorTx(editValidator *staking.EditValid
} }
newRate := wrapper.Validator.Rate newRate := wrapper.Validator.Rate
// TODO: Use snapshot of validator in this epoch. // TODO: make sure we are reading from the correct snapshot
rateAtBeginningOfEpoch := wrapper.Validator.Rate snapshotValidator, err := st.bc.ReadValidatorSnapshot(wrapper.Address)
if err != nil {
return err
}
rateAtBeginningOfEpoch := snapshotValidator.Rate
if rateAtBeginningOfEpoch.IsNil() || (!newRate.IsNil() && !rateAtBeginningOfEpoch.Equal(newRate)) { if rateAtBeginningOfEpoch.IsNil() || (!newRate.IsNil() && !rateAtBeginningOfEpoch.Equal(newRate)) {
wrapper.Validator.UpdateHeight = blockNum wrapper.Validator.UpdateHeight = blockNum
} }

@ -90,6 +90,10 @@ type BodyInterface interface {
// It returns nil if index is out of bounds. // It returns nil if index is out of bounds.
TransactionAt(index int) *Transaction TransactionAt(index int) *Transaction
// StakingTransactionAt returns the staking transaction at the given index in this block.
// It returns nil if index is out of bounds.
StakingTransactionAt(index int) *staking.StakingTransaction
// CXReceiptAt returns the CXReceipt given index (calculated from IncomingReceipts) // CXReceiptAt returns the CXReceipt given index (calculated from IncomingReceipts)
// It returns nil if index is out of bounds // It returns nil if index is out of bounds
CXReceiptAt(index int) *CXReceipt CXReceiptAt(index int) *CXReceipt
@ -98,6 +102,10 @@ type BodyInterface interface {
// given list. // given list.
SetTransactions(newTransactions []*Transaction) SetTransactions(newTransactions []*Transaction)
// SetStakingTransactions sets the list of staking transactions with a deep copy of the
// given list.
SetStakingTransactions(newStakingTransactions []*staking.StakingTransaction)
// Uncles returns a deep copy of the list of uncle headers of this block. // Uncles returns a deep copy of the list of uncle headers of this block.
Uncles() []*block.Header Uncles() []*block.Header
@ -193,7 +201,7 @@ type Block struct {
header *block.Header header *block.Header
uncles []*block.Header uncles []*block.Header
transactions Transactions transactions Transactions
stks staking.StakingTransactions stakingTransactions staking.StakingTransactions
incomingReceipts CXReceiptsProofs incomingReceipts CXReceiptsProofs
// caches // caches
@ -301,8 +309,8 @@ func NewBlock(header *block.Header, txs []*Transaction, receipts []*Receipt, out
} }
if len(stks) > 0 { if len(stks) > 0 {
b.stks = make(staking.StakingTransactions, len(stks)) b.stakingTransactions = make(staking.StakingTransactions, len(stks))
copy(b.stks, stks) copy(b.stakingTransactions, stks)
} }
return b return b
@ -332,7 +340,7 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error {
} }
switch eb := eb.(type) { switch eb := eb.(type) {
case *extblockV2: case *extblockV2:
b.header, b.uncles, b.transactions, b.incomingReceipts, b.stks = eb.Header, eb.Uncles, eb.Txs, eb.IncomingReceipts, eb.Stks b.header, b.uncles, b.transactions, b.incomingReceipts, b.stakingTransactions = eb.Header, eb.Uncles, eb.Txs, eb.IncomingReceipts, eb.Stks
case *extblockV1: case *extblockV1:
b.header, b.uncles, b.transactions, b.incomingReceipts = eb.Header, eb.Uncles, eb.Txs, eb.IncomingReceipts b.header, b.uncles, b.transactions, b.incomingReceipts = eb.Header, eb.Uncles, eb.Txs, eb.IncomingReceipts
case *extblock: case *extblock:
@ -349,7 +357,7 @@ func (b *Block) EncodeRLP(w io.Writer) error {
var eb interface{} var eb interface{}
switch h := b.header.Header.(type) { switch h := b.header.Header.(type) {
case *v3.Header: case *v3.Header:
eb = extblockV2{b.header, b.transactions, b.stks, b.uncles, b.incomingReceipts} eb = extblockV2{b.header, b.transactions, b.stakingTransactions, b.uncles, b.incomingReceipts}
case *v2.Header, *v1.Header: case *v2.Header, *v1.Header:
eb = extblockV1{b.header, b.transactions, b.uncles, b.incomingReceipts} eb = extblockV1{b.header, b.transactions, b.uncles, b.incomingReceipts}
case *v0.Header: case *v0.Header:
@ -376,7 +384,7 @@ func (b *Block) Transactions() Transactions {
// StakingTransactions returns stakingTransactions. // StakingTransactions returns stakingTransactions.
func (b *Block) StakingTransactions() staking.StakingTransactions { func (b *Block) StakingTransactions() staking.StakingTransactions {
return b.stks return b.stakingTransactions
} }
// IncomingReceipts returns verified outgoing receipts // IncomingReceipts returns verified outgoing receipts
@ -454,6 +462,7 @@ func (b *Block) Body() *Body {
} }
return body.With(). return body.With().
Transactions(b.transactions). Transactions(b.transactions).
StakingTransactions(b.stakingTransactions).
Uncles(b.uncles). Uncles(b.uncles).
IncomingReceipts(b.incomingReceipts). IncomingReceipts(b.incomingReceipts).
Body() Body()

@ -1,6 +1,9 @@
package types package types
import "github.com/harmony-one/harmony/block" import (
"github.com/harmony-one/harmony/block"
"github.com/harmony-one/harmony/staking/types"
)
// BodyFieldSetter is a body field setter. // BodyFieldSetter is a body field setter.
type BodyFieldSetter struct { type BodyFieldSetter struct {
@ -13,6 +16,12 @@ func (bfs BodyFieldSetter) Transactions(newTransactions []*Transaction) BodyFiel
return bfs return bfs
} }
// StakingTransactions sets the StakingTransactions field of the body.
func (bfs BodyFieldSetter) StakingTransactions(newStakingTransactions []*types.StakingTransaction) BodyFieldSetter {
bfs.b.SetStakingTransactions(newStakingTransactions)
return bfs
}
// Uncles sets the Uncles field of the body. // Uncles sets the Uncles field of the body.
func (bfs BodyFieldSetter) Uncles(newUncles []*block.Header) BodyFieldSetter { func (bfs BodyFieldSetter) Uncles(newUncles []*block.Header) BodyFieldSetter {
bfs.b.SetUncles(newUncles) bfs.b.SetUncles(newUncles)

@ -40,6 +40,13 @@ func (b *BodyV0) TransactionAt(index int) *Transaction {
return b.f.Transactions[index].Copy() return b.f.Transactions[index].Copy()
} }
// StakingTransactionAt returns the staking transaction at the given index in this block.
// It returns nil if index is out of bounds. (not supported by Body V0)
func (b *BodyV0) StakingTransactionAt(index int) *staking.StakingTransaction {
// not supported
return nil
}
// CXReceiptAt returns the CXReceipt at given index in this block // CXReceiptAt returns the CXReceipt at given index in this block
// It returns nil if index is out of bounds // It returns nil if index is out of bounds
// V0 will just return nil because we don't support CXReceipt // V0 will just return nil because we don't support CXReceipt
@ -57,6 +64,13 @@ func (b *BodyV0) SetTransactions(newTransactions []*Transaction) {
b.f.Transactions = txs b.f.Transactions = txs
} }
// SetStakingTransactions sets the list of staking transactions with a deep copy of the given
// list. (not supported by Body V0)
func (b *BodyV0) SetStakingTransactions(newTransactions []*staking.StakingTransaction) {
// not supported
return
}
// Uncles returns a deep copy of the list of uncle headers of this block. // Uncles returns a deep copy of the list of uncle headers of this block.
func (b *BodyV0) Uncles() (uncles []*block.Header) { func (b *BodyV0) Uncles() (uncles []*block.Header) {
for _, uncle := range b.f.Uncles { for _, uncle := range b.f.Uncles {

@ -73,6 +73,20 @@ func (b *BodyV1) SetTransactions(newTransactions []*Transaction) {
b.f.Transactions = txs b.f.Transactions = txs
} }
// SetStakingTransactions sets the list of staking transactions with a deep copy of the given
// list. (not supported by Body V1)
func (b *BodyV1) SetStakingTransactions(newTransactions []*staking.StakingTransaction) {
// not supported
return
}
// StakingTransactionAt returns the staking transaction at the given index in this block.
// It returns nil if index is out of bounds. (not supported by Body V1)
func (b *BodyV1) StakingTransactionAt(index int) *staking.StakingTransaction {
// not supported
return nil
}
// Uncles returns a deep copy of the list of uncle headers of this block. // Uncles returns a deep copy of the list of uncle headers of this block.
func (b *BodyV1) Uncles() (uncles []*block.Header) { func (b *BodyV1) Uncles() (uncles []*block.Header) {
for _, uncle := range b.f.Uncles { for _, uncle := range b.f.Uncles {

@ -51,6 +51,15 @@ func (b *BodyV2) TransactionAt(index int) *Transaction {
return b.f.Transactions[index].Copy() return b.f.Transactions[index].Copy()
} }
// StakingTransactionAt returns the staking transaction at the given index in this block.
// It returns nil if index is out of bounds.
func (b *BodyV2) StakingTransactionAt(index int) *staking.StakingTransaction {
if index < 0 || index >= len(b.f.StakingTransactions) {
return nil
}
return b.f.StakingTransactions[index].Copy()
}
// CXReceiptAt returns the CXReceipt at given index in this block // CXReceiptAt returns the CXReceipt at given index in this block
// It returns nil if index is out of bounds // It returns nil if index is out of bounds
func (b *BodyV2) CXReceiptAt(index int) *CXReceipt { func (b *BodyV2) CXReceiptAt(index int) *CXReceipt {

@ -290,13 +290,14 @@ func (b *APIBackend) SendStakingTx(
return nil return nil
} }
// GetCurrentValidatorAddresses returns the address of active validators for current epoch // GetActiveValidatorAddresses returns the address of active validators for current epoch
func (b *APIBackend) GetCurrentValidatorAddresses() []common.Address { func (b *APIBackend) GetActiveValidatorAddresses() []common.Address {
return b.hmy.BlockChain().CurrentValidatorAddresses() list, _ := b.hmy.BlockChain().ReadActiveValidatorList()
return list
} }
// GetValidatorCandidates returns the up to date validator candidates for next epoch // GetAllValidatorAddresses returns the up to date validator candidates for next epoch
func (b *APIBackend) GetValidatorCandidates() []common.Address { func (b *APIBackend) GetAllValidatorAddresses() []common.Address {
return b.hmy.BlockChain().ValidatorCandidates() return b.hmy.BlockChain().ValidatorCandidates()
} }

@ -2,7 +2,6 @@ package chain
import ( import (
"encoding/binary" "encoding/binary"
"math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
@ -194,29 +193,23 @@ func (e *engineImpl) Finalize(
// Only do such at the last block of an epoch // Only do such at the last block of an epoch
if len(header.ShardState()) > 0 { if len(header.ShardState()) > 0 {
// TODO: make sure we are using the correct validator list // TODO: make sure we are using the correct validator list
validators := chain.CurrentValidatorAddresses() validators, err := chain.ReadActiveValidatorList()
if err != nil {
return nil, ctxerror.New("failed to read active validators").WithCause(err)
}
for _, validator := range validators { for _, validator := range validators {
wrapper := state.GetStakingInfo(validator) wrapper := state.GetStakingInfo(validator)
if wrapper != nil { if wrapper != nil {
for i := range wrapper.Delegations { for i := range wrapper.Delegations {
delegation := wrapper.Delegations[i] delegation := wrapper.Delegations[i]
totalWithdraw := big.NewInt(0) totalWithdraw := delegation.RemoveUnlockedUndelegations(header.Epoch())
count := 0
for j := range delegation.Entries {
if delegation.Entries[j].Epoch.Cmp(header.Epoch()) > 14 { // need to wait at least 14 epochs to withdraw;
totalWithdraw.Add(totalWithdraw, delegation.Entries[j].Amount)
count++
} else {
break
}
}
state.AddBalance(delegation.DelegatorAddress, totalWithdraw) state.AddBalance(delegation.DelegatorAddress, totalWithdraw)
delegation.Entries = delegation.Entries[count:]
} }
if err := state.UpdateStakingInfo(validator, wrapper); err != nil { if err := state.UpdateStakingInfo(validator, wrapper); err != nil {
return nil, ctxerror.New("failed update validator info").WithCause(err) return nil, ctxerror.New("failed update validator info").WithCause(err)
} }
} else {
return nil, ctxerror.New("failed getting validator info").WithCause(err)
} }
} }
} }
@ -244,7 +237,7 @@ func QuorumForBlock(
return 0, errors.Errorf( return 0, errors.Errorf(
"cannot find shard %d in shard state", h.ShardID()) "cannot find shard %d in shard state", h.ShardID())
} }
return (len(c.NodeList))*2/3 + 1, nil return (len(c.Slots))*2/3 + 1, nil
} }
// Similiar to VerifyHeader, which is only for verifying the block headers of one's own chain, this verification // Similiar to VerifyHeader, which is only for verifying the block headers of one's own chain, this verification
@ -306,7 +299,7 @@ func GetPublicKeys(chain engine.ChainReader, header *block.Header, reCalculate b
) )
} }
var committerKeys []*bls.PublicKey var committerKeys []*bls.PublicKey
for _, member := range committee.NodeList { for _, member := range committee.Slots {
committerKey := new(bls.PublicKey) committerKey := new(bls.PublicKey)
err := member.BlsPublicKey.ToLibBLSPublicKey(committerKey) err := member.BlsPublicKey.ToLibBLSPublicKey(committerKey)
if err != nil { if err != nil {

@ -66,7 +66,7 @@ func AccumulateRewards(
) )
} }
var committerKeys []*bls.PublicKey var committerKeys []*bls.PublicKey
for _, member := range parentCommittee.NodeList { for _, member := range parentCommittee.Slots {
committerKey := new(bls.PublicKey) committerKey := new(bls.PublicKey)
err := member.BlsPublicKey.ToLibBLSPublicKey(committerKey) err := member.BlsPublicKey.ToLibBLSPublicKey(committerKey)
if err != nil { if err != nil {
@ -86,7 +86,7 @@ func AccumulateRewards(
accounts := []common.Address{} accounts := []common.Address{}
missing := shard.NodeIDList{} missing := shard.NodeIDList{}
for idx, member := range parentCommittee.NodeList { for idx, member := range parentCommittee.Slots {
switch signed, err := mask.IndexEnabled(idx); true { switch signed, err := mask.IndexEnabled(idx); true {
case err != nil: case err != nil:
return ctxerror.New("cannot check for committer bit", return ctxerror.New("cannot check for committer bit",
@ -120,28 +120,6 @@ func AccumulateRewards(
*big.Int *big.Int
}{} }{}
// fmt.Println("Calling reward", rewarder)
// hack :=
type t struct {
Count int `json:"count"`
Members []string `json:"members"`
ShardID uint32 `json:"shard-id"`
}
// g := rewarder.(quorum.Decider).DumpParticipants()
// if e := parentHeader.Epoch(); e.Cmp(big.NewInt(0)) == 0 {
// b, _ := json.Marshal(t{len(g), g, parentHeader.ShardID()})
// fmt.Println("epoch", e, string(b), "\n")
// }
// if e := parentHeader.Epoch(); e.Cmp(big.NewInt(1)) == 0 {
// b, _ := json.Marshal(t{len(g), g, parentHeader.ShardID()})
// fmt.Println("epoch", e, string(b), "\n")
// }
totalAmount := rewarder.Award( totalAmount := rewarder.Award(
BlockReward, accounts, func(receipient common.Address, amount *big.Int) { BlockReward, accounts, func(receipient common.Address, amount *big.Int) {
payable = append(payable, struct { payable = append(payable, struct {

@ -73,8 +73,8 @@ type Backend interface {
ResendCx(ctx context.Context, txID common.Hash) (uint64, bool) ResendCx(ctx context.Context, txID common.Hash) (uint64, bool)
IsLeader() bool IsLeader() bool
SendStakingTx(ctx context.Context, newStakingTx *staking.StakingTransaction) error SendStakingTx(ctx context.Context, newStakingTx *staking.StakingTransaction) error
GetCurrentValidatorAddresses() []common.Address GetActiveValidatorAddresses() []common.Address
GetValidatorCandidates() []common.Address GetAllValidatorAddresses() []common.Address
GetValidatorInformation(addr common.Address) *staking.Validator GetValidatorInformation(addr common.Address) *staking.Validator
GetDelegatorsInformation(addr common.Address) []*staking.Delegation GetDelegatorsInformation(addr common.Address) []*staking.Delegation
GetValidatorStakingWithDelegation(addr common.Address) *big.Int GetValidatorStakingWithDelegation(addr common.Address) *big.Int

@ -153,7 +153,7 @@ func (s *PublicBlockChainAPI) GetValidators(ctx context.Context, epoch int64) (m
return nil, err return nil, err
} }
validators := make([]map[string]interface{}, 0) validators := make([]map[string]interface{}, 0)
for _, validator := range committee.NodeList { for _, validator := range committee.Slots {
validatorBalance := new(hexutil.Big) validatorBalance := new(hexutil.Big)
validatorBalance, err = s.b.GetBalance(validator.EcdsaAddress) validatorBalance, err = s.b.GetBalance(validator.EcdsaAddress)
if err != nil { if err != nil {
@ -193,8 +193,8 @@ func (s *PublicBlockChainAPI) GetBlockSigners(ctx context.Context, blockNr rpc.B
if err != nil { if err != nil {
return nil, err return nil, err
} }
pubkeys := make([]*bls.PublicKey, len(committee.NodeList)) pubkeys := make([]*bls.PublicKey, len(committee.Slots))
for i, validator := range committee.NodeList { for i, validator := range committee.Slots {
pubkeys[i] = new(bls.PublicKey) pubkeys[i] = new(bls.PublicKey)
validator.BlsPublicKey.ToLibBLSPublicKey(pubkeys[i]) validator.BlsPublicKey.ToLibBLSPublicKey(pubkeys[i])
} }
@ -210,7 +210,7 @@ func (s *PublicBlockChainAPI) GetBlockSigners(ctx context.Context, blockNr rpc.B
if err != nil { if err != nil {
return result, err return result, err
} }
for _, validator := range committee.NodeList { for _, validator := range committee.Slots {
oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress) oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress)
if err != nil { if err != nil {
return result, err return result, err
@ -241,8 +241,8 @@ func (s *PublicBlockChainAPI) IsBlockSigner(ctx context.Context, blockNr rpc.Blo
if err != nil { if err != nil {
return false, err return false, err
} }
pubkeys := make([]*bls.PublicKey, len(committee.NodeList)) pubkeys := make([]*bls.PublicKey, len(committee.Slots))
for i, validator := range committee.NodeList { for i, validator := range committee.Slots {
pubkeys[i] = new(bls.PublicKey) pubkeys[i] = new(bls.PublicKey)
validator.BlsPublicKey.ToLibBLSPublicKey(pubkeys[i]) validator.BlsPublicKey.ToLibBLSPublicKey(pubkeys[i])
} }
@ -254,7 +254,7 @@ func (s *PublicBlockChainAPI) IsBlockSigner(ctx context.Context, blockNr rpc.Blo
if err != nil { if err != nil {
return false, err return false, err
} }
for _, validator := range committee.NodeList { for _, validator := range committee.Slots {
oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress) oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress)
if err != nil { if err != nil {
return false, err return false, err
@ -298,33 +298,6 @@ func (s *PublicBlockChainAPI) GetLeader(ctx context.Context) string {
return s.LatestHeader(ctx).Leader return s.LatestHeader(ctx).Leader
} }
// GetValidatorInformation returns full validator info.
func (s *PublicBlockChainAPI) GetValidatorInformation(ctx context.Context, address string) (map[string]interface{}, error) {
validator := s.b.GetValidatorInformation(internal_common.ParseAddr(address))
slotPubKeys := make([]string, 0)
for _, slotPubKey := range validator.SlotPubKeys {
slotPubKeys = append(slotPubKeys, slotPubKey.Hex())
}
fields := map[string]interface{}{
"address": validator.Address.String(),
"stake": hexutil.Uint64(validator.Stake.Uint64()),
"name": validator.Description.Name,
"slotPubKeys": slotPubKeys,
"unbondingHeight": hexutil.Uint64(validator.UnbondingHeight.Uint64()),
"minSelfDelegation": hexutil.Uint64(validator.MinSelfDelegation.Uint64()),
"active": validator.Active,
"identity": validator.Description.Identity,
"commissionRate": hexutil.Uint64(validator.Commission.CommissionRates.Rate.Int.Uint64()),
"commissionUpdateHeight": hexutil.Uint64(validator.Commission.UpdateHeight.Uint64()),
"commissionMaxRate": hexutil.Uint64(validator.Commission.CommissionRates.MaxRate.Uint64()),
"commissionMaxChangeRate": hexutil.Uint64(validator.Commission.CommissionRates.MaxChangeRate.Uint64()),
"website": validator.Description.Website,
"securityContact": validator.Description.SecurityContact,
"details": validator.Description.Details,
}
return fields, nil
}
// GetStake returns validator stake. // GetStake returns validator stake.
func (s *PublicBlockChainAPI) GetStake(ctx context.Context, address string) hexutil.Uint64 { func (s *PublicBlockChainAPI) GetStake(ctx context.Context, address string) hexutil.Uint64 {
validator := s.b.GetValidatorInformation(internal_common.ParseAddr(address)) validator := s.b.GetValidatorInformation(internal_common.ParseAddr(address))
@ -529,3 +502,22 @@ func (s *PublicBlockChainAPI) LatestHeader(ctx context.Context) *HeaderInformati
header, _ := s.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available header, _ := s.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available
return newHeaderInformation(header) return newHeaderInformation(header)
} }
// GetAllValidatorAddresses returns all validator addresses.
func (s *PublicBlockChainAPI) GetAllValidatorAddresses() ([]common.Address, error) {
return s.b.GetAllValidatorAddresses(), nil
}
// GetActiveValidatorAddresses returns active validator addresses.
func (s *PublicBlockChainAPI) GetActiveValidatorAddresses() ([]common.Address, error) {
return s.b.GetActiveValidatorAddresses(), nil
}
// GetValidatorInfo returns information about a validator.
func (s *PublicBlockChainAPI) GetValidatorInfo(ctx context.Context, address common.Address) (*RPCValidator, error) {
validator := s.b.GetValidatorInformation(address)
if validator == nil {
return nil, fmt.Errorf("validator not found: %s", address.Hex())
}
return newRPCValidator(validator), nil
}

@ -6,6 +6,10 @@ import (
"strings" "strings"
"time" "time"
"github.com/harmony-one/harmony/shard"
types2 "github.com/harmony-one/harmony/staking/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
@ -60,6 +64,20 @@ type HeaderInformation struct {
LastCommitBitmap string `json:"lastCommitBitmap"` LastCommitBitmap string `json:"lastCommitBitmap"`
} }
// RPCValidator represents a validator
type RPCValidator struct {
Address common.Address `json:"address"`
SlotPubKeys []shard.BlsPublicKey `json:"slot_pub_keys"`
Stake *big.Int `json:"stake" yaml:"stake"`
UnbondingHeight *big.Int `json:"unbonding_height"`
MinSelfDelegation *big.Int `json:"min_self_delegation"`
MaxTotalDelegation *big.Int `json:"min_self_delegation"`
Active bool `json:"active"`
Commission types2.Commission `json:"commission"`
Description types2.Description `json:"description"`
CreationHeight *big.Int `json:"creation_height"`
}
func newHeaderInformation(header *block.Header) *HeaderInformation { func newHeaderInformation(header *block.Header) *HeaderInformation {
if header == nil { if header == nil {
return nil return nil
@ -118,6 +136,21 @@ func newRPCCXReceipt(cx *types.CXReceipt, blockHash common.Hash, blockNumber uin
return result return result
} }
func newRPCValidator(validator *types2.Validator) *RPCValidator {
return &RPCValidator{
validator.Address,
validator.SlotPubKeys,
validator.Stake,
validator.UnbondingHeight,
validator.MinSelfDelegation,
validator.MaxTotalDelegation,
validator.Active,
validator.Commission,
validator.Description,
validator.CreationHeight,
}
}
// newRPCTransaction returns a transaction that will serialize to the RPC // newRPCTransaction returns a transaction that will serialize to the RPC
// representation, with the given location metadata set (if available). // representation, with the given location metadata set (if available).
func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) *RPCTransaction { func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) *RPCTransaction {

@ -298,7 +298,7 @@ func (node *Node) VerifyCrosslinkHeader(prevHeader, header *block.Header) error
var committerKeys []*bls.PublicKey var committerKeys []*bls.PublicKey
parseKeysSuccess := true parseKeysSuccess := true
for _, member := range committee.NodeList { for _, member := range committee.Slots {
committerKey := new(bls.PublicKey) committerKey := new(bls.PublicKey)
err = member.BlsPublicKey.ToLibBLSPublicKey(committerKey) err = member.BlsPublicKey.ToLibBLSPublicKey(committerKey)
if err != nil { if err != nil {

@ -430,23 +430,21 @@ func (node *Node) PostConsensusProcessing(newBlock *types.Block, commitSigAndBit
func (node *Node) AddNewBlock(newBlock *types.Block) error { func (node *Node) AddNewBlock(newBlock *types.Block) error {
_, err := node.Blockchain().InsertChain([]*types.Block{newBlock}, true /* verifyHeaders */) _, err := node.Blockchain().InsertChain([]*types.Block{newBlock}, true /* verifyHeaders */)
/*
// Debug only // Debug only
addrs, err := node.Blockchain().ReadValidatorList() //addrs, err := node.Blockchain().ReadValidatorList()
utils.Logger().Debug().Msgf("validator list updated, err=%v, len(addrs)=%v", err, len(addrs)) //utils.Logger().Debug().Msgf("validator list updated, err=%v, len(addrs)=%v", err, len(addrs))
for i, addr := range addrs { //for i, addr := range addrs {
val, err := node.Blockchain().ValidatorInformation(addr) // val, err := node.Blockchain().ValidatorInformation(addr)
if err != nil { // if err != nil {
utils.Logger().Debug().Msgf("ValidatorInformation Error %v: err %v", i, err) // utils.Logger().Debug().Msgf("ValidatorInformation Error %v: err %v", i, err)
} // }
utils.Logger().Debug().Msgf("ValidatorInformation %v: %v", i, val) // utils.Logger().Debug().Msgf("ValidatorInformation %v: %v", i, val)
} //}
currAddrs := node.Blockchain().CurrentValidatorAddresses() //currAddrs, err := node.Blockchain().ReadActiveValidatorList()
utils.Logger().Debug().Msgf("CurrentValidators : %v", currAddrs) //utils.Logger().Debug().Msgf("CurrentValidators : %v", currAddrs)
candidates := node.Blockchain().ValidatorCandidates() //candidates := node.Blockchain().ValidatorCandidates()
utils.Logger().Debug().Msgf("CandidateValidators : %v", candidates) //utils.Logger().Debug().Msgf("CandidateValidators : %v", candidates)
// Finish debug // Finish debug
*/
if err != nil { if err != nil {
utils.Logger().Error(). utils.Logger().Error().

@ -92,6 +92,7 @@ func (node *Node) proposeNewBlock() (*types.Block, error) {
for _, tx := range node.pendingStakingTransactions { for _, tx := range node.pendingStakingTransactions {
pendingStakingTransactions = append(pendingStakingTransactions, tx) pendingStakingTransactions = append(pendingStakingTransactions, tx)
} }
node.pendingStakingTransactions = make(map[common.Hash]*types2.StakingTransaction)
node.Worker.UpdateCurrent(coinbase) node.Worker.UpdateCurrent(coinbase)
if err := node.Worker.CommitTransactions(pending, pendingStakingTransactions, coinbase); err != nil { if err := node.Worker.CommitTransactions(pending, pendingStakingTransactions, coinbase); err != nil {

@ -201,7 +201,7 @@ func (node *Node) transitionIntoNextEpoch(shardState types.State) {
for _, c := range shardState { for _, c := range shardState {
utils.Logger().Debug(). utils.Logger().Debug().
Uint32("shardID", c.ShardID). Uint32("shardID", c.ShardID).
Str("nodeList", c.NodeList). Str("nodeList", c.Slots).
Msg("new shard information") Msg("new shard information")
} }
myShardID, isNextLeader := findRoleInShardState( myShardID, isNextLeader := findRoleInShardState(
@ -219,7 +219,7 @@ func (node *Node) transitionIntoNextEpoch(shardState types.State) {
// Update public keys // Update public keys
var publicKeys []*bls.PublicKey var publicKeys []*bls.PublicKey
for idx, nodeID := range myShardState.NodeList { for idx, nodeID := range myShardState.Slots {
key := &bls.PublicKey{} key := &bls.PublicKey{}
err := key.Deserialize(nodeID.BlsPublicKey[:]) err := key.Deserialize(nodeID.BlsPublicKey[:])
if err != nil { if err != nil {
@ -249,7 +249,7 @@ func findRoleInShardState(
) (shardID uint32, isLeader bool) { ) (shardID uint32, isLeader bool) {
keyBytes := key.Serialize() keyBytes := key.Serialize()
for idx, shard := range state { for idx, shard := range state {
for nodeIdx, nodeID := range shard.NodeList { 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
} }

@ -85,12 +85,12 @@ func preStakingEnabledCommittee(s shardingconfig.Instance) shard.State {
pubKey := shard.BlsPublicKey{} pubKey := shard.BlsPublicKey{}
pubKey.FromLibBLSPublicKey(pub) pubKey.FromLibBLSPublicKey(pub)
// TODO: directly read address for bls too // TODO: directly read address for bls too
curNodeID := shard.NodeID{ curNodeID := shard.Slot{
common2.ParseAddr(hmyAccounts[index].Address), common2.ParseAddr(hmyAccounts[index].Address),
pubKey, pubKey,
nil, nil,
} }
com.NodeList = append(com.NodeList, curNodeID) com.Slots = append(com.Slots, curNodeID)
} }
// add FN runner's key // add FN runner's key
for j := shardHarmonyNodes; j < shardSize; j++ { for j := shardHarmonyNodes; j < shardSize; j++ {
@ -100,12 +100,12 @@ func preStakingEnabledCommittee(s shardingconfig.Instance) shard.State {
pubKey := shard.BlsPublicKey{} pubKey := shard.BlsPublicKey{}
pubKey.FromLibBLSPublicKey(pub) pubKey.FromLibBLSPublicKey(pub)
// TODO: directly read address for bls too // TODO: directly read address for bls too
curNodeID := shard.NodeID{ curNodeID := shard.Slot{
common2.ParseAddr(fnAccounts[index].Address), common2.ParseAddr(fnAccounts[index].Address),
pubKey, pubKey,
nil, nil,
} }
com.NodeList = append(com.NodeList, curNodeID) com.Slots = append(com.Slots, curNodeID)
} }
shardState = append(shardState, com) shardState = append(shardState, com)
} }
@ -137,7 +137,7 @@ func eposStakedCommittee(
hAccounts := s.HmyAccounts() hAccounts := s.HmyAccounts()
for i := 0; i < shardCount; i++ { for i := 0; i < shardCount; i++ {
superComm[i] = shard.Committee{uint32(i), shard.NodeIDList{}} superComm[i] = shard.Committee{uint32(i), shard.SlotList{}}
} }
for i := range hAccounts { for i := range hAccounts {
@ -146,7 +146,7 @@ func eposStakedCommittee(
pub.DeserializeHexStr(hAccounts[i].BlsPublicKey) pub.DeserializeHexStr(hAccounts[i].BlsPublicKey)
pubKey := shard.BlsPublicKey{} pubKey := shard.BlsPublicKey{}
pubKey.FromLibBLSPublicKey(pub) pubKey.FromLibBLSPublicKey(pub)
superComm[spot].NodeList = append(superComm[spot].NodeList, shard.NodeID{ superComm[spot].Slots = append(superComm[spot].Slots, shard.Slot{
common2.ParseAddr(hAccounts[i].Address), common2.ParseAddr(hAccounts[i].Address),
pubKey, pubKey,
nil, nil,
@ -164,7 +164,7 @@ 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()) bucket := int(new(big.Int).Mod(staked[i].BlsPublicKey.Big(), shardBig).Int64())
slot := staked[i] slot := staked[i]
superComm[bucket].NodeList = append(superComm[bucket].NodeList, shard.NodeID{ superComm[bucket].Slots = append(superComm[bucket].Slots, shard.Slot{
slot.Address, slot.Address,
staked[i].BlsPublicKey, staked[i].BlsPublicKey,
&slot.Dec, &slot.Dec,
@ -194,10 +194,10 @@ func (def partialStakingEnabled) ComputePublicKeys(
allIdentities := make([][]*bls.PublicKey, len(superComm)) allIdentities := make([][]*bls.PublicKey, len(superComm))
for i := range superComm { for i := range superComm {
allIdentities[i] = make([]*bls.PublicKey, len(superComm[i].NodeList)) allIdentities[i] = make([]*bls.PublicKey, len(superComm[i].Slots))
for j := range superComm[i].NodeList { for j := range superComm[i].Slots {
identity := &bls.PublicKey{} identity := &bls.PublicKey{}
superComm[i].NodeList[j].BlsPublicKey.ToLibBLSPublicKey(identity) superComm[i].Slots[j].BlsPublicKey.ToLibBLSPublicKey(identity)
allIdentities[i][j] = identity allIdentities[i][j] = identity
} }
} }
@ -223,12 +223,12 @@ func (def partialStakingEnabled) ReadPublicKeysFromDB(
} }
committerKeys := []*bls.PublicKey{} committerKeys := []*bls.PublicKey{}
for i := range subCommittee.NodeList { for i := range subCommittee.Slots {
committerKey := new(bls.PublicKey) committerKey := new(bls.PublicKey)
err := subCommittee.NodeList[i].BlsPublicKey.ToLibBLSPublicKey(committerKey) err := subCommittee.Slots[i].BlsPublicKey.ToLibBLSPublicKey(committerKey)
if err != nil { if err != nil {
return nil, ctxerror.New("cannot convert BLS public key", return nil, ctxerror.New("cannot convert BLS public key",
"blsPublicKey", subCommittee.NodeList[i].BlsPublicKey).WithCause(err) "blsPublicKey", subCommittee.Slots[i].BlsPublicKey).WithCause(err)
} }
committerKeys = append(committerKeys, committerKey) committerKeys = append(committerKeys, committerKey)
} }

@ -34,27 +34,27 @@ type State []Committee
// BlsPublicKey defines the bls public key // BlsPublicKey defines the bls public key
type BlsPublicKey [PublicKeySizeInBytes]byte type BlsPublicKey [PublicKeySizeInBytes]byte
// NodeID represents node id (BLS address) // Slot represents node id (BLS address)
type NodeID struct { type Slot struct {
EcdsaAddress common.Address `json:"ecdsa-address"` EcdsaAddress common.Address `json:"ecdsa-address"`
BlsPublicKey BlsPublicKey `json:"bls-pubkey"` BlsPublicKey BlsPublicKey `json:"bls-pubkey"`
// nil means not active, 0 means our node, >= 0 means staked node // nil means our node, 0 means not active, >= 0 means staked node
StakeWithDelegationApplied *numeric.Dec `json:"staked-validator" rlp:"nil"` StakeWithDelegationApplied *numeric.Dec `json:"staked-validator" rlp:"nil"`
} }
// NodeIDList is a list of NodeIDList. // SlotList is a list of SlotList.
type NodeIDList []NodeID type SlotList []Slot
// Committee contains the active nodes in one shard // Committee contains the active nodes in one shard
type Committee struct { type Committee struct {
ShardID uint32 `json:"shard-id"` ShardID uint32 `json:"shard-id"`
NodeList NodeIDList `json:"subcommittee"` Slots SlotList `json:"subcommittee"`
} }
// 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 {
NodeID Slot
EcdsaAddress string `json:"ecdsa-address"` EcdsaAddress string `json:"ecdsa-address"`
} }
type v struct { type v struct {
@ -64,12 +64,12 @@ func (ss State) JSON() string {
} }
dump := make([]v, len(ss)) dump := make([]v, len(ss))
for i := range ss { for i := range ss {
c := len(ss[i].NodeList) c := len(ss[i].Slots)
dump[i].ShardID = ss[i].ShardID dump[i].ShardID = ss[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].NodeList { for j := range ss[i].Slots {
n := ss[i].NodeList[j] n := ss[i].Slots[j]
dump[i].NodeList[j].BlsPublicKey = n.BlsPublicKey dump[i].NodeList[j].BlsPublicKey = n.BlsPublicKey
dump[i].NodeList[j].StakeWithDelegationApplied = n.StakeWithDelegationApplied dump[i].NodeList[j].StakeWithDelegationApplied = n.StakeWithDelegationApplied
dump[i].NodeList[j].EcdsaAddress = common2.MustAddressToBech32(n.EcdsaAddress) dump[i].NodeList[j].EcdsaAddress = common2.MustAddressToBech32(n.EcdsaAddress)
@ -166,7 +166,7 @@ func CompareBlsPublicKey(k1, k2 BlsPublicKey) int {
} }
// CompareNodeID compares two node IDs. // CompareNodeID compares two node IDs.
func CompareNodeID(id1, id2 *NodeID) int { func CompareNodeID(id1, id2 *Slot) int {
if c := bytes.Compare(id1.EcdsaAddress[:], id2.EcdsaAddress[:]); c != 0 { if c := bytes.Compare(id1.EcdsaAddress[:], id2.EcdsaAddress[:]); c != 0 {
return c return c
} }
@ -177,12 +177,12 @@ func CompareNodeID(id1, id2 *NodeID) int {
} }
// DeepCopy returns a deep copy of the receiver. // DeepCopy returns a deep copy of the receiver.
func (l NodeIDList) DeepCopy() NodeIDList { func (l SlotList) DeepCopy() SlotList {
return append(l[:0:0], l...) return append(l[:0:0], l...)
} }
// CompareNodeIDList compares two node ID lists. // CompareNodeIDList compares two node ID lists.
func CompareNodeIDList(l1, l2 NodeIDList) int { func CompareNodeIDList(l1, l2 SlotList) int {
commonLen := len(l1) commonLen := len(l1)
if commonLen > len(l2) { if commonLen > len(l2) {
commonLen = len(l2) commonLen = len(l2)
@ -205,7 +205,7 @@ func CompareNodeIDList(l1, l2 NodeIDList) int {
func (c Committee) DeepCopy() Committee { func (c Committee) DeepCopy() Committee {
r := Committee{} r := Committee{}
r.ShardID = c.ShardID r.ShardID = c.ShardID
r.NodeList = c.NodeList.DeepCopy() r.Slots = c.Slots.DeepCopy()
return r return r
} }
@ -217,7 +217,7 @@ func CompareCommittee(c1, c2 *Committee) int {
case c1.ShardID > c2.ShardID: case c1.ShardID > c2.ShardID:
return +1 return +1
} }
if c := CompareNodeIDList(c1.NodeList, c2.NodeList); c != 0 { if c := CompareNodeIDList(c1.Slots, c2.Slots); c != 0 {
return c return c
} }
return 0 return 0
@ -225,7 +225,7 @@ func CompareCommittee(c1, c2 *Committee) int {
// GetHashFromNodeList will sort the list, then use Keccak256 to hash the list // GetHashFromNodeList will sort the list, then use Keccak256 to hash the list
// NOTE: do not modify the underlining content for hash // NOTE: do not modify the underlining content for hash
func GetHashFromNodeList(nodeList []NodeID) []byte { func GetHashFromNodeList(nodeList []Slot) []byte {
// in general, nodeList should not be empty // in general, nodeList should not be empty
if nodeList == nil || len(nodeList) == 0 { if nodeList == nil || len(nodeList) == 0 {
return []byte{} return []byte{}
@ -248,7 +248,7 @@ func (ss State) Hash() (h common.Hash) {
}) })
d := sha3.NewLegacyKeccak256() d := sha3.NewLegacyKeccak256()
for i := range copy { for i := range copy {
hash := GetHashFromNodeList(copy[i].NodeList) hash := GetHashFromNodeList(copy[i].Slots)
d.Write(hash) d.Write(hash)
} }
d.Sum(h[:0]) d.Sum(h[:0])
@ -256,15 +256,15 @@ func (ss State) Hash() (h common.Hash) {
} }
// CompareNodeIDByBLSKey compares two nodes by their ID; used to sort node list // CompareNodeIDByBLSKey compares two nodes by their ID; used to sort node list
func CompareNodeIDByBLSKey(n1 NodeID, n2 NodeID) int { func CompareNodeIDByBLSKey(n1 Slot, n2 Slot) int {
return bytes.Compare(n1.BlsPublicKey[:], n2.BlsPublicKey[:]) return bytes.Compare(n1.BlsPublicKey[:], n2.BlsPublicKey[:])
} }
// Serialize serialize NodeID into bytes // Serialize serialize Slot into bytes
func (n NodeID) Serialize() []byte { func (n Slot) Serialize() []byte {
return append(n.EcdsaAddress[:], n.BlsPublicKey[:]...) return append(n.EcdsaAddress[:], n.BlsPublicKey[:]...)
} }
func (n NodeID) String() string { func (n Slot) String() string {
return "ECDSA: " + common2.MustAddressToBech32(n.EcdsaAddress) + ", BLS: " + hex.EncodeToString(n.BlsPublicKey[:]) return "ECDSA: " + common2.MustAddressToBech32(n.EcdsaAddress) + ", BLS: " + hex.EncodeToString(n.BlsPublicKey[:])
} }

@ -30,12 +30,12 @@ func init() {
} }
func TestGetHashFromNodeList(t *testing.T) { func TestGetHashFromNodeList(t *testing.T) {
l1 := []NodeID{ l1 := []Slot{
{common.Address{0x11}, blsPubKey1, nil}, {common.Address{0x11}, blsPubKey1, nil},
{common.Address{0x22}, blsPubKey2, nil}, {common.Address{0x22}, blsPubKey2, nil},
{common.Address{0x33}, blsPubKey3, nil}, {common.Address{0x33}, blsPubKey3, nil},
} }
l2 := []NodeID{ l2 := []Slot{
{common.Address{0x22}, blsPubKey2, nil}, {common.Address{0x22}, blsPubKey2, nil},
{common.Address{0x11}, blsPubKey1, nil}, {common.Address{0x11}, blsPubKey1, nil},
{common.Address{0x33}, blsPubKey3, nil}, {common.Address{0x33}, blsPubKey3, nil},
@ -51,7 +51,7 @@ func TestGetHashFromNodeList(t *testing.T) {
func TestHash(t *testing.T) { func TestHash(t *testing.T) {
com1 := Committee{ com1 := Committee{
ShardID: 22, ShardID: 22,
NodeList: []NodeID{ Slots: []Slot{
{common.Address{0x12}, blsPubKey11, nil}, {common.Address{0x12}, blsPubKey11, nil},
{common.Address{0x23}, blsPubKey22, nil}, {common.Address{0x23}, blsPubKey22, nil},
{common.Address{0x11}, blsPubKey1, nil}, {common.Address{0x11}, blsPubKey1, nil},
@ -59,7 +59,7 @@ func TestHash(t *testing.T) {
} }
com2 := Committee{ com2 := Committee{
ShardID: 2, ShardID: 2,
NodeList: []NodeID{ Slots: []Slot{
{common.Address{0x44}, blsPubKey4, nil}, {common.Address{0x44}, blsPubKey4, nil},
{common.Address{0x55}, blsPubKey5, nil}, {common.Address{0x55}, blsPubKey5, nil},
{common.Address{0x66}, blsPubKey6, nil}, {common.Address{0x66}, blsPubKey6, nil},
@ -70,7 +70,7 @@ func TestHash(t *testing.T) {
com3 := Committee{ com3 := Committee{
ShardID: 2, ShardID: 2,
NodeList: []NodeID{ Slots: []Slot{
{common.Address{0x44}, blsPubKey4, nil}, {common.Address{0x44}, blsPubKey4, nil},
{common.Address{0x55}, blsPubKey5, nil}, {common.Address{0x55}, blsPubKey5, nil},
{common.Address{0x66}, blsPubKey6, nil}, {common.Address{0x66}, blsPubKey6, nil},
@ -78,7 +78,7 @@ func TestHash(t *testing.T) {
} }
com4 := Committee{ com4 := Committee{
ShardID: 22, ShardID: 22,
NodeList: []NodeID{ Slots: []Slot{
{common.Address{0x12}, blsPubKey11, nil}, {common.Address{0x12}, blsPubKey11, nil},
{common.Address{0x23}, blsPubKey22, nil}, {common.Address{0x23}, blsPubKey22, nil},
{common.Address{0x11}, blsPubKey1, nil}, {common.Address{0x11}, blsPubKey1, nil},

@ -13,6 +13,11 @@ var (
errInvalidAmount = errors.New("Invalid amount, must be positive") errInvalidAmount = errors.New("Invalid amount, must be positive")
) )
const (
// LockPeriodInEpoch is the number of epochs a undelegated token needs to be before it's released to the delegator's balance
LockPeriodInEpoch = 14
)
// Delegation represents the bond with tokens held by an account. It is // Delegation represents the bond with tokens held by an account. It is
// owned by one delegator, and is associated with the voting power of one // owned by one delegator, and is associated with the voting power of one
// validator. // validator.
@ -93,3 +98,20 @@ func (d *Delegation) DeleteEntry(epoch *big.Int) {
d.Entries = entries d.Entries = entries
} }
} }
// RemoveUnlockedUndelegations removes all fully unlocked undelegations and returns the total sum
func (d *Delegation) RemoveUnlockedUndelegations(curEpoch *big.Int) *big.Int {
totalWithdraw := big.NewInt(0)
count := 0
for j := range d.Entries {
if big.NewInt(0).Sub(curEpoch, d.Entries[j].Epoch).Int64() > LockPeriodInEpoch { // need to wait at least 14 epochs to withdraw;
totalWithdraw.Add(totalWithdraw, d.Entries[j].Amount)
count++
} else {
break
}
}
d.Entries = d.Entries[count:]
return totalWithdraw
}

@ -30,15 +30,14 @@ var (
errInvalidTotalDelegation = errors.New("total delegation can not be bigger than max_total_delegation") errInvalidTotalDelegation = errors.New("total delegation can not be bigger than max_total_delegation")
errMinSelfDelegationTooSmall = errors.New("min_self_delegation has to be greater than 1 ONE") errMinSelfDelegationTooSmall = errors.New("min_self_delegation has to be greater than 1 ONE")
errInvalidMaxTotalDelegation = errors.New("max_total_delegation can not be less than min_self_delegation") errInvalidMaxTotalDelegation = errors.New("max_total_delegation can not be less than min_self_delegation")
errCommissionRateTooLarge = errors.New("commission rate and change rate can not be larger than max commission rate")
errInvalidComissionRate = errors.New("commission rate, change rate and max rate should be within 0-100 percent")
) )
// ValidatorWrapper contains validator and its delegation information // ValidatorWrapper contains validator and its delegation information
type ValidatorWrapper struct { type ValidatorWrapper struct {
Validator `json:"validator" yaml:"validator" rlp:"nil"` Validator `json:"validator" yaml:"validator" rlp:"nil"`
Delegations []Delegation `json:"delegations" yaml:"delegations" rlp:"nil"` Delegations []Delegation `json:"delegations" yaml:"delegations" rlp:"nil"`
// TODO: move snapshot into off-chain db.
SnapshotValidator *Validator `json:"snapshot_validator" yaml:"snaphost_validator" rlp:"nil"`
SnapshotDelegations []Delegation `json:"snapshot_delegations" yaml:"snapshot_delegations" rlp:"nil"`
} }
// Validator - data fields for a validator // Validator - data fields for a validator
@ -46,7 +45,7 @@ type Validator struct {
// ECDSA address of the validator // ECDSA address of the validator
Address common.Address `json:"address" yaml:"address"` Address common.Address `json:"address" yaml:"address"`
// The BLS public key of the validator for consensus // The BLS public key of the validator for consensus
SlotPubKeys []shard.BlsPublicKey `json:"validating_pub_key" yaml:"validating_pub_key"` SlotPubKeys []shard.BlsPublicKey `json:"slot_pub_keys" yaml:"slot_pub_keys"`
// The stake put by the validator itself // The stake put by the validator itself
Stake *big.Int `json:"stake" yaml:"stake"` Stake *big.Int `json:"stake" yaml:"stake"`
// if unbonding, height at which this validator has begun unbonding // if unbonding, height at which this validator has begun unbonding
@ -106,6 +105,26 @@ func (w *ValidatorWrapper) SanityCheck() error {
if totalDelegation.Cmp(w.Validator.MaxTotalDelegation) > 0 { if totalDelegation.Cmp(w.Validator.MaxTotalDelegation) > 0 {
return errInvalidTotalDelegation return errInvalidTotalDelegation
} }
hundredPercent := numeric.NewDec(1)
zeroPercent := numeric.NewDec(0)
if w.Validator.Rate.LT(zeroPercent) || w.Validator.Rate.GT(hundredPercent) {
return errInvalidComissionRate
}
if w.Validator.MaxRate.LT(zeroPercent) || w.Validator.MaxRate.GT(hundredPercent) {
return errInvalidComissionRate
}
if w.Validator.MaxChangeRate.LT(zeroPercent) || w.Validator.MaxChangeRate.GT(hundredPercent) {
return errInvalidComissionRate
}
if w.Validator.Rate.GT(w.Validator.MaxRate) {
return errCommissionRateTooLarge
}
if w.Validator.MaxChangeRate.GT(w.Validator.MaxRate) {
return errCommissionRateTooLarge
}
return nil return nil
} }

@ -59,6 +59,17 @@ function cleanup_and_result() {
[ -e $RESULT_FILE ] && cat $RESULT_FILE [ -e $RESULT_FILE ] && cat $RESULT_FILE
} }
function debug_staking() {
hmy_one_dir="$(go env GOPATH)/src/github.com/harmony-one"
hmy_bin="${hmy_one_dir}/go-sdk/hmy"
keystore="${hmy_one_dir}/harmony-ops/test-automation/api-tests/LocalnetValidatorKeys"
python3 -m pip install pyhmy
python3 -m pip install requests
python3 "${hmy_one_dir}/harmony-ops/test-automation/api-tests/test.py" --keystore ${keystore} \
--cli_path ${hmy_bin} --test_dir "${hmy_one_dir}/harmony-ops/test-automation/api-tests/tests/" \
--rpc_endpoint_src="http://localhost:9500/" --rpc_endpoint_dst="http://localhost:9501/" --ignore_regression_test
}
trap cleanup_and_result SIGINT SIGTERM trap cleanup_and_result SIGINT SIGTERM
function usage { function usage {
@ -192,6 +203,7 @@ while IFS='' read -r line || [[ -n "$line" ]]; do
done < $config done < $config
if [ "$DOTEST" == "true" ]; then if [ "$DOTEST" == "true" ]; then
debug_staking
echo "waiting for some block rewards" echo "waiting for some block rewards"
sleep 60 sleep 60
i=1 i=1

Loading…
Cancel
Save