From dacbaab8306e470dd4849f698c87e8fb88f22d9e Mon Sep 17 00:00:00 2001 From: envestcc Date: Wed, 11 Oct 2023 17:26:20 +0800 Subject: [PATCH] address comments --- action/protocol/staking/vote_bucket.go | 7 +- action/protocol/staking/vote_bucket_test.go | 4 + blockindex/contractstaking/bucket.go | 5 +- blockindex/contractstaking/cache.go | 18 ++- blockindex/contractstaking/cache_test.go | 24 ++-- blockindex/contractstaking/delta_cache.go | 2 +- .../contractstaking/dirty_cache_test.go | 12 +- blockindex/contractstaking/indexer.go | 47 ++++--- blockindex/contractstaking/indexer_test.go | 115 +++++++++++++++--- chainservice/builder.go | 13 +- e2etest/contract_staking_test.go | 10 +- 11 files changed, 178 insertions(+), 79 deletions(-) diff --git a/action/protocol/staking/vote_bucket.go b/action/protocol/staking/vote_bucket.go index 0af0424d7a..494d6001e7 100644 --- a/action/protocol/staking/vote_bucket.go +++ b/action/protocol/staking/vote_bucket.go @@ -23,8 +23,7 @@ import ( ) const ( - maxBlockNumber = math.MaxUint64 - blockProduceInterval = 5 // produce one block per 5 seconds + maxBlockNumber = math.MaxUint64 ) type ( @@ -221,10 +220,6 @@ func bucketKey(index uint64) []byte { // CalculateVoteWeight calculates the vote weight func CalculateVoteWeight(c genesis.VoteWeightCalConsts, v *VoteBucket, selfStake bool) *big.Int { remainingTime := v.StakedDuration.Seconds() - if !v.isNative() { - // for contract staking, use block number to calculate remaining time - remainingTime = float64(v.StakedDurationBlockNumber) * blockProduceInterval - } weight := float64(1) var m float64 if v.AutoStake { diff --git a/action/protocol/staking/vote_bucket_test.go b/action/protocol/staking/vote_bucket_test.go index 65a39b724a..ffa410e3a7 100644 --- a/action/protocol/staking/vote_bucket_test.go +++ b/action/protocol/staking/vote_bucket_test.go @@ -162,6 +162,7 @@ func TestCalculateVoteWeight(t *testing.T) { name: "NFT, auto-stake enabled, self-stake enabled", consts: consts, voteBucket: &VoteBucket{ + StakedDuration: 30 * 24 * time.Hour, StakedDurationBlockNumber: 30 * 17280, AutoStake: true, StakedAmount: big.NewInt(10000), @@ -174,6 +175,7 @@ func TestCalculateVoteWeight(t *testing.T) { name: "NFT, auto-stake enabled, self-stake disabled", consts: consts, voteBucket: &VoteBucket{ + StakedDuration: 30 * 24 * time.Hour, StakedDurationBlockNumber: 30 * 17280, AutoStake: true, StakedAmount: big.NewInt(10000), @@ -186,6 +188,7 @@ func TestCalculateVoteWeight(t *testing.T) { name: "NFT, auto-stake disabled, self-stake enabled", consts: consts, voteBucket: &VoteBucket{ + StakedDuration: 30 * 24 * time.Hour, StakedDurationBlockNumber: 30 * 17280, AutoStake: false, StakedAmount: big.NewInt(10000), @@ -198,6 +201,7 @@ func TestCalculateVoteWeight(t *testing.T) { name: "NFT, auto-stake disabled, self-stake disabled", consts: consts, voteBucket: &VoteBucket{ + StakedDuration: 30 * 24 * time.Hour, StakedDurationBlockNumber: 30 * 17280, AutoStake: false, StakedAmount: big.NewInt(10000), diff --git a/blockindex/contractstaking/bucket.go b/blockindex/contractstaking/bucket.go index 045b96d749..a5a028139a 100644 --- a/blockindex/contractstaking/bucket.go +++ b/blockindex/contractstaking/bucket.go @@ -6,16 +6,19 @@ package contractstaking import ( + "time" + "github.com/iotexproject/iotex-core/action/protocol/staking" ) // Bucket defines the bucket struct for contract staking type Bucket = staking.VoteBucket -func assembleBucket(token uint64, bi *bucketInfo, bt *BucketType, contractAddr string) *Bucket { +func assembleBucket(token uint64, bi *bucketInfo, bt *BucketType, contractAddr string, blockInterval time.Duration) *Bucket { vb := Bucket{ Index: token, StakedAmount: bt.Amount, + StakedDuration: time.Duration(bt.Duration) * blockInterval, StakedDurationBlockNumber: bt.Duration, CreateBlockHeight: bi.CreatedAt, StakeStartBlockHeight: bi.CreatedAt, diff --git a/blockindex/contractstaking/cache.go b/blockindex/contractstaking/cache.go index 646592b396..b25d017f8b 100644 --- a/blockindex/contractstaking/cache.go +++ b/blockindex/contractstaking/cache.go @@ -26,12 +26,9 @@ type ( propertyBucketTypeMap map[int64]map[uint64]uint64 // map[amount][duration]index totalBucketCount uint64 // total number of buckets including burned buckets height uint64 // current block height, it's put in cache for consistency on merge - contractAddress string // contract address for the bucket mutex sync.RWMutex // a RW mutex for the cache to protect concurrent access - calculateVoteWeight calculateVoteWeightFunc // calculateVoteWeight is a function to calculate vote weight + config Config } - - calculateVoteWeightFunc func(v *Bucket, selfStake bool) *big.Int ) var ( @@ -41,14 +38,13 @@ var ( ErrInvalidHeight = errors.New("invalid height") ) -func newContractStakingCache(contractAddr string, calculateVoteWeight calculateVoteWeightFunc) *contractStakingCache { +func newContractStakingCache(config Config) *contractStakingCache { return &contractStakingCache{ bucketInfoMap: make(map[uint64]*bucketInfo), bucketTypeMap: make(map[uint64]*BucketType), propertyBucketTypeMap: make(map[int64]map[uint64]uint64), candidateBucketMap: make(map[string]map[uint64]bool), - contractAddress: contractAddr, - calculateVoteWeight: calculateVoteWeight, + config: config, } } @@ -82,7 +78,7 @@ func (s *contractStakingCache) CandidateVotes(ctx context.Context, candidate add } bt := s.mustGetBucketType(bi.TypeIndex) if featureCtx.FixContractStakingWeightedVotes { - votes.Add(votes, s.calculateVoteWeight(assembleBucket(id, bi, bt, s.contractAddress), false)) + votes.Add(votes, s.config.CalculateVoteWeight(assembleBucket(id, bi, bt, s.config.ContractAddress, s.config.BlockInterval))) } else { votes.Add(votes, bt.Amount) } @@ -101,7 +97,7 @@ func (s *contractStakingCache) Buckets(height uint64) ([]*Bucket, error) { vbs := []*Bucket{} for id, bi := range s.bucketInfoMap { bt := s.mustGetBucketType(bi.TypeIndex) - vb := assembleBucket(id, bi.clone(), bt, s.contractAddress) + vb := assembleBucket(id, bi.clone(), bt, s.config.ContractAddress, s.config.BlockInterval) vbs = append(vbs, vb) } return vbs, nil @@ -362,7 +358,7 @@ func (s *contractStakingCache) mustGetBucketInfo(id uint64) *bucketInfo { func (s *contractStakingCache) mustGetBucket(id uint64) *Bucket { bi := s.mustGetBucketInfo(id) bt := s.mustGetBucketType(bi.TypeIndex) - return assembleBucket(id, bi, bt, s.contractAddress) + return assembleBucket(id, bi, bt, s.config.ContractAddress, s.config.BlockInterval) } func (s *contractStakingCache) getBucket(id uint64) (*Bucket, bool) { @@ -371,7 +367,7 @@ func (s *contractStakingCache) getBucket(id uint64) (*Bucket, bool) { return nil, false } bt := s.mustGetBucketType(bi.TypeIndex) - return assembleBucket(id, bi, bt, s.contractAddress), true + return assembleBucket(id, bi, bt, s.config.ContractAddress, s.config.BlockInterval), true } func (s *contractStakingCache) putBucketType(id uint64, bt *BucketType) { diff --git a/blockindex/contractstaking/cache_test.go b/blockindex/contractstaking/cache_test.go index 0b356906cd..9d8eba2639 100644 --- a/blockindex/contractstaking/cache_test.go +++ b/blockindex/contractstaking/cache_test.go @@ -26,8 +26,8 @@ func _checkCacheCandidateVotes(ctx context.Context, r *require.Assertions, cache } func calculateVoteWeightGen(c genesis.VoteWeightCalConsts) calculateVoteWeightFunc { - return func(v *Bucket, selfStake bool) *big.Int { - return staking.CalculateVoteWeight(c, v, selfStake) + return func(v *Bucket) *big.Int { + return staking.CalculateVoteWeight(c, v, false) } } @@ -38,7 +38,7 @@ func TestContractStakingCache_CandidateVotes(t *testing.T) { } } require := require.New(t) - cache := newContractStakingCache(identityset.Address(1).String(), calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + cache := newContractStakingCache(Config{ContractAddress: identityset.Address(1).String(), CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), BlockInterval: _blockInterval}) checkCacheCandidateVotes := checkCacheCandidateVotesGen(protocol.WithFeatureCtx(protocol.WithBlockCtx(genesis.WithGenesisContext(context.Background(), genesis.Default), protocol.BlockCtx{BlockHeight: 1}))) checkCacheCandidateVotesAfterRedsea := checkCacheCandidateVotesGen(protocol.WithFeatureCtx(protocol.WithBlockCtx(genesis.WithGenesisContext(context.Background(), genesis.Default), protocol.BlockCtx{BlockHeight: genesis.Default.RedseaBlockHeight}))) // no bucket @@ -115,7 +115,7 @@ func TestContractStakingCache_CandidateVotes(t *testing.T) { func TestContractStakingCache_Buckets(t *testing.T) { require := require.New(t) contractAddr := identityset.Address(27).String() - cache := newContractStakingCache(contractAddr, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + cache := newContractStakingCache(Config{ContractAddress: contractAddr, CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), BlockInterval: _blockInterval}) height := uint64(0) // no bucket @@ -189,7 +189,7 @@ func TestContractStakingCache_Buckets(t *testing.T) { func TestContractStakingCache_BucketsByCandidate(t *testing.T) { require := require.New(t) contractAddr := identityset.Address(27).String() - cache := newContractStakingCache(contractAddr, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + cache := newContractStakingCache(Config{ContractAddress: contractAddr, CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), BlockInterval: _blockInterval}) height := uint64(0) // no bucket @@ -261,7 +261,7 @@ func TestContractStakingCache_BucketsByCandidate(t *testing.T) { func TestContractStakingCache_BucketsByIndices(t *testing.T) { require := require.New(t) contractAddr := identityset.Address(27).String() - cache := newContractStakingCache(contractAddr, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + cache := newContractStakingCache(Config{ContractAddress: contractAddr, CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), BlockInterval: _blockInterval}) height := uint64(0) // no bucket @@ -308,7 +308,7 @@ func TestContractStakingCache_BucketsByIndices(t *testing.T) { func TestContractStakingCache_TotalBucketCount(t *testing.T) { require := require.New(t) - cache := newContractStakingCache(identityset.Address(27).String(), calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + cache := newContractStakingCache(Config{ContractAddress: identityset.Address(27).String(), CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), BlockInterval: _blockInterval}) height := uint64(0) // no bucket @@ -337,7 +337,7 @@ func TestContractStakingCache_TotalBucketCount(t *testing.T) { func TestContractStakingCache_ActiveBucketTypes(t *testing.T) { require := require.New(t) - cache := newContractStakingCache(identityset.Address(27).String(), calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + cache := newContractStakingCache(Config{ContractAddress: identityset.Address(27).String(), CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), BlockInterval: _blockInterval}) height := uint64(0) // no bucket type @@ -402,7 +402,7 @@ func TestContractStakingCache_ActiveBucketTypes(t *testing.T) { func TestContractStakingCache_Merge(t *testing.T) { require := require.New(t) - cache := newContractStakingCache(identityset.Address(27).String(), calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + cache := newContractStakingCache(Config{ContractAddress: identityset.Address(27).String(), CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), BlockInterval: _blockInterval}) height := uint64(1) ctx := protocol.WithFeatureCtx(protocol.WithBlockCtx(genesis.WithGenesisContext(context.Background(), genesis.Default), protocol.BlockCtx{BlockHeight: height})) @@ -460,7 +460,7 @@ func TestContractStakingCache_Merge(t *testing.T) { func TestContractStakingCache_MatchBucketType(t *testing.T) { require := require.New(t) - cache := newContractStakingCache(identityset.Address(27).String(), calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + cache := newContractStakingCache(Config{ContractAddress: identityset.Address(27).String(), CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), BlockInterval: _blockInterval}) // no bucket types _, bucketType, ok := cache.MatchBucketType(big.NewInt(100), 100) @@ -495,7 +495,7 @@ func TestContractStakingCache_MatchBucketType(t *testing.T) { func TestContractStakingCache_BucketTypeCount(t *testing.T) { require := require.New(t) - cache := newContractStakingCache(identityset.Address(27).String(), calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + cache := newContractStakingCache(Config{ContractAddress: identityset.Address(27).String(), CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), BlockInterval: _blockInterval}) height := uint64(0) // no bucket type @@ -524,7 +524,7 @@ func TestContractStakingCache_BucketTypeCount(t *testing.T) { func TestContractStakingCache_LoadFromDB(t *testing.T) { require := require.New(t) - cache := newContractStakingCache(identityset.Address(27).String(), calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + cache := newContractStakingCache(Config{ContractAddress: identityset.Address(27).String(), CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), BlockInterval: _blockInterval}) // load from empty db path, err := testutil.PathOfTempFile("staking.db") diff --git a/blockindex/contractstaking/delta_cache.go b/blockindex/contractstaking/delta_cache.go index a906d0ba99..293a9534e7 100644 --- a/blockindex/contractstaking/delta_cache.go +++ b/blockindex/contractstaking/delta_cache.go @@ -20,7 +20,7 @@ type ( func newContractStakingDelta() *contractStakingDelta { return &contractStakingDelta{ - cache: newContractStakingCache("", nil), + cache: newContractStakingCache(Config{}), bucketTypeDeltaState: make(map[uint64]deltaState), bucketInfoDeltaState: make(map[uint64]deltaState), } diff --git a/blockindex/contractstaking/dirty_cache_test.go b/blockindex/contractstaking/dirty_cache_test.go index dbc89ddb26..c19ffbf7ed 100644 --- a/blockindex/contractstaking/dirty_cache_test.go +++ b/blockindex/contractstaking/dirty_cache_test.go @@ -14,7 +14,7 @@ import ( func TestContractStakingDirty_getBucketType(t *testing.T) { require := require.New(t) - clean := newContractStakingCache("", calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + clean := newContractStakingCache(Config{CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)}) dirty := newContractStakingDirty(clean) // no bucket type @@ -41,7 +41,7 @@ func TestContractStakingDirty_getBucketType(t *testing.T) { func TestContractStakingDirty_getBucketInfo(t *testing.T) { require := require.New(t) - clean := newContractStakingCache("", calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + clean := newContractStakingCache(Config{CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)}) dirty := newContractStakingDirty(clean) // no bucket info @@ -93,7 +93,7 @@ func TestContractStakingDirty_getBucketInfo(t *testing.T) { func TestContractStakingDirty_matchBucketType(t *testing.T) { require := require.New(t) - clean := newContractStakingCache("", calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + clean := newContractStakingCache(Config{CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)}) dirty := newContractStakingDirty(clean) // no bucket type @@ -123,7 +123,7 @@ func TestContractStakingDirty_matchBucketType(t *testing.T) { func TestContractStakingDirty_getBucketTypeCount(t *testing.T) { require := require.New(t) - clean := newContractStakingCache("", calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + clean := newContractStakingCache(Config{CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)}) dirty := newContractStakingDirty(clean) // no bucket type @@ -143,7 +143,7 @@ func TestContractStakingDirty_getBucketTypeCount(t *testing.T) { func TestContractStakingDirty_finalize(t *testing.T) { require := require.New(t) - clean := newContractStakingCache("", calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + clean := newContractStakingCache(Config{CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)}) dirty := newContractStakingDirty(clean) // no dirty data @@ -205,7 +205,7 @@ func TestContractStakingDirty_finalize(t *testing.T) { func TestContractStakingDirty_noSideEffectOnClean(t *testing.T) { require := require.New(t) - clean := newContractStakingCache("", calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + clean := newContractStakingCache(Config{CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)}) dirty := newContractStakingDirty(clean) // add bucket type to dirty cache diff --git a/blockindex/contractstaking/indexer.go b/blockindex/contractstaking/indexer.go index 55751c9388..e02e2e860c 100644 --- a/blockindex/contractstaking/indexer.go +++ b/blockindex/contractstaking/indexer.go @@ -8,6 +8,7 @@ package contractstaking import ( "context" "math/big" + "time" "github.com/ethereum/go-ethereum/common/math" "github.com/iotexproject/iotex-address/address" @@ -29,28 +30,34 @@ type ( // 1. handle contract staking contract events when new block comes to generate index data // 2. provide query interface for contract staking index data Indexer struct { - kvstore db.KVStore // persistent storage, used to initialize index cache at startup - cache *contractStakingCache // in-memory index for clean data, used to query index data - contractAddress string // stake contract address - contractDeployHeight uint64 // height of the contract deployment - calculateVoteWeight calculateVoteWeightFunc // calculate vote weight function + kvstore db.KVStore // persistent storage, used to initialize index cache at startup + cache *contractStakingCache // in-memory index for clean data, used to query index data + config Config // indexer config } + + // Config is the config for contract staking indexer + Config struct { + ContractAddress string // stake contract ContractAddress + ContractDeployHeight uint64 // height of the contract deployment + CalculateVoteWeight calculateVoteWeightFunc // calculate vote weight function + BlockInterval time.Duration // block produce interval + } + + calculateVoteWeightFunc func(v *Bucket) *big.Int ) // NewContractStakingIndexer creates a new contract staking indexer -func NewContractStakingIndexer(kvStore db.KVStore, contractAddr string, contractDeployHeight uint64, calculateVoteWeight calculateVoteWeightFunc) (*Indexer, error) { +func NewContractStakingIndexer(kvStore db.KVStore, config Config) (*Indexer, error) { if kvStore == nil { return nil, errors.New("kv store is nil") } - if _, err := address.FromString(contractAddr); err != nil { - return nil, errors.Wrapf(err, "invalid contract address %s", contractAddr) + if _, err := address.FromString(config.ContractAddress); err != nil { + return nil, errors.Wrapf(err, "invalid contract address %s", config.ContractAddress) } return &Indexer{ - kvstore: kvStore, - cache: newContractStakingCache(contractAddr, calculateVoteWeight), - contractAddress: contractAddr, - contractDeployHeight: contractDeployHeight, - calculateVoteWeight: calculateVoteWeight, + kvstore: kvStore, + cache: newContractStakingCache(config), + config: config, }, nil } @@ -67,7 +74,7 @@ func (s *Indexer) Stop(ctx context.Context) error { if err := s.kvstore.Stop(ctx); err != nil { return err } - s.cache = newContractStakingCache(s.contractAddress, s.calculateVoteWeight) + s.cache = newContractStakingCache(s.config) return nil } @@ -78,7 +85,7 @@ func (s *Indexer) Height() (uint64, error) { // StartHeight returns the start height of the indexer func (s *Indexer) StartHeight() uint64 { - return s.contractDeployHeight + return s.config.ContractDeployHeight } // CandidateVotes returns the candidate votes @@ -148,8 +155,8 @@ func (s *Indexer) BucketTypes(height uint64) ([]*BucketType, error) { // PutBlock puts a block into indexer func (s *Indexer) PutBlock(ctx context.Context, blk *block.Block) error { expectHeight := s.cache.Height() + 1 - if expectHeight < s.contractDeployHeight { - expectHeight = s.contractDeployHeight + if expectHeight < s.config.ContractDeployHeight { + expectHeight = s.config.ContractDeployHeight } if blk.Height() < expectHeight { return nil @@ -166,7 +173,7 @@ func (s *Indexer) PutBlock(ctx context.Context, blk *block.Block) error { continue } for _, log := range receipt.Logs() { - if log.Address != s.contractAddress { + if log.Address != s.config.ContractAddress { continue } if err := handler.HandleEvent(ctx, blk, log); err != nil { @@ -201,7 +208,7 @@ func (s *Indexer) commit(handler *contractStakingEventHandler, height uint64) er } func (s *Indexer) reloadCache() error { - s.cache = newContractStakingCache(s.contractAddress, s.calculateVoteWeight) + s.cache = newContractStakingCache(s.config) return s.loadFromDB() } @@ -213,5 +220,5 @@ func (s *Indexer) loadFromDB() error { // it aims to be compatible with blocks between feature hard-fork and contract deployed // read interface should return empty result instead of invalid height error if it returns true func (s *Indexer) isIgnored(height uint64) bool { - return height < s.contractDeployHeight + return height < s.config.ContractDeployHeight } diff --git a/blockindex/contractstaking/indexer_test.go b/blockindex/contractstaking/indexer_test.go index 56e4a553b6..b34b4aec09 100644 --- a/blockindex/contractstaking/indexer_test.go +++ b/blockindex/contractstaking/indexer_test.go @@ -30,20 +30,31 @@ import ( const ( _testStakingContractAddress = "io19ys8f4uhwms6lq6ulexr5fwht9gsjes8mvuugd" + _blockInterval = 5 * time.Second ) func TestNewContractStakingIndexer(t *testing.T) { r := require.New(t) t.Run("kvStore is nil", func(t *testing.T) { - _, err := NewContractStakingIndexer(nil, "io19ys8f4uhwms6lq6ulexr5fwht9gsjes8mvuugd", 0, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + _, err := NewContractStakingIndexer(nil, Config{ + ContractAddress: "io19ys8f4uhwms6lq6ulexr5fwht9gsjes8mvuugd", + ContractDeployHeight: 0, + CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), + BlockInterval: _blockInterval, + }) r.Error(err) r.Contains(err.Error(), "kv store is nil") }) t.Run("invalid contract address", func(t *testing.T) { kvStore := db.NewMemKVStore() - _, err := NewContractStakingIndexer(kvStore, "invalid address", 0, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + _, err := NewContractStakingIndexer(kvStore, Config{ + ContractAddress: "invalid address", + ContractDeployHeight: 0, + CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), + BlockInterval: _blockInterval, + }) r.Error(err) r.Contains(err.Error(), "invalid contract address") }) @@ -51,7 +62,12 @@ func TestNewContractStakingIndexer(t *testing.T) { t.Run("valid input", func(t *testing.T) { contractAddr, err := address.FromString("io19ys8f4uhwms6lq6ulexr5fwht9gsjes8mvuugd") r.NoError(err) - indexer, err := NewContractStakingIndexer(db.NewMemKVStore(), contractAddr.String(), 0, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + indexer, err := NewContractStakingIndexer(db.NewMemKVStore(), Config{ + ContractAddress: contractAddr.String(), + ContractDeployHeight: 0, + CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), + BlockInterval: _blockInterval, + }) r.NoError(err) r.NotNil(indexer) }) @@ -65,7 +81,12 @@ func TestContractStakingIndexerLoadCache(t *testing.T) { cfg := db.DefaultConfig cfg.DbPath = testDBPath kvStore := db.NewBoltDB(cfg) - indexer, err := NewContractStakingIndexer(kvStore, _testStakingContractAddress, 0, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + indexer, err := NewContractStakingIndexer(kvStore, Config{ + ContractAddress: _testStakingContractAddress, + ContractDeployHeight: 0, + CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), + BlockInterval: _blockInterval, + }) r.NoError(err) r.NoError(indexer.Start(context.Background())) @@ -92,7 +113,12 @@ func TestContractStakingIndexerLoadCache(t *testing.T) { r.NoError(indexer.Stop(context.Background())) // load cache from db - newIndexer, err := NewContractStakingIndexer(db.NewBoltDB(cfg), _testStakingContractAddress, startHeight, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + newIndexer, err := NewContractStakingIndexer(db.NewBoltDB(cfg), Config{ + ContractAddress: _testStakingContractAddress, + ContractDeployHeight: startHeight, + CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), + BlockInterval: _blockInterval, + }) r.NoError(err) r.NoError(newIndexer.Start(context.Background())) @@ -121,7 +147,12 @@ func TestContractStakingIndexerDirty(t *testing.T) { cfg := db.DefaultConfig cfg.DbPath = testDBPath kvStore := db.NewBoltDB(cfg) - indexer, err := NewContractStakingIndexer(kvStore, _testStakingContractAddress, 0, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + indexer, err := NewContractStakingIndexer(kvStore, Config{ + ContractAddress: _testStakingContractAddress, + ContractDeployHeight: 0, + CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), + BlockInterval: _blockInterval, + }) r.NoError(err) r.NoError(indexer.Start(context.Background())) @@ -149,7 +180,12 @@ func TestContractStakingIndexerThreadSafe(t *testing.T) { cfg := db.DefaultConfig cfg.DbPath = testDBPath kvStore := db.NewBoltDB(cfg) - indexer, err := NewContractStakingIndexer(kvStore, _testStakingContractAddress, 0, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + indexer, err := NewContractStakingIndexer(kvStore, Config{ + ContractAddress: _testStakingContractAddress, + ContractDeployHeight: 0, + CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), + BlockInterval: _blockInterval, + }) r.NoError(err) r.NoError(indexer.Start(context.Background())) @@ -204,7 +240,12 @@ func TestContractStakingIndexerBucketType(t *testing.T) { cfg := db.DefaultConfig cfg.DbPath = testDBPath kvStore := db.NewBoltDB(cfg) - indexer, err := NewContractStakingIndexer(kvStore, _testStakingContractAddress, 0, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + indexer, err := NewContractStakingIndexer(kvStore, Config{ + ContractAddress: _testStakingContractAddress, + ContractDeployHeight: 0, + CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), + BlockInterval: _blockInterval, + }) r.NoError(err) r.NoError(indexer.Start(context.Background())) @@ -287,7 +328,12 @@ func TestContractStakingIndexerBucketInfo(t *testing.T) { cfg := db.DefaultConfig cfg.DbPath = testDBPath kvStore := db.NewBoltDB(cfg) - indexer, err := NewContractStakingIndexer(kvStore, _testStakingContractAddress, 0, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + indexer, err := NewContractStakingIndexer(kvStore, Config{ + ContractAddress: _testStakingContractAddress, + ContractDeployHeight: 0, + CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), + BlockInterval: _blockInterval, + }) r.NoError(err) r.NoError(indexer.Start(context.Background())) @@ -447,7 +493,12 @@ func TestContractStakingIndexerChangeBucketType(t *testing.T) { cfg := db.DefaultConfig cfg.DbPath = testDBPath kvStore := db.NewBoltDB(cfg) - indexer, err := NewContractStakingIndexer(kvStore, _testStakingContractAddress, 0, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + indexer, err := NewContractStakingIndexer(kvStore, Config{ + ContractAddress: _testStakingContractAddress, + ContractDeployHeight: 0, + CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), + BlockInterval: _blockInterval, + }) r.NoError(err) r.NoError(indexer.Start(context.Background())) @@ -496,7 +547,12 @@ func TestContractStakingIndexerReadBuckets(t *testing.T) { cfg := db.DefaultConfig cfg.DbPath = testDBPath kvStore := db.NewBoltDB(cfg) - indexer, err := NewContractStakingIndexer(kvStore, _testStakingContractAddress, 0, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + indexer, err := NewContractStakingIndexer(kvStore, Config{ + ContractAddress: _testStakingContractAddress, + ContractDeployHeight: 0, + CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), + BlockInterval: _blockInterval, + }) r.NoError(err) r.NoError(indexer.Start(context.Background())) @@ -596,7 +652,12 @@ func TestContractStakingIndexerCacheClean(t *testing.T) { cfg := db.DefaultConfig cfg.DbPath = testDBPath kvStore := db.NewBoltDB(cfg) - indexer, err := NewContractStakingIndexer(kvStore, _testStakingContractAddress, 0, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + indexer, err := NewContractStakingIndexer(kvStore, Config{ + ContractAddress: _testStakingContractAddress, + ContractDeployHeight: 0, + CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), + BlockInterval: _blockInterval, + }) r.NoError(err) r.NoError(indexer.Start(context.Background())) @@ -658,7 +719,12 @@ func TestContractStakingIndexerVotes(t *testing.T) { cfg := db.DefaultConfig cfg.DbPath = testDBPath kvStore := db.NewBoltDB(cfg) - indexer, err := NewContractStakingIndexer(kvStore, _testStakingContractAddress, 0, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + indexer, err := NewContractStakingIndexer(kvStore, Config{ + ContractAddress: _testStakingContractAddress, + ContractDeployHeight: 0, + CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), + BlockInterval: _blockInterval, + }) r.NoError(err) r.NoError(indexer.Start(context.Background())) ctx := protocol.WithFeatureCtx(protocol.WithBlockCtx(genesis.WithGenesisContext(context.Background(), genesis.Default), protocol.BlockCtx{BlockHeight: 1})) @@ -840,6 +906,12 @@ func TestContractStakingIndexerVotes(t *testing.T) { r.EqualValues(4, bts[3].Index) r.EqualValues(5, bts[4].Index) r.EqualValues(8, bts[5].Index) + r.EqualValues(10*_blockInterval, bts[0].StakedDuration) + r.EqualValues(20*_blockInterval, bts[1].StakedDuration) + r.EqualValues(20*_blockInterval, bts[2].StakedDuration) + r.EqualValues(20*_blockInterval, bts[3].StakedDuration) + r.EqualValues(20*_blockInterval, bts[4].StakedDuration) + r.EqualValues(20*_blockInterval, bts[5].StakedDuration) r.EqualValues(10, bts[0].StakedDurationBlockNumber) r.EqualValues(20, bts[1].StakedDurationBlockNumber) r.EqualValues(20, bts[2].StakedDurationBlockNumber) @@ -889,7 +961,6 @@ func TestContractStakingIndexerVotes(t *testing.T) { r.EqualValues(9, bts[4].UnstakeStartBlockHeight) r.EqualValues(maxBlockNumber, bts[5].UnstakeStartBlockHeight) for _, b := range bts { - r.EqualValues(0, b.StakedDuration) r.EqualValues(time.Time{}, b.CreateTime) r.EqualValues(time.Time{}, b.StakeStartTime) r.EqualValues(time.Time{}, b.UnstakeStartTime) @@ -962,7 +1033,12 @@ func TestIndexer_ReadHeightRestriction(t *testing.T) { dbPath, err := testutil.PathOfTempFile("db") r.NoError(err) cfg.DbPath = dbPath - indexer, err := NewContractStakingIndexer(db.NewBoltDB(cfg), identityset.Address(1).String(), startHeight, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + indexer, err := NewContractStakingIndexer(db.NewBoltDB(cfg), Config{ + ContractAddress: identityset.Address(1).String(), + ContractDeployHeight: startHeight, + CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), + BlockInterval: _blockInterval, + }) r.NoError(err) r.NoError(indexer.Start(context.Background())) defer func() { @@ -1042,7 +1118,12 @@ func TestIndexer_PutBlock(t *testing.T) { dbPath, err := testutil.PathOfTempFile("db") r.NoError(err) cfg.DbPath = dbPath - indexer, err := NewContractStakingIndexer(db.NewBoltDB(cfg), identityset.Address(1).String(), startHeight, calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts)) + indexer, err := NewContractStakingIndexer(db.NewBoltDB(cfg), Config{ + ContractAddress: identityset.Address(1).String(), + ContractDeployHeight: startHeight, + CalculateVoteWeight: calculateVoteWeightGen(genesis.Default.VoteWeightCalConsts), + BlockInterval: _blockInterval, + }) r.NoError(err) r.NoError(indexer.Start(context.Background())) defer func() { @@ -1071,7 +1152,7 @@ func TestIndexer_PutBlock(t *testing.T) { func BenchmarkIndexer_PutBlockBeforeContractHeight(b *testing.B) { // Create a new Indexer with a contract height of 100 - indexer := &Indexer{contractDeployHeight: 100} + indexer := &Indexer{config: Config{ContractDeployHeight: 100}} // Create a mock block with a height of 50 blk := &block.Block{} diff --git a/chainservice/builder.go b/chainservice/builder.go index 206fa455b2..b93f6d41f6 100644 --- a/chainservice/builder.go +++ b/chainservice/builder.go @@ -319,9 +319,16 @@ func (builder *Builder) buildContractStakingIndexer(forTest bool) error { dbConfig := builder.cfg.DB dbConfig.DbPath = builder.cfg.Chain.ContractStakingIndexDBPath voteCalcConsts := builder.cfg.Genesis.VoteWeightCalConsts - indexer, err := contractstaking.NewContractStakingIndexer(db.NewBoltDB(dbConfig), builder.cfg.Genesis.SystemStakingContractAddress, builder.cfg.Genesis.SystemStakingContractHeight, func(v *staking.VoteBucket, selfStake bool) *big.Int { - return staking.CalculateVoteWeight(voteCalcConsts, v, selfStake) - }) + indexer, err := contractstaking.NewContractStakingIndexer( + db.NewBoltDB(dbConfig), + contractstaking.Config{ + ContractAddress: builder.cfg.Genesis.SystemStakingContractAddress, + ContractDeployHeight: builder.cfg.Genesis.SystemStakingContractHeight, + CalculateVoteWeight: func(v *staking.VoteBucket) *big.Int { + return staking.CalculateVoteWeight(voteCalcConsts, v, false) + }, + BlockInterval: builder.cfg.Genesis.BlockInterval, + }) if err != nil { return err } diff --git a/e2etest/contract_staking_test.go b/e2etest/contract_staking_test.go index 69db2224ba..80eb4c3da3 100644 --- a/e2etest/contract_staking_test.go +++ b/e2etest/contract_staking_test.go @@ -7,6 +7,7 @@ import ( "math/big" "strings" "testing" + "time" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" @@ -1957,8 +1958,13 @@ func prepareContractStakingBlockchain(ctx context.Context, cfg config.Config, r r.NoError(err) cc := cfg.DB cc.DbPath = testContractStakeIndexerPath - contractStakeIndexer, err := contractstaking.NewContractStakingIndexer(db.NewBoltDB(cc), _stakingContractAddress, 0, func(v *staking.VoteBucket, selfStake bool) *big.Int { - return staking.CalculateVoteWeight(genesis.Default.VoteWeightCalConsts, v, selfStake) + contractStakeIndexer, err := contractstaking.NewContractStakingIndexer(db.NewBoltDB(cc), contractstaking.Config{ + ContractAddress: _stakingContractAddress, + ContractDeployHeight: 0, + CalculateVoteWeight: func(v *staking.VoteBucket) *big.Int { + return staking.CalculateVoteWeight(genesis.Default.VoteWeightCalConsts, v, false) + }, + BlockInterval: 5 * time.Second, }) r.NoError(err) // create BlockDAO