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