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  }