Skip to content

Commit 7a09f4b

Browse files
authored
[contractstaking] fix transaction for merge after create bucket (#3924)
1 parent 0bd670c commit 7a09f4b

File tree

5 files changed

+123
-17
lines changed

5 files changed

+123
-17
lines changed

blockindex/contractstaking/delta_state.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ var (
2727
},
2828
deltaStateAdded: {
2929
deltaActionModify: deltaStateAdded,
30+
deltaActionRemove: deltaStateUnchanged,
3031
},
3132
deltaStateModified: {
3233
deltaActionModify: deltaStateModified,

blockindex/contractstaking/delta_state_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func TestDeltaState_Transfer(t *testing.T) {
2020
{"unchanged->remove", deltaStateUnchanged, deltaActionRemove, deltaStateRemoved, ""},
2121
{"unchanged->modify", deltaStateUnchanged, deltaActionModify, deltaStateModified, ""},
2222
{"added->add", deltaStateAdded, deltaActionAdd, deltaStateUnchanged, "invalid delta action 0 on state 1"},
23-
{"added->remove", deltaStateAdded, deltaActionRemove, deltaStateUnchanged, "invalid delta action 1 on state 1"},
23+
{"added->remove", deltaStateAdded, deltaActionRemove, deltaStateUnchanged, ""},
2424
{"added->modify", deltaStateAdded, deltaActionModify, deltaStateAdded, ""},
2525
{"removed->add", deltaStateRemoved, deltaActionAdd, deltaStateUnchanged, "invalid delta state 2"},
2626
{"removed->remove", deltaStateRemoved, deltaActionRemove, deltaStateUnchanged, "invalid delta state 2"},

blockindex/contractstaking/indexer.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,16 @@ func (s *Indexer) BucketTypes() ([]*BucketType, error) {
121121

122122
// PutBlock puts a block into indexer
123123
func (s *Indexer) PutBlock(ctx context.Context, blk *block.Block) error {
124-
if blk.Height() < s.contractDeployHeight || blk.Height() <= s.cache.Height() {
124+
expectHeight := s.cache.Height() + 1
125+
if expectHeight < s.contractDeployHeight {
126+
expectHeight = s.contractDeployHeight
127+
}
128+
if blk.Height() < expectHeight {
125129
return nil
126130
}
131+
if blk.Height() > expectHeight {
132+
return errors.Errorf("invalid block height %d, expect %d", blk.Height(), expectHeight)
133+
}
127134
// new event handler for this block
128135
handler := newContractStakingEventHandler(s.cache)
129136

blockindex/contractstaking/indexer_test.go

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,17 @@ func TestContractStakingIndexerVotes(t *testing.T) {
697697
r.EqualValues(50, indexer.CandidateVotes(delegate1).Uint64())
698698
r.EqualValues(20, indexer.CandidateVotes(delegate2).Uint64())
699699

700+
// create & merge bucket 8, 9, 10
701+
height++
702+
handler = newContractStakingEventHandler(indexer.cache)
703+
stake(r, handler, owner, delegate1, 8, 20, 20, height)
704+
stake(r, handler, owner, delegate2, 9, 20, 20, height)
705+
stake(r, handler, owner, delegate2, 10, 20, 20, height)
706+
mergeBuckets(r, handler, []int64{8, 9, 10}, 60, 20)
707+
r.NoError(indexer.commit(handler, height))
708+
r.EqualValues(110, indexer.CandidateVotes(delegate1).Uint64())
709+
r.EqualValues(20, indexer.CandidateVotes(delegate2).Uint64())
710+
700711
t.Run("Height", func(t *testing.T) {
701712
h, err := indexer.Height()
702713
r.NoError(err)
@@ -723,7 +734,7 @@ func TestContractStakingIndexerVotes(t *testing.T) {
723734
t.Run("Buckets", func(t *testing.T) {
724735
bts, err := indexer.Buckets()
725736
r.NoError(err)
726-
r.Len(bts, 5)
737+
r.Len(bts, 6)
727738
slices.SortFunc(bts, func(i, j *staking.VoteBucket) bool {
728739
return i.Index < j.Index
729740
})
@@ -732,46 +743,55 @@ func TestContractStakingIndexerVotes(t *testing.T) {
732743
r.EqualValues(3, bts[2].Index)
733744
r.EqualValues(4, bts[3].Index)
734745
r.EqualValues(5, bts[4].Index)
746+
r.EqualValues(8, bts[5].Index)
735747
r.EqualValues(10, bts[0].StakedDurationBlockNumber)
736748
r.EqualValues(20, bts[1].StakedDurationBlockNumber)
737749
r.EqualValues(20, bts[2].StakedDurationBlockNumber)
738750
r.EqualValues(20, bts[3].StakedDurationBlockNumber)
739751
r.EqualValues(20, bts[4].StakedDurationBlockNumber)
752+
r.EqualValues(20, bts[5].StakedDurationBlockNumber)
740753
r.EqualValues(10, bts[0].StakedAmount.Int64())
741754
r.EqualValues(30, bts[1].StakedAmount.Int64())
742755
r.EqualValues(20, bts[2].StakedAmount.Int64())
743756
r.EqualValues(20, bts[3].StakedAmount.Int64())
744757
r.EqualValues(60, bts[4].StakedAmount.Int64())
758+
r.EqualValues(60, bts[5].StakedAmount.Int64())
745759
r.EqualValues(delegate1.String(), bts[0].Candidate.String())
746760
r.EqualValues(delegate1.String(), bts[1].Candidate.String())
747761
r.EqualValues(delegate1.String(), bts[2].Candidate.String())
748762
r.EqualValues(delegate2.String(), bts[3].Candidate.String())
749763
r.EqualValues(delegate2.String(), bts[4].Candidate.String())
764+
r.EqualValues(delegate1.String(), bts[5].Candidate.String())
750765
r.EqualValues(owner.String(), bts[0].Owner.String())
751766
r.EqualValues(owner.String(), bts[1].Owner.String())
752767
r.EqualValues(owner.String(), bts[2].Owner.String())
753768
r.EqualValues(delegate2.String(), bts[3].Owner.String())
754769
r.EqualValues(owner.String(), bts[4].Owner.String())
770+
r.EqualValues(owner.String(), bts[5].Owner.String())
755771
r.False(bts[0].AutoStake)
756772
r.True(bts[1].AutoStake)
757773
r.True(bts[2].AutoStake)
758774
r.True(bts[3].AutoStake)
759775
r.False(bts[4].AutoStake)
776+
r.True(bts[5].AutoStake)
760777
r.EqualValues(1, bts[0].CreateBlockHeight)
761778
r.EqualValues(1, bts[1].CreateBlockHeight)
762779
r.EqualValues(1, bts[2].CreateBlockHeight)
763780
r.EqualValues(1, bts[3].CreateBlockHeight)
764781
r.EqualValues(7, bts[4].CreateBlockHeight)
782+
r.EqualValues(10, bts[5].CreateBlockHeight)
765783
r.EqualValues(3, bts[0].StakeStartBlockHeight)
766784
r.EqualValues(1, bts[1].StakeStartBlockHeight)
767785
r.EqualValues(1, bts[2].StakeStartBlockHeight)
768786
r.EqualValues(1, bts[3].StakeStartBlockHeight)
769787
r.EqualValues(9, bts[4].StakeStartBlockHeight)
788+
r.EqualValues(10, bts[5].StakeStartBlockHeight)
770789
r.EqualValues(4, bts[0].UnstakeStartBlockHeight)
771790
r.EqualValues(maxBlockNumber, bts[1].UnstakeStartBlockHeight)
772791
r.EqualValues(maxBlockNumber, bts[2].UnstakeStartBlockHeight)
773792
r.EqualValues(maxBlockNumber, bts[3].UnstakeStartBlockHeight)
774793
r.EqualValues(9, bts[4].UnstakeStartBlockHeight)
794+
r.EqualValues(maxBlockNumber, bts[5].UnstakeStartBlockHeight)
775795
for _, b := range bts {
776796
r.EqualValues(0, b.StakedDuration)
777797
r.EqualValues(time.Time{}, b.CreateTime)
@@ -784,13 +804,14 @@ func TestContractStakingIndexerVotes(t *testing.T) {
784804
t.Run("BucketsByCandidate", func(t *testing.T) {
785805
d1Bts, err := indexer.BucketsByCandidate(delegate1)
786806
r.NoError(err)
787-
r.Len(d1Bts, 3)
807+
r.Len(d1Bts, 4)
788808
slices.SortFunc(d1Bts, func(i, j *staking.VoteBucket) bool {
789809
return i.Index < j.Index
790810
})
791811
r.EqualValues(1, d1Bts[0].Index)
792812
r.EqualValues(2, d1Bts[1].Index)
793813
r.EqualValues(3, d1Bts[2].Index)
814+
r.EqualValues(8, d1Bts[3].Index)
794815
d2Bts, err := indexer.BucketsByCandidate(delegate2)
795816
r.NoError(err)
796817
r.Len(d2Bts, 2)
@@ -802,9 +823,9 @@ func TestContractStakingIndexerVotes(t *testing.T) {
802823
})
803824

804825
t.Run("BucketsByIndices", func(t *testing.T) {
805-
bts, err := indexer.BucketsByIndices([]uint64{1, 2, 3, 4, 5})
826+
bts, err := indexer.BucketsByIndices([]uint64{1, 2, 3, 4, 5, 8})
806827
r.NoError(err)
807-
r.Len(bts, 5)
828+
r.Len(bts, 6)
808829
})
809830
}
810831

@@ -817,17 +838,19 @@ func TestIndexer_PutBlock(t *testing.T) {
817838
startHeight uint64
818839
blockHeight uint64
819840
expectedHeight uint64
841+
errMsg string
820842
}{
821-
{"block < height < start", 10, 20, 9, 10},
822-
{"block = height < start", 10, 20, 10, 10},
823-
{"height < block < start", 10, 20, 11, 10},
824-
{"height < block = start", 10, 20, 20, 20},
825-
{"height < start < block", 10, 20, 21, 21},
826-
{"block < start < height", 20, 10, 9, 20},
827-
{"block = start < height", 20, 10, 10, 20},
828-
{"start < block < height", 20, 10, 11, 20},
829-
{"start < block = height", 20, 10, 20, 20},
830-
{"start < height < block", 20, 10, 21, 21},
843+
{"block < height < start", 10, 20, 9, 10, ""},
844+
{"block = height < start", 10, 20, 10, 10, ""},
845+
{"height < block < start", 10, 20, 11, 10, ""},
846+
{"height < block = start", 10, 20, 20, 20, ""},
847+
{"height < start < block", 10, 20, 21, 10, "invalid block height 21, expect 20"},
848+
{"block < start < height", 20, 10, 9, 20, ""},
849+
{"block = start < height", 20, 10, 10, 20, ""},
850+
{"start < block < height", 20, 10, 11, 20, ""},
851+
{"start < block = height", 20, 10, 20, 20, ""},
852+
{"start < height < block", 20, 10, 21, 21, ""},
853+
{"start < height < block+", 20, 10, 22, 20, "invalid block height 22, expect 21"},
831854
}
832855

833856
for _, c := range cases {
@@ -854,7 +877,11 @@ func TestIndexer_PutBlock(t *testing.T) {
854877
r.NoError(err)
855878
// Put the block
856879
err = indexer.PutBlock(context.Background(), &blk)
857-
r.NoError(err)
880+
if c.errMsg != "" {
881+
r.ErrorContains(err, c.errMsg)
882+
} else {
883+
r.NoError(err)
884+
}
858885
// Check the block height
859886
r.EqualValues(c.expectedHeight, indexer.cache.Height())
860887
})

e2etest/contract_staking_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,6 +1432,58 @@ func TestContractStaking(t *testing.T) {
14321432
bt, ok = indexer.Bucket(uint64(tokenID))
14331433
r.False(ok)
14341434
r.EqualValues(1, indexer.TotalBucketCount())
1435+
1436+
t.Run("cannot withdraw again", func(t *testing.T) {
1437+
data, err := lsdABI.Pack("withdraw", big.NewInt(int64(tokenID)), addr)
1438+
r.NoError(err)
1439+
param = callParam{
1440+
contractAddr: contractAddresses,
1441+
bytecode: hex.EncodeToString(data),
1442+
amount: big.NewInt(0),
1443+
gasLimit: 1000000,
1444+
gasPrice: big.NewInt(0),
1445+
sk: identityset.PrivateKey(adminID),
1446+
}
1447+
receipts, _ = writeContract(bc, sf, dao, ap, []*callParam{&param}, r)
1448+
r.Len(receipts, 1)
1449+
r.EqualValues("ERC721: invalid token ID", receipts[0].ExecutionRevertMsg())
1450+
r.EqualValues(iotextypes.ReceiptStatus_ErrExecutionReverted, receipts[0].Status)
1451+
})
1452+
1453+
t.Run("cannot expand after withdraw", func(t *testing.T) {
1454+
data, err := lsdABI.Pack("expandBucket", big.NewInt(int64(tokenID)), big.NewInt(100), big.NewInt(10))
1455+
r.NoError(err)
1456+
param = callParam{
1457+
contractAddr: contractAddresses,
1458+
bytecode: hex.EncodeToString(data),
1459+
amount: big.NewInt(90),
1460+
gasLimit: 1000000,
1461+
gasPrice: big.NewInt(0),
1462+
sk: identityset.PrivateKey(adminID),
1463+
}
1464+
receipts, _ = writeContract(bc, sf, dao, ap, []*callParam{&param}, r)
1465+
r.Len(receipts, 1)
1466+
r.EqualValues("ERC721: invalid token ID", receipts[0].ExecutionRevertMsg())
1467+
r.EqualValues(iotextypes.ReceiptStatus_ErrExecutionReverted, receipts[0].Status)
1468+
})
1469+
1470+
t.Run("cannot merge after withdraw", func(t *testing.T) {
1471+
bt := simpleStake(_delegates[3], big.NewInt(10), big.NewInt(10))
1472+
data, err := lsdABI.Pack("merge", []*big.Int{big.NewInt(int64(bt.Index)), big.NewInt(int64(tokenID))}, big.NewInt(100))
1473+
r.NoError(err)
1474+
param := callParam{
1475+
contractAddr: contractAddresses,
1476+
bytecode: hex.EncodeToString(data),
1477+
amount: big.NewInt(0),
1478+
gasLimit: 1000000,
1479+
gasPrice: big.NewInt(0),
1480+
sk: identityset.PrivateKey(adminID),
1481+
}
1482+
receipts, _ = writeContract(bc, sf, dao, ap, []*callParam{&param}, r)
1483+
r.Len(receipts, 1)
1484+
r.EqualValues("ERC721: invalid token ID", receipts[0].ExecutionRevertMsg())
1485+
r.EqualValues(iotextypes.ReceiptStatus_ErrExecutionReverted, receipts[0].Status)
1486+
})
14351487
})
14361488
})
14371489
})
@@ -1534,6 +1586,24 @@ func TestContractStaking(t *testing.T) {
15341586
r.False(ok)
15351587
}
15361588
}
1589+
1590+
t.Run("cannot expand after merge", func(t *testing.T) {
1591+
tokenID := newBuckets[1].Index
1592+
data, err := lsdABI.Pack("expandBucket", big.NewInt(int64(tokenID)), big.NewInt(100), big.NewInt(10))
1593+
r.NoError(err)
1594+
param = callParam{
1595+
contractAddr: contractAddresses,
1596+
bytecode: hex.EncodeToString(data),
1597+
amount: big.NewInt(90),
1598+
gasLimit: 1000000,
1599+
gasPrice: big.NewInt(0),
1600+
sk: identityset.PrivateKey(adminID),
1601+
}
1602+
receipts, _ = writeContract(bc, sf, dao, ap, []*callParam{&param}, r)
1603+
r.Len(receipts, 1)
1604+
r.EqualValues("ERC721: invalid token ID", receipts[0].ExecutionRevertMsg())
1605+
r.EqualValues(iotextypes.ReceiptStatus_ErrExecutionReverted, receipts[0].Status)
1606+
})
15371607
})
15381608

15391609
t.Run("extend duration", func(t *testing.T) {
@@ -1771,6 +1841,7 @@ func TestContractStaking(t *testing.T) {
17711841
r.EqualValues(identityset.Address(newOwnerIdx).String(), bt.Owner.String())
17721842
})
17731843
})
1844+
17741845
}
17751846

17761847
func prepareContractStakingBlockchain(ctx context.Context, cfg config.Config, r *require.Assertions) (blockchain.Blockchain, factory.Factory, blockdao.BlockDAO, actpool.ActPool, *contractstaking.Indexer) {

0 commit comments

Comments
 (0)