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  }