github.com/iotexproject/iotex-core@v1.14.1-rc1/action/candidate_endorsement.go (about) 1 package action 2 3 import ( 4 "bytes" 5 "math/big" 6 "strings" 7 8 "github.com/ethereum/go-ethereum/accounts/abi" 9 "github.com/ethereum/go-ethereum/core/types" 10 "github.com/iotexproject/iotex-proto/golang/iotextypes" 11 "github.com/pkg/errors" 12 13 "github.com/iotexproject/iotex-core/pkg/version" 14 ) 15 16 const ( 17 // CandidateEndorsementBaseIntrinsicGas represents the base intrinsic gas for CandidateEndorsement 18 CandidateEndorsementBaseIntrinsicGas = uint64(10000) 19 20 candidateEndorsementInterfaceABI = `[ 21 { 22 "inputs": [ 23 { 24 "internalType": "uint64", 25 "name": "bucketIndex", 26 "type": "uint64" 27 }, 28 { 29 "internalType": "bool", 30 "name": "endorse", 31 "type": "bool" 32 } 33 ], 34 "name": "candidateEndorsement", 35 "outputs": [], 36 "stateMutability": "nonpayable", 37 "type": "function" 38 } 39 ]` 40 ) 41 42 var ( 43 candidateEndorsementMethod abi.Method 44 ) 45 46 // CandidateEndorsement is the action to endorse or unendorse a candidate 47 type CandidateEndorsement struct { 48 AbstractAction 49 50 // bucketIndex is the bucket index want to be endorsed or unendorsed 51 bucketIndex uint64 52 // endorse is true if the action is to endorse a candidate, false if unendorse 53 endorse bool 54 } 55 56 func init() { 57 candidateEndorsementInterface, err := abi.JSON(strings.NewReader(candidateEndorsementInterfaceABI)) 58 if err != nil { 59 panic(err) 60 } 61 var ok bool 62 candidateEndorsementMethod, ok = candidateEndorsementInterface.Methods["candidateEndorsement"] 63 if !ok { 64 panic("fail to load the candidateEndorsement method") 65 } 66 } 67 68 // BucketIndex returns the bucket index of the action 69 func (act *CandidateEndorsement) BucketIndex() uint64 { 70 return act.bucketIndex 71 } 72 73 // IsEndorse returns true if the action is to endorse a candidate 74 func (act *CandidateEndorsement) IsEndorse() bool { 75 return act.endorse 76 } 77 78 // IntrinsicGas returns the intrinsic gas of a CandidateEndorsement 79 func (act *CandidateEndorsement) IntrinsicGas() (uint64, error) { 80 return CandidateEndorsementBaseIntrinsicGas, nil 81 } 82 83 // Cost returns the total cost of a CandidateEndorsement 84 func (act *CandidateEndorsement) Cost() (*big.Int, error) { 85 intrinsicGas, err := act.IntrinsicGas() 86 if err != nil { 87 return nil, errors.Wrap(err, "failed to get intrinsic gas for the CandidateEndorsement") 88 } 89 fee := big.NewInt(0).Mul(act.GasPrice(), big.NewInt(0).SetUint64(intrinsicGas)) 90 return fee, nil 91 } 92 93 // Proto converts CandidateEndorsement to protobuf's Action 94 func (act *CandidateEndorsement) Proto() *iotextypes.CandidateEndorsement { 95 return &iotextypes.CandidateEndorsement{ 96 BucketIndex: act.bucketIndex, 97 Endorse: act.endorse, 98 } 99 } 100 101 // LoadProto converts a protobuf's Action to CandidateEndorsement 102 func (act *CandidateEndorsement) LoadProto(pbAct *iotextypes.CandidateEndorsement) error { 103 if pbAct == nil { 104 return ErrNilProto 105 } 106 act.bucketIndex = pbAct.GetBucketIndex() 107 act.endorse = pbAct.GetEndorse() 108 return nil 109 } 110 111 func (act *CandidateEndorsement) encodeABIBinary() ([]byte, error) { 112 data, err := candidateEndorsementMethod.Inputs.Pack(act.bucketIndex, act.endorse) 113 if err != nil { 114 return nil, err 115 } 116 return append(candidateEndorsementMethod.ID, data...), nil 117 } 118 119 // ToEthTx returns an Ethereum transaction which corresponds to this action 120 func (act *CandidateEndorsement) ToEthTx(_ uint32) (*types.Transaction, error) { 121 data, err := act.encodeABIBinary() 122 if err != nil { 123 return nil, err 124 } 125 return types.NewTx(&types.LegacyTx{ 126 Nonce: act.Nonce(), 127 GasPrice: act.GasPrice(), 128 Gas: act.GasLimit(), 129 To: &_stakingProtocolEthAddr, 130 Value: big.NewInt(0), 131 Data: data, 132 }), nil 133 } 134 135 // NewCandidateEndorsement returns a CandidateEndorsement action 136 func NewCandidateEndorsement(nonce, gasLimit uint64, gasPrice *big.Int, bucketIndex uint64, endorse bool) *CandidateEndorsement { 137 return &CandidateEndorsement{ 138 AbstractAction: AbstractAction{ 139 version: version.ProtocolVersion, 140 nonce: nonce, 141 gasLimit: gasLimit, 142 gasPrice: gasPrice, 143 }, 144 bucketIndex: bucketIndex, 145 endorse: endorse, 146 } 147 } 148 149 // NewCandidateEndorsementFromABIBinary parses the smart contract input and creates an action 150 func NewCandidateEndorsementFromABIBinary(data []byte) (*CandidateEndorsement, error) { 151 var ( 152 paramsMap = map[string]any{} 153 cr CandidateEndorsement 154 ) 155 // sanity check 156 if len(data) <= 4 || !bytes.Equal(candidateEndorsementMethod.ID, data[:4]) { 157 return nil, errDecodeFailure 158 } 159 if err := candidateEndorsementMethod.Inputs.UnpackIntoMap(paramsMap, data[4:]); err != nil { 160 return nil, err 161 } 162 bucketID, ok := paramsMap["bucketIndex"].(uint64) 163 if !ok { 164 return nil, errDecodeFailure 165 } 166 endorse, ok := paramsMap["endorse"].(bool) 167 if !ok { 168 return nil, errDecodeFailure 169 } 170 cr.bucketIndex = bucketID 171 cr.endorse = endorse 172 return &cr, nil 173 }