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  }