Skip to content

Commit 66edd61

Browse files
authored
[blockindex] restrict height for sgdindexer during write and read operation (#3926)
1 parent 1a63b13 commit 66edd61

File tree

4 files changed

+99
-25
lines changed

4 files changed

+99
-25
lines changed

action/protocol/execution/evm/evm.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ type (
5555

5656
// SGDRegistry is the interface for handling Sharing of Gas-fee with DApps
5757
SGDRegistry interface {
58-
CheckContract(context.Context, string) (address.Address, uint64, bool, error)
58+
CheckContract(context.Context, string, uint64) (address.Address, uint64, bool, error)
5959
}
6060
)
6161

@@ -303,8 +303,11 @@ func processSGD(ctx context.Context, sm protocol.StateManager, execution *action
303303
if execution.Contract() == action.EmptyAddress {
304304
return nil, 0, nil
305305
}
306-
307-
receiver, percentage, ok, err := sgd.CheckContract(ctx, execution.Contract())
306+
height, err := sm.Height()
307+
if err != nil {
308+
return nil, 0, err
309+
}
310+
receiver, percentage, ok, err := sgd.CheckContract(ctx, execution.Contract(), height-1)
308311
if err != nil || !ok {
309312
return nil, 0, err
310313
}

blockindex/sgd_indexer.go

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,9 @@ type (
193193
SGDRegistry interface {
194194
blockdao.BlockIndexerWithStart
195195
// CheckContract returns the contract's eligibility for SGD and percentage
196-
CheckContract(context.Context, string) (address.Address, uint64, bool, error)
196+
CheckContract(context.Context, string, uint64) (address.Address, uint64, bool, error)
197197
// FetchContracts returns all contracts that are eligible for SGD
198-
FetchContracts(context.Context) ([]*SGDIndex, error)
198+
FetchContracts(context.Context, uint64) ([]*SGDIndex, error)
199199
}
200200

201201
sgdRegistry struct {
@@ -271,11 +271,7 @@ func (sgd *sgdRegistry) Stop(ctx context.Context) error {
271271

272272
// Height returns the current height of the SGDIndexer
273273
func (sgd *sgdRegistry) Height() (uint64, error) {
274-
h, err := sgd.kvStore.Get(_sgdToHeightNS, _sgdCurrentHeight)
275-
if err != nil {
276-
return 0, err
277-
}
278-
return byteutil.BytesToUint64BigEndian(h), nil
274+
return sgd.height()
279275
}
280276

281277
// StartHeight returns the start height of the indexer
@@ -285,9 +281,17 @@ func (sgd *sgdRegistry) StartHeight() uint64 {
285281

286282
// PutBlock puts a block into SGDIndexer
287283
func (sgd *sgdRegistry) PutBlock(ctx context.Context, blk *block.Block) error {
288-
if blk.Height() < sgd.startHeight {
284+
expectHeight, err := sgd.expectHeight()
285+
if err != nil {
286+
return err
287+
}
288+
if blk.Height() < expectHeight {
289289
return nil
290290
}
291+
if blk.Height() > expectHeight {
292+
return errors.Errorf("invalid block height %d, expect %d", blk.Height(), expectHeight)
293+
}
294+
291295
var (
292296
r *action.Receipt
293297
ok bool
@@ -430,7 +434,10 @@ func (sgd *sgdRegistry) DeleteTipBlock(context.Context, *block.Block) error {
430434
}
431435

432436
// CheckContract checks if the contract is a SGD contract
433-
func (sgd *sgdRegistry) CheckContract(ctx context.Context, contract string) (address.Address, uint64, bool, error) {
437+
func (sgd *sgdRegistry) CheckContract(ctx context.Context, contract string, height uint64) (address.Address, uint64, bool, error) {
438+
if err := sgd.validateQueryHeight(height); err != nil {
439+
return nil, 0, false, err
440+
}
434441
addr, err := address.FromString(contract)
435442
if err != nil {
436443
return nil, 0, false, err
@@ -458,7 +465,10 @@ func (sgd *sgdRegistry) getSGDIndex(contract []byte) (*indexpb.SGDIndex, error)
458465
}
459466

460467
// FetchContracts returns all contracts that are eligible for SGD
461-
func (sgd *sgdRegistry) FetchContracts(ctx context.Context) ([]*SGDIndex, error) {
468+
func (sgd *sgdRegistry) FetchContracts(ctx context.Context, height uint64) ([]*SGDIndex, error) {
469+
if err := sgd.validateQueryHeight(height); err != nil {
470+
return nil, err
471+
}
462472
_, values, err := sgd.kvStore.Filter(_sgdBucket, func(k, v []byte) bool { return true }, nil, nil)
463473
if err != nil {
464474
if errors.Cause(err) == db.ErrNotExist || errors.Cause(err) == db.ErrBucketNotExist {
@@ -481,6 +491,41 @@ func (sgd *sgdRegistry) FetchContracts(ctx context.Context) ([]*SGDIndex, error)
481491
return sgdIndexes, nil
482492
}
483493

494+
func (sgd *sgdRegistry) validateQueryHeight(height uint64) error {
495+
// 0 means latest height
496+
if height == 0 {
497+
return nil
498+
}
499+
tipHeight, err := sgd.height()
500+
if err != nil {
501+
return err
502+
}
503+
if height != tipHeight {
504+
return errors.Errorf("invalid height %d, expect %d", height, tipHeight)
505+
}
506+
return nil
507+
}
508+
509+
func (sgd *sgdRegistry) expectHeight() (uint64, error) {
510+
tipHeight, err := sgd.height()
511+
if err != nil {
512+
return 0, err
513+
}
514+
expectHeight := tipHeight + 1
515+
if expectHeight < sgd.startHeight {
516+
expectHeight = sgd.startHeight
517+
}
518+
return expectHeight, nil
519+
}
520+
521+
func (sgd *sgdRegistry) height() (uint64, error) {
522+
h, err := sgd.kvStore.Get(_sgdToHeightNS, _sgdCurrentHeight)
523+
if err != nil {
524+
return 0, err
525+
}
526+
return byteutil.BytesToUint64BigEndian(h), nil
527+
}
528+
484529
func getReceiptsFromBlock(blk *block.Block) map[hash.Hash256]*action.Receipt {
485530
receipts := make(map[hash.Hash256]*action.Receipt, len(blk.Receipts))
486531
for _, receipt := range blk.Receipts {

blockindex/sgd_indexer_test.go

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,13 @@ func TestNewSGDRegistry(t *testing.T) {
7777
}
7878
blk := createTestingBlock(builder, 1, h, exec, logs)
7979
r.NoError(sgdRegistry.PutBlock(ctx, blk))
80-
receiver, percentage, isApproved, err := sgdRegistry.CheckContract(ctx, registerAddress.String())
80+
receiver, percentage, isApproved, err := sgdRegistry.CheckContract(ctx, registerAddress.String(), 1)
8181
r.NoError(err)
8282
r.Equal(_sgdPercentage, percentage)
8383
r.Equal(receiverAddress, receiver)
8484
r.False(isApproved)
8585

86-
lists, err := sgdRegistry.FetchContracts(ctx)
86+
lists, err := sgdRegistry.FetchContracts(ctx, 1)
8787
r.NoError(err)
8888
r.Equal(1, len(lists))
8989
r.Equal(registerAddress.Bytes(), lists[0].Contract.Bytes())
@@ -102,13 +102,38 @@ func TestNewSGDRegistry(t *testing.T) {
102102
Topics: []hash.Hash256{hash.Hash256(event.ID)},
103103
Data: data,
104104
}
105-
blk := createTestingBlock(builder, 1, h, exec, logs)
105+
blk := createTestingBlock(builder, 2, h, exec, logs)
106106
r.NoError(sgdRegistry.PutBlock(ctx, blk))
107-
receiver, percentage, isApproved, err := sgdRegistry.CheckContract(ctx, registerAddress.String())
107+
receiver, percentage, isApproved, err := sgdRegistry.CheckContract(ctx, registerAddress.String(), 2)
108108
r.NoError(err)
109109
r.Equal(receiverAddress, receiver)
110110
r.True(isApproved)
111111
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+
})
112137
})
113138
t.Run("disapproveContract", func(t *testing.T) {
114139
builder := block.NewTestingBuilder()
@@ -122,9 +147,9 @@ func TestNewSGDRegistry(t *testing.T) {
122147
Topics: []hash.Hash256{hash.Hash256(event.ID)},
123148
Data: data,
124149
}
125-
blk := createTestingBlock(builder, 1, h, exec, logs)
150+
blk := createTestingBlock(builder, 3, h, exec, logs)
126151
r.NoError(sgdRegistry.PutBlock(ctx, blk))
127-
receiver, percentage, isApproved, err := sgdRegistry.CheckContract(ctx, registerAddress.String())
152+
receiver, percentage, isApproved, err := sgdRegistry.CheckContract(ctx, registerAddress.String(), 3)
128153
r.NoError(err)
129154
r.Equal(receiverAddress, receiver)
130155
r.False(isApproved)
@@ -142,9 +167,9 @@ func TestNewSGDRegistry(t *testing.T) {
142167
Topics: []hash.Hash256{hash.Hash256(event.ID)},
143168
Data: data,
144169
}
145-
blk := createTestingBlock(builder, 2, h, exec, logs)
170+
blk := createTestingBlock(builder, 4, h, exec, logs)
146171
r.NoError(sgdRegistry.PutBlock(ctx, blk))
147-
receiver, percentage, isApproved, err := sgdRegistry.CheckContract(ctx, registerAddress.String())
172+
receiver, percentage, isApproved, err := sgdRegistry.CheckContract(ctx, registerAddress.String(), 4)
148173
r.ErrorContains(err, "not exist in DB")
149174
r.Nil(receiver)
150175
r.False(isApproved)
@@ -153,7 +178,7 @@ func TestNewSGDRegistry(t *testing.T) {
153178
r.Equal(blk.Height(), hh)
154179
r.Equal(uint64(0), percentage)
155180

156-
_, err = sgdRegistry.FetchContracts(ctx)
181+
_, err = sgdRegistry.FetchContracts(ctx, blk.Height())
157182
r.ErrorIs(err, state.ErrStateNotExist)
158183
})
159184
})

e2etest/sgd_registry_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010

1111
"github.com/iotexproject/go-pkgs/crypto"
1212
"github.com/iotexproject/iotex-address/address"
13+
"github.com/stretchr/testify/require"
14+
1315
"github.com/iotexproject/iotex-core/action"
1416
"github.com/iotexproject/iotex-core/action/protocol"
1517
"github.com/iotexproject/iotex-core/action/protocol/account"
@@ -27,7 +29,6 @@ import (
2729
"github.com/iotexproject/iotex-core/db"
2830
"github.com/iotexproject/iotex-core/state/factory"
2931
"github.com/iotexproject/iotex-core/testutil"
30-
"github.com/stretchr/testify/require"
3132
)
3233

3334
type checkContractExpectation struct {
@@ -109,7 +110,7 @@ func TestSGDRegistry(t *testing.T) {
109110
r.NoError(err)
110111
kvstore, err := db.CreateKVStore(db.DefaultConfig, indexSGDDBPath)
111112
r.NoError(err)
112-
sgdRegistry := blockindex.NewSGDRegistry(contractAddress, 0, kvstore)
113+
sgdRegistry := blockindex.NewSGDRegistry(contractAddress, 2, kvstore)
113114
r.NoError(sgdRegistry.Start(ctx))
114115
defer func() {
115116
r.NoError(sgdRegistry.Stop(ctx))
@@ -256,7 +257,7 @@ func TestSGDRegistry(t *testing.T) {
256257
)
257258
r.NoError(sgdRegistry.PutBlock(ctx, blk))
258259
}
259-
receiver, percentage, isApproved, err := sgdRegistry.CheckContract(ctx, tt.checkContractExpect.contractAddress)
260+
receiver, percentage, isApproved, err := sgdRegistry.CheckContract(ctx, tt.checkContractExpect.contractAddress, height)
260261
if tt.checkContractExpect.errorContains != "" {
261262
r.ErrorContains(err, tt.checkContractExpect.errorContains)
262263
} else {

0 commit comments

Comments
 (0)