Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rosetta Implementation - pt2 FIX2 (Stage 3.2 of Node API Overhaul) #3338

Merged
merged 22 commits into from
Sep 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
381f19a
[rosetta] Update staking operations to account for re-delegation
Daniel-VDM Sep 9, 2020
e76a3df
[hmy] Add GetUndelegationChange
Daniel-VDM Sep 9, 2020
d375f77
[hmy] Add GetAllUndelegatedDelegators
Daniel-VDM Sep 9, 2020
6210745
[hmy] Fix GetAllUndelegatedDelegators & add GetDelegationLockingPerio…
Daniel-VDM Sep 9, 2020
8b4bd8a
[rosetta] Fix block reward TX ID formatting
Daniel-VDM Sep 9, 2020
db848aa
[hmy] Remove unused GetUndelegationChange
Daniel-VDM Sep 10, 2020
536640b
[hmy] Remove debug print & update comments for GetUndelegationPayouts
Daniel-VDM Sep 10, 2020
dba804a
[core] Add last garbage collected number to blockchain.go
Daniel-VDM Sep 10, 2020
fe9b6c7
[rosetta] Add oldest block ID in net stat for non-archival nodes
Daniel-VDM Sep 10, 2020
3e704b1
[rosetta] Fix network oldest block case when garb col blk unknown
Daniel-VDM Sep 10, 2020
62019d4
[core] Rename lastGarbCollectedBlkNum to maxGarbCollectedBlkNum
Daniel-VDM Sep 10, 2020
40ed0ee
[internal/chain] Refactor token lock period getter & expose
Daniel-VDM Sep 10, 2020
d19fae8
[hmy] Add UndelegationPayouts type
Daniel-VDM Sep 10, 2020
a162470
[rosetta] Improve NewError detail failure message
Daniel-VDM Sep 10, 2020
3a91bd6
[rosetta] Integrate Correct undelegation payout operations
Daniel-VDM Sep 11, 2020
c767c4f
[hmy] Add caching to GetUndelegationPayouts
Daniel-VDM Sep 11, 2020
0892c76
[hmy] Nit - fix comment
Daniel-VDM Sep 11, 2020
6f7f652
[rosetta] Add block not found error
Daniel-VDM Sep 11, 2020
257041d
[rosetta] Refactor special case txID to be for general
Daniel-VDM Sep 11, 2020
8516927
[rosetta] Fix lint
Daniel-VDM Sep 11, 2020
e504b04
[rosetta] Nit - fix comment
Daniel-VDM Sep 11, 2020
834e09b
[hmy] Nit - Make GetUndelegationPayouts more readable
Daniel-VDM Sep 11, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,14 @@ type BlockChain struct {
procInterrupt int32 // interrupt signaler for block processing
wg sync.WaitGroup // chain processing wait group for shutting down

engine consensus_engine.Engine
processor Processor // block processor interface
validator Validator // block and state validator interface
vmConfig vm.Config
badBlocks *lru.Cache // Bad block cache
shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block.
pendingSlashes slash.Records
engine consensus_engine.Engine
processor Processor // block processor interface
validator Validator // block and state validator interface
vmConfig vm.Config
badBlocks *lru.Cache // Bad block cache
shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block.
pendingSlashes slash.Records
maxGarbCollectedBlkNum int64
}

// NewBlockChain returns a fully initialised block chain using information
Expand Down Expand Up @@ -228,6 +229,7 @@ func NewBlockChain(
vmConfig: vmConfig,
badBlocks: badBlocks,
pendingSlashes: slash.Records{},
maxGarbCollectedBlkNum: -1,
}
bc.SetValidator(NewBlockValidator(chainConfig, bc, engine))
bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine))
Expand Down Expand Up @@ -1168,6 +1170,9 @@ func (bc *BlockChain) WriteBlockWithState(
bc.triegc.Push(root, number)
break
}
if -number > bc.maxGarbCollectedBlkNum {
rlan35 marked this conversation as resolved.
Show resolved Hide resolved
bc.maxGarbCollectedBlkNum = -number
}
triedb.Dereference(root.(common.Hash))
}
}
Expand Down Expand Up @@ -1202,6 +1207,11 @@ func (bc *BlockChain) WriteBlockWithState(
return CanonStatTy, nil
}

// GetMaxGarbageCollectedBlockNumber ..
func (bc *BlockChain) GetMaxGarbageCollectedBlockNumber() int64 {
return bc.maxGarbCollectedBlkNum
}

// InsertChain attempts to insert the given batch of blocks in to the canonical
// chain or, otherwise, create a fork. If an error is returned it will return
// the index number of the failing block as well an error describing what went
Expand Down Expand Up @@ -2021,10 +2031,10 @@ func (bc *BlockChain) ReadPendingCrossLinks() ([]types.CrossLink, error) {
// WritePendingCrossLinks saves the pending crosslinks
func (bc *BlockChain) WritePendingCrossLinks(crossLinks []types.CrossLink) error {
// deduplicate crosslinks if any
m := map[uint32]map[uint64](types.CrossLink){}
m := map[uint32]map[uint64]types.CrossLink{}
for _, cl := range crossLinks {
if _, ok := m[cl.ShardID()]; !ok {
m[cl.ShardID()] = map[uint64](types.CrossLink){}
m[cl.ShardID()] = map[uint64]types.CrossLink{}
}
m[cl.ShardID()][cl.BlockNum()] = cl
}
Expand Down Expand Up @@ -2111,10 +2121,10 @@ func (bc *BlockChain) DeleteFromPendingCrossLinks(crossLinks []types.CrossLink)
return 0, err
}

m := map[uint32]map[uint64](struct{}){}
m := map[uint32]map[uint64]struct{}{}
for _, cl := range crossLinks {
if _, ok := m[cl.ShardID()]; !ok {
m[cl.ShardID()] = map[uint64](struct{}){}
m[cl.ShardID()] = map[uint64]struct{}{}
}
m[cl.ShardID()][cl.BlockNum()] = struct{}{}
}
Expand Down
39 changes: 22 additions & 17 deletions hmy/hmy.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ import (
const (
// BloomBitsBlocks is the number of blocks a single bloom bit section vector
// contains on the server side.
BloomBitsBlocks uint64 = 4096
leaderCacheSize = 250 // Approx number of BLS keys in committee
totalStakeCacheDuration = 20 // number of blocks where the returned total stake will remain the same
BloomBitsBlocks uint64 = 4096
leaderCacheSize = 250 // Approx number of BLS keys in committee
undelegationPayoutsCacheSize = 500 // max number of epochs to store in cache
totalStakeCacheDuration = 20 // number of blocks where the returned total stake will remain the same
)

var (
Expand Down Expand Up @@ -64,6 +65,8 @@ type Harmony struct {
group singleflight.Group
// leaderCache to save on recomputation every epoch.
leaderCache *lru.Cache
// undelegationPayoutsCache to save on recomputation every epoch
undelegationPayoutsCache *lru.Cache
// totalStakeCache to save on recomputation for `totalStakeCacheDuration` blocks.
totalStakeCache *totalStakeCache
}
Expand Down Expand Up @@ -98,24 +101,26 @@ func New(
) *Harmony {
chainDb := nodeAPI.Blockchain().ChainDB()
leaderCache, _ := lru.New(leaderCacheSize)
undelegationPayoutsCache, _ := lru.New(undelegationPayoutsCacheSize)
totalStakeCache := newTotalStakeCache(totalStakeCacheDuration)
bloomIndexer := NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms)
bloomIndexer.Start(nodeAPI.Blockchain())
return &Harmony{
ShutdownChan: make(chan bool),
BloomRequests: make(chan chan *bloombits.Retrieval),
BloomIndexer: bloomIndexer,
BlockChain: nodeAPI.Blockchain(),
BeaconChain: nodeAPI.Beaconchain(),
TxPool: txPool,
CxPool: cxPool,
eventMux: new(event.TypeMux),
chainDb: chainDb,
NodeAPI: nodeAPI,
ChainID: nodeAPI.Blockchain().Config().ChainID.Uint64(),
ShardID: shardID,
leaderCache: leaderCache,
totalStakeCache: totalStakeCache,
ShutdownChan: make(chan bool),
BloomRequests: make(chan chan *bloombits.Retrieval),
BloomIndexer: bloomIndexer,
BlockChain: nodeAPI.Blockchain(),
BeaconChain: nodeAPI.Beaconchain(),
TxPool: txPool,
CxPool: cxPool,
eventMux: new(event.TypeMux),
chainDb: chainDb,
NodeAPI: nodeAPI,
ChainID: nodeAPI.Blockchain().Config().ChainID.Uint64(),
ShardID: shardID,
leaderCache: leaderCache,
totalStakeCache: totalStakeCache,
undelegationPayoutsCache: undelegationPayoutsCache,
}
}

Expand Down
84 changes: 81 additions & 3 deletions hmy/staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import (
"sync"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rpc"
"github.com/harmony-one/harmony/consensus/quorum"
"github.com/harmony-one/harmony/core/rawdb"
"github.com/harmony-one/harmony/core/types"
internal_common "github.com/harmony-one/harmony/internal/common"
"github.com/harmony-one/harmony/internal/chain"
internalCommon "github.com/harmony-one/harmony/internal/common"
"github.com/harmony-one/harmony/numeric"
commonRPC "github.com/harmony-one/harmony/rpc/common"
"github.com/harmony-one/harmony/shard"
Expand All @@ -22,7 +24,8 @@ import (
)

var (
zero = numeric.ZeroDec()
zero = numeric.ZeroDec()
bigZero = big.NewInt(0)
)

func (hmy *Harmony) readAndUpdateRawStakes(
Expand Down Expand Up @@ -125,6 +128,16 @@ func (hmy *Harmony) IsStakingEpoch(epoch *big.Int) bool {
return hmy.BlockChain.Config().IsStaking(epoch)
}

// IsPreStakingEpoch ...
func (hmy *Harmony) IsPreStakingEpoch(epoch *big.Int) bool {
return hmy.BlockChain.Config().IsPreStaking(epoch)
}

// GetDelegationLockingPeriodInEpoch ...
func (hmy *Harmony) GetDelegationLockingPeriodInEpoch(epoch *big.Int) int {
return chain.GetLockPeriodInEpoch(hmy.BlockChain, epoch)
}

// SendStakingTx adds a staking transaction
func (hmy *Harmony) SendStakingTx(ctx context.Context, signedStakingTx *staking.StakingTransaction) error {
stx, _, _, _ := rawdb.ReadStakingTransaction(hmy.chainDb, signedStakingTx.Hash())
Expand Down Expand Up @@ -252,7 +265,7 @@ func (hmy *Harmony) GetValidatorInformation(
bc := hmy.BlockChain
wrapper, err := bc.ReadValidatorInformationAt(addr, block.Root())
if err != nil {
s, _ := internal_common.AddressToBech32(addr)
s, _ := internalCommon.AddressToBech32(addr)
return nil, errors.Wrapf(err, "not found address in current state %s", s)
}

Expand Down Expand Up @@ -433,6 +446,21 @@ func (hmy *Harmony) GetDelegationsByValidator(validator common.Address) []*staki
return delegations
}

// GetDelegationsByValidatorAtBlock returns all delegation information of a validator at the given block
func (hmy *Harmony) GetDelegationsByValidatorAtBlock(
validator common.Address, block *types.Block,
) []*staking.Delegation {
wrapper, err := hmy.BlockChain.ReadValidatorInformationAt(validator, block.Root())
if err != nil || wrapper == nil {
return nil
}
delegations := []*staking.Delegation{}
for i := range wrapper.Delegations {
delegations = append(delegations, &wrapper.Delegations[i])
}
return delegations
}

// GetDelegationsByDelegator returns all delegation information of a delegator
func (hmy *Harmony) GetDelegationsByDelegator(
delegator common.Address,
Expand Down Expand Up @@ -471,6 +499,56 @@ func (hmy *Harmony) GetDelegationsByDelegatorByBlock(
return addresses, delegations
}

// UndelegationPayouts ..
type UndelegationPayouts map[common.Address]*big.Int

// GetUndelegationPayouts returns the undelegation payouts for each delegator
//
// Due to in-memory caching, it is possible to get undelegation payouts for a state / epoch
// that has been pruned but have it be lost (and unable to recompute) after the node restarts.
// This not a problem if a full (archival) DB is used.
func (hmy *Harmony) GetUndelegationPayouts(
ctx context.Context, epoch *big.Int,
) (UndelegationPayouts, error) {
if !hmy.IsPreStakingEpoch(epoch) {
return nil, fmt.Errorf("not pre-staking epoch or later")
}

payouts, ok := hmy.undelegationPayoutsCache.Get(epoch.Uint64())
if ok {
return payouts.(UndelegationPayouts), nil
}
undelegationPayouts := UndelegationPayouts{}
// require second to last block as saved undelegations are AFTER undelegations are payed out
blockNumber := shard.Schedule.EpochLastBlock(epoch.Uint64()) - 1
undelegationPayoutBlock, err := hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNumber))
if err != nil || undelegationPayoutBlock == nil {
// Block not found, so no undelegationPayouts (not an error)
return undelegationPayouts, nil
}

lockingPeriod := hmy.GetDelegationLockingPeriodInEpoch(undelegationPayoutBlock.Epoch())
for _, validator := range hmy.GetAllValidatorAddresses() {
wrapper, err := hmy.BlockChain.ReadValidatorInformationAt(validator, undelegationPayoutBlock.Root())
if err != nil || wrapper == nil {
continue // Not a validator at this epoch or unable to fetch validator info because of pruned state.
}
for _, delegation := range wrapper.Delegations {
withdraw := delegation.RemoveUnlockedUndelegations(epoch, wrapper.LastEpochInCommittee, lockingPeriod)
if withdraw.Cmp(bigZero) == 1 {
if totalPayout, ok := undelegationPayouts[delegation.DelegatorAddress]; ok {
undelegationPayouts[delegation.DelegatorAddress] = new(big.Int).Add(totalPayout, withdraw)
} else {
undelegationPayouts[delegation.DelegatorAddress] = withdraw
}
}
}
}

hmy.undelegationPayoutsCache.Add(epoch.Uint64(), undelegationPayouts)
return undelegationPayouts, nil
}

// GetTotalStakingSnapshot ..
func (hmy *Harmony) GetTotalStakingSnapshot() *big.Int {
if stake := hmy.totalStakeCache.pop(hmy.CurrentBlock().NumberU64()); stake != nil {
Expand Down
18 changes: 12 additions & 6 deletions internal/chain/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,12 +292,7 @@ func payoutUndelegations(
"[Finalize] failed to get validator from state to finalize",
)
}
lockPeriod := staking.LockPeriodInEpoch
if chain.Config().IsRedelegation(header.Epoch()) {
lockPeriod = staking.LockPeriodInEpoch
} else if chain.Config().IsQuickUnlock(header.Epoch()) {
lockPeriod = staking.LockPeriodInEpochV2
}
lockPeriod := GetLockPeriodInEpoch(chain, header.Epoch())
for i := range wrapper.Delegations {
delegation := &wrapper.Delegations[i]
totalWithdraw := delegation.RemoveUnlockedUndelegations(
Expand Down Expand Up @@ -547,3 +542,14 @@ func GetPublicKeys(
}
return subCommittee.BLSPublicKeys()
}

// GetLockPeriodInEpoch returns the delegation lock period for the given chain
func GetLockPeriodInEpoch(chain engine.ChainReader, epoch *big.Int) int {
lockPeriod := staking.LockPeriodInEpoch
if chain.Config().IsRedelegation(epoch) {
lockPeriod = staking.LockPeriodInEpoch
} else if chain.Config().IsQuickUnlock(epoch) {
lockPeriod = staking.LockPeriodInEpochV2
}
return lockPeriod
}
13 changes: 7 additions & 6 deletions rosetta/common/errors.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package common

import (
"fmt"

"github.com/coinbase/rosetta-sdk-go/types"
"github.com/harmony-one/harmony/rpc"
)
Expand Down Expand Up @@ -65,15 +67,14 @@ var (

// NewError create a new error with a given detail structure
func NewError(rosettaError types.Error, detailStructure interface{}) *types.Error {
newError := rosettaError
details, err := rpc.NewStructuredResponse(detailStructure)
if err != nil {
newError := CatchAllError
CatchAllError.Details = map[string]interface{}{
"message": err.Error(),
newError.Details = map[string]interface{}{
"message": fmt.Sprintf("unable to get error details: %v", err.Error()),
}
return &newError
} else {
newError.Details = details
}
newError := rosettaError
newError.Details = details
return &newError
}
10 changes: 7 additions & 3 deletions rosetta/common/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ const (
// GenesisFundsOperation ..
GenesisFundsOperation = "Genesis"

// PreStakingEraBlockRewardOperation ..
PreStakingEraBlockRewardOperation = "PreStakingBlockReward"
// PreStakingBlockRewardOperation ..
PreStakingBlockRewardOperation = "PreOpenStakingBlockReward"

// UndelegationPayoutOperation ..
UndelegationPayoutOperation = "UndelegationPayout"
)

var (
Expand All @@ -33,7 +36,8 @@ var (
CrossShardTransferOperation,
ContractCreationOperation,
GenesisFundsOperation,
PreStakingEraBlockRewardOperation,
PreStakingBlockRewardOperation,
UndelegationPayoutOperation,
}

// StakingOperationTypes ..
Expand Down
3 changes: 2 additions & 1 deletion rosetta/common/operations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ func TestPlainOperationTypes(t *testing.T) {
CrossShardTransferOperation,
ContractCreationOperation,
GenesisFundsOperation,
PreStakingEraBlockRewardOperation,
PreStakingBlockRewardOperation,
UndelegationPayoutOperation,
}
sort.Strings(referenceOperationTypes)
sort.Strings(plainOperationTypes)
Expand Down
Loading