26
26
27
27
namespace silkworm ::rpc {
28
28
29
- Task<intx::uint256> EstimateGasOracle::estimate_gas (const Call& call, const silkworm::Block& block, std::optional<TxnId> txn_id, std::optional<BlockNum> block_num_for_gas_limit ) {
29
+ Task<intx::uint256> EstimateGasOracle::estimate_gas (const Call& call, const silkworm::Block& block, std::optional<TxnId> txn_id) {
30
30
SILK_DEBUG << " EstimateGasOracle::estimate_gas called" ;
31
31
32
32
const auto block_num = block.header .number ;
33
33
34
34
uint64_t hi = 0 ;
35
- uint64_t lo = kTxGas - 1 ;
35
+ uint64_t lo;
36
36
37
37
if (call.gas .value_or (0 ) >= kTxGas ) {
38
38
SILK_DEBUG << " Set gas limit using call args: " << call.gas .value_or (0 );
39
39
hi = call.gas .value ();
40
40
} else {
41
- const auto header = co_await block_header_provider_ (block_num_for_gas_limit. value_or ( block_num) );
41
+ const auto header = co_await block_header_provider_ (block_num);
42
42
if (!header) {
43
43
co_return 0 ;
44
44
}
45
45
hi = header->gas_limit ;
46
46
SILK_DEBUG << " Set gas limit using block: " << header->gas_limit ;
47
47
}
48
48
49
+ if (hi > kGasCap ) {
50
+ SILK_WARN << " caller gas above allowance, capping: requested " << hi << " , cap " << kGasCap ;
51
+ hi = kGasCap ;
52
+ }
53
+
49
54
std::optional<intx::uint256> gas_price = call.gas_price ;
50
55
if (gas_price && gas_price != 0 ) {
51
56
evmc::address from = call.from .value_or (evmc::address{0 });
@@ -71,14 +76,6 @@ Task<intx::uint256> EstimateGasOracle::estimate_gas(const Call& call, const silk
71
76
}
72
77
}
73
78
74
- if (hi > kGasCap ) {
75
- SILK_WARN << " caller gas above allowance, capping: requested " << hi << " , cap " << kGasCap ;
76
- hi = kGasCap ;
77
- }
78
- auto cap = hi;
79
-
80
- SILK_DEBUG << " hi: " << hi << " , lo: " << lo << " , cap: " << cap;
81
-
82
79
auto this_executor = co_await boost::asio::this_coro::executor;
83
80
84
81
execution::StateFactory state_factory{transaction_, /* state_cache=*/ nullptr };
@@ -88,33 +85,40 @@ Task<intx::uint256> EstimateGasOracle::estimate_gas(const Call& call, const silk
88
85
89
86
ExecutionResult result{evmc_status_code::EVMC_SUCCESS};
90
87
silkworm::Transaction transaction{call.to_transaction ()};
88
+
89
+ auto state_overrides = std::make_shared<state::OverrideState>(*state, accounts_overrides_);
90
+ EVMExecutor executor{block, config_, workers_, state_overrides};
91
+ transaction.gas_limit = hi;
92
+ result = try_execution (executor, transaction);
93
+ if (!result.success ()) {
94
+ return result;
95
+ }
96
+
97
+ // Assuming a contract can freely run all the instructions, we have
98
+ // the true amount of gas it wants to consume to execute fully.
99
+ // We want to ensure that the gas used doesn't fall below this
100
+ auto true_gas = result.gas_used .value_or (0 );
101
+ uint64_t refund = result.gas_refund .value_or (0 );
102
+ lo = std::min (true_gas + refund - 1 , kTxGas - 1 );
103
+
104
+ SILK_DEBUG << " hi: " << hi << " , lo: " << lo;
105
+
91
106
while (lo + 1 < hi) {
92
- auto state_overrides = std::make_shared<state::OverrideState>(*state, accounts_overrides_);
93
- EVMExecutor executor {block, config_, workers_, state_overrides};
107
+ state_overrides = std::make_shared<state::OverrideState>(*state, accounts_overrides_);
108
+ EVMExecutor curr_executor {block, config_, workers_, state_overrides};
94
109
auto mid = (hi + lo) / 2 ;
95
110
transaction.gas_limit = mid;
96
- result = try_execution (executor, transaction);
97
- if (result.success ()) {
98
- hi = mid;
99
- } else {
100
- if (result.pre_check_error_code && result.pre_check_error_code != PreCheckErrorCode::kIntrinsicGasTooLow ) {
101
- result.status_code = evmc_status_code::EVMC_SUCCESS;
102
- return result;
103
- }
111
+ result = try_execution (curr_executor, transaction);
112
+ if (result.pre_check_error_code ) {
113
+ break ;
114
+ }
115
+ if (!result.success () || result.gas_used .value_or (0 ) < true_gas) {
104
116
lo = mid;
117
+ } else {
118
+ hi = mid;
105
119
}
106
120
}
107
-
108
- if (hi == cap) {
109
- auto state_overrides = std::make_shared<state::OverrideState>(*state, accounts_overrides_);
110
- EVMExecutor executor{block, config_, workers_, state_overrides};
111
- transaction.gas_limit = hi;
112
- result = try_execution (executor, transaction);
113
- SILK_DEBUG << " HI == cap tested again with " << (result.status_code == evmc_status_code::EVMC_SUCCESS ? " succeed" : " failed" );
114
- } else if (!result.pre_check_error_code || result.pre_check_error_code == PreCheckErrorCode::kIntrinsicGasTooLow ) {
115
- result.pre_check_error = std::nullopt;
116
- result.status_code = evmc_status_code::EVMC_SUCCESS;
117
- }
121
+ result.status_code = evmc_status_code::EVMC_SUCCESS;
118
122
119
123
SILK_DEBUG << " EstimateGasOracle::estimate_gas returns " << hi;
120
124
0 commit comments