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

[action] New MigrateStake Action #4299

Merged
merged 21 commits into from
Jun 19, 2024
Merged
46 changes: 24 additions & 22 deletions action/protocol/staking/handler_stake_migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,14 @@ func (p *Protocol) handleStakeMigrate(ctx context.Context, act *action.MigrateSt
actLogs := make([]*action.Log, 0)
transferLogs := make([]*action.TransactionLog, 0)
nonceUpdated := false
si := csm.SM().Snapshot()
revertSM := func() {
if revertErr := csm.SM().Revert(si); revertErr != nil {
log.L().Panic("failed to revert state", zap.Error(revertErr))
}
}
insGas, err := act.IntrinsicGas()
if err != nil {
return nil, nil, 0, nonceUpdated, err
}

// validate bucket index
bucket, rErr := p.fetchBucket(csm, act.BucketIndex())
if rErr != nil {
return nil, nil, 0, nonceUpdated, rErr
}
if err := p.validateStakeMigrate(ctx, bucket, csm); err != nil {
return nil, nil, 0, nonceUpdated, err
}

// force-withdraw native bucket
staker, rerr := fetchCaller(ctx, csm, big.NewInt(0))
if rerr != nil {
return nil, nil, 0, nonceUpdated, errors.Wrap(rerr, "failed to fetch caller")
Expand All @@ -53,20 +40,31 @@ func (p *Protocol) handleStakeMigrate(ctx context.Context, act *action.MigrateSt
if candidate == nil {
return nil, nil, 0, nonceUpdated, errCandNotExist
}
duration := uint64(bucket.StakedDuration / p.helperCtx.BlockInterval(protocol.MustGetBlockCtx(ctx).BlockHeight))
exec, err := p.constructExecution(candidate.GetIdentifier(), bucket.StakedAmount, duration, act.Nonce(), act.GasLimit(), act.GasPrice())
if err != nil {
return nil, nil, 0, nonceUpdated, errors.Wrap(err, "failed to construct execution")
}
// validate bucket index
if err := p.validateStakeMigrate(ctx, bucket, csm); err != nil {
return nil, nil, 0, nonceUpdated, err
}

// snapshot for sm in case of failure of hybrid protocol handling
si := csm.SM().Snapshot()
revertSM := func() {
if revertErr := csm.SM().Revert(si); revertErr != nil {
log.L().Panic("failed to revert state", zap.Error(revertErr))
}
}
// force-withdraw native bucket
actLog, tLog, err := p.withdrawBucket(ctx, staker, bucket, candidate, csm)
if err != nil {
return nil, nil, 0, nonceUpdated, err
}
actLogs = append(actLogs, actLog.Build(ctx, nil))
transferLogs = append(transferLogs, tLog)

// call staking contract to stake
duration := uint64(bucket.StakedDuration / p.helperCtx.BlockInterval(protocol.MustGetBlockCtx(ctx).BlockHeight))
exec, err := p.constructExecution(candidate.GetIdentifier(), bucket.StakedAmount, duration, act.Nonce(), act.GasLimit(), act.GasPrice())
if err != nil {
revertSM()
return nil, nil, 0, nonceUpdated, errors.Wrap(err, "failed to construct execution")
}
excReceipt, err := p.createNFTBucket(ctx, exec, csm.SM())
if err != nil {
revertSM()
Expand All @@ -75,12 +73,16 @@ func (p *Protocol) handleStakeMigrate(ctx context.Context, act *action.MigrateSt
nonceUpdated = true
if excReceipt.Status != uint64(iotextypes.ReceiptStatus_Success) {
revertSM()
envestcc marked this conversation as resolved.
Show resolved Hide resolved
return nil, nil, 0, nonceUpdated, errors.Errorf("execution failed with status %d", excReceipt.Status)
nonceUpdated = false
return excReceipt.Logs(), excReceipt.TransactionLogs(), excReceipt.GasConsumed + insGas, nonceUpdated, &handleError{
err: errors.Errorf("staking contract failure: %s", excReceipt.ExecutionRevertMsg()),
failureStatus: iotextypes.ReceiptStatus(excReceipt.Status),
}
}
// add sub-receipts logs
actLogs = append(actLogs, excReceipt.Logs()...)
transferLogs = append(transferLogs, excReceipt.TransactionLogs()...)
return actLogs, transferLogs, excReceipt.GasConsumed + insGas, nonceUpdated, nil
return actLogs, transferLogs, insGas, nonceUpdated, nil
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return actLogs, transferLogs, insGas, true, nil
it seems that if err != nil, nonceUpdated is always false, else it is always true.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, seems like nonceUpdated is not necessary, and just not update nonce again if returned error == nil

}

func (p *Protocol) validateStakeMigrate(ctx context.Context, bucket *VoteBucket, csm CandidateStateManager) error {
Expand Down
50 changes: 38 additions & 12 deletions action/protocol/staking/handler_stake_migrate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,7 @@ func TestHandleStakeMigrate(t *testing.T) {
receipts := make([]*action.Receipt, 0)
errs := make([]error, 0)
for _, act := range acts {
// si := sm.Snapshot()
receipt, err := runAction(ctx, p, act, sm)
// if err != nil {
// r.NoError(sm.Revert(si))
// }
receipts = append(receipts, receipt)
errs = append(errs, err)
}
Expand Down Expand Up @@ -218,23 +214,53 @@ func TestHandleStakeMigrate(t *testing.T) {
t.Run("failure from contract call", func(t *testing.T) {
pa := NewPatches()
defer pa.Reset()
sm.EXPECT().Revert(gomock.Any()).Return(nil).AnyTimes()
pa.ApplyMethodReturn(excPrtl, "Handle", &action.Receipt{
Status: uint64(iotextypes.ReceiptStatus_Failure),
}, nil)
sm.EXPECT().Revert(gomock.Any()).Return(nil).Times(1)
receipt := &action.Receipt{
Status: uint64(iotextypes.ReceiptStatus_Failure),
GasConsumed: 1000000,
}
actLog := &action.Log{
Address: address.ZeroAddress,
Topics: action.Topics{
hash.BytesToHash256([]byte("withdraw")),
},
}
txLog := &action.TransactionLog{
Type: iotextypes.TransactionLogType_GAS_FEE,
Sender: "",
Recipient: "",
Amount: new(big.Int).Mul(new(big.Int).SetUint64(receipt.GasConsumed), gasPrice),
}
receipt.AddLogs(actLog)
receipt.AddTransactionLogs(txLog)
pa.ApplyMethodReturn(excPrtl, "Handle", receipt, nil)
act := assertions.MustNoErrorV(action.SignedMigrateStake(popNonce(&stakerNonce), 1, gasLimit, gasPrice, identityset.PrivateKey(stakerID)))
receipts, errs := runBlock(ctx, p, sm, 8, timeBlock,
assertions.MustNoErrorV(action.SignedMigrateStake(stakerNonce, 1, gasLimit, gasPrice, identityset.PrivateKey(stakerID))),
act,
)
r.Len(receipts, 1)
r.ErrorContains(errs[0], "execution failed with status")
r.NoError(errs[0])
h, err := act.Hash()
r.NoError(err)
expectReceipt := &action.Receipt{
Status: receipt.Status,
ActionHash: h,
BlockHeight: 8,
GasConsumed: receipt.GasConsumed + action.MigrateStakeBaseIntrinsicGas,
ContractAddress: address.StakingProtocolAddr,
TxIndex: uint32(0),
}
expectReceipt.AddLogs(actLog)
expectReceipt.AddTransactionLogs(txLog)
r.Equal(expectReceipt, receipts[0])
})
t.Run("error from contract call", func(t *testing.T) {
pa := NewPatches()
defer pa.Reset()
pa.ApplyMethodFunc(excPrtl, "Handle", func(ctx context.Context, act action.Action, sm protocol.StateManager) (*action.Receipt, error) {
return nil, errors.New("execution failed error")
})
sm.EXPECT().Revert(gomock.Any()).Return(nil).AnyTimes()
sm.EXPECT().Revert(gomock.Any()).Return(nil).Times(1)
receipts, errs := runBlock(ctx, p, sm, 9, timeBlock,
assertions.MustNoErrorV(action.SignedCreateStake(popNonce(&stakerNonce), "cand1", stakeAmount.String(), stakeDurationDays, true, nil, gasLimit, gasPrice, identityset.PrivateKey(stakerID))),
assertions.MustNoErrorV(action.SignedMigrateStake(stakerNonce, 5, gasLimit, gasPrice, identityset.PrivateKey(stakerID))),
Expand Down Expand Up @@ -284,7 +310,7 @@ func TestHandleStakeMigrate(t *testing.T) {
r.Equal(uint64(iotextypes.ReceiptStatus_Success), receipts[0].Status)
// gas = instrinsic + contract call
instriGas, _ := act.IntrinsicGas()
r.Equal(receipt.GasConsumed+instriGas, receipts[0].GasConsumed)
r.Equal(instriGas, receipts[0].GasConsumed)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

receipts[0].GasConsumed should be intrinsicGas + execution.GasConsumed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

// withdraw log + stake log
r.Len(receipts[0].Logs(), 2)
r.Equal(&action.Log{
Expand Down
Loading