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

[blockindex] return empty result when contract not deployed #3928

Merged
merged 1 commit into from
Aug 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 28 additions & 0 deletions blockindex/contractstaking/indexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,36 +81,57 @@ func (s *Indexer) StartHeight() uint64 {

// CandidateVotes returns the candidate votes
func (s *Indexer) CandidateVotes(candidate address.Address, height uint64) (*big.Int, error) {
if s.isIgnored(height) {
return big.NewInt(0), nil
}
return s.cache.CandidateVotes(candidate, height)
}

// Buckets returns the buckets
func (s *Indexer) Buckets(height uint64) ([]*Bucket, error) {
if s.isIgnored(height) {
return []*Bucket{}, nil
}
return s.cache.Buckets(height)
}

// Bucket returns the bucket
func (s *Indexer) Bucket(id uint64, height uint64) (*Bucket, bool, error) {
if s.isIgnored(height) {
return nil, false, nil
}
return s.cache.Bucket(id, height)
}

// BucketsByIndices returns the buckets by indices
func (s *Indexer) BucketsByIndices(indices []uint64, height uint64) ([]*Bucket, error) {
if s.isIgnored(height) {
return []*Bucket{}, nil
}
return s.cache.BucketsByIndices(indices, height)
}

// BucketsByCandidate returns the buckets by candidate
func (s *Indexer) BucketsByCandidate(candidate address.Address, height uint64) ([]*Bucket, error) {
if s.isIgnored(height) {
return []*Bucket{}, nil
}
return s.cache.BucketsByCandidate(candidate, height)
}

// TotalBucketCount returns the total bucket count including active and burnt buckets
func (s *Indexer) TotalBucketCount(height uint64) (uint64, error) {
if s.isIgnored(height) {
return 0, nil
}
return s.cache.TotalBucketCount(height)
}

// BucketTypes returns the active bucket types
func (s *Indexer) BucketTypes(height uint64) ([]*BucketType, error) {
if s.isIgnored(height) {
return []*BucketType{}, nil
}
btMap, err := s.cache.ActiveBucketTypes(height)
if err != nil {
return nil, err
Expand Down Expand Up @@ -185,3 +206,10 @@ func (s *Indexer) reloadCache() error {
func (s *Indexer) loadFromDB() error {
return s.cache.LoadFromDB(s.kvstore)
}

// isIgnored returns true if before cotractDeployHeight.
// 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
}
76 changes: 57 additions & 19 deletions blockindex/contractstaking/indexer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package contractstaking
import (
"context"
"math/big"
"strconv"
"sync"
"testing"
"time"
Expand Down Expand Up @@ -916,29 +917,66 @@ func TestContractStakingIndexerVotes(t *testing.T) {
r.NoError(err)
r.Len(bts, 6)
})
}

t.Run("heightRestriction", func(t *testing.T) {
cases := []struct {
height uint64
valid bool
}{
{0, true},
{height - 1, true},
{height, true},
{height + 1, false},
}
for i := range cases {
h := cases[i].height
if cases[i].valid {
func TestIndexer_ReadHeightRestriction(t *testing.T) {
r := require.New(t)

cases := []struct {
startHeight uint64
height uint64
readHeight uint64
valid bool
}{
{0, 0, 0, true},
{0, 0, 1, false},
{0, 2, 0, true},
{0, 2, 1, true},
{0, 2, 2, true},
{0, 2, 3, false},
{10, 0, 0, true},
{10, 0, 1, true},
{10, 0, 9, true},
{10, 0, 10, false},
{10, 0, 11, false},
{10, 10, 0, true},
{10, 10, 1, true},
{10, 10, 9, true},
{10, 10, 10, true},
{10, 10, 11, false},
}

for idx, c := range cases {
name := strconv.FormatInt(int64(idx), 10)
t.Run(name, func(t *testing.T) {
// Create a new Indexer
height := c.height
startHeight := c.startHeight
cfg := config.Default.DB
dbPath, err := testutil.PathOfTempFile("db")
r.NoError(err)
cfg.DbPath = dbPath
indexer, err := NewContractStakingIndexer(db.NewBoltDB(cfg), identityset.Address(1).String(), startHeight)
r.NoError(err)
r.NoError(indexer.Start(context.Background()))
defer func() {
r.NoError(indexer.Stop(context.Background()))
testutil.CleanupPath(dbPath)
}()
indexer.cache.putHeight(height)
// check read api
h := c.readHeight
delegate := identityset.Address(1)
if c.valid {
_, err = indexer.Buckets(h)
r.NoError(err)
_, err = indexer.BucketTypes(h)
r.NoError(err)
_, err = indexer.BucketsByCandidate(delegate1, h)
_, err = indexer.BucketsByCandidate(delegate, h)
r.NoError(err)
_, err = indexer.BucketsByIndices([]uint64{1, 2, 3, 4, 5, 8}, h)
r.NoError(err)
_, err = indexer.CandidateVotes(delegate1, h)
_, err = indexer.CandidateVotes(delegate, h)
r.NoError(err)
_, _, err = indexer.Bucket(1, h)
r.NoError(err)
Expand All @@ -949,19 +987,19 @@ func TestContractStakingIndexerVotes(t *testing.T) {
r.ErrorIs(err, ErrInvalidHeight)
_, err = indexer.BucketTypes(h)
r.ErrorIs(err, ErrInvalidHeight)
_, err = indexer.BucketsByCandidate(delegate1, h)
_, err = indexer.BucketsByCandidate(delegate, h)
r.ErrorIs(err, ErrInvalidHeight)
_, err = indexer.BucketsByIndices([]uint64{1, 2, 3, 4, 5, 8}, h)
r.ErrorIs(err, ErrInvalidHeight)
_, err = indexer.CandidateVotes(delegate1, h)
_, err = indexer.CandidateVotes(delegate, h)
r.ErrorIs(err, ErrInvalidHeight)
_, _, err = indexer.Bucket(1, h)
r.ErrorIs(err, ErrInvalidHeight)
_, err = indexer.TotalBucketCount(h)
r.ErrorIs(err, ErrInvalidHeight)
}
}
})
})
}
}

func TestIndexer_PutBlock(t *testing.T) {
Expand Down
4 changes: 4 additions & 0 deletions blockindex/sgd_indexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,10 @@ func (sgd *sgdRegistry) validateQueryHeight(height uint64) error {
if height == 0 {
return nil
}
// Compatible with blocks between feature hard-fork and contract deployed
if height < sgd.startHeight {
return nil
}
tipHeight, err := sgd.height()
if err != nil {
return err
Expand Down
111 changes: 86 additions & 25 deletions blockindex/sgd_indexer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/hex"
"math/big"
"strconv"
"sync/atomic"
"testing"

Expand All @@ -15,6 +16,8 @@ import (
"github.com/iotexproject/iotex-core/blockchain/block"
"github.com/iotexproject/iotex-core/blockchain/genesis"
"github.com/iotexproject/iotex-core/db"
"github.com/iotexproject/iotex-core/db/batch"
"github.com/iotexproject/iotex-core/pkg/util/byteutil"
"github.com/iotexproject/iotex-core/state"
"github.com/iotexproject/iotex-core/test/identityset"
"github.com/iotexproject/iotex-core/testutil"
Expand Down Expand Up @@ -109,31 +112,6 @@ func TestNewSGDRegistry(t *testing.T) {
r.Equal(receiverAddress, receiver)
r.True(isApproved)
r.Equal(_sgdPercentage, percentage)

t.Run("heightRestriction", func(t *testing.T) {
cases := []struct {
height uint64
isErr bool
}{
{0, false},
{1, true},
{2, false},
{3, true},
}
for i := range cases {
if cases[i].isErr {
_, err = sgdRegistry.FetchContracts(ctx, cases[i].height)
r.ErrorContains(err, "invalid height")
_, _, _, err = sgdRegistry.CheckContract(ctx, registerAddress.String(), cases[i].height)
r.ErrorContains(err, "invalid height")
} else {
_, err = sgdRegistry.FetchContracts(ctx, cases[i].height)
r.Nil(err)
_, _, _, err = sgdRegistry.CheckContract(ctx, registerAddress.String(), cases[i].height)
r.Nil(err)
}
}
})
})
t.Run("disapproveContract", func(t *testing.T) {
builder := block.NewTestingBuilder()
Expand Down Expand Up @@ -182,6 +160,89 @@ func TestNewSGDRegistry(t *testing.T) {
r.ErrorIs(err, state.ErrStateNotExist)
})
})
t.Run("heightRestriction", func(t *testing.T) {
cases := []struct {
startHeight uint64
height uint64
readHeight uint64
valid bool
}{
{0, 0, 0, true},
{0, 0, 1, false},
{0, 2, 0, true},
{0, 2, 1, false},
{0, 2, 2, true},
{0, 2, 3, false},
{10, 0, 0, true},
{10, 0, 1, true},
{10, 0, 9, true},
{10, 0, 10, false},
{10, 0, 11, false},
{10, 10, 0, true},
{10, 10, 1, true},
{10, 10, 9, true},
{10, 10, 10, true},
{10, 10, 11, false},
}
for i := range cases {
name := strconv.FormatInt(int64(i), 10)
t.Run(name, func(t *testing.T) {
testDBPath, err := testutil.PathOfTempFile("sgd")
r.NoError(err)
ctx := context.Background()
cfg := db.DefaultConfig
cfg.DbPath = testDBPath
kvStore := db.NewBoltDB(cfg)
sgdRegistry := &sgdRegistry{
contract: _testSGDContractAddress,
startHeight: cases[i].startHeight,
kvStore: kvStore,
}
r.NoError(sgdRegistry.Start(ctx))
defer func() {
r.NoError(sgdRegistry.Stop(ctx))
testutil.CleanupPath(testDBPath)
}()
// register
nonce := uint64(0)
registerAddress, err := address.FromHex("5b38da6a701c568545dcfcb03fcb875f56beddc4")
r.NoError(err)
builder := block.NewTestingBuilder()
event := _sgdABI.Events["ContractRegistered"]
data, _ := hex.DecodeString("0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc400000000000000000000000078731d3ca6b7e34ac0f824c42a7cc18a495cabab")
exec, err := action.SignedExecution(_testSGDContractAddress, identityset.PrivateKey(27), atomic.AddUint64(&nonce, 1), big.NewInt(0), 10000000, big.NewInt(9000000000000), data)
r.NoError(err)
h, _ := exec.Hash()
logs := &action.Log{
Address: _testSGDContractAddress,
Topics: []hash.Hash256{hash.Hash256(event.ID)},
Data: data,
}
expectHeight, err := sgdRegistry.expectHeight()
r.NoError(err)
blk := createTestingBlock(builder, expectHeight, h, exec, logs)
r.NoError(sgdRegistry.PutBlock(ctx, blk))
_, _, _, err = sgdRegistry.CheckContract(ctx, registerAddress.String(), 1)
r.NoError(err)
// update height
b := batch.NewBatch()
b.Put(_sgdToHeightNS, _sgdCurrentHeight, byteutil.Uint64ToBytesBigEndian(cases[i].height), "failed to put current height")
sgdRegistry.kvStore.WriteBatch(b)
// check
if !cases[i].valid {
_, err = sgdRegistry.FetchContracts(ctx, cases[i].readHeight)
r.ErrorContains(err, "invalid height")
_, _, _, err = sgdRegistry.CheckContract(ctx, registerAddress.String(), cases[i].readHeight)
r.ErrorContains(err, "invalid height")
} else {
_, err = sgdRegistry.FetchContracts(ctx, cases[i].readHeight)
r.Nil(err)
_, _, _, err = sgdRegistry.CheckContract(ctx, registerAddress.String(), cases[i].readHeight)
r.Nil(err)
}
})
}
})
}

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