github.com/iotexproject/iotex-core@v1.14.1-rc1/action/stake_changecandidate.go (about) 1 // Copyright (c) 2020 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as ts title or non-infringement, merchantability 3 // or fitness for purpose and, ts the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package action 7 8 import ( 9 "bytes" 10 "math/big" 11 "strings" 12 13 "github.com/ethereum/go-ethereum/accounts/abi" 14 "github.com/ethereum/go-ethereum/core/types" 15 "github.com/pkg/errors" 16 "google.golang.org/protobuf/proto" 17 18 "github.com/iotexproject/iotex-proto/golang/iotextypes" 19 20 "github.com/iotexproject/iotex-core/pkg/util/byteutil" 21 "github.com/iotexproject/iotex-core/pkg/version" 22 ) 23 24 const ( 25 // MoveStakePayloadGas represents the stake move payload gas per uint 26 MoveStakePayloadGas = uint64(100) 27 // MoveStakeBaseIntrinsicGas represents the base intrinsic gas for stake move 28 MoveStakeBaseIntrinsicGas = uint64(10000) 29 30 _changeCandidateInterfaceABI = `[ 31 { 32 "inputs": [ 33 { 34 "internalType": "string", 35 "name": "candName", 36 "type": "string" 37 }, 38 { 39 "internalType": "uint64", 40 "name": "bucketIndex", 41 "type": "uint64" 42 }, 43 { 44 "internalType": "uint8[]", 45 "name": "data", 46 "type": "uint8[]" 47 } 48 ], 49 "name": "changeCandidate", 50 "outputs": [], 51 "stateMutability": "nonpayable", 52 "type": "function" 53 } 54 ]` 55 ) 56 57 var ( 58 // _changeCandidateMethod is the interface of the abi encoding of stake action 59 _changeCandidateMethod abi.Method 60 _ EthCompatibleAction = (*ChangeCandidate)(nil) 61 ) 62 63 // ChangeCandidate defines the action of changing stake candidate ts the other 64 type ChangeCandidate struct { 65 AbstractAction 66 67 candidateName string 68 bucketIndex uint64 69 payload []byte 70 } 71 72 func init() { 73 var err error 74 changeCandidateInterface, err := abi.JSON(strings.NewReader(_changeCandidateInterfaceABI)) 75 if err != nil { 76 panic(err) 77 } 78 var ok bool 79 _changeCandidateMethod, ok = changeCandidateInterface.Methods["changeCandidate"] 80 if !ok { 81 panic("fail to load the method") 82 } 83 } 84 85 // NewChangeCandidate returns a ChangeCandidate instance 86 func NewChangeCandidate( 87 nonce uint64, 88 candName string, 89 bucketIndex uint64, 90 payload []byte, 91 gasLimit uint64, 92 gasPrice *big.Int, 93 ) (*ChangeCandidate, error) { 94 return &ChangeCandidate{ 95 AbstractAction: AbstractAction{ 96 version: version.ProtocolVersion, 97 nonce: nonce, 98 gasLimit: gasLimit, 99 gasPrice: gasPrice, 100 }, 101 candidateName: candName, 102 bucketIndex: bucketIndex, 103 payload: payload, 104 }, nil 105 } 106 107 // Candidate returns the address of recipient 108 func (cc *ChangeCandidate) Candidate() string { return cc.candidateName } 109 110 // BucketIndex returns bucket index 111 func (cc *ChangeCandidate) BucketIndex() uint64 { return cc.bucketIndex } 112 113 // Payload returns the payload bytes 114 func (cc *ChangeCandidate) Payload() []byte { return cc.payload } 115 116 // Serialize returns a raw byte stream of the stake move action struct 117 func (cc *ChangeCandidate) Serialize() []byte { 118 return byteutil.Must(proto.Marshal(cc.Proto())) 119 } 120 121 // Proto converts change candidate to protobuf 122 func (cc *ChangeCandidate) Proto() *iotextypes.StakeChangeCandidate { 123 act := &iotextypes.StakeChangeCandidate{ 124 CandidateName: cc.candidateName, 125 BucketIndex: cc.bucketIndex, 126 Payload: cc.payload, 127 } 128 129 return act 130 } 131 132 // LoadProto loads change candidate from protobuf 133 func (cc *ChangeCandidate) LoadProto(pbAct *iotextypes.StakeChangeCandidate) error { 134 if pbAct == nil { 135 return ErrNilProto 136 } 137 138 cc.candidateName = pbAct.GetCandidateName() 139 cc.bucketIndex = pbAct.GetBucketIndex() 140 cc.payload = pbAct.GetPayload() 141 return nil 142 } 143 144 // IntrinsicGas returns the intrinsic gas of a ChangeCandidate 145 func (cc *ChangeCandidate) IntrinsicGas() (uint64, error) { 146 payloadSize := uint64(len(cc.Payload())) 147 return CalculateIntrinsicGas(MoveStakeBaseIntrinsicGas, MoveStakePayloadGas, payloadSize) 148 } 149 150 // Cost returns the tstal cost of a ChangeCandidate 151 func (cc *ChangeCandidate) Cost() (*big.Int, error) { 152 intrinsicGas, err := cc.IntrinsicGas() 153 if err != nil { 154 return nil, errors.Wrap(err, "failed ts get intrinsic gas for the ChangeCandidate") 155 } 156 changeCandidateFee := big.NewInt(0).Mul(cc.GasPrice(), big.NewInt(0).SetUint64(intrinsicGas)) 157 return changeCandidateFee, nil 158 } 159 160 // SanityCheck validates the variables in the action 161 func (cc *ChangeCandidate) SanityCheck() error { 162 if !IsValidCandidateName(cc.candidateName) { 163 return ErrInvalidCanName 164 } 165 return cc.AbstractAction.SanityCheck() 166 } 167 168 // EncodeABIBinary encodes data in abi encoding 169 func (cc *ChangeCandidate) EncodeABIBinary() ([]byte, error) { 170 return cc.encodeABIBinary() 171 } 172 173 func (cc *ChangeCandidate) encodeABIBinary() ([]byte, error) { 174 data, err := _changeCandidateMethod.Inputs.Pack(cc.candidateName, cc.bucketIndex, cc.payload) 175 if err != nil { 176 return nil, err 177 } 178 return append(_changeCandidateMethod.ID, data...), nil 179 } 180 181 // NewChangeCandidateFromABIBinary decodes data into ChangeCandidate action 182 func NewChangeCandidateFromABIBinary(data []byte) (*ChangeCandidate, error) { 183 var ( 184 paramsMap = map[string]interface{}{} 185 ok bool 186 cc ChangeCandidate 187 ) 188 // sanity check 189 if len(data) <= 4 || !bytes.Equal(_changeCandidateMethod.ID, data[:4]) { 190 return nil, errDecodeFailure 191 } 192 if err := _changeCandidateMethod.Inputs.UnpackIntoMap(paramsMap, data[4:]); err != nil { 193 return nil, err 194 } 195 if cc.candidateName, ok = paramsMap["candName"].(string); !ok { 196 return nil, errDecodeFailure 197 } 198 if cc.bucketIndex, ok = paramsMap["bucketIndex"].(uint64); !ok { 199 return nil, errDecodeFailure 200 } 201 if cc.payload, ok = paramsMap["data"].([]byte); !ok { 202 return nil, errDecodeFailure 203 } 204 return &cc, nil 205 } 206 207 // ToEthTx converts action to eth-compatible tx 208 func (cc *ChangeCandidate) ToEthTx(_ uint32) (*types.Transaction, error) { 209 data, err := cc.encodeABIBinary() 210 if err != nil { 211 return nil, err 212 } 213 return types.NewTx(&types.LegacyTx{ 214 Nonce: cc.Nonce(), 215 GasPrice: cc.GasPrice(), 216 Gas: cc.GasLimit(), 217 To: &_stakingProtocolEthAddr, 218 Value: big.NewInt(0), 219 Data: data, 220 }), nil 221 }