github.com/iotexproject/iotex-core@v1.14.1-rc1/action/stake_create.go (about) 1 // Copyright (c) 2020 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to 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/iotexproject/iotex-proto/golang/iotextypes" 16 "github.com/pkg/errors" 17 "google.golang.org/protobuf/proto" 18 19 "github.com/iotexproject/iotex-core/pkg/util/byteutil" 20 "github.com/iotexproject/iotex-core/pkg/version" 21 ) 22 23 const ( 24 // CreateStakePayloadGas represents the CreateStake payload gas per uint 25 CreateStakePayloadGas = uint64(100) 26 // CreateStakeBaseIntrinsicGas represents the base intrinsic gas for CreateStake 27 CreateStakeBaseIntrinsicGas = uint64(10000) 28 29 _createStakeInterfaceABI = `[ 30 { 31 "inputs": [ 32 { 33 "internalType": "string", 34 "name": "candName", 35 "type": "string" 36 }, 37 { 38 "internalType": "uint256", 39 "name": "amount", 40 "type": "uint256" 41 }, 42 { 43 "internalType": "uint32", 44 "name": "duration", 45 "type": "uint32" 46 }, 47 { 48 "internalType": "bool", 49 "name": "autoStake", 50 "type": "bool" 51 }, 52 { 53 "internalType": "uint8[]", 54 "name": "data", 55 "type": "uint8[]" 56 } 57 ], 58 "name": "createStake", 59 "outputs": [], 60 "stateMutability": "nonpayable", 61 "type": "function" 62 } 63 ]` 64 ) 65 66 var ( 67 // _createStakeMethod is the interface of the abi encoding of stake action 68 _createStakeMethod abi.Method 69 _ EthCompatibleAction = (*CreateStake)(nil) 70 71 errDecodeFailure = errors.New("failed to decode the data") 72 ) 73 74 // CreateStake defines the action of CreateStake creation 75 type CreateStake struct { 76 AbstractAction 77 78 candName string 79 amount *big.Int 80 duration uint32 81 autoStake bool 82 payload []byte 83 } 84 85 func init() { 86 createStakeInterface, err := abi.JSON(strings.NewReader(_createStakeInterfaceABI)) 87 if err != nil { 88 panic(err) 89 } 90 var ok bool 91 _createStakeMethod, ok = createStakeInterface.Methods["createStake"] 92 if !ok { 93 panic("fail to load the method") 94 } 95 } 96 97 // NewCreateStake returns a CreateStake instance 98 func NewCreateStake( 99 nonce uint64, 100 candidateName, amount string, 101 duration uint32, 102 autoStake bool, 103 payload []byte, 104 gasLimit uint64, 105 gasPrice *big.Int, 106 ) (*CreateStake, error) { 107 stake, ok := new(big.Int).SetString(amount, 10) 108 if !ok { 109 return nil, errors.Wrapf(ErrInvalidAmount, "amount %s", amount) 110 } 111 112 return &CreateStake{ 113 AbstractAction: AbstractAction{ 114 version: version.ProtocolVersion, 115 nonce: nonce, 116 gasLimit: gasLimit, 117 gasPrice: gasPrice, 118 }, 119 candName: candidateName, 120 amount: stake, 121 duration: duration, 122 autoStake: autoStake, 123 payload: payload, 124 }, nil 125 } 126 127 // Amount returns the amount 128 func (cs *CreateStake) Amount() *big.Int { return cs.amount } 129 130 // Payload returns the payload bytes 131 func (cs *CreateStake) Payload() []byte { return cs.payload } 132 133 // Candidate returns the candidate name 134 func (cs *CreateStake) Candidate() string { return cs.candName } 135 136 // Duration returns the CreateStaked duration 137 func (cs *CreateStake) Duration() uint32 { return cs.duration } 138 139 // AutoStake returns the flag of AutoStake s 140 func (cs *CreateStake) AutoStake() bool { return cs.autoStake } 141 142 // Serialize returns a raw byte stream of the CreateStake struct 143 func (cs *CreateStake) Serialize() []byte { 144 return byteutil.Must(proto.Marshal(cs.Proto())) 145 } 146 147 // Proto converts to protobuf CreateStake Action 148 func (cs *CreateStake) Proto() *iotextypes.StakeCreate { 149 act := iotextypes.StakeCreate{ 150 CandidateName: cs.candName, 151 StakedDuration: cs.duration, 152 AutoStake: cs.autoStake, 153 } 154 155 if cs.amount != nil { 156 act.StakedAmount = cs.amount.String() 157 } 158 159 if len(cs.payload) > 0 { 160 act.Payload = make([]byte, len(cs.payload)) 161 copy(act.Payload, cs.payload) 162 } 163 return &act 164 } 165 166 // LoadProto converts a protobuf's Action to CreateStake 167 func (cs *CreateStake) LoadProto(pbAct *iotextypes.StakeCreate) error { 168 if pbAct == nil { 169 return ErrNilProto 170 } 171 172 cs.candName = pbAct.GetCandidateName() 173 cs.duration = pbAct.StakedDuration 174 cs.autoStake = pbAct.AutoStake 175 176 if len(pbAct.GetStakedAmount()) > 0 { 177 var ok bool 178 if cs.amount, ok = new(big.Int).SetString(pbAct.StakedAmount, 10); !ok { 179 return errors.Errorf("invalid amount %s", pbAct.StakedAmount) 180 } 181 } 182 183 cs.payload = nil 184 if len(pbAct.Payload) > 0 { 185 cs.payload = make([]byte, len(pbAct.Payload)) 186 copy(cs.payload, pbAct.Payload) 187 } 188 return nil 189 } 190 191 // IntrinsicGas returns the intrinsic gas of a CreateStake 192 func (cs *CreateStake) IntrinsicGas() (uint64, error) { 193 payloadSize := uint64(len(cs.Payload())) 194 return CalculateIntrinsicGas(CreateStakeBaseIntrinsicGas, CreateStakePayloadGas, payloadSize) 195 } 196 197 // Cost returns the total cost of a CreateStake 198 func (cs *CreateStake) Cost() (*big.Int, error) { 199 intrinsicGas, err := cs.IntrinsicGas() 200 if err != nil { 201 return nil, errors.Wrap(err, "failed to get intrinsic gas for the CreateStake creates") 202 } 203 CreateStakeFee := big.NewInt(0).Mul(cs.GasPrice(), big.NewInt(0).SetUint64(intrinsicGas)) 204 return big.NewInt(0).Add(cs.Amount(), CreateStakeFee), nil 205 } 206 207 // SanityCheck validates the variables in the action 208 func (cs *CreateStake) SanityCheck() error { 209 if cs.Amount().Sign() <= 0 { 210 return errors.Wrap(ErrInvalidAmount, "negative value") 211 } 212 if !IsValidCandidateName(cs.candName) { 213 return ErrInvalidCanName 214 } 215 return cs.AbstractAction.SanityCheck() 216 } 217 218 // EncodeABIBinary encodes data in abi encoding 219 func (cs *CreateStake) EncodeABIBinary() ([]byte, error) { 220 return cs.encodeABIBinary() 221 } 222 223 func (cs *CreateStake) encodeABIBinary() ([]byte, error) { 224 data, err := _createStakeMethod.Inputs.Pack(cs.candName, cs.amount, cs.duration, cs.autoStake, cs.payload) 225 if err != nil { 226 return nil, err 227 } 228 return append(_createStakeMethod.ID, data...), nil 229 } 230 231 // NewCreateStakeFromABIBinary decodes data into createStake action 232 func NewCreateStakeFromABIBinary(data []byte) (*CreateStake, error) { 233 var ( 234 paramsMap = map[string]interface{}{} 235 ok bool 236 cs CreateStake 237 ) 238 // sanity check 239 if len(data) <= 4 || !bytes.Equal(_createStakeMethod.ID, data[:4]) { 240 return nil, errDecodeFailure 241 } 242 if err := _createStakeMethod.Inputs.UnpackIntoMap(paramsMap, data[4:]); err != nil { 243 return nil, err 244 } 245 if cs.candName, ok = paramsMap["candName"].(string); !ok { 246 return nil, errDecodeFailure 247 } 248 if cs.amount, ok = paramsMap["amount"].(*big.Int); !ok { 249 return nil, errDecodeFailure 250 } 251 if cs.duration, ok = paramsMap["duration"].(uint32); !ok { 252 return nil, errDecodeFailure 253 } 254 if cs.autoStake, ok = paramsMap["autoStake"].(bool); !ok { 255 return nil, errDecodeFailure 256 } 257 if cs.payload, ok = paramsMap["data"].([]byte); !ok { 258 return nil, errDecodeFailure 259 } 260 return &cs, nil 261 } 262 263 // ToEthTx converts action to eth-compatible tx 264 func (cs *CreateStake) ToEthTx(_ uint32) (*types.Transaction, error) { 265 data, err := cs.encodeABIBinary() 266 if err != nil { 267 return nil, err 268 } 269 return types.NewTx(&types.LegacyTx{ 270 Nonce: cs.Nonce(), 271 GasPrice: cs.GasPrice(), 272 Gas: cs.GasLimit(), 273 To: &_stakingProtocolEthAddr, 274 Value: big.NewInt(0), 275 Data: data, 276 }), nil 277 }