Skip to content

Commit

Permalink
[e2etest] add tests for original actions after candidate ownership tr…
Browse files Browse the repository at this point in the history
…ansfer (#4295)
  • Loading branch information
envestcc authored Jun 12, 2024
1 parent 296bf4d commit 1009b32
Show file tree
Hide file tree
Showing 6 changed files with 542 additions and 37 deletions.
1 change: 0 additions & 1 deletion action/protocol/staking/candidate_center_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,6 @@ func candCenterFromNewCandidateStateManager(r *require.Assertions, view protocol
}

func TestCandidateUpsert(t *testing.T) {
// t.Skip()
r := require.New(t)

m, err := NewCandidateCenter(nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ import (
)

func TestProtocol_HandleCandidateTransferOwnership(t *testing.T) {
//TODO: fix this test
t.Skip()
require := require.New(t)
ctrl := gomock.NewController(t)
sm := testdb.NewMockStateManager(ctrl)
Expand Down Expand Up @@ -212,6 +210,7 @@ func TestProtocol_HandleCandidateTransferOwnership(t *testing.T) {
})
cfg := deepcopy.Copy(genesis.Default).(genesis.Genesis)
cfg.TsunamiBlockHeight = 1
cfg.ToBeEnabledBlockHeight = 1 // enable candidate owner transfer feature
ctx = genesis.WithGenesisContext(ctx, cfg)
ctx = protocol.WithFeatureCtx(protocol.WithFeatureWithHeightCtx(ctx))
require.NoError(p.Validate(ctx, act, sm))
Expand Down
6 changes: 4 additions & 2 deletions action/protocol/staking/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -562,8 +562,10 @@ func (p *Protocol) ReadState(ctx context.Context, sr protocol.StateReader, metho
if err != nil {
return nil, 0, err
}
rp := rolldpos.MustGetProtocol(protocol.MustGetRegistry(ctx))
epochStartHeight := rp.GetEpochHeight(rp.GetEpochNum(inputHeight))
epochStartHeight := inputHeight
if rp := rolldpos.FindProtocol(protocol.MustGetRegistry(ctx)); rp != nil {
epochStartHeight = rp.GetEpochHeight(rp.GetEpochNum(inputHeight))
}
nativeSR, err := ConstructBaseView(sr)
if err != nil {
return nil, 0, err
Expand Down
152 changes: 152 additions & 0 deletions e2etest/e2etest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package e2etest

import (
"context"
"testing"
"time"

"github.com/pkg/errors"
"github.com/stretchr/testify/require"

"github.com/iotexproject/iotex-core/action"
"github.com/iotexproject/iotex-core/actpool"
"github.com/iotexproject/iotex-core/blockchain"
"github.com/iotexproject/iotex-core/blockchain/block"
"github.com/iotexproject/iotex-core/chainservice"
"github.com/iotexproject/iotex-core/config"
"github.com/iotexproject/iotex-core/server/itx"
"github.com/iotexproject/iotex-core/testutil"
)

type (
actionWithTime struct {
act *action.SealedEnvelope
t time.Time
}
testcase struct {
name string
preActs []*actionWithTime
act *actionWithTime
expect []actionExpect
}
accountNonceManager map[string]uint64
e2etest struct {
cfg config.Config
svr *itx.Server
cs *chainservice.ChainService
t *testing.T
nonceMgr accountNonceManager
}
)

func (m accountNonceManager) pop(addr string) uint64 {
nonce := m[addr]
m[addr] = nonce + 1
return nonce
}

func newE2ETest(t *testing.T, cfg config.Config) *e2etest {
require := require.New(t)
// Create a new blockchain
svr, err := itx.NewServer(cfg)
require.NoError(err)
ctx := context.Background()
require.NoError(svr.Start(ctx))
return &e2etest{
cfg: cfg,
svr: svr,
cs: svr.ChainService(cfg.Chain.ID),
t: t,
nonceMgr: make(accountNonceManager),
}
}

func (e *e2etest) run(cases []*testcase) {
ctx := context.Background()
// run subcases
for _, sub := range cases {
e.t.Run(sub.name, func(t *testing.T) {
e.withTest(t).runCase(ctx, sub)
})
}
}

func (e *e2etest) runCase(ctx context.Context, c *testcase) {
require := require.New(e.t)
bc := e.cs.Blockchain()
ap := e.cs.ActionPool()
// run pre-actions
for _, act := range c.preActs {
_, _, err := addOneTx(ctx, ap, bc, act)
require.NoError(err)
}
// run action
act, receipt, err := addOneTx(ctx, ap, bc, c.act)
for _, exp := range c.expect {
exp.expect(e, act, receipt, err)
}
}

func (e *e2etest) teardown() {
require := require.New(e.t)
// clean up
testutil.CleanupPath(e.cfg.Chain.ChainDBPath)
testutil.CleanupPath(e.cfg.Chain.TrieDBPath)
testutil.CleanupPath(e.cfg.Chain.BloomfilterIndexDBPath)
testutil.CleanupPath(e.cfg.Chain.CandidateIndexDBPath)
testutil.CleanupPath(e.cfg.Chain.StakingIndexDBPath)
testutil.CleanupPath(e.cfg.Chain.ContractStakingIndexDBPath)
testutil.CleanupPath(e.cfg.DB.DbPath)
testutil.CleanupPath(e.cfg.Chain.IndexDBPath)
testutil.CleanupPath(e.cfg.System.SystemLogDBPath)
testutil.CleanupPath(e.cfg.Chain.SGDIndexDBPath)
require.NoError(e.svr.Stop(context.Background()))
}

func (e *e2etest) withTest(t *testing.T) *e2etest {
return &e2etest{
cfg: e.cfg,
svr: e.svr,
cs: e.cs,
t: t,
}
}

func addOneTx(ctx context.Context, ap actpool.ActPool, bc blockchain.Blockchain, tx *actionWithTime) (*action.SealedEnvelope, *action.Receipt, error) {
if err := ap.Add(ctx, tx.act); err != nil {
return tx.act, nil, err
}
blk, err := createAndCommitBlock(bc, ap, tx.t)
if err != nil {
return tx.act, nil, err
}
h, err := tx.act.Hash()
if err != nil {
return tx.act, nil, err
}
for _, r := range blk.Receipts {
if r.ActionHash == h {
return tx.act, r, nil
}
}
return tx.act, nil, errors.Errorf("failed to find receipt for %x", h)
}

func createAndCommitBlock(bc blockchain.Blockchain, ap actpool.ActPool, blkTime time.Time) (*block.Block, error) {
blk, err := bc.MintNewBlock(blkTime)
if err != nil {
return nil, err
}
if err := bc.CommitBlock(blk); err != nil {
return nil, err
}
ap.Reset()
return blk, nil
}

func mustNoErr[T any](t T, err error) T {
if err != nil {
panic(err)
}
return t
}
115 changes: 115 additions & 0 deletions e2etest/expect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package e2etest

import (
"context"
"slices"

"github.com/iotexproject/iotex-proto/golang/iotexapi"
"github.com/iotexproject/iotex-proto/golang/iotextypes"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"

"github.com/iotexproject/iotex-core/action"
"github.com/iotexproject/iotex-core/action/protocol"
"github.com/iotexproject/iotex-core/action/protocol/staking"
"github.com/iotexproject/iotex-core/blockchain/genesis"
)

type (
actionExpect interface {
expect(test *e2etest, act *action.SealedEnvelope, receipt *action.Receipt, err error)
}
basicActionExpect struct {
err error
status uint64
executionRevertMsg string
}
candidateExpect struct {
candName string
cand *iotextypes.CandidateV2
}
bucketExpect struct {
bucket *iotextypes.VoteBucket
}
)

func (be *basicActionExpect) expect(test *e2etest, act *action.SealedEnvelope, receipt *action.Receipt, err error) {
require := require.New(test.t)
require.ErrorIs(err, be.err)
require.Equal(be.status, receipt.Status)
require.Equal(be.executionRevertMsg, receipt.ExecutionRevertMsg())
}

func (ce *candidateExpect) expect(test *e2etest, act *action.SealedEnvelope, receipt *action.Receipt, err error) {
require := require.New(test.t)
method := &iotexapi.ReadStakingDataMethod{
Method: iotexapi.ReadStakingDataMethod_CANDIDATE_BY_NAME,
}
methodBytes, err := proto.Marshal(method)
require.NoError(err)
r := &iotexapi.ReadStakingDataRequest{
Request: &iotexapi.ReadStakingDataRequest_CandidateByName_{
CandidateByName: &iotexapi.ReadStakingDataRequest_CandidateByName{
CandName: ce.candName,
},
},
}
cs := test.svr.ChainService(test.cfg.Chain.ID)
sr := cs.StateFactory()
bc := cs.Blockchain()
prtcl, ok := cs.Registry().Find("staking")
require.True(ok)
stkPrtcl := prtcl.(*staking.Protocol)
reqBytes, err := proto.Marshal(r)
require.NoError(err)
ctx := protocol.WithRegistry(context.Background(), cs.Registry())
ctx = genesis.WithGenesisContext(ctx, test.cfg.Genesis)
ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{
BlockHeight: bc.TipHeight(),
})
ctx = protocol.WithFeatureCtx(ctx)
respData, _, err := stkPrtcl.ReadState(ctx, sr, methodBytes, reqBytes)
require.NoError(err)
candidate := &iotextypes.CandidateV2{}
require.NoError(proto.Unmarshal(respData, candidate))
require.EqualValues(ce.cand.String(), candidate.String())
}

func (be *bucketExpect) expect(test *e2etest, act *action.SealedEnvelope, receipt *action.Receipt, err error) {
require := require.New(test.t)
method := &iotexapi.ReadStakingDataMethod{
Method: iotexapi.ReadStakingDataMethod_BUCKETS_BY_INDEXES,
}
methodBytes, err := proto.Marshal(method)
require.NoError(err)
r := &iotexapi.ReadStakingDataRequest{
Request: &iotexapi.ReadStakingDataRequest_BucketsByIndexes{
BucketsByIndexes: &iotexapi.ReadStakingDataRequest_VoteBucketsByIndexes{
Index: []uint64{be.bucket.Index},
},
},
}
cs := test.svr.ChainService(test.cfg.Chain.ID)
sr := cs.StateFactory()
bc := cs.Blockchain()
prtcl, ok := cs.Registry().Find("staking")
require.True(ok)
stkPrtcl := prtcl.(*staking.Protocol)
reqBytes, err := proto.Marshal(r)
require.NoError(err)
ctx := protocol.WithRegistry(context.Background(), cs.Registry())
ctx = genesis.WithGenesisContext(ctx, test.cfg.Genesis)
ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{
BlockHeight: bc.TipHeight(),
})
ctx = protocol.WithFeatureCtx(ctx)
respData, _, err := stkPrtcl.ReadState(ctx, sr, methodBytes, reqBytes)
require.NoError(err)
vbs := &iotextypes.VoteBucketList{}
require.NoError(proto.Unmarshal(respData, vbs))
idx := slices.IndexFunc(vbs.Buckets, func(vb *iotextypes.VoteBucket) bool {
return vb.ContractAddress == be.bucket.ContractAddress
})
require.True(idx != -1)
require.EqualValues(be.bucket.String(), vbs.Buckets[idx].String())
}
Loading

0 comments on commit 1009b32

Please sign in to comment.