Merge pull request #1846 from rlan35/staking_tx

Add delegation index to avoid looping through all delegations
pull/1853/head
Rongjian Lan 5 years ago committed by GitHub
commit e747ee71d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 105
      core/blockchain.go
  2. 4
      core/evm.go
  3. 20
      core/rawdb/accessors_chain.go
  4. 23
      core/state_transition.go
  5. 22
      hmy/api_backend.go
  6. 6
      staking/types/delegation.go

@ -1173,7 +1173,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
// Do bookkeeping for new staking txns
if bc.chainConfig.IsStaking(block.Epoch()) {
for _, tx := range block.StakingTransactions() {
err = bc.UpdateStakingMetaData(tx)
err = bc.UpdateStakingMetaData(tx, root)
// keep offchain database consistency with onchain we need revert
// but it should not happend unless local database corrupted
if err != nil {
@ -2307,9 +2307,9 @@ func (bc *BlockChain) ReadTxLookupEntry(txID common.Hash) (common.Hash, uint64,
return rawdb.ReadTxLookupEntry(bc.db, txID)
}
// ReadValidatorData reads staking information of given validatorWrapper
func (bc *BlockChain) ReadValidatorData(addr common.Address) (*staking.ValidatorWrapper, error) {
state, err := bc.StateAt(bc.CurrentBlock().Root())
// ReadValidatorDataAt reads staking information of given validatorWrapper at a specific state root
func (bc *BlockChain) ReadValidatorDataAt(addr common.Address, root common.Hash) (*staking.ValidatorWrapper, error) {
state, err := bc.StateAt(root)
if err != nil || state == nil {
return nil, err
}
@ -2320,6 +2320,11 @@ func (bc *BlockChain) ReadValidatorData(addr common.Address) (*staking.Validator
return wrapper, nil
}
// ReadValidatorData reads staking information of given validatorWrapper
func (bc *BlockChain) ReadValidatorData(addr common.Address) (*staking.ValidatorWrapper, error) {
return bc.ReadValidatorDataAt(addr, bc.CurrentBlock().Root())
}
// 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) {
@ -2455,26 +2460,26 @@ func (bc *BlockChain) WriteActiveValidatorList(addrs []common.Address) error {
return nil
}
// ReadValidatorListByDelegator reads the addresses of validators delegated by a delegator
func (bc *BlockChain) ReadValidatorListByDelegator(delegator common.Address) ([]common.Address, error) {
// ReadDelegationsByDelegator reads the addresses of validators delegated by a delegator
func (bc *BlockChain) ReadDelegationsByDelegator(delegator common.Address) ([]staking.DelegationIndex, error) {
if cached, ok := bc.validatorListByDelegatorCache.Get(string(delegator.Bytes())); ok {
by := cached.([]byte)
m := []common.Address{}
m := []staking.DelegationIndex{}
if err := rlp.DecodeBytes(by, &m); err != nil {
return nil, err
}
return m, nil
}
return rawdb.ReadValidatorListByDelegator(bc.db, delegator)
return rawdb.ReadDelegationsByDelegator(bc.db, delegator)
}
// WriteValidatorListByDelegator writes the list of validator addresses to database
func (bc *BlockChain) WriteValidatorListByDelegator(delegator common.Address, addrs []common.Address) error {
err := rawdb.WriteValidatorListByDelegator(bc.db, delegator, addrs)
// WriteDelegationsByDelegator writes the list of validator addresses to database
func (bc *BlockChain) WriteDelegationsByDelegator(delegator common.Address, indices []staking.DelegationIndex) error {
err := rawdb.WriteDelegationsByDelegator(bc.db, delegator, indices)
if err != nil {
return err
}
bytes, err := rlp.EncodeToBytes(addrs)
bytes, err := rlp.EncodeToBytes(indices)
if err == nil {
bc.validatorListByDelegatorCache.Add(string(delegator.Bytes()), bytes)
}
@ -2482,7 +2487,7 @@ func (bc *BlockChain) WriteValidatorListByDelegator(delegator common.Address, ad
}
// UpdateStakingMetaData updates the validator's and the delegator's meta data according to staking transaction
func (bc *BlockChain) UpdateStakingMetaData(tx *staking.StakingTransaction) error {
func (bc *BlockChain) UpdateStakingMetaData(tx *staking.StakingTransaction, root common.Hash) error {
// TODO: simply the logic here in staking/types/transaction.go
payload, err := tx.RLPEncodeStakeMsg()
if err != nil {
@ -2509,44 +2514,24 @@ func (bc *BlockChain) UpdateStakingMetaData(tx *staking.StakingTransaction) erro
err = bc.WriteValidatorList(list)
}
// TODO: combine this code with the one for Delegate.
validators, err := bc.ReadValidatorListByDelegator(createValidator.ValidatorAddress)
// Add self delegation into the index
delegations, err := bc.ReadDelegationsByDelegator(createValidator.ValidatorAddress)
if err != nil {
return err
}
found := false
for _, validator := range validators {
if bytes.Compare(validator.Bytes(), createValidator.ValidatorAddress.Bytes()) == 0 {
found = true
break
}
}
if !found {
validators = append(validators, createValidator.ValidatorAddress)
}
err = bc.WriteValidatorListByDelegator(createValidator.ValidatorAddress, validators)
return err
// following cases are placeholder for now
delegations = append(delegations, staking.DelegationIndex{
createValidator.ValidatorAddress,
0,
})
err = bc.WriteDelegationsByDelegator(createValidator.ValidatorAddress, delegations)
return err
case staking.DirectiveEditValidator:
case staking.DirectiveDelegate:
delegate := decodePayload.(*staking.Delegate)
validators, err := bc.ReadValidatorListByDelegator(delegate.DelegatorAddress)
if err != nil {
return err
}
found := false
for _, validator := range validators {
if bytes.Compare(validator.Bytes(), delegate.ValidatorAddress.Bytes()) == 0 {
found = true
break
}
}
if !found {
validators = append(validators, delegate.ValidatorAddress)
}
err = bc.WriteValidatorListByDelegator(delegate.DelegatorAddress, validators)
return err
return bc.addDelegationIndex(delegate.DelegatorAddress, delegate.ValidatorAddress, root)
case staking.DirectiveUndelegate:
case staking.DirectiveCollectRewards:
// TODO: Check whether the delegation reward can be cleared after reward is collected
@ -2555,6 +2540,38 @@ func (bc *BlockChain) UpdateStakingMetaData(tx *staking.StakingTransaction) erro
return nil
}
func (bc *BlockChain) addDelegationIndex(delegatorAddress, validatorAddress common.Address, root common.Hash) error {
// Get existing delegations
delegations, err := bc.ReadDelegationsByDelegator(delegatorAddress)
if err != nil {
return err
}
// If there is an existing delegation, just return
validatorAddressBytes := validatorAddress.Bytes()
for _, delegation := range delegations {
if bytes.Compare(delegation.ValidatorAddress.Bytes(), validatorAddressBytes) == 0 {
return nil
}
}
// Found the delegation from state and add the delegation index
// Note this should read from the state of current block in concern
wrapper, err := bc.ReadValidatorDataAt(validatorAddress, root)
if err != nil {
return err
}
for i := range wrapper.Delegations {
if bytes.Compare(wrapper.Delegations[i].DelegatorAddress.Bytes(), delegatorAddress.Bytes()) == 0 {
delegations = append(delegations, staking.DelegationIndex{
validatorAddress,
uint64(i),
})
}
}
return bc.WriteDelegationsByDelegator(delegatorAddress, delegations)
}
// ValidatorCandidates returns the up to date validator candidates for next epoch
func (bc *BlockChain) ValidatorCandidates() []common.Address {
list, err := bc.ReadValidatorList()

@ -38,8 +38,8 @@ type ChainContext interface {
// GetHeader returns the hash corresponding to their hash.
GetHeader(common.Hash, uint64) *block.Header
// ReadValidatorListByDelegator returns the validators list of a delegator
ReadValidatorListByDelegator(common.Address) ([]common.Address, error)
// ReadDelegationsByDelegator returns the validators list of a delegator
ReadDelegationsByDelegator(common.Address) ([]types2.DelegationIndex, error)
// ReadValidatorSnapshot returns the snapshot of validator at the beginning of current epoch.
ReadValidatorSnapshot(common.Address) (*types2.ValidatorWrapper, error)

@ -716,28 +716,28 @@ func WriteValidatorList(db DatabaseWriter, addrs []common.Address, activeOnly bo
return err
}
// ReadValidatorListByDelegator retrieves the list of validators delegated by a delegator
func ReadValidatorListByDelegator(db DatabaseReader, delegator common.Address) ([]common.Address, error) {
// ReadDelegationsByDelegator retrieves the list of validators delegated by a delegator
func ReadDelegationsByDelegator(db DatabaseReader, delegator common.Address) ([]staking.DelegationIndex, error) {
data, err := db.Get(delegatorValidatorListKey(delegator))
if err != nil || len(data) == 0 {
return []common.Address{}, nil
return []staking.DelegationIndex{}, nil
}
addrs := []common.Address{}
addrs := []staking.DelegationIndex{}
if err := rlp.DecodeBytes(data, &addrs); err != nil {
utils.Logger().Error().Err(err).Msg("Unable to Decode validator List from database")
utils.Logger().Error().Err(err).Msg("Unable to Decode delegations from database")
return nil, err
}
return addrs, nil
}
// WriteValidatorListByDelegator stores the list of validators delegated by a delegator
func WriteValidatorListByDelegator(db DatabaseWriter, delegator common.Address, addrs []common.Address) error {
bytes, err := rlp.EncodeToBytes(addrs)
// 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 {
utils.Logger().Error().Msg("[WriteValidatorListByDelegator] Failed to encode")
utils.Logger().Error().Msg("[WriteDelegationsByDelegator] Failed to encode")
}
if err := db.Put(delegatorValidatorListKey(delegator), bytes); err != nil {
utils.Logger().Error().Msg("[WriteValidatorListByDelegator] Failed to store to database")
utils.Logger().Error().Msg("[WriteDelegationsByDelegator] Failed to store to database")
}
return err
}

@ -496,31 +496,28 @@ func (st *StateTransition) applyCollectRewards(collectRewards *staking.CollectRe
return errors.New("[CollectRewards] No chain context provided")
}
chainContext := st.bc
validators, err := chainContext.ReadValidatorListByDelegator(collectRewards.DelegatorAddress)
delegations, err := chainContext.ReadDelegationsByDelegator(collectRewards.DelegatorAddress)
if err != nil {
return err
}
totalRewards := big.NewInt(0)
for i := range validators {
wrapper := st.state.GetStakingInfo(validators[i])
for i := range delegations {
wrapper := st.state.GetStakingInfo(delegations[i].ValidatorAddress)
if wrapper == nil {
return errValidatorNotExist
}
// TODO: add the index of the validator-delegation position in the ReadValidatorListByDelegator record to avoid looping
for j := range wrapper.Delegations {
delegation := &wrapper.Delegations[j]
if bytes.Equal(delegation.DelegatorAddress.Bytes(), collectRewards.DelegatorAddress.Bytes()) {
if delegation.Reward.Cmp(big.NewInt(0)) > 0 {
totalRewards.Add(totalRewards, delegation.Reward)
}
delegation.Reward.SetUint64(0)
break
if uint64(len(wrapper.Delegations)) > delegations[i].Index {
delegation := &wrapper.Delegations[delegations[i].Index]
if delegation.Reward.Cmp(big.NewInt(0)) > 0 {
totalRewards.Add(totalRewards, delegation.Reward)
}
delegation.Reward.SetUint64(0)
}
err = st.state.UpdateStakingInfo(wrapper.Validator.Address, wrapper)
if err != nil {
return err

@ -1,7 +1,6 @@
package hmy
import (
"bytes"
"context"
"errors"
"math/big"
@ -323,32 +322,25 @@ func (b *APIBackend) GetDelegationsByValidator(validator common.Address) []*stak
// GetDelegationsByDelegator returns all delegation information of a delegator
func (b *APIBackend) GetDelegationsByDelegator(delegator common.Address) ([]common.Address, []*staking.Delegation) {
addresses := []common.Address{}
delegations := []*staking.Delegation{}
addresses, err := b.hmy.BlockChain().ReadValidatorListByDelegator(delegator)
delegationIndexes, err := b.hmy.BlockChain().ReadDelegationsByDelegator(delegator)
if err != nil {
return nil, nil
}
for i := range addresses {
wrapper, err := b.hmy.BlockChain().ReadValidatorData(addresses[i])
for i := range delegationIndexes {
wrapper, err := b.hmy.BlockChain().ReadValidatorData(delegationIndexes[i].ValidatorAddress)
if err != nil || wrapper == nil {
return nil, nil
}
// TODO: add the index of the validator-delegation position in the ReadValidatorListByDelegator record to avoid looping
found := -1
for j := range wrapper.Delegations {
delegation := wrapper.Delegations[j]
if bytes.Equal(delegation.DelegatorAddress.Bytes(), delegator.Bytes()) {
found = j
break
}
}
if found != -1 {
delegations = append(delegations, &wrapper.Delegations[found])
if uint64(len(wrapper.Delegations)) > delegationIndexes[i].Index {
delegations = append(delegations, &wrapper.Delegations[delegationIndexes[i].Index])
} else {
delegations = append(delegations, nil)
}
addresses = append(addresses, delegationIndexes[i].ValidatorAddress)
}
return addresses, delegations
}

@ -34,6 +34,12 @@ type Undelegation struct {
Epoch *big.Int
}
// DelegationIndex stored the index of a delegation in the validator's delegation list
type DelegationIndex struct {
ValidatorAddress common.Address
Index uint64
}
// NewDelegation creates a new delegation object
func NewDelegation(delegatorAddr common.Address,
amount *big.Int) Delegation {

Loading…
Cancel
Save