From f297ee7d36796c53947cf40ab90fc85816767e79 Mon Sep 17 00:00:00 2001 From: trinhdn97 Date: Tue, 10 Dec 2024 16:00:02 +0700 Subject: [PATCH 1/6] Return a close-enough amount of gas instead of the exact number --- ethapi/api.go | 127 ++++++++++++++++++++++--------------- ethapi/transaction_args.go | 2 +- 2 files changed, 78 insertions(+), 51 deletions(-) diff --git a/ethapi/api.go b/ethapi/api.go index a24c7540..0981b2b7 100644 --- a/ethapi/api.go +++ b/ethapi/api.go @@ -1033,11 +1033,17 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash if state == nil || err != nil { return nil, err } + + return doCall(ctx, b, args, state, header, overrides, timeout, globalGasCap) +} + +func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *evmcore.EvmHeader, + overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*evmcore.ExecutionResult, error) { if err := overrides.Apply(state); err != nil { return nil, err } // Setup context so it may be cancelled the call has completed - // or, in case of unmetered gas, setup a context with a timeout. + // or, in case of unmetered gas, set up a context with a timeout. var cancel context.CancelFunc if timeout > 0 { ctx, cancel = context.WithTimeout(ctx, timeout) @@ -1131,13 +1137,30 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, bl return result.Return(), result.Err } -// DoEstimateGas - binary search the gas requirement, as it may be higher than the amount used -func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, gasCap uint64) (hexutil.Uint64, error) { - // Binary search the gas requirement, as it may be higher than the amount used +// executeEstimate is a helper that executes the transaction under a given gas limit and returns +// true if the transaction fails for a reason that might be related to not enough gas. A non-nil +// error means execution failed due to reasons unrelated to the gas limit. +func executeEstimate(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *evmcore.EvmHeader, gasCap uint64, gasLimit uint64) (bool, *evmcore.ExecutionResult, error) { + args.Gas = (*hexutil.Uint64)(&gasLimit) + result, err := doCall(ctx, b, args, state, header, nil, 0, gasCap) + if err != nil { + if errors.Is(err, core.ErrIntrinsicGas) { + return true, nil, nil // Special case, raise gas limit + } + return true, nil, err // Bail out + } + return result.Failed(), result, nil +} + +// DoEstimateGas returns the lowest possible gas limit that allows the transaction to run +// successfully at block `blockNrOrHash`. It returns error if the transaction would revert, or if +// there are unexpected failures. The gas limit is capped by both `args.Gas` (if non-nil & +// non-zero) and `gasCap` (if non-zero). +func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, gasCap uint64) (hexutil.Uint64, error) { + // Binary search the gas limit, as it may need to be higher than the amount used var ( - lo uint64 = params.TxGas - 1 - hi uint64 - cap uint64 + lo uint64 // lowest-known gas limit where tx execution fails + hi uint64 // lowest-known gas limit where tx execution succeeds ) // Use zero address if sender unspecified. if args.From == nil { @@ -1160,17 +1183,22 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr } else { feeCap = common.Big0 } + + state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + if state == nil || err != nil { + return 0, err + } + if err := overrides.Apply(state); err != nil { + return 0, err + } + // Recap the highest gas limit with account's available balance. if feeCap.BitLen() != 0 { - state, _, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) - if state == nil || err != nil { - return 0, err - } balance := state.GetBalance(*args.From) // from can't be nil available := new(big.Int).Set(balance) if args.Value != nil { if args.Value.ToInt().Cmp(available) >= 0 { - return 0, errors.New("insufficient funds for transfer") + return 0, core.ErrInsufficientFundsForTransfer } available.Sub(available, args.Value.ToInt()) } @@ -1192,30 +1220,42 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr log.Warn("Caller gas above allowance, capping", "requested", hi, "cap", gasCap) hi = gasCap } - cap = hi - - // Create a helper to check if a gas allowance results in an executable transaction - executable := func(gas uint64) (bool, *evmcore.ExecutionResult, error) { - args.Gas = (*hexutil.Uint64)(&gas) - result, err := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap) - if err != nil { - if errors.Is(err, evmcore.ErrIntrinsicGas) { - return true, nil, nil // Special case, raise gas limit + // We first execute the transaction at the highest allowable gas limit, since if this fails we + // can return error immediately. + failed, result, err := executeEstimate(ctx, b, args, state.Copy(), header, gasCap, hi) + if err != nil { + return 0, err + } + if failed { + if result != nil && result.Err != vm.ErrOutOfGas { + if len(result.Revert()) > 0 { + return 0, newRevertError(result) } - return true, nil, err // Bail out + return 0, result.Err } - return result.Failed(), result, nil + return 0, fmt.Errorf("gas required exceeds allowance (%d)", hi) } - // Execute the binary search and hone in on an executable gas limit + // For almost any transaction, the gas consumed by the unconstrained execution above + // lower-bounds the gas limit required for it to succeed. One exception is those txs that + // explicitly check gas remaining in order to successfully execute within a given limit, but we + // probably don't want to return a lowest possible gas limit for these cases anyway. + lo = result.UsedGas - 1 + + // Binary search for the smallest gas limit that allows the tx to execute successfully. for lo+1 < hi { mid := (hi + lo) / 2 - failed, _, err := executable(mid) - - // If the error is not nil(consensus error), it means the provided message - // call or transaction will never be accepted no matter how much gas it is - // assigned. Return the error directly, don't struggle any more. + if mid > lo*2 { + // Most txs don't need much higher gas limit than their gas used, and most txs don't + // require near the full block limit of gas, so the selection of where to bisect the + // range here is skewed to favor the low side. + mid = lo * 2 + } + failed, _, err = executeEstimate(ctx, b, args, state.Copy(), header, gasCap, mid) if err != nil { + // This should not happen under normal conditions since if we make it this far the + // transaction had run without error at least once before. + log.Error("execution error in estimate gas", "err", err) return 0, err } if failed { @@ -1224,34 +1264,21 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr hi = mid } } - // Reject the transaction as invalid if it still fails at the highest allowance - if hi == cap { - failed, result, err := executable(hi) - if err != nil { - return 0, err - } - if failed { - if result != nil && result.Err != vm.ErrOutOfGas { - if len(result.Revert()) > 0 { - return 0, newRevertError(result) - } - return 0, result.Err - } - // Otherwise, the specified gas cap is too low - return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap) - } - } return hexutil.Uint64(hi), nil } -// EstimateGas returns an estimate of the amount of gas needed to execute the -// given transaction against the current pending block. -func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash) (hexutil.Uint64, error) { +// EstimateGas returns the lowest possible gas limit that allows the transaction to run +// successfully at block `blockNrOrHash`, or the latest block if `blockNrOrHash` is unspecified. It +// returns error if the transaction would revert or if there are unexpected failures. The returned +// value is capped by both `args.Gas` (if non-nil & non-zero) and the backend's RPCGasCap +// configuration (if non-zero). +func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, + overrides *StateOverride) (hexutil.Uint64, error) { bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) if blockNrOrHash != nil { bNrOrHash = *blockNrOrHash } - return DoEstimateGas(ctx, s.b, args, bNrOrHash, s.b.RPCGasCap()) + return DoEstimateGas(ctx, s.b, args, bNrOrHash, overrides, s.b.RPCGasCap()) } // ExecutionResult groups all structured logs emitted by the EVM diff --git a/ethapi/transaction_args.go b/ethapi/transaction_args.go index 5275afe2..eea043cb 100644 --- a/ethapi/transaction_args.go +++ b/ethapi/transaction_args.go @@ -148,7 +148,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { AccessList: args.AccessList, } pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) - estimated, err := DoEstimateGas(ctx, b, callArgs, pendingBlockNr, b.RPCGasCap()) + estimated, err := DoEstimateGas(ctx, b, callArgs, pendingBlockNr, nil, b.RPCGasCap()) if err != nil { return err } From 315124a463c641652a8fd70b138eeee4928d6690 Mon Sep 17 00:00:00 2001 From: trinhdn97 Date: Tue, 10 Dec 2024 16:04:47 +0700 Subject: [PATCH 2/6] Use go-u2u/evmcore --- ethapi/api.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ethapi/api.go b/ethapi/api.go index 0981b2b7..d81f8321 100644 --- a/ethapi/api.go +++ b/ethapi/api.go @@ -29,7 +29,6 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/syndtr/goleveldb/leveldb/opt" - "github.com/unicornultrafoundation/go-helios/hash" "github.com/unicornultrafoundation/go-helios/native/idx" @@ -1144,7 +1143,7 @@ func executeEstimate(ctx context.Context, b Backend, args TransactionArgs, state args.Gas = (*hexutil.Uint64)(&gasLimit) result, err := doCall(ctx, b, args, state, header, nil, 0, gasCap) if err != nil { - if errors.Is(err, core.ErrIntrinsicGas) { + if errors.Is(err, evmcore.ErrIntrinsicGas) { return true, nil, nil // Special case, raise gas limit } return true, nil, err // Bail out @@ -1198,7 +1197,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr available := new(big.Int).Set(balance) if args.Value != nil { if args.Value.ToInt().Cmp(available) >= 0 { - return 0, core.ErrInsufficientFundsForTransfer + return 0, evmcore.ErrInsufficientFundsForTransfer } available.Sub(available, args.Value.ToInt()) } From 13501fbfeb1fc2881fea22feb1c7c9b2f1d7e7ef Mon Sep 17 00:00:00 2001 From: trinhdn97 Date: Wed, 11 Dec 2024 18:25:33 +0700 Subject: [PATCH 3/6] Less iterations of eth_estimateGas --- core/types/transaction.go | 23 +-- eth/gasestimator/gasestimator.go | 240 +++++++++++++++++++++++++++++++ ethapi/api.go | 142 +++++------------- ethapi/transaction_args.go | 58 +++++++- evmcore/state_transition.go | 25 ++-- 5 files changed, 358 insertions(+), 130 deletions(-) create mode 100644 eth/gasestimator/gasestimator.go diff --git a/core/types/transaction.go b/core/types/transaction.go index f58baa92..f32e76d8 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -638,14 +638,15 @@ func (tx *Transaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) { return msg, err } -func (m Message) From() common.Address { return m.from } -func (m Message) To() *common.Address { return m.to } -func (m Message) GasPrice() *big.Int { return m.gasPrice } -func (m Message) GasFeeCap() *big.Int { return m.gasFeeCap } -func (m Message) GasTipCap() *big.Int { return m.gasTipCap } -func (m Message) Value() *big.Int { return m.amount } -func (m Message) Gas() uint64 { return m.gasLimit } -func (m Message) Nonce() uint64 { return m.nonce } -func (m Message) Data() []byte { return m.data } -func (m Message) AccessList() AccessList { return m.accessList } -func (m Message) IsFake() bool { return m.isFake } +func (m Message) From() common.Address { return m.from } +func (m Message) To() *common.Address { return m.to } +func (m Message) GasPrice() *big.Int { return m.gasPrice } +func (m Message) GasFeeCap() *big.Int { return m.gasFeeCap } +func (m Message) GasTipCap() *big.Int { return m.gasTipCap } +func (m Message) Value() *big.Int { return m.amount } +func (m Message) Gas() uint64 { return m.gasLimit } +func (m Message) Nonce() uint64 { return m.nonce } +func (m Message) Data() []byte { return m.data } +func (m Message) AccessList() AccessList { return m.accessList } +func (m Message) IsFake() bool { return m.isFake } +func (m Message) SetGasLimit(gasLimit uint64) { m.gasLimit = gasLimit } diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go new file mode 100644 index 00000000..5d2aae49 --- /dev/null +++ b/eth/gasestimator/gasestimator.go @@ -0,0 +1,240 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package gasestimator + +import ( + "context" + "errors" + "fmt" + "math" + "math/big" + + "github.com/unicornultrafoundation/go-u2u/common" + "github.com/unicornultrafoundation/go-u2u/core/state" + "github.com/unicornultrafoundation/go-u2u/core/types" + "github.com/unicornultrafoundation/go-u2u/core/vm" + "github.com/unicornultrafoundation/go-u2u/evmcore" + "github.com/unicornultrafoundation/go-u2u/log" + "github.com/unicornultrafoundation/go-u2u/params" +) + +// Options are the contextual parameters to execute the requested call. +// +// Whilst it would be possible to pass a blockchain object that aggregates all +// these together, it would be excessively hard to test. Splitting the parts out +// allows testing without needing a proper live chain. +type Options struct { + Config *params.ChainConfig // Chain configuration for hard fork selection + Chain evmcore.DummyChain // Chain context to access past block hashes + Header *evmcore.EvmHeader // Header defining the block context to execute in + State *state.StateDB // Pre-state on top of which to estimate the gas + + ErrorRatio float64 // Allowed an overestimation ratio for faster estimation termination +} + +// Estimate returns the lowest possible gas limit that allows the transaction to +// run successfully with the provided context options. It returns an error if the +// transaction always reverts, or if there are unexpected failures. +func Estimate(ctx context.Context, call *types.Message, opts *Options, gasCap uint64) (uint64, []byte, error) { + // Binary search the gas limit, as it may need to be higher than the amount used + var ( + lo uint64 // lowest-known gas limit where tx execution fails + hi uint64 // lowest-known gas limit where tx execution succeeds + ) + // Determine the highest gas limit can be used during the estimation. + hi = opts.Header.GasLimit + if call.Gas() >= params.TxGas { + hi = call.Gas() + } + // Normalize the max fee per gas the call is willing to spend. + var feeCap *big.Int + if call.GasFeeCap() != nil { + feeCap = call.GasFeeCap() + } else if call.GasPrice() != nil { + feeCap = call.GasPrice() + } else { + feeCap = common.Big0 + } + // Recap the highest gas limit with account's available balance. + if feeCap.BitLen() != 0 { + balance := opts.State.GetBalance(call.From()) + + available := balance + if call.Value() != nil { + if call.Value().Cmp(available) >= 0 { + return 0, nil, evmcore.ErrInsufficientFundsForTransfer + } + available.Sub(available, call.Value()) + } + allowance := new(big.Int).Div(available, feeCap) + + // If the allowance is larger than maximum uint64, skip checking + if allowance.IsUint64() && hi > allowance.Uint64() { + transfer := call.Value() + if transfer == nil { + transfer = new(big.Int) + } + log.Debug("Gas estimation capped by limited funds", "original", hi, "balance", balance, + "sent", transfer, "maxFeePerGas", feeCap, "fundable", allowance) + hi = allowance.Uint64() + } + } + // Recap the highest gas allowance with specified gas cap. + if gasCap != 0 && hi > gasCap { + log.Debug("Caller gas above allowance, capping", "requested", hi, "cap", gasCap) + hi = gasCap + } + // If the transaction is a plain value transfer, short circuit estimation and + // directly try 21000. Returning 21000 without any execution is dangerous as + // some tx field combos might bump the price up even for plain transfers (e.g. + // unused access list items). Ever so slightly wasteful, but safer overall. + if len(call.Data()) == 0 { + if call.To() != nil && opts.State.GetCodeSize(*call.To()) == 0 { + failed, _, err := execute(ctx, call, opts, params.TxGas) + if !failed && err == nil { + return params.TxGas, nil, nil + } + } + } + // We first execute the transaction at the highest allowable gas limit, since if this fails we + // can return error immediately. + failed, result, err := execute(ctx, call, opts, hi) + if err != nil { + return 0, nil, err + } + if failed { + if result != nil && !errors.Is(result.Err, vm.ErrOutOfGas) { + return 0, result.Revert(), result.Err + } + return 0, nil, fmt.Errorf("gas required exceeds allowance (%d)", hi) + } + // For almost any transaction, the gas consumed by the unconstrained execution + // above lower-bounds the gas limit required for it to succeed. One exception + // is those that explicitly check gas remaining in order to execute within a + // given limit, but we probably don't want to return the lowest possible gas + // limit for these cases anyway. + lo = result.UsedGas - 1 + + // There's a fairly high chance for the transaction to execute successfully + // with gasLimit set to the first execution's usedGas + gasRefund. Explicitly + // check that gas amount and use as a limit for the binary search. + optimisticGasLimit := (result.UsedGas + result.RefundedGas + params.CallStipend) * 64 / 63 + if optimisticGasLimit < hi { + failed, _, err = execute(ctx, call, opts, optimisticGasLimit) + if err != nil { + // This should not happen under normal conditions since if we make it this far the + // transaction had run without an error at least once before. + log.Error("Execution error in estimate gas", "err", err) + return 0, nil, err + } + if failed { + lo = optimisticGasLimit + } else { + hi = optimisticGasLimit + } + } + // Binary search for the smallest gas limit that allows the tx to execute successfully. + for lo+1 < hi { + if opts.ErrorRatio > 0 { + // It is a bit pointless to return a perfect estimation, as changing + // network conditions require the caller to bump it up anyway. Since + // wallets tend to use 20-25% bump, allowing a small approximation + // error is fine (as long as it's upwards). + if float64(hi-lo)/float64(hi) < opts.ErrorRatio { + break + } + } + mid := (hi + lo) / 2 + if mid > lo*2 { + // Most txs don't need that much higher gas limit than their gas used. Most txs don't + // require near the full block limit of gas, so the selection of where to bisect the + // range here is skewed to favor the low side. + mid = lo * 2 + } + failed, _, err = execute(ctx, call, opts, mid) + if err != nil { + // This should not happen under normal conditions since if we make it this far the + // transaction had run without an error at least once before. + log.Error("Execution error in estimate gas", "err", err) + return 0, nil, err + } + if failed { + lo = mid + } else { + hi = mid + } + } + return hi, nil, nil +} + +// execute is a helper that executes the transaction under a given gas limit and +// returns true if the transaction fails for a reason that might be related to +// not enough gas. A non-nil error means execution failed due to reasons unrelated +// to the gas limit. +func execute(ctx context.Context, call *types.Message, opts *Options, gasLimit uint64) (bool, *evmcore.ExecutionResult, error) { + // Configure the call for this specific execution (and revert the change after) + defer func(gas uint64) { call.SetGasLimit(gas) }(call.Gas()) + call.SetGasLimit(gasLimit) + + // Execute the call and separate execution faults caused by a lack of gas or + // other non-fixable conditions + result, err := run(ctx, call, opts) + if err != nil { + if errors.Is(err, evmcore.ErrIntrinsicGas) { + return true, nil, nil // Special case, raise gas limit + } + return true, nil, err // Bail out + } + return result.Failed(), result, nil +} + +// run assembles the EVM as defined by the consensus rules and runs the requested +// call invocation. +func run(ctx context.Context, call *types.Message, opts *Options) (*evmcore.ExecutionResult, error) { + // Assemble the call and the call context + var ( + msgContext = evmcore.NewEVMTxContext(call) + evmContext = evmcore.NewEVMBlockContext(opts.Header, opts.Chain, nil) + + dirtyState = opts.State.Copy() + ) + // Lower the base fee to 0 to avoid breaking EVM + // invariants (basefee < feecap). + if msgContext.GasPrice.Sign() == 0 { + evmContext.BaseFee = new(big.Int) + } + evm := vm.NewEVM(evmContext, msgContext, dirtyState, opts.Config, vm.Config{NoBaseFee: true}) + // Monitor the outer context and interrupt the EVM upon cancellation. To avoid + // a dangling goroutine until the outer estimation finishes, create an internal + // context for the lifetime of this method call. + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + go func() { + <-ctx.Done() + evm.Cancel() + }() + // Execute the call, returning a wrapped error or the result + result, err := evmcore.ApplyMessage(evm, call, new(evmcore.GasPool).AddGas(math.MaxUint64)) + if vmerr := dirtyState.Error(); vmerr != nil { + return nil, vmerr + } + if err != nil { + return result, fmt.Errorf("failed with %d gas: %w", call.Gas(), err) + } + return result, nil +} diff --git a/ethapi/api.go b/ethapi/api.go index d81f8321..bc61fd5f 100644 --- a/ethapi/api.go +++ b/ethapi/api.go @@ -42,6 +42,7 @@ import ( "github.com/unicornultrafoundation/go-u2u/core/types" "github.com/unicornultrafoundation/go-u2u/core/vm" "github.com/unicornultrafoundation/go-u2u/crypto" + "github.com/unicornultrafoundation/go-u2u/eth/gasestimator" "github.com/unicornultrafoundation/go-u2u/eth/tracers" "github.com/unicornultrafoundation/go-u2u/evmcore" "github.com/unicornultrafoundation/go-u2u/gossip/gasprice" @@ -67,6 +68,10 @@ const ( // and reexecute to produce missing historical state necessary to run a specific // trace. defaultTraceReexec = uint64(128) + + // estimateGasErrorRatio is the amount of overestimation eth_estimateGas is + // allowed to produce in order to speed up calculations. + estimateGasErrorRatio = 0.015 ) var ( @@ -1088,15 +1093,17 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S return result, nil } -func newRevertError(result *evmcore.ExecutionResult) *revertError { - reason, errUnpack := abi.UnpackRevert(result.Revert()) - err := errors.New("execution reverted") +// newRevertError creates a revertError instance with the provided revert data. +func newRevertError(revert []byte) *revertError { + err := vm.ErrExecutionReverted + + reason, errUnpack := abi.UnpackRevert(revert) if errUnpack == nil { - err = fmt.Errorf("execution reverted: %v", reason) + err = fmt.Errorf("%w: %v", vm.ErrExecutionReverted, reason) } return &revertError{ error: err, - reason: hexutil.Encode(result.Revert()), + reason: hexutil.Encode(revert), } } @@ -1131,7 +1138,7 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, bl } // If the result contains a revert reason, try to unpack and return it. if len(result.Revert()) > 0 { - return nil, newRevertError(result) + return nil, newRevertError(result.Revert()) } return result.Return(), result.Err } @@ -1152,37 +1159,13 @@ func executeEstimate(ctx context.Context, b Backend, args TransactionArgs, state } // DoEstimateGas returns the lowest possible gas limit that allows the transaction to run -// successfully at block `blockNrOrHash`. It returns error if the transaction would revert, or if -// there are unexpected failures. The gas limit is capped by both `args.Gas` (if non-nil & +// successfully at block `blockNrOrHash`. +// It returns error if the transaction reverts, or if +// there are unexpected failures. +// The gas limit is capped by both `args.Gas` (if non-nil & // non-zero) and `gasCap` (if non-zero). func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, gasCap uint64) (hexutil.Uint64, error) { - // Binary search the gas limit, as it may need to be higher than the amount used - var ( - lo uint64 // lowest-known gas limit where tx execution fails - hi uint64 // lowest-known gas limit where tx execution succeeds - ) - // Use zero address if sender unspecified. - if args.From == nil { - args.From = new(common.Address) - } - // Determine the highest gas limit can be used during the estimation. - if args.Gas != nil && uint64(*args.Gas) >= params.TxGas { - hi = uint64(*args.Gas) - } else { - hi = b.MaxGasLimit() - } - // Normalize the max fee per gas the call is willing to spend. - var feeCap *big.Int - if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { - return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") - } else if args.GasPrice != nil { - feeCap = args.GasPrice.ToInt() - } else if args.MaxFeePerGas != nil { - feeCap = args.MaxFeePerGas.ToInt() - } else { - feeCap = common.Big0 - } - + // Retrieve the base state and mutate it with any overrides state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { return 0, err @@ -1190,80 +1173,33 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr if err := overrides.Apply(state); err != nil { return 0, err } - - // Recap the highest gas limit with account's available balance. - if feeCap.BitLen() != 0 { - balance := state.GetBalance(*args.From) // from can't be nil - available := new(big.Int).Set(balance) - if args.Value != nil { - if args.Value.ToInt().Cmp(available) >= 0 { - return 0, evmcore.ErrInsufficientFundsForTransfer - } - available.Sub(available, args.Value.ToInt()) - } - allowance := new(big.Int).Div(available, feeCap) - - // If the allowance is larger than maximum uint64, skip checking - if allowance.IsUint64() && hi > allowance.Uint64() { - transfer := args.Value - if transfer == nil { - transfer = new(hexutil.Big) - } - log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance, - "sent", transfer.ToInt(), "maxFeePerGas", feeCap, "fundable", allowance) - hi = allowance.Uint64() - } + // Construct the gas estimator option from the user input + opts := &gasestimator.Options{ + Config: b.ChainConfig(), + Chain: NewChainContext(ctx, b), + Header: header, + State: state, + ErrorRatio: estimateGasErrorRatio, } - // Recap the highest gas allowance with specified gascap. - if gasCap != 0 && hi > gasCap { - log.Warn("Caller gas above allowance, capping", "requested", hi, "cap", gasCap) - hi = gasCap + // Set any required transaction default, but make sure the gas cap itself is not messed with + // if it was not specified in the original argument list. + if args.Gas == nil { + args.Gas = new(hexutil.Uint64) } - - // We first execute the transaction at the highest allowable gas limit, since if this fails we - // can return error immediately. - failed, result, err := executeEstimate(ctx, b, args, state.Copy(), header, gasCap, hi) - if err != nil { + if err := args.CallDefaults(gasCap, header.BaseFee, b.ChainConfig().ChainID); err != nil { return 0, err } - if failed { - if result != nil && result.Err != vm.ErrOutOfGas { - if len(result.Revert()) > 0 { - return 0, newRevertError(result) - } - return 0, result.Err - } - return 0, fmt.Errorf("gas required exceeds allowance (%d)", hi) - } - // For almost any transaction, the gas consumed by the unconstrained execution above - // lower-bounds the gas limit required for it to succeed. One exception is those txs that - // explicitly check gas remaining in order to successfully execute within a given limit, but we - // probably don't want to return a lowest possible gas limit for these cases anyway. - lo = result.UsedGas - 1 - - // Binary search for the smallest gas limit that allows the tx to execute successfully. - for lo+1 < hi { - mid := (hi + lo) / 2 - if mid > lo*2 { - // Most txs don't need much higher gas limit than their gas used, and most txs don't - // require near the full block limit of gas, so the selection of where to bisect the - // range here is skewed to favor the low side. - mid = lo * 2 - } - failed, _, err = executeEstimate(ctx, b, args, state.Copy(), header, gasCap, mid) - if err != nil { - // This should not happen under normal conditions since if we make it this far the - // transaction had run without error at least once before. - log.Error("execution error in estimate gas", "err", err) - return 0, err - } - if failed { - lo = mid - } else { - hi = mid + call, _ := args.ToMessage(gasCap, header.BaseFee) + + // Run the gas estimation and wrap any revert reason into a custom return + estimate, revert, err := gasestimator.Estimate(ctx, &call, opts, gasCap) + if err != nil { + if len(revert) > 0 { + return 0, newRevertError(revert) } + return 0, err } - return hexutil.Uint64(hi), nil + return hexutil.Uint64(estimate), nil } // EstimateGas returns the lowest possible gas limit that allows the transaction to run diff --git a/ethapi/transaction_args.go b/ethapi/transaction_args.go index eea043cb..bd2e6d91 100644 --- a/ethapi/transaction_args.go +++ b/ethapi/transaction_args.go @@ -82,7 +82,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { } // After london, default to 1559 unless gasPrice is set head := b.CurrentBlock().Header() - // If user specifies both maxPriorityfee and maxFee, then we do not + // If user specifies both maxPriorityFee and maxFee, then we do not // need to consult the chain for defaults. It's definitely a London tx. if args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil { // In this clause, user left some fields unspecified. @@ -162,14 +162,60 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { return nil } +// CallDefaults sanitizes the transaction arguments, often filling in zero values, +// for the purpose of eth_call class of RPC methods. +func (args *TransactionArgs) CallDefaults(globalGasCap uint64, baseFee *big.Int, chainID *big.Int) error { + // Reject invalid combinations of pre- and post-1559 fee styles + if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { + return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") + } + if args.ChainID == nil { + args.ChainID = (*hexutil.Big)(chainID) + } else { + if have := (*big.Int)(args.ChainID); have.Cmp(chainID) != 0 { + return fmt.Errorf("chainId does not match node's (have=%v, want=%v)", have, chainID) + } + } + if args.Gas == nil { + gas := globalGasCap + if gas == 0 { + gas = uint64(math.MaxUint64 / 2) + } + args.Gas = (*hexutil.Uint64)(&gas) + } else { + if globalGasCap > 0 && globalGasCap < uint64(*args.Gas) { + log.Warn("Caller gas above allowance, capping", "requested", args.Gas, "cap", globalGasCap) + args.Gas = (*hexutil.Uint64)(&globalGasCap) + } + } + if args.Nonce == nil { + args.Nonce = new(hexutil.Uint64) + } + if args.Value == nil { + args.Value = new(hexutil.Big) + } + if baseFee == nil { + // If there's no basefee, then it must be a non-1559 execution + if args.GasPrice == nil { + args.GasPrice = new(hexutil.Big) + } + } else { + // A basefee is provided, requiring 1559-type execution + if args.MaxFeePerGas == nil { + args.MaxFeePerGas = new(hexutil.Big) + } + if args.MaxPriorityFeePerGas == nil { + args.MaxPriorityFeePerGas = new(hexutil.Big) + } + } + + return nil +} + // ToMessage converts the transaction arguments to the Message type used by the // core evm. This method is used in calls and traces that do not require a real // live transaction. func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (types.Message, error) { - // Reject invalid combinations of pre- and post-1559 fee styles - if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { - return types.Message{}, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") - } // Set sender address or use zero address if none specified. addr := args.from() @@ -198,7 +244,7 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (t } gasFeeCap, gasTipCap = gasPrice, gasPrice } else { - // A basefee is provided, necessitating 1559-type execution + // A basefee is provided, requiring 1559-type execution if args.GasPrice != nil { // User specified the legacy gas field, convert to 1559 gas typing gasPrice = args.GasPrice.ToInt() diff --git a/evmcore/state_transition.go b/evmcore/state_transition.go index 1e6dabce..2fba64ec 100644 --- a/evmcore/state_transition.go +++ b/evmcore/state_transition.go @@ -79,12 +79,13 @@ type Message interface { AccessList() types.AccessList } -// ExecutionResult includes all output after executing given evm +// ExecutionResult includes all output after executing given an evm // message no matter the execution itself is successful or not. type ExecutionResult struct { - UsedGas uint64 // Total used gas but include the refunded gas - Err error // Any error encountered during the execution(listed in core/vm/errors.go) - ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode) + UsedGas uint64 // Total used gas but include the refunded gas + RefundedGas uint64 // Total gas refunded after execution + Err error // Any error encountered during the execution (listed in core/vm/errors.go) + ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode) } // Unwrap returns the internal evm error which allows us for further @@ -298,22 +299,24 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { st.gas -= st.gas / 10 } + var gasRefund uint64 if !london { // Before EIP-3529: refunds were capped to gasUsed / 2 - st.refundGas(params.RefundQuotient) + gasRefund = st.refundGas(params.RefundQuotient) } else { // After EIP-3529: refunds are capped to gasUsed / 5 - st.refundGas(params.RefundQuotientEIP3529) + gasRefund = st.refundGas(params.RefundQuotientEIP3529) } return &ExecutionResult{ - UsedGas: st.gasUsed(), - Err: vmerr, - ReturnData: ret, + UsedGas: st.gasUsed(), + RefundedGas: gasRefund, + Err: vmerr, + ReturnData: ret, }, nil } -func (st *StateTransition) refundGas(refundQuotient uint64) { +func (st *StateTransition) refundGas(refundQuotient uint64) uint64 { // Apply refund counter, capped to a refund quotient refund := st.gasUsed() / refundQuotient if refund > st.state.GetRefund() { @@ -328,6 +331,8 @@ func (st *StateTransition) refundGas(refundQuotient uint64) { // Also return remaining gas to the block gas counter so it is // available for the next transaction. st.gp.AddGas(st.gas) + + return refund } // gasUsed returns the amount of gas used up by the state transition. From 29b18974445a463964d8741e8b47fc6925bb9248 Mon Sep 17 00:00:00 2001 From: trinhdn97 Date: Wed, 11 Dec 2024 19:00:47 +0700 Subject: [PATCH 4/6] Fix unit test --- cmd/u2u/launcher/testdata/txtracer_test.js | 2 +- cmd/u2u/launcher/txtracer_test.go | 4 ++-- ethapi/api.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/u2u/launcher/testdata/txtracer_test.js b/cmd/u2u/launcher/testdata/txtracer_test.js index ae9b6f41..b53d15ad 100644 --- a/cmd/u2u/launcher/testdata/txtracer_test.js +++ b/cmd/u2u/launcher/testdata/txtracer_test.js @@ -1,7 +1,7 @@ var a1 = personal.newAccount(""); personal.unlockAccount(a1, "", 300); admin.sleep(1); -var tx = u2u.sendTransaction({from:u2u.accounts[0], to:a1, value:"10000000000000000000"}); +var tx = u2u.sendTransaction({from:u2u.accounts[0], to:a1, value:"10000000000000000000", gas:"4700000"}); admin.sleep(1); var abi = [{"inputs":[],"name":"deploy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getA","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_a","type":"uint256"}],"name":"setA","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_a","type":"uint256"}],"name":"setInA","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tst","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]; var bytecode = "0x608060405234801561001057600080fd5b50610589806100206000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c8063775c300c1461005c57806391888f2e146100a6578063d46300fd146100f0578063ea87f3731461010e578063ee919d501461013c575b600080fd5b61006461016a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100ae61032a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100f8610350565b6040518082815260200191505060405180910390f35b61013a6004803603602081101561012457600080fd5b8101908080359060200190929190505050610359565b005b6101686004803603602081101561015257600080fd5b81019080803590602001909291905050506103ef565b005b6000807f746573740000000000000000000000000000000000000000000000000000000060001b607b60405161019f906103f9565b808281526020019150508190604051809103906000f59050801580156101c9573d6000803e3d6000fd5b5090508073ffffffffffffffffffffffffffffffffffffffff1663d46300fd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561021257600080fd5b505afa158015610226573d6000803e3d6000fd5b505050506040513d602081101561023c57600080fd5b81019080805190602001909291905050506000819055508073ffffffffffffffffffffffffffffffffffffffff1663ee919d506101416040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156102a857600080fd5b505af11580156102bc573d6000803e3d6000fd5b5050505080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691505090565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008054905090565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff1663ee919d50836040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156103d357600080fd5b505af11580156103e7573d6000803e3d6000fd5b505050505050565b8060008190555050565b61014d806104078339019056fe608060405234801561001057600080fd5b5060405161014d38038061014d8339818101604052602081101561003357600080fd5b8101908080519060200190929190505050806000819055505060f38061005a6000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80630dbe671f146041578063d46300fd14605d578063ee919d50146079575b600080fd5b604760a4565b6040518082815260200191505060405180910390f35b606360aa565b6040518082815260200191505060405180910390f35b60a260048036036020811015608d57600080fd5b810190808035906020019092919050505060b3565b005b60005481565b60008054905090565b806000819055505056fea264697066735822122084a43fd050d3de9bc0cdc2f86b02db81bc69b5639cedd1a5b72010d2a664879a64736f6c63430006020033a2646970667358221220a00266288222dc5d9803c3840f0e4d5d04630cb6bac841d30c42511d9b58045864736f6c63430006020033"; diff --git a/cmd/u2u/launcher/txtracer_test.go b/cmd/u2u/launcher/txtracer_test.go index 241ff003..0f40984b 100644 --- a/cmd/u2u/launcher/txtracer_test.go +++ b/cmd/u2u/launcher/txtracer_test.go @@ -50,11 +50,11 @@ func TestTxTracing(t *testing.T) { cliConsoleOutput = *cliConsole.GetOutDataTillCursor() // Call simple contract call to check created trace - cliConsole.InputLine("testContract.setA.sendTransaction(24, {from:u2u.accounts[1]})") + cliConsole.InputLine("testContract.setA.sendTransaction(24, {from:u2u.accounts[1],gas:\"3000000\"})") cliConsoleOutput = *cliConsole.GetOutDataTillCursor() txHashCall := cliConsoleOutput[strings.Index(cliConsoleOutput, "0x") : len(cliConsoleOutput)-3] - cliConsole.InputLine("testContract.deploy.sendTransaction({from:u2u.accounts[1]})") + cliConsole.InputLine("testContract.deploy.sendTransaction({from:u2u.accounts[1],gas:\"3000000\"})") cliConsoleOutput = *cliConsole.GetOutDataTillCursor() txHashDeploy := cliConsoleOutput[strings.Index(cliConsoleOutput, "0x") : len(cliConsoleOutput)-3] time.Sleep(5000 * time.Millisecond) diff --git a/ethapi/api.go b/ethapi/api.go index bc61fd5f..fe53fd19 100644 --- a/ethapi/api.go +++ b/ethapi/api.go @@ -1831,7 +1831,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Tra } if args.Nonce == nil { - // Hold the addresse's mutex around signing to prevent concurrent assignment of + // Hold the addresses' mutex around signing to prevent concurrent assignment of // the same nonce to multiple accounts. s.nonceLock.LockAddr(args.from()) defer s.nonceLock.UnlockAddr(args.from()) From a7c3e419a527b737c9720658c3d68eba55a388c9 Mon Sep 17 00:00:00 2001 From: trinhdn97 Date: Wed, 11 Dec 2024 20:05:28 +0700 Subject: [PATCH 5/6] Fix gasLimit setter of types.Message --- core/types/transaction.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/core/types/transaction.go b/core/types/transaction.go index f32e76d8..5c86f987 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -638,15 +638,15 @@ func (tx *Transaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) { return msg, err } -func (m Message) From() common.Address { return m.from } -func (m Message) To() *common.Address { return m.to } -func (m Message) GasPrice() *big.Int { return m.gasPrice } -func (m Message) GasFeeCap() *big.Int { return m.gasFeeCap } -func (m Message) GasTipCap() *big.Int { return m.gasTipCap } -func (m Message) Value() *big.Int { return m.amount } -func (m Message) Gas() uint64 { return m.gasLimit } -func (m Message) Nonce() uint64 { return m.nonce } -func (m Message) Data() []byte { return m.data } -func (m Message) AccessList() AccessList { return m.accessList } -func (m Message) IsFake() bool { return m.isFake } -func (m Message) SetGasLimit(gasLimit uint64) { m.gasLimit = gasLimit } +func (m Message) From() common.Address { return m.from } +func (m Message) To() *common.Address { return m.to } +func (m Message) GasPrice() *big.Int { return m.gasPrice } +func (m Message) GasFeeCap() *big.Int { return m.gasFeeCap } +func (m Message) GasTipCap() *big.Int { return m.gasTipCap } +func (m Message) Value() *big.Int { return m.amount } +func (m Message) Gas() uint64 { return m.gasLimit } +func (m Message) Nonce() uint64 { return m.nonce } +func (m Message) Data() []byte { return m.data } +func (m Message) AccessList() AccessList { return m.accessList } +func (m Message) IsFake() bool { return m.isFake } +func (m *Message) SetGasLimit(gasLimit uint64) { m.gasLimit = gasLimit } From 2d498782d99368b3a67f0a4d9ec7e373835413db Mon Sep 17 00:00:00 2001 From: trinhdn97 Date: Wed, 11 Dec 2024 20:09:12 +0700 Subject: [PATCH 6/6] Revert tx tracer unit test --- cmd/u2u/launcher/testdata/txtracer_test.js | 2 +- cmd/u2u/launcher/txtracer_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/u2u/launcher/testdata/txtracer_test.js b/cmd/u2u/launcher/testdata/txtracer_test.js index b53d15ad..ae9b6f41 100644 --- a/cmd/u2u/launcher/testdata/txtracer_test.js +++ b/cmd/u2u/launcher/testdata/txtracer_test.js @@ -1,7 +1,7 @@ var a1 = personal.newAccount(""); personal.unlockAccount(a1, "", 300); admin.sleep(1); -var tx = u2u.sendTransaction({from:u2u.accounts[0], to:a1, value:"10000000000000000000", gas:"4700000"}); +var tx = u2u.sendTransaction({from:u2u.accounts[0], to:a1, value:"10000000000000000000"}); admin.sleep(1); var abi = [{"inputs":[],"name":"deploy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getA","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_a","type":"uint256"}],"name":"setA","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_a","type":"uint256"}],"name":"setInA","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tst","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]; var bytecode = "0x608060405234801561001057600080fd5b50610589806100206000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c8063775c300c1461005c57806391888f2e146100a6578063d46300fd146100f0578063ea87f3731461010e578063ee919d501461013c575b600080fd5b61006461016a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100ae61032a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100f8610350565b6040518082815260200191505060405180910390f35b61013a6004803603602081101561012457600080fd5b8101908080359060200190929190505050610359565b005b6101686004803603602081101561015257600080fd5b81019080803590602001909291905050506103ef565b005b6000807f746573740000000000000000000000000000000000000000000000000000000060001b607b60405161019f906103f9565b808281526020019150508190604051809103906000f59050801580156101c9573d6000803e3d6000fd5b5090508073ffffffffffffffffffffffffffffffffffffffff1663d46300fd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561021257600080fd5b505afa158015610226573d6000803e3d6000fd5b505050506040513d602081101561023c57600080fd5b81019080805190602001909291905050506000819055508073ffffffffffffffffffffffffffffffffffffffff1663ee919d506101416040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156102a857600080fd5b505af11580156102bc573d6000803e3d6000fd5b5050505080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691505090565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008054905090565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff1663ee919d50836040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156103d357600080fd5b505af11580156103e7573d6000803e3d6000fd5b505050505050565b8060008190555050565b61014d806104078339019056fe608060405234801561001057600080fd5b5060405161014d38038061014d8339818101604052602081101561003357600080fd5b8101908080519060200190929190505050806000819055505060f38061005a6000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80630dbe671f146041578063d46300fd14605d578063ee919d50146079575b600080fd5b604760a4565b6040518082815260200191505060405180910390f35b606360aa565b6040518082815260200191505060405180910390f35b60a260048036036020811015608d57600080fd5b810190808035906020019092919050505060b3565b005b60005481565b60008054905090565b806000819055505056fea264697066735822122084a43fd050d3de9bc0cdc2f86b02db81bc69b5639cedd1a5b72010d2a664879a64736f6c63430006020033a2646970667358221220a00266288222dc5d9803c3840f0e4d5d04630cb6bac841d30c42511d9b58045864736f6c63430006020033"; diff --git a/cmd/u2u/launcher/txtracer_test.go b/cmd/u2u/launcher/txtracer_test.go index 0f40984b..241ff003 100644 --- a/cmd/u2u/launcher/txtracer_test.go +++ b/cmd/u2u/launcher/txtracer_test.go @@ -50,11 +50,11 @@ func TestTxTracing(t *testing.T) { cliConsoleOutput = *cliConsole.GetOutDataTillCursor() // Call simple contract call to check created trace - cliConsole.InputLine("testContract.setA.sendTransaction(24, {from:u2u.accounts[1],gas:\"3000000\"})") + cliConsole.InputLine("testContract.setA.sendTransaction(24, {from:u2u.accounts[1]})") cliConsoleOutput = *cliConsole.GetOutDataTillCursor() txHashCall := cliConsoleOutput[strings.Index(cliConsoleOutput, "0x") : len(cliConsoleOutput)-3] - cliConsole.InputLine("testContract.deploy.sendTransaction({from:u2u.accounts[1],gas:\"3000000\"})") + cliConsole.InputLine("testContract.deploy.sendTransaction({from:u2u.accounts[1]})") cliConsoleOutput = *cliConsole.GetOutDataTillCursor() txHashDeploy := cliConsoleOutput[strings.Index(cliConsoleOutput, "0x") : len(cliConsoleOutput)-3] time.Sleep(5000 * time.Millisecond)