Skip to content

Commit 39af08a

Browse files
committed
Deactivate process
1 parent aa809bb commit 39af08a

22 files changed

+1099
-402
lines changed

action/action.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func CalculateIntrinsicGas(baseIntrinsicGas uint64, payloadGas uint64, payloadSi
114114
// IsSystemAction determine whether input action belongs to system action
115115
func IsSystemAction(act *SealedEnvelope) bool {
116116
switch act.Action().(type) {
117-
case *GrantReward, *PutPollResult:
117+
case *GrantReward, *PutPollResult, *ScheduleCandidateDeactivation:
118118
return true
119119
default:
120120
return false

action/candidate_activate.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ var (
1717
_ EthCompatibleAction = (*CandidateActivate)(nil)
1818
)
1919

20-
// CandidateActivate is the action to update a candidate's bucket
20+
// CandidateActivate is the action to activate a candidate
2121
type CandidateActivate struct {
2222
stake_common
2323
// bucketID is the bucket index want to be changed to
@@ -42,7 +42,7 @@ func NewCandidateActivate(bucketID uint64) *CandidateActivate {
4242
// BucketID returns the bucket index want to be changed to
4343
func (cr *CandidateActivate) BucketID() uint64 { return cr.bucketID }
4444

45-
// IntrinsicGas returns the intrinsic gas of a CandidateRegister
45+
// IntrinsicGas returns the intrinsic gas of a CandidateActivate
4646
func (cr *CandidateActivate) IntrinsicGas() (uint64, error) {
4747
return CandidateActivateBaseIntrinsicGas, nil
4848
}

action/candidate_deactivate.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package action
2+
3+
import (
4+
"bytes"
5+
6+
"github.com/ethereum/go-ethereum/accounts/abi"
7+
"github.com/iotexproject/iotex-proto/golang/iotextypes"
8+
"github.com/pkg/errors"
9+
)
10+
11+
const (
12+
// CandidateDeactivateBaseIntrinsicGas represents the base intrinsic gas for CandidateActivate
13+
CandidateDeactivateBaseIntrinsicGas = uint64(10000)
14+
15+
// CandidateDeactivateOpRequest is an operation to request deactivation
16+
CandidateDeactivateOpRequest = iota
17+
// CandidateDeactivateOpCancel is an operation to cancel deactivation
18+
CandidateDeactivateOpCancel
19+
// CandidateDeactivateOpConfirm is an operation to confirm deactivation
20+
CandidateDeactivateOpConfirm
21+
)
22+
23+
var (
24+
requestCandidateDeactivationMethod abi.Method
25+
cancelCandidateDeactivationMethod abi.Method
26+
confirmCandidateDeactivationMethod abi.Method
27+
_ EthCompatibleAction = (*CandidateDeactivate)(nil)
28+
)
29+
30+
type (
31+
// CandidateDeactivateOp is operation type of candidate deactivation
32+
CandidateDeactivateOp uint8
33+
// CandidateDeactivate is the action to deactivate a candidate
34+
CandidateDeactivate struct {
35+
stake_common
36+
op CandidateDeactivateOp
37+
}
38+
)
39+
40+
func init() {
41+
var ok bool
42+
methods := NativeStakingContractABI().Methods
43+
requestCandidateDeactivationMethod, ok = methods["requestCandidateDeactivation"]
44+
if !ok {
45+
panic("fail to load the requestCandidateDeactivation method")
46+
}
47+
cancelCandidateDeactivationMethod, ok = methods["cancelCandidateDeactivation"]
48+
if !ok {
49+
panic("fail to load the cancelCandidateDeactivation method")
50+
}
51+
}
52+
53+
// NewCandidateDeactivate returns a CandidateDeactivate action
54+
func NewCandidateDeactivate() *CandidateDeactivate {
55+
return &CandidateDeactivate{}
56+
}
57+
58+
// IntrinsicGas returns the intrinsic gas of a CandidateDeactivate
59+
func (cd *CandidateDeactivate) IntrinsicGas() (uint64, error) {
60+
return CandidateActivateBaseIntrinsicGas, nil
61+
}
62+
63+
func (cd *CandidateDeactivate) SanityCheck() error {
64+
return nil
65+
}
66+
67+
func (cd *CandidateDeactivate) FillAction(act *iotextypes.ActionCore) {
68+
act.Action = &iotextypes.ActionCore_CandidateDeactivate{CandidateDeactivate: cd.Proto()}
69+
}
70+
71+
func (cd *CandidateDeactivate) Op() CandidateDeactivateOp {
72+
return cd.op
73+
}
74+
75+
// Proto converts CandidateDeactivate to protobuf's Action
76+
func (cd *CandidateDeactivate) Proto() *iotextypes.CandidateDeactivate {
77+
return &iotextypes.CandidateDeactivate{
78+
Op: uint32(cd.op),
79+
}
80+
}
81+
82+
// LoadProto converts a protobuf's Action to CandidateDeactivate
83+
func (cd *CandidateDeactivate) LoadProto(pbAct *iotextypes.CandidateDeactivate) error {
84+
if pbAct == nil {
85+
return ErrNilProto
86+
}
87+
cd.op = CandidateDeactivateOp(pbAct.GetOp())
88+
return nil
89+
}
90+
91+
// EthData returns the ABI-encoded data for converting to eth tx
92+
func (cd *CandidateDeactivate) EthData() ([]byte, error) {
93+
var method abi.Method
94+
switch cd.op {
95+
case CandidateDeactivateOpCancel:
96+
method = requestCandidateDeactivationMethod
97+
case CandidateDeactivateOpRequest:
98+
method = cancelCandidateDeactivationMethod
99+
case CandidateDeactivateOpConfirm:
100+
method = confirmCandidateDeactivationMethod
101+
default:
102+
return nil, errors.New("invalid operation")
103+
}
104+
data, err := method.Inputs.Pack()
105+
if err != nil {
106+
return nil, err
107+
}
108+
return append(method.ID, data...), nil
109+
}
110+
111+
// NewCandidateDeactivateFromABIBinary parses the smart contract input and creates an action
112+
func NewCandidateDeactivateFromABIBinary(data []byte) (*CandidateDeactivate, error) {
113+
var cd CandidateDeactivate
114+
// sanity check
115+
switch {
116+
case len(data) <= 4:
117+
return nil, errDecodeFailure
118+
case bytes.Equal(requestCandidateDeactivationMethod.ID, data[:4]):
119+
cd.op = CandidateDeactivateOpRequest
120+
case bytes.Equal(cancelCandidateDeactivationMethod.ID, data[:4]):
121+
cd.op = CandidateDeactivateOpCancel
122+
case bytes.Equal(confirmCandidateDeactivationMethod.ID, data[:4]):
123+
cd.op = CandidateDeactivateOpConfirm
124+
default:
125+
return nil, errDecodeFailure
126+
}
127+
128+
return &cd, nil
129+
}

action/candidate_register.go

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,15 @@ const (
3030

3131
var (
3232
// _candidateRegisterInterface is the interface of the abi encoding of stake action
33-
_candidateRegisterMethod abi.Method
34-
_candidateRegisterWithBLSMethod abi.Method
35-
_candidateRegisteredEvent abi.Event
36-
_stakedEvent abi.Event
37-
_candidateActivatedEvent abi.Event
33+
_candidateRegisterMethod abi.Method
34+
_candidateRegisterWithBLSMethod abi.Method
35+
_candidateRegisteredEvent abi.Event
36+
_stakedEvent abi.Event
37+
_candidateActivatedEvent abi.Event
38+
_candidateDeactivationRequestedEvent abi.Event
39+
_candidateDeactivationCanceledEvent abi.Event
40+
_candidateDeactivationScheduledEvent abi.Event
41+
_candidateDeactivatedEvent abi.Event
3842

3943
// ErrInvalidAmount represents that amount is 0 or negative
4044
ErrInvalidAmount = errors.New("invalid amount")
@@ -89,6 +93,22 @@ func init() {
8993
if !ok {
9094
panic("fail to load the event")
9195
}
96+
_candidateDeactivationRequestedEvent, ok = abi.Events["CandidateDeactivationRequested"]
97+
if !ok {
98+
panic("fail to load the event")
99+
}
100+
_candidateDeactivationCanceledEvent, ok = abi.Events["CandidateDeactivationCanceled"]
101+
if !ok {
102+
panic("fail to load the event")
103+
}
104+
_candidateDeactivationScheduledEvent, ok = abi.Events["CandidateDeactivationScheduled"]
105+
if !ok {
106+
panic("fail to load the event")
107+
}
108+
_candidateDeactivatedEvent, ok = abi.Events["CandidateDeactivated"]
109+
if !ok {
110+
panic("fail to load the event")
111+
}
92112
}
93113

94114
// NewCandidateRegister creates a CandidateRegister instance
@@ -426,6 +446,54 @@ func PackCandidateActivatedEvent(
426446
return topics, data, nil
427447
}
428448

449+
// PackCandidateDeactivationRequestedEvent packs the CandidateDeactivationRequested event
450+
func PackCandidateDeactivationRequestedEvent(candidate address.Address) (Topics, []byte, error) {
451+
data, err := _candidateDeactivationRequestedEvent.Inputs.NonIndexed().Pack()
452+
if err != nil {
453+
return nil, nil, errors.Wrap(err, "failed to pack CandidateDeactivationRequested event")
454+
}
455+
topics := make(Topics, 2)
456+
topics[0] = hash.Hash256(_candidateDeactivationRequestedEvent.ID)
457+
topics[1] = hash.BytesToHash256(candidate.Bytes())
458+
return topics, data, nil
459+
}
460+
461+
// PackCandidateDeactivationCanceledEvent packs the CandidateDeactivationCanceled event
462+
func PackCandidateDeactivationCanceledEvent(candidate address.Address) (Topics, []byte, error) {
463+
data, err := _candidateDeactivationCanceledEvent.Inputs.NonIndexed().Pack()
464+
if err != nil {
465+
return nil, nil, errors.Wrap(err, "failed to pack CandidateDeactivationCanceled event")
466+
}
467+
topics := make(Topics, 2)
468+
topics[0] = hash.Hash256(_candidateDeactivationCanceledEvent.ID)
469+
topics[1] = hash.BytesToHash256(candidate.Bytes())
470+
return topics, data, nil
471+
}
472+
473+
// PackCandidateDeactivationScheduledEvent packs the CandidateDeactivationScheduled event
474+
func PackCandidateDeactivationScheduledEvent(candidate address.Address, blkHeight uint64) (Topics, []byte, error) {
475+
data, err := _candidateDeactivationScheduledEvent.Inputs.NonIndexed().Pack(blkHeight)
476+
if err != nil {
477+
return nil, nil, errors.Wrap(err, "failed to pack ScheduleCandidateDeactivation event")
478+
}
479+
topics := make(Topics, 2)
480+
topics[0] = hash.Hash256(_candidateDeactivationScheduledEvent.ID)
481+
topics[1] = hash.BytesToHash256(candidate.Bytes())
482+
return topics, data, nil
483+
}
484+
485+
// PackCandidateDeactivatedEvent packs the CandidateDeactivated event
486+
func PackCandidateDeactivatedEvent(candidate address.Address) (Topics, []byte, error) {
487+
data, err := _candidateDeactivatedEvent.Inputs.NonIndexed().Pack()
488+
if err != nil {
489+
return nil, nil, errors.Wrap(err, "failed to pack CandidateDeactivated event")
490+
}
491+
topics := make(Topics, 2)
492+
topics[0] = hash.Hash256(_candidateDeactivatedEvent.ID)
493+
topics[1] = hash.BytesToHash256(candidate.Bytes())
494+
return topics, data, nil
495+
}
496+
429497
// NewCandidateRegisterFromABIBinary decodes data into CandidateRegister action
430498
func NewCandidateRegisterFromABIBinary(data []byte, value *big.Int) (*CandidateRegister, error) {
431499
var (

action/candidateregister_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,16 @@ func TestStakingEvent(t *testing.T) {
275275
require.NoError(err)
276276
checkActivatedEvent(require, topics, data, cand, bktIdx)
277277
})
278+
t.Run("deactivate", func(t *testing.T) {
279+
topics, data, err := PackCandidateDeactivationRequestedEvent(cand)
280+
require.NoError(err)
281+
paramsNonIndexed, err := _candidateDeactivationRequestedEvent.Inputs.Unpack(data)
282+
require.NoError(err)
283+
require.Equal(0, len(paramsNonIndexed))
284+
require.Equal(2, len(topics))
285+
require.Equal(hash.Hash256(_candidateDeactivationRequestedEvent.ID), topics[0])
286+
require.Equal(hash.BytesToHash256(cand.Bytes()), topics[1])
287+
})
278288
t.Run("update", func(t *testing.T) {
279289
topics, data, err := PackCandidateUpdatedEvent(cand, operator, owner, name, reward, blsPubKey)
280290
require.NoError(err)

action/native_staking_contract_abi.json

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,64 @@
1818
"name": "CandidateActivated",
1919
"type": "event"
2020
},
21+
{
22+
"anonymous": false,
23+
"inputs": [
24+
{
25+
"indexed": true,
26+
"internalType": "address",
27+
"name": "candidate",
28+
"type": "address"
29+
}
30+
],
31+
"name": "CandidateDeactivationRequested",
32+
"type": "event"
33+
},
34+
{
35+
"anonymous": false,
36+
"inputs": [
37+
{
38+
"indexed": true,
39+
"internalType": "address",
40+
"name": "candidate",
41+
"type": "address"
42+
}
43+
],
44+
"name": "CandidateDeactivationCanceled",
45+
"type": "event"
46+
},
47+
{
48+
"anonymous": false,
49+
"inputs": [
50+
{
51+
"indexed": true,
52+
"internalType": "address",
53+
"name": "candidate",
54+
"type": "address"
55+
},
56+
{
57+
"indexed": false,
58+
"internalType": "uint64",
59+
"name": "blockNumber",
60+
"type": "uint64"
61+
}
62+
],
63+
"name": "CandidateDeactivationScheduled",
64+
"type": "event"
65+
},
66+
{
67+
"anonymous": false,
68+
"inputs": [
69+
{
70+
"indexed": true,
71+
"internalType": "address",
72+
"name": "candidate",
73+
"type": "address"
74+
}
75+
],
76+
"name": "CandidateDeactivated",
77+
"type": "event"
78+
},
2179
{
2280
"anonymous": false,
2381
"inputs": [
@@ -160,6 +218,20 @@
160218
"stateMutability": "nonpayable",
161219
"type": "function"
162220
},
221+
{
222+
"inputs": [],
223+
"name": "requestCandidateDeactivation",
224+
"outputs": [],
225+
"stateMutability": "nonpayable",
226+
"type": "function"
227+
},
228+
{
229+
"inputs": [],
230+
"name": "cancelCandidateDeactivation",
231+
"outputs": [],
232+
"stateMutability": "nonpayable",
233+
"type": "function"
234+
},
163235
{
164236
"inputs": [
165237
{

action/protocol/context.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ type (
166166
CandidateSlashByOwner bool
167167
CandidateBLSPublicKeyNotCopied bool
168168
OnlyOwnerCanUpdateBLSPublicKey bool
169+
NoCandidateExitQueue bool
169170
}
170171

171172
// FeatureWithHeightCtx provides feature check functions.
@@ -335,6 +336,7 @@ func WithFeatureCtx(ctx context.Context) context.Context {
335336
CandidateSlashByOwner: !g.IsXinguBeta(height),
336337
CandidateBLSPublicKeyNotCopied: !g.IsXinguBeta(height),
337338
OnlyOwnerCanUpdateBLSPublicKey: !g.IsToBeEnabled(height),
339+
NoCandidateExitQueue: !g.IsToBeEnabled(height),
338340
},
339341
)
340342
}

0 commit comments

Comments
 (0)