Skip to content

Commit 11b446b

Browse files
authored
[blockindex] return empty result before contract deploy height (#3928)
1 parent 66edd61 commit 11b446b

File tree

4 files changed

+175
-44
lines changed

4 files changed

+175
-44
lines changed

blockindex/contractstaking/indexer.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,36 +81,57 @@ func (s *Indexer) StartHeight() uint64 {
8181

8282
// CandidateVotes returns the candidate votes
8383
func (s *Indexer) CandidateVotes(candidate address.Address, height uint64) (*big.Int, error) {
84+
if s.isIgnored(height) {
85+
return big.NewInt(0), nil
86+
}
8487
return s.cache.CandidateVotes(candidate, height)
8588
}
8689

8790
// Buckets returns the buckets
8891
func (s *Indexer) Buckets(height uint64) ([]*Bucket, error) {
92+
if s.isIgnored(height) {
93+
return []*Bucket{}, nil
94+
}
8995
return s.cache.Buckets(height)
9096
}
9197

9298
// Bucket returns the bucket
9399
func (s *Indexer) Bucket(id uint64, height uint64) (*Bucket, bool, error) {
100+
if s.isIgnored(height) {
101+
return nil, false, nil
102+
}
94103
return s.cache.Bucket(id, height)
95104
}
96105

97106
// BucketsByIndices returns the buckets by indices
98107
func (s *Indexer) BucketsByIndices(indices []uint64, height uint64) ([]*Bucket, error) {
108+
if s.isIgnored(height) {
109+
return []*Bucket{}, nil
110+
}
99111
return s.cache.BucketsByIndices(indices, height)
100112
}
101113

102114
// BucketsByCandidate returns the buckets by candidate
103115
func (s *Indexer) BucketsByCandidate(candidate address.Address, height uint64) ([]*Bucket, error) {
116+
if s.isIgnored(height) {
117+
return []*Bucket{}, nil
118+
}
104119
return s.cache.BucketsByCandidate(candidate, height)
105120
}
106121

107122
// TotalBucketCount returns the total bucket count including active and burnt buckets
108123
func (s *Indexer) TotalBucketCount(height uint64) (uint64, error) {
124+
if s.isIgnored(height) {
125+
return 0, nil
126+
}
109127
return s.cache.TotalBucketCount(height)
110128
}
111129

112130
// BucketTypes returns the active bucket types
113131
func (s *Indexer) BucketTypes(height uint64) ([]*BucketType, error) {
132+
if s.isIgnored(height) {
133+
return []*BucketType{}, nil
134+
}
114135
btMap, err := s.cache.ActiveBucketTypes(height)
115136
if err != nil {
116137
return nil, err
@@ -185,3 +206,10 @@ func (s *Indexer) reloadCache() error {
185206
func (s *Indexer) loadFromDB() error {
186207
return s.cache.LoadFromDB(s.kvstore)
187208
}
209+
210+
// isIgnored returns true if before cotractDeployHeight.
211+
// it aims to be compatible with blocks between feature hard-fork and contract deployed
212+
// read interface should return empty result instead of invalid height error if it returns true
213+
func (s *Indexer) isIgnored(height uint64) bool {
214+
return height < s.contractDeployHeight
215+
}

blockindex/contractstaking/indexer_test.go

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package contractstaking
88
import (
99
"context"
1010
"math/big"
11+
"strconv"
1112
"sync"
1213
"testing"
1314
"time"
@@ -916,29 +917,66 @@ func TestContractStakingIndexerVotes(t *testing.T) {
916917
r.NoError(err)
917918
r.Len(bts, 6)
918919
})
920+
}
919921

920-
t.Run("heightRestriction", func(t *testing.T) {
921-
cases := []struct {
922-
height uint64
923-
valid bool
924-
}{
925-
{0, true},
926-
{height - 1, true},
927-
{height, true},
928-
{height + 1, false},
929-
}
930-
for i := range cases {
931-
h := cases[i].height
932-
if cases[i].valid {
922+
func TestIndexer_ReadHeightRestriction(t *testing.T) {
923+
r := require.New(t)
924+
925+
cases := []struct {
926+
startHeight uint64
927+
height uint64
928+
readHeight uint64
929+
valid bool
930+
}{
931+
{0, 0, 0, true},
932+
{0, 0, 1, false},
933+
{0, 2, 0, true},
934+
{0, 2, 1, true},
935+
{0, 2, 2, true},
936+
{0, 2, 3, false},
937+
{10, 0, 0, true},
938+
{10, 0, 1, true},
939+
{10, 0, 9, true},
940+
{10, 0, 10, false},
941+
{10, 0, 11, false},
942+
{10, 10, 0, true},
943+
{10, 10, 1, true},
944+
{10, 10, 9, true},
945+
{10, 10, 10, true},
946+
{10, 10, 11, false},
947+
}
948+
949+
for idx, c := range cases {
950+
name := strconv.FormatInt(int64(idx), 10)
951+
t.Run(name, func(t *testing.T) {
952+
// Create a new Indexer
953+
height := c.height
954+
startHeight := c.startHeight
955+
cfg := config.Default.DB
956+
dbPath, err := testutil.PathOfTempFile("db")
957+
r.NoError(err)
958+
cfg.DbPath = dbPath
959+
indexer, err := NewContractStakingIndexer(db.NewBoltDB(cfg), identityset.Address(1).String(), startHeight)
960+
r.NoError(err)
961+
r.NoError(indexer.Start(context.Background()))
962+
defer func() {
963+
r.NoError(indexer.Stop(context.Background()))
964+
testutil.CleanupPath(dbPath)
965+
}()
966+
indexer.cache.putHeight(height)
967+
// check read api
968+
h := c.readHeight
969+
delegate := identityset.Address(1)
970+
if c.valid {
933971
_, err = indexer.Buckets(h)
934972
r.NoError(err)
935973
_, err = indexer.BucketTypes(h)
936974
r.NoError(err)
937-
_, err = indexer.BucketsByCandidate(delegate1, h)
975+
_, err = indexer.BucketsByCandidate(delegate, h)
938976
r.NoError(err)
939977
_, err = indexer.BucketsByIndices([]uint64{1, 2, 3, 4, 5, 8}, h)
940978
r.NoError(err)
941-
_, err = indexer.CandidateVotes(delegate1, h)
979+
_, err = indexer.CandidateVotes(delegate, h)
942980
r.NoError(err)
943981
_, _, err = indexer.Bucket(1, h)
944982
r.NoError(err)
@@ -949,19 +987,19 @@ func TestContractStakingIndexerVotes(t *testing.T) {
949987
r.ErrorIs(err, ErrInvalidHeight)
950988
_, err = indexer.BucketTypes(h)
951989
r.ErrorIs(err, ErrInvalidHeight)
952-
_, err = indexer.BucketsByCandidate(delegate1, h)
990+
_, err = indexer.BucketsByCandidate(delegate, h)
953991
r.ErrorIs(err, ErrInvalidHeight)
954992
_, err = indexer.BucketsByIndices([]uint64{1, 2, 3, 4, 5, 8}, h)
955993
r.ErrorIs(err, ErrInvalidHeight)
956-
_, err = indexer.CandidateVotes(delegate1, h)
994+
_, err = indexer.CandidateVotes(delegate, h)
957995
r.ErrorIs(err, ErrInvalidHeight)
958996
_, _, err = indexer.Bucket(1, h)
959997
r.ErrorIs(err, ErrInvalidHeight)
960998
_, err = indexer.TotalBucketCount(h)
961999
r.ErrorIs(err, ErrInvalidHeight)
9621000
}
963-
}
964-
})
1001+
})
1002+
}
9651003
}
9661004

9671005
func TestIndexer_PutBlock(t *testing.T) {

blockindex/sgd_indexer.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,10 @@ func (sgd *sgdRegistry) validateQueryHeight(height uint64) error {
496496
if height == 0 {
497497
return nil
498498
}
499+
// Compatible with blocks between feature hard-fork and contract deployed
500+
if height < sgd.startHeight {
501+
return nil
502+
}
499503
tipHeight, err := sgd.height()
500504
if err != nil {
501505
return err

blockindex/sgd_indexer_test.go

Lines changed: 86 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/hex"
66
"math/big"
7+
"strconv"
78
"sync/atomic"
89
"testing"
910

@@ -15,6 +16,8 @@ import (
1516
"github.com/iotexproject/iotex-core/blockchain/block"
1617
"github.com/iotexproject/iotex-core/blockchain/genesis"
1718
"github.com/iotexproject/iotex-core/db"
19+
"github.com/iotexproject/iotex-core/db/batch"
20+
"github.com/iotexproject/iotex-core/pkg/util/byteutil"
1821
"github.com/iotexproject/iotex-core/state"
1922
"github.com/iotexproject/iotex-core/test/identityset"
2023
"github.com/iotexproject/iotex-core/testutil"
@@ -109,31 +112,6 @@ func TestNewSGDRegistry(t *testing.T) {
109112
r.Equal(receiverAddress, receiver)
110113
r.True(isApproved)
111114
r.Equal(_sgdPercentage, percentage)
112-
113-
t.Run("heightRestriction", func(t *testing.T) {
114-
cases := []struct {
115-
height uint64
116-
isErr bool
117-
}{
118-
{0, false},
119-
{1, true},
120-
{2, false},
121-
{3, true},
122-
}
123-
for i := range cases {
124-
if cases[i].isErr {
125-
_, err = sgdRegistry.FetchContracts(ctx, cases[i].height)
126-
r.ErrorContains(err, "invalid height")
127-
_, _, _, err = sgdRegistry.CheckContract(ctx, registerAddress.String(), cases[i].height)
128-
r.ErrorContains(err, "invalid height")
129-
} else {
130-
_, err = sgdRegistry.FetchContracts(ctx, cases[i].height)
131-
r.Nil(err)
132-
_, _, _, err = sgdRegistry.CheckContract(ctx, registerAddress.String(), cases[i].height)
133-
r.Nil(err)
134-
}
135-
}
136-
})
137115
})
138116
t.Run("disapproveContract", func(t *testing.T) {
139117
builder := block.NewTestingBuilder()
@@ -182,6 +160,89 @@ func TestNewSGDRegistry(t *testing.T) {
182160
r.ErrorIs(err, state.ErrStateNotExist)
183161
})
184162
})
163+
t.Run("heightRestriction", func(t *testing.T) {
164+
cases := []struct {
165+
startHeight uint64
166+
height uint64
167+
readHeight uint64
168+
valid bool
169+
}{
170+
{0, 0, 0, true},
171+
{0, 0, 1, false},
172+
{0, 2, 0, true},
173+
{0, 2, 1, false},
174+
{0, 2, 2, true},
175+
{0, 2, 3, false},
176+
{10, 0, 0, true},
177+
{10, 0, 1, true},
178+
{10, 0, 9, true},
179+
{10, 0, 10, false},
180+
{10, 0, 11, false},
181+
{10, 10, 0, true},
182+
{10, 10, 1, true},
183+
{10, 10, 9, true},
184+
{10, 10, 10, true},
185+
{10, 10, 11, false},
186+
}
187+
for i := range cases {
188+
name := strconv.FormatInt(int64(i), 10)
189+
t.Run(name, func(t *testing.T) {
190+
testDBPath, err := testutil.PathOfTempFile("sgd")
191+
r.NoError(err)
192+
ctx := context.Background()
193+
cfg := db.DefaultConfig
194+
cfg.DbPath = testDBPath
195+
kvStore := db.NewBoltDB(cfg)
196+
sgdRegistry := &sgdRegistry{
197+
contract: _testSGDContractAddress,
198+
startHeight: cases[i].startHeight,
199+
kvStore: kvStore,
200+
}
201+
r.NoError(sgdRegistry.Start(ctx))
202+
defer func() {
203+
r.NoError(sgdRegistry.Stop(ctx))
204+
testutil.CleanupPath(testDBPath)
205+
}()
206+
// register
207+
nonce := uint64(0)
208+
registerAddress, err := address.FromHex("5b38da6a701c568545dcfcb03fcb875f56beddc4")
209+
r.NoError(err)
210+
builder := block.NewTestingBuilder()
211+
event := _sgdABI.Events["ContractRegistered"]
212+
data, _ := hex.DecodeString("0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc400000000000000000000000078731d3ca6b7e34ac0f824c42a7cc18a495cabab")
213+
exec, err := action.SignedExecution(_testSGDContractAddress, identityset.PrivateKey(27), atomic.AddUint64(&nonce, 1), big.NewInt(0), 10000000, big.NewInt(9000000000000), data)
214+
r.NoError(err)
215+
h, _ := exec.Hash()
216+
logs := &action.Log{
217+
Address: _testSGDContractAddress,
218+
Topics: []hash.Hash256{hash.Hash256(event.ID)},
219+
Data: data,
220+
}
221+
expectHeight, err := sgdRegistry.expectHeight()
222+
r.NoError(err)
223+
blk := createTestingBlock(builder, expectHeight, h, exec, logs)
224+
r.NoError(sgdRegistry.PutBlock(ctx, blk))
225+
_, _, _, err = sgdRegistry.CheckContract(ctx, registerAddress.String(), 1)
226+
r.NoError(err)
227+
// update height
228+
b := batch.NewBatch()
229+
b.Put(_sgdToHeightNS, _sgdCurrentHeight, byteutil.Uint64ToBytesBigEndian(cases[i].height), "failed to put current height")
230+
sgdRegistry.kvStore.WriteBatch(b)
231+
// check
232+
if !cases[i].valid {
233+
_, err = sgdRegistry.FetchContracts(ctx, cases[i].readHeight)
234+
r.ErrorContains(err, "invalid height")
235+
_, _, _, err = sgdRegistry.CheckContract(ctx, registerAddress.String(), cases[i].readHeight)
236+
r.ErrorContains(err, "invalid height")
237+
} else {
238+
_, err = sgdRegistry.FetchContracts(ctx, cases[i].readHeight)
239+
r.Nil(err)
240+
_, _, _, err = sgdRegistry.CheckContract(ctx, registerAddress.String(), cases[i].readHeight)
241+
r.Nil(err)
242+
}
243+
})
244+
}
245+
})
185246
}
186247

187248
func createTestingBlock(builder *block.TestingBuilder, height uint64, h hash.Hash256, act action.SealedEnvelope, logs *action.Log) *block.Block {

0 commit comments

Comments
 (0)