github.com/amazechain/amc@v0.1.3/internal/api/transaction_args.go (about)

     1  // Copyright 2022 The AmazeChain Authors
     2  // This file is part of the AmazeChain library.
     3  //
     4  // The AmazeChain library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The AmazeChain library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the AmazeChain library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package api
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"errors"
    23  	"fmt"
    24  	"github.com/amazechain/amc/log"
    25  	"github.com/holiman/uint256"
    26  	"math/big"
    27  
    28  	"github.com/amazechain/amc/common/block"
    29  	"github.com/amazechain/amc/common/hexutil"
    30  	"github.com/amazechain/amc/common/math"
    31  	"github.com/amazechain/amc/common/transaction"
    32  	"github.com/amazechain/amc/common/types"
    33  	mvm_common "github.com/amazechain/amc/internal/avm/common"
    34  	mvm_types "github.com/amazechain/amc/internal/avm/types"
    35  	"github.com/amazechain/amc/modules/rpc/jsonrpc"
    36  )
    37  
    38  // TransactionArgs represents
    39  type TransactionArgs struct {
    40  	From                 *mvm_common.Address `json:"from"`
    41  	To                   *mvm_common.Address `json:"to"`
    42  	Gas                  *hexutil.Uint64     `json:"gas"`
    43  	GasPrice             *hexutil.Big        `json:"gasPrice"`
    44  	MaxFeePerGas         *hexutil.Big        `json:"maxFeePerGas"`
    45  	MaxPriorityFeePerGas *hexutil.Big        `json:"maxPriorityFeePerGas"`
    46  	Value                *hexutil.Big        `json:"value"`
    47  	Nonce                *hexutil.Uint64     `json:"nonce"`
    48  
    49  	Data  *hexutil.Bytes `json:"data"`
    50  	Input *hexutil.Bytes `json:"input"`
    51  
    52  	// Introduced by AccessListTxType transaction.
    53  	AccessList *mvm_types.AccessList `json:"accessList,omitempty"`
    54  	ChainID    *hexutil.Big          `json:"chainId,omitempty"`
    55  }
    56  
    57  // RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction
    58  type RPCTransaction struct {
    59  	BlockHash        *mvm_common.Hash      `json:"blockHash"`
    60  	BlockNumber      *hexutil.Big          `json:"blockNumber"`
    61  	From             mvm_common.Address    `json:"from"`
    62  	Gas              hexutil.Uint64        `json:"gas"`
    63  	GasPrice         *hexutil.Big          `json:"gasPrice"`
    64  	GasFeeCap        *hexutil.Big          `json:"maxFeePerGas,omitempty"`
    65  	GasTipCap        *hexutil.Big          `json:"maxPriorityFeePerGas,omitempty"`
    66  	Hash             mvm_common.Hash       `json:"hash"`
    67  	Input            hexutil.Bytes         `json:"input"`
    68  	Nonce            hexutil.Uint64        `json:"nonce"`
    69  	To               *mvm_common.Address   `json:"to"`
    70  	TransactionIndex *hexutil.Uint64       `json:"transactionIndex"`
    71  	Value            *hexutil.Big          `json:"value"`
    72  	Type             hexutil.Uint64        `json:"type"`
    73  	Accesses         *mvm_types.AccessList `json:"accessList,omitempty"`
    74  	ChainID          *hexutil.Big          `json:"chainId,omitempty"`
    75  	V                *hexutil.Big          `json:"v"`
    76  	R                *hexutil.Big          `json:"r"`
    77  	S                *hexutil.Big          `json:"s"`
    78  }
    79  
    80  // from retrieves the transaction sender address.
    81  func (args *TransactionArgs) from() types.Address {
    82  	if args.From == nil {
    83  		return types.Address{}
    84  	}
    85  	from := *mvm_types.ToAmcAddress(args.From)
    86  	return from
    87  }
    88  
    89  // data retrieves the transaction calldata. Input field is preferred.
    90  func (args *TransactionArgs) data() []byte {
    91  	if args.Input != nil {
    92  		return *args.Input
    93  	}
    94  	if args.Data != nil {
    95  		return *args.Data
    96  	}
    97  	return nil
    98  }
    99  
   100  // setDefaults
   101  func (args *TransactionArgs) setDefaults(ctx context.Context, api *API) error {
   102  	//if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
   103  	//	return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
   104  	//}
   105  	if err := args.setFeeDefaults(ctx, api); err != nil {
   106  		return err
   107  	}
   108  
   109  	if args.Value == nil {
   110  		args.Value = new(hexutil.Big)
   111  	}
   112  	if args.Nonce == nil {
   113  		nonce := api.TxsPool().Nonce(args.from())
   114  		args.Nonce = (*hexutil.Uint64)(&nonce)
   115  	}
   116  	if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) {
   117  		return errors.New(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`)
   118  	}
   119  	if args.To == nil && len(args.data()) == 0 {
   120  		return errors.New(`contract creation without any data provided`)
   121  	}
   122  	if args.Gas == nil {
   123  		data := args.data()
   124  		callArgs := TransactionArgs{
   125  			From:                 args.From,
   126  			To:                   args.To,
   127  			GasPrice:             args.GasPrice,
   128  			MaxFeePerGas:         args.MaxFeePerGas,
   129  			MaxPriorityFeePerGas: args.MaxPriorityFeePerGas,
   130  			Value:                args.Value,
   131  			Data:                 (*hexutil.Bytes)(&data),
   132  		}
   133  		pendingBlockNr := jsonrpc.BlockNumberOrHashWithNumber(jsonrpc.PendingBlockNumber)
   134  		//todo gasCap
   135  		estimated, err := DoEstimateGas(ctx, api, callArgs, pendingBlockNr, 50000000)
   136  		if err != nil {
   137  			return err
   138  		}
   139  		args.Gas = &estimated
   140  	}
   141  	if args.ChainID == nil {
   142  		id := (*hexutil.Big)(api.GetChainConfig().ChainID)
   143  		args.ChainID = id
   144  	}
   145  	return nil
   146  }
   147  
   148  // ToMessage to evm message
   149  func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (transaction.Message, error) {
   150  	//header := api.BlockChain().CurrentBlock().Header().(*block.Header)
   151  	//signer := transaction.MakeSigner(api.chainConfig, header.Number.ToBig())
   152  	//args.setDefaults(context.Background(), api)
   153  	//return args.toTransaction().AsMessage(signer, header.BaseFee)
   154  	// msg := mvm_types.AsMessage(, header.BaseFee.ToBig(), true)
   155  
   156  	// Reject invalid combinations of pre- and post-1559 fee styles
   157  	if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
   158  		return transaction.Message{}, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
   159  	}
   160  	// Set sender address or use zero address if none specified.
   161  	addr := args.from()
   162  
   163  	// Set default gas & gas price if none were set
   164  	gas := globalGasCap
   165  	if gas == 0 {
   166  		gas = uint64(math.MaxUint64 / 2)
   167  	}
   168  	if args.Gas != nil {
   169  		gas = uint64(*args.Gas)
   170  	}
   171  	if globalGasCap != 0 && globalGasCap < gas {
   172  		log.Warn("Caller gas above allowance, capping", "requested", gas, "cap", globalGasCap)
   173  		gas = globalGasCap
   174  	}
   175  	var (
   176  		gasPrice  *big.Int
   177  		gasFeeCap *big.Int
   178  		gasTipCap *big.Int
   179  	)
   180  	if baseFee == nil {
   181  		// If there's no basefee, then it must be a non-1559 execution
   182  		gasPrice = new(big.Int)
   183  		if args.GasPrice != nil {
   184  			gasPrice = args.GasPrice.ToInt()
   185  		}
   186  		gasFeeCap, gasTipCap = gasPrice, gasPrice
   187  	} else {
   188  		// A basefee is provided, necessitating 1559-type execution
   189  		if args.GasPrice != nil {
   190  			// User specified the legacy gas field, convert to 1559 gas typing
   191  			gasPrice = args.GasPrice.ToInt()
   192  			gasFeeCap, gasTipCap = gasPrice, gasPrice
   193  		} else {
   194  			// User specified 1559 gas fields (or none), use those
   195  			gasFeeCap = new(big.Int)
   196  			if args.MaxFeePerGas != nil {
   197  				gasFeeCap = args.MaxFeePerGas.ToInt()
   198  			}
   199  			gasTipCap = new(big.Int)
   200  			if args.MaxPriorityFeePerGas != nil {
   201  				gasTipCap = args.MaxPriorityFeePerGas.ToInt()
   202  			}
   203  			// Backfill the legacy gasPrice for EVM execution, unless we're all zeroes
   204  			gasPrice = new(big.Int)
   205  			if gasFeeCap.BitLen() > 0 || gasTipCap.BitLen() > 0 {
   206  				gasPrice = math.BigMin(new(big.Int).Add(gasTipCap, baseFee), gasFeeCap)
   207  			}
   208  		}
   209  	}
   210  	value := new(big.Int)
   211  	if args.Value != nil {
   212  		value = args.Value.ToInt()
   213  	}
   214  	data := args.data()
   215  	var accessList transaction.AccessList
   216  	if args.AccessList != nil {
   217  		accessList = mvm_types.ToAmcAccessList(*args.AccessList)
   218  	}
   219  	val, is1 := uint256.FromBig(value)
   220  	gp, is2 := uint256.FromBig(gasPrice)
   221  	gfc, is3 := uint256.FromBig(gasFeeCap)
   222  	gtc, is4 := uint256.FromBig(gasTipCap)
   223  	if is1 || is2 || is3 || is4 {
   224  		return transaction.Message{}, fmt.Errorf("args.Value higher than 2^256-1")
   225  	}
   226  	msg := transaction.NewMessage(addr, mvm_types.ToAmcAddress(args.To), 0, val, gas, gp, gfc, gtc, data, accessList, false, true)
   227  	return msg, nil
   228  
   229  }
   230  
   231  // toTransaction assemble Transaction
   232  func (args *TransactionArgs) toTransaction() *transaction.Transaction {
   233  	var data transaction.TxData
   234  	switch {
   235  	case args.MaxFeePerGas != nil:
   236  		al := transaction.AccessList{}
   237  		if args.AccessList != nil {
   238  			al = mvm_types.ToAmcAccessList(*args.AccessList)
   239  		}
   240  		dy := &transaction.DynamicFeeTx{
   241  			To:         mvm_types.ToAmcAddress(args.To),
   242  			Nonce:      uint64(*args.Nonce),
   243  			Gas:        uint64(*args.Gas),
   244  			Data:       args.data(),
   245  			AccessList: al,
   246  		}
   247  		var is bool
   248  		dy.GasFeeCap, is = uint256.FromBig((*big.Int)(args.MaxFeePerGas))
   249  		if is {
   250  			log.Error("GasFeeCap to uint256 failed")
   251  		}
   252  		dy.ChainID, is = uint256.FromBig((*big.Int)(args.ChainID))
   253  		if is {
   254  			log.Error("ChainID to uint256 failed")
   255  		}
   256  		dy.GasTipCap, is = uint256.FromBig((*big.Int)(args.MaxPriorityFeePerGas))
   257  		if is {
   258  			log.Error("GasTipCap to uint256 failed")
   259  		}
   260  		dy.Value, is = uint256.FromBig((*big.Int)(args.Value))
   261  		if is {
   262  			log.Error("Value to uint256 failed")
   263  		}
   264  		data = dy
   265  	case args.AccessList != nil:
   266  		alt := &transaction.AccessListTx{
   267  			To:    mvm_types.ToAmcAddress(args.To),
   268  			Nonce: uint64(*args.Nonce),
   269  			Gas:   uint64(*args.Gas),
   270  			Data:  args.data(),
   271  		}
   272  		alt.AccessList = mvm_types.ToAmcAccessList(*args.AccessList)
   273  		var is bool
   274  		alt.GasPrice, is = uint256.FromBig((*big.Int)(args.GasPrice))
   275  		if is {
   276  			log.Error("GasPrice to uint256 failed")
   277  		}
   278  		alt.ChainID, is = uint256.FromBig((*big.Int)(args.ChainID))
   279  		if is {
   280  			log.Error("ChainID to uint256 failed")
   281  		}
   282  		alt.Value, is = uint256.FromBig((*big.Int)(args.Value))
   283  		if is {
   284  			log.Error("Value to uint256 failed")
   285  		}
   286  		data = alt
   287  	default:
   288  		lt := &transaction.LegacyTx{
   289  			To:    mvm_types.ToAmcAddress(args.To),
   290  			Nonce: uint64(*args.Nonce),
   291  			Gas:   uint64(*args.Gas),
   292  			Data:  args.data(),
   293  		}
   294  		var is bool
   295  		lt.GasPrice, is = uint256.FromBig((*big.Int)(args.GasPrice))
   296  		if is {
   297  			log.Error("GasPrice to uint256 failed")
   298  		}
   299  		lt.Value, is = uint256.FromBig((*big.Int)(args.Value))
   300  		if is {
   301  			log.Error("Value to uint256 failed")
   302  		}
   303  		data = lt
   304  	}
   305  	return transaction.NewTx(data)
   306  }
   307  
   308  // ToTransaction
   309  //func (args *TransactionArgs) ToTransaction() *transaction.Transaction {
   310  //	return args.toTransaction()
   311  //}
   312  
   313  // LrpLegacyTx is the transaction data of regular Ethereum transactions.
   314  
   315  // newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation
   316  func newRPCPendingTransaction(tx *transaction.Transaction, current block.IHeader) *RPCTransaction {
   317  	blockNumber := uint64(0)
   318  	//todo baseFee
   319  	return newRPCTransaction(tx, types.Hash{}, blockNumber, 0, big.NewInt(baseFee))
   320  }
   321  
   322  // newRPCTransaction returns a transaction that will serialize to the RPC
   323  // representation, with the given location metadata set (if available).
   324  func newRPCTransaction(tx *transaction.Transaction, blockHash types.Hash, blockNumber uint64, index uint64, baseFee *big.Int) *RPCTransaction {
   325  
   326  	v, r, s := tx.RawSignatureValues()
   327  	from := tx.From()
   328  	hash := tx.Hash()
   329  	result := &RPCTransaction{
   330  		Type:     hexutil.Uint64(tx.Type()),
   331  		From:     *mvm_types.FromAmcAddress(from),
   332  		Gas:      hexutil.Uint64(tx.Gas()),
   333  		GasPrice: (*hexutil.Big)(tx.GasPrice().ToBig()),
   334  		Hash:     mvm_types.FromAmcHash(hash),
   335  		Input:    hexutil.Bytes(tx.Data()),
   336  		Nonce:    hexutil.Uint64(tx.Nonce()),
   337  		To:       mvm_types.FromAmcAddress(tx.To()),
   338  		Value:    (*hexutil.Big)(tx.Value().ToBig()),
   339  		V:        (*hexutil.Big)(v.ToBig()),
   340  		R:        (*hexutil.Big)(r.ToBig()),
   341  		S:        (*hexutil.Big)(s.ToBig()),
   342  	}
   343  	if blockHash != (types.Hash{}) {
   344  		hash := mvm_types.FromAmcHash(blockHash)
   345  		result.BlockHash = &hash
   346  		result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
   347  		result.TransactionIndex = (*hexutil.Uint64)(&index)
   348  	}
   349  	switch tx.Type() {
   350  	case transaction.LegacyTxType:
   351  		// if a legacy transaction has an EIP-155 chain id, include it explicitly
   352  		if id := tx.ChainId(); id.Sign() != 0 {
   353  			result.ChainID = (*hexutil.Big)(id.ToBig())
   354  		}
   355  	case transaction.AccessListTxType:
   356  		// todo copy al
   357  		//al := tx.AccessList()
   358  		//result.Accesses = &al
   359  		result.ChainID = (*hexutil.Big)(tx.ChainId().ToBig())
   360  	case transaction.DynamicFeeTxType:
   361  		// todo copy al
   362  		//al := tx.AccessList()
   363  		//result.Accesses = &al
   364  		result.ChainID = (*hexutil.Big)(tx.ChainId().ToBig())
   365  		result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap().ToBig())
   366  		result.GasTipCap = (*hexutil.Big)(tx.GasTipCap().ToBig())
   367  		// if the transaction has been mined, compute the effective gas price
   368  		if baseFee != nil && blockHash != (types.Hash{}) {
   369  			// price = min(tip, gasFeeCap - baseFee) + baseFee
   370  			price := math.BigMin(new(big.Int).Add(tx.GasTipCap().ToBig(), baseFee), tx.GasFeeCap().ToBig())
   371  			result.GasPrice = (*hexutil.Big)(price)
   372  		} else {
   373  			result.GasPrice = (*hexutil.Big)(tx.GasFeeCap().ToBig())
   374  		}
   375  	}
   376  	return result
   377  }
   378  
   379  // setFeeDefaults fills in default fee values for unspecified tx fields.
   380  func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b *API) error {
   381  	// If both gasPrice and at least one of the EIP-1559 fee parameters are specified, error.
   382  	if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
   383  		return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
   384  	}
   385  	// If the tx has completely specified a fee mechanism, no default is needed. This allows users
   386  	// who are not yet synced past London to get defaults for other tx values. See
   387  	// https://github.com/ethereum/go-ethereum/pull/23274 for more information.
   388  	eip1559ParamsSet := args.MaxFeePerGas != nil && args.MaxPriorityFeePerGas != nil
   389  	if (args.GasPrice != nil && !eip1559ParamsSet) || (args.GasPrice == nil && eip1559ParamsSet) {
   390  		// Sanity check the EIP-1559 fee parameters if present.
   391  		if args.GasPrice == nil && args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 {
   392  			return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas)
   393  		}
   394  		return nil
   395  	}
   396  	// Now attempt to fill in default value depending on whether London is active or not.
   397  	head := b.BlockChain().CurrentBlock()
   398  	if b.chainConfig.IsLondon(head.Number64().Uint64()) {
   399  		// London is active, set maxPriorityFeePerGas and maxFeePerGas.
   400  		if err := args.setLondonFeeDefaults(ctx, head.Header().(*block.Header), b); err != nil {
   401  			return err
   402  		}
   403  	} else {
   404  		if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil {
   405  			return fmt.Errorf("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active")
   406  		}
   407  		// London not active, set gas price.
   408  		price, err := b.gpo.SuggestTipCap(ctx, b.chainConfig)
   409  		if err != nil {
   410  			return err
   411  		}
   412  		args.GasPrice = (*hexutil.Big)(price)
   413  	}
   414  	return nil
   415  }
   416  
   417  // setLondonFeeDefaults fills in reasonable default fee values for unspecified fields.
   418  func (args *TransactionArgs) setLondonFeeDefaults(ctx context.Context, head *block.Header, b *API) error {
   419  	// Set maxPriorityFeePerGas if it is missing.
   420  	if args.MaxPriorityFeePerGas == nil {
   421  		tip, err := b.gpo.SuggestTipCap(ctx, b.chainConfig)
   422  		if err != nil {
   423  			return err
   424  		}
   425  		args.MaxPriorityFeePerGas = (*hexutil.Big)(tip)
   426  	}
   427  	// Set maxFeePerGas if it is missing.
   428  	if args.MaxFeePerGas == nil {
   429  		// Set the max fee to be 2 times larger than the previous block's base fee.
   430  		// The additional slack allows the tx to not become invalidated if the base
   431  		// fee is rising.
   432  		val := new(big.Int).Add(
   433  			args.MaxPriorityFeePerGas.ToInt(),
   434  			new(big.Int).Mul(head.BaseFee.ToBig(), big.NewInt(2)),
   435  		)
   436  		args.MaxFeePerGas = (*hexutil.Big)(val)
   437  	}
   438  	// Both EIP-1559 fee parameters are now set; sanity check them.
   439  	if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 {
   440  		return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas)
   441  	}
   442  	return nil
   443  }