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. 103
      core/blockchain.go
  2. 4
      core/evm.go
  3. 20
      core/rawdb/accessors_chain.go
  4. 15
      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 // Do bookkeeping for new staking txns
if bc.chainConfig.IsStaking(block.Epoch()) { if bc.chainConfig.IsStaking(block.Epoch()) {
for _, tx := range block.StakingTransactions() { for _, tx := range block.StakingTransactions() {
err = bc.UpdateStakingMetaData(tx) err = bc.UpdateStakingMetaData(tx, root)
// keep offchain database consistency with onchain we need revert // keep offchain database consistency with onchain we need revert
// but it should not happend unless local database corrupted // but it should not happend unless local database corrupted
if err != nil { if err != nil {
@ -2307,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)
} }
// ReadValidatorData reads staking information of given validatorWrapper // ReadValidatorDataAt reads staking information of given validatorWrapper at a specific state root
func (bc *BlockChain) ReadValidatorData(addr common.Address) (*staking.ValidatorWrapper, error) { func (bc *BlockChain) ReadValidatorDataAt(addr common.Address, root common.Hash) (*staking.ValidatorWrapper, error) {
state, err := bc.StateAt(bc.CurrentBlock().Root()) state, err := bc.StateAt(root)
if err != nil || state == nil { if err != nil || state == nil {
return nil, err return nil, err
} }
@ -2320,6 +2320,11 @@ func (bc *BlockChain) ReadValidatorData(addr common.Address) (*staking.Validator
return wrapper, nil 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 // ReadValidatorSnapshot reads the snapshot staking information of given validator address
// TODO: put epoch number in to snapshot too. // TODO: put epoch number in to snapshot too.
func (bc *BlockChain) ReadValidatorSnapshot(addr common.Address) (*staking.ValidatorWrapper, error) { func (bc *BlockChain) ReadValidatorSnapshot(addr common.Address) (*staking.ValidatorWrapper, error) {
@ -2455,26 +2460,26 @@ func (bc *BlockChain) WriteActiveValidatorList(addrs []common.Address) error {
return nil return nil
} }
// ReadValidatorListByDelegator reads the addresses of validators delegated by a delegator // ReadDelegationsByDelegator reads the addresses of validators delegated by a delegator
func (bc *BlockChain) ReadValidatorListByDelegator(delegator common.Address) ([]common.Address, error) { func (bc *BlockChain) ReadDelegationsByDelegator(delegator common.Address) ([]staking.DelegationIndex, error) {
if cached, ok := bc.validatorListByDelegatorCache.Get(string(delegator.Bytes())); ok { if cached, ok := bc.validatorListByDelegatorCache.Get(string(delegator.Bytes())); ok {
by := cached.([]byte) by := cached.([]byte)
m := []common.Address{} m := []staking.DelegationIndex{}
if err := rlp.DecodeBytes(by, &m); err != nil { if err := rlp.DecodeBytes(by, &m); err != nil {
return nil, err return nil, err
} }
return m, nil return m, nil
} }
return rawdb.ReadValidatorListByDelegator(bc.db, delegator) return rawdb.ReadDelegationsByDelegator(bc.db, delegator)
} }
// WriteValidatorListByDelegator writes the list of validator addresses to database // WriteDelegationsByDelegator writes the list of validator addresses to database
func (bc *BlockChain) WriteValidatorListByDelegator(delegator common.Address, addrs []common.Address) error { func (bc *BlockChain) WriteDelegationsByDelegator(delegator common.Address, indices []staking.DelegationIndex) error {
err := rawdb.WriteValidatorListByDelegator(bc.db, delegator, addrs) err := rawdb.WriteDelegationsByDelegator(bc.db, delegator, indices)
if err != nil { if err != nil {
return err return err
} }
bytes, err := rlp.EncodeToBytes(addrs) bytes, err := rlp.EncodeToBytes(indices)
if err == nil { if err == nil {
bc.validatorListByDelegatorCache.Add(string(delegator.Bytes()), bytes) 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 // 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 // TODO: simply the logic here in staking/types/transaction.go
payload, err := tx.RLPEncodeStakeMsg() payload, err := tx.RLPEncodeStakeMsg()
if err != nil { if err != nil {
@ -2509,50 +2514,62 @@ func (bc *BlockChain) UpdateStakingMetaData(tx *staking.StakingTransaction) erro
err = bc.WriteValidatorList(list) err = bc.WriteValidatorList(list)
} }
// TODO: combine this code with the one for Delegate. // Add self delegation into the index
validators, err := bc.ReadValidatorListByDelegator(createValidator.ValidatorAddress) delegations, err := bc.ReadDelegationsByDelegator(createValidator.ValidatorAddress)
if err != nil { if err != nil {
return err 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.DirectiveEditValidator:
case staking.DirectiveDelegate: case staking.DirectiveDelegate:
delegate := decodePayload.(*staking.Delegate) delegate := decodePayload.(*staking.Delegate)
validators, err := bc.ReadValidatorListByDelegator(delegate.DelegatorAddress)
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
default:
}
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 { if err != nil {
return err return err
} }
found := false
for _, validator := range validators { // If there is an existing delegation, just return
if bytes.Compare(validator.Bytes(), delegate.ValidatorAddress.Bytes()) == 0 { validatorAddressBytes := validatorAddress.Bytes()
found = true for _, delegation := range delegations {
break if bytes.Compare(delegation.ValidatorAddress.Bytes(), validatorAddressBytes) == 0 {
} return nil
} }
if !found {
validators = append(validators, delegate.ValidatorAddress)
} }
err = bc.WriteValidatorListByDelegator(delegate.DelegatorAddress, validators)
// 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 return err
case staking.DirectiveUndelegate:
case staking.DirectiveCollectRewards:
// TODO: Check whether the delegation reward can be cleared after reward is collected
default:
} }
return nil 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 // ValidatorCandidates returns the up to date validator candidates for next epoch

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

@ -716,28 +716,28 @@ func WriteValidatorList(db DatabaseWriter, addrs []common.Address, activeOnly bo
return err return err
} }
// ReadValidatorListByDelegator retrieves the list of validators delegated by a delegator // ReadDelegationsByDelegator retrieves the list of validators delegated by a delegator
func ReadValidatorListByDelegator(db DatabaseReader, delegator common.Address) ([]common.Address, error) { func ReadDelegationsByDelegator(db DatabaseReader, delegator common.Address) ([]staking.DelegationIndex, error) {
data, err := db.Get(delegatorValidatorListKey(delegator)) data, err := db.Get(delegatorValidatorListKey(delegator))
if err != nil || len(data) == 0 { 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 { 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 nil, err
} }
return addrs, nil return addrs, nil
} }
// WriteValidatorListByDelegator stores the list of validators delegated by a delegator // WriteDelegationsByDelegator stores the list of validators delegated by a delegator
func WriteValidatorListByDelegator(db DatabaseWriter, delegator common.Address, addrs []common.Address) error { func WriteDelegationsByDelegator(db DatabaseWriter, delegator common.Address, indices []staking.DelegationIndex) error {
bytes, err := rlp.EncodeToBytes(addrs) bytes, err := rlp.EncodeToBytes(indices)
if err != nil { 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 { 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 return err
} }

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

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

@ -34,6 +34,12 @@ type Undelegation struct {
Epoch *big.Int 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 // NewDelegation creates a new delegation object
func NewDelegation(delegatorAddr common.Address, func NewDelegation(delegatorAddr common.Address,
amount *big.Int) Delegation { amount *big.Int) Delegation {

Loading…
Cancel
Save