github.com/iotexproject/iotex-core@v1.14.1-rc1/action/stake_restake.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/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 // RestakePayloadGas represents the Restake payload gas per uint 26 RestakePayloadGas = uint64(100) 27 // RestakeBaseIntrinsicGas represents the base intrinsic gas for stake again 28 RestakeBaseIntrinsicGas = uint64(10000) 29 30 _restakeInterfaceABI = `[ 31 { 32 "inputs": [ 33 { 34 "internalType": "uint64", 35 "name": "bucketIndex", 36 "type": "uint64" 37 }, 38 { 39 "internalType": "uint32", 40 "name": "duration", 41 "type": "uint32" 42 }, 43 { 44 "internalType": "bool", 45 "name": "autoStake", 46 "type": "bool" 47 }, 48 { 49 "internalType": "uint8[]", 50 "name": "data", 51 "type": "uint8[]" 52 } 53 ], 54 "name": "restake", 55 "outputs": [], 56 "stateMutability": "nonpayable", 57 "type": "function" 58 } 59 ]` 60 ) 61 62 var ( 63 // _restakeMethod is the interface of the abi encoding of stake action 64 _restakeMethod abi.Method 65 _ EthCompatibleAction = (*Restake)(nil) 66 ) 67 68 // Restake defines the action of stake again 69 type Restake struct { 70 AbstractAction 71 72 bucketIndex uint64 73 duration uint32 74 autoStake bool 75 payload []byte 76 } 77 78 func init() { 79 restakeInterface, err := abi.JSON(strings.NewReader(_restakeInterfaceABI)) 80 if err != nil { 81 panic(err) 82 } 83 var ok bool 84 _restakeMethod, ok = restakeInterface.Methods["restake"] 85 if !ok { 86 panic("fail to load the method") 87 } 88 } 89 90 // NewRestake returns a Restake instance 91 func NewRestake( 92 nonce uint64, 93 index uint64, 94 duration uint32, 95 autoStake bool, 96 payload []byte, 97 gasLimit uint64, 98 gasPrice *big.Int, 99 ) (*Restake, error) { 100 return &Restake{ 101 AbstractAction: AbstractAction{ 102 version: version.ProtocolVersion, 103 nonce: nonce, 104 gasLimit: gasLimit, 105 gasPrice: gasPrice, 106 }, 107 bucketIndex: index, 108 duration: duration, 109 autoStake: autoStake, 110 payload: payload, 111 }, nil 112 } 113 114 // Payload returns the payload bytes 115 func (rs *Restake) Payload() []byte { return rs.payload } 116 117 // BucketIndex returns bucket index 118 func (rs *Restake) BucketIndex() uint64 { return rs.bucketIndex } 119 120 // Duration returns the updated duration 121 func (rs *Restake) Duration() uint32 { return rs.duration } 122 123 // AutoStake returns the autoStake boolean 124 func (rs *Restake) AutoStake() bool { return rs.autoStake } 125 126 // Serialize returns a raw byte stream of the Stake again struct 127 func (rs *Restake) Serialize() []byte { 128 return byteutil.Must(proto.Marshal(rs.Proto())) 129 } 130 131 // Proto converts to protobuf Restake Action 132 func (rs *Restake) Proto() *iotextypes.StakeRestake { 133 act := &iotextypes.StakeRestake{ 134 BucketIndex: rs.bucketIndex, 135 Payload: rs.payload, 136 StakedDuration: rs.duration, 137 AutoStake: rs.autoStake, 138 } 139 return act 140 } 141 142 // LoadProto converts a protobuf's Action to Restake 143 func (rs *Restake) LoadProto(pbAct *iotextypes.StakeRestake) error { 144 if pbAct == nil { 145 return ErrNilProto 146 } 147 148 rs.bucketIndex = pbAct.GetBucketIndex() 149 rs.payload = pbAct.GetPayload() 150 rs.duration = pbAct.GetStakedDuration() 151 rs.autoStake = pbAct.GetAutoStake() 152 153 return nil 154 } 155 156 // IntrinsicGas returns the intrinsic gas of a Restake 157 func (rs *Restake) IntrinsicGas() (uint64, error) { 158 payloadSize := uint64(len(rs.Payload())) 159 return CalculateIntrinsicGas(RestakeBaseIntrinsicGas, RestakePayloadGas, payloadSize) 160 } 161 162 // Cost returns the total cost of a Restake 163 func (rs *Restake) Cost() (*big.Int, error) { 164 intrinsicGas, err := rs.IntrinsicGas() 165 if err != nil { 166 return nil, errors.Wrap(err, "failed to get intrinsic gas for the stake creates") 167 } 168 restakeFee := big.NewInt(0).Mul(rs.GasPrice(), big.NewInt(0).SetUint64(intrinsicGas)) 169 return restakeFee, nil 170 } 171 172 // EncodeABIBinary encodes data in abi encoding 173 func (rs *Restake) EncodeABIBinary() ([]byte, error) { 174 return rs.encodeABIBinary() 175 } 176 177 func (rs *Restake) encodeABIBinary() ([]byte, error) { 178 data, err := _restakeMethod.Inputs.Pack(rs.bucketIndex, rs.duration, rs.autoStake, rs.payload) 179 if err != nil { 180 return nil, err 181 } 182 return append(_restakeMethod.ID, data...), nil 183 } 184 185 // NewRestakeFromABIBinary decodes data into Restake action 186 func NewRestakeFromABIBinary(data []byte) (*Restake, error) { 187 var ( 188 paramsMap = map[string]interface{}{} 189 ok bool 190 rs Restake 191 ) 192 // sanity check 193 if len(data) <= 4 || !bytes.Equal(_restakeMethod.ID, data[:4]) { 194 return nil, errDecodeFailure 195 } 196 if err := _restakeMethod.Inputs.UnpackIntoMap(paramsMap, data[4:]); err != nil { 197 return nil, err 198 } 199 if rs.bucketIndex, ok = paramsMap["bucketIndex"].(uint64); !ok { 200 return nil, errDecodeFailure 201 } 202 if rs.duration, ok = paramsMap["duration"].(uint32); !ok { 203 return nil, errDecodeFailure 204 } 205 if rs.autoStake, ok = paramsMap["autoStake"].(bool); !ok { 206 return nil, errDecodeFailure 207 } 208 if rs.payload, ok = paramsMap["data"].([]byte); !ok { 209 return nil, errDecodeFailure 210 } 211 return &rs, nil 212 } 213 214 // ToEthTx converts action to eth-compatible tx 215 func (rs *Restake) ToEthTx(_ uint32) (*types.Transaction, error) { 216 data, err := rs.encodeABIBinary() 217 if err != nil { 218 return nil, err 219 } 220 return types.NewTx(&types.LegacyTx{ 221 Nonce: rs.Nonce(), 222 GasPrice: rs.GasPrice(), 223 Gas: rs.GasLimit(), 224 To: &_stakingProtocolEthAddr, 225 Value: big.NewInt(0), 226 Data: data, 227 }), nil 228 }