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 }