/
gas.py
207 lines (173 loc) · 5.17 KB
/
gas.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
"""
Ethereum Virtual Machine (EVM) Gas
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. contents:: Table of Contents
:backlinks: none
:local:
Introduction
------------
EVM gas constants and calculators.
"""
from dataclasses import dataclass
from typing import List, Tuple
from ethereum.base_types import U256, Uint
from ethereum.trace import GasAndRefund, evm_trace
from ethereum.utils.numeric import ceil32
from ..fork_types import Address
from ..state import State, account_exists
from . import Evm
from .exceptions import OutOfGasError
GAS_JUMPDEST = Uint(1)
GAS_BASE = Uint(2)
GAS_VERY_LOW = Uint(3)
GAS_SLOAD = Uint(50)
GAS_STORAGE_SET = Uint(20000)
GAS_STORAGE_UPDATE = Uint(5000)
GAS_STORAGE_CLEAR_REFUND = Uint(15000)
GAS_LOW = Uint(5)
GAS_MID = Uint(8)
GAS_HIGH = Uint(10)
GAS_EXPONENTIATION = Uint(10)
GAS_EXPONENTIATION_PER_BYTE = Uint(10)
GAS_MEMORY = Uint(3)
GAS_KECCAK256 = Uint(30)
GAS_KECCAK256_WORD = Uint(6)
GAS_COPY = Uint(3)
GAS_BLOCK_HASH = Uint(20)
GAS_EXTERNAL = Uint(20)
GAS_BALANCE = Uint(20)
GAS_LOG = Uint(375)
GAS_LOG_DATA = Uint(8)
GAS_LOG_TOPIC = Uint(375)
GAS_CREATE = Uint(32000)
GAS_CODE_DEPOSIT = Uint(200)
GAS_ZERO = Uint(0)
GAS_CALL = Uint(40)
GAS_NEW_ACCOUNT = Uint(25000)
GAS_CALL_VALUE = Uint(9000)
GAS_CALL_STIPEND = Uint(2300)
REFUND_SELF_DESTRUCT = Uint(24000)
GAS_ECRECOVER = Uint(3000)
GAS_SHA256 = Uint(60)
GAS_SHA256_WORD = Uint(12)
GAS_RIPEMD160 = Uint(600)
GAS_RIPEMD160_WORD = Uint(120)
GAS_IDENTITY = Uint(15)
GAS_IDENTITY_WORD = Uint(3)
@dataclass
class ExtendMemory:
"""
Define the parameters for memory extension in opcodes
`cost`: `ethereum.base_types.Uint`
The gas required to perform the extension
`expand_by`: `ethereum.base_types.Uint`
The size by which the memory will be extended
"""
cost: Uint
expand_by: Uint
@dataclass
class MessageCallGas:
"""
Define the gas cost and stipend for executing the call opcodes.
`cost`: `ethereum.base_types.Uint`
The non-refundable portion of gas reserved for executing the
call opcode.
`stipend`: `ethereum.base_types.Uint`
The portion of gas available to sub-calls that is refundable
if not consumed
"""
cost: Uint
stipend: Uint
def charge_gas(evm: Evm, amount: Uint) -> None:
"""
Subtracts `amount` from `evm.gas_left`.
Parameters
----------
evm :
The current EVM.
amount :
The amount of gas the current operation requires.
"""
evm_trace(evm, GasAndRefund(amount))
if evm.gas_left < amount:
raise OutOfGasError
else:
evm.gas_left -= U256(amount)
def calculate_memory_gas_cost(size_in_bytes: Uint) -> Uint:
"""
Calculates the gas cost for allocating memory
to the smallest multiple of 32 bytes,
such that the allocated size is at least as big as the given size.
Parameters
----------
size_in_bytes :
The size of the data in bytes.
Returns
-------
total_gas_cost : `ethereum.base_types.Uint`
The gas cost for storing data in memory.
"""
size_in_words = ceil32(size_in_bytes) // 32
linear_cost = size_in_words * GAS_MEMORY
quadratic_cost = size_in_words**2 // 512
total_gas_cost = linear_cost + quadratic_cost
try:
return total_gas_cost
except ValueError:
raise OutOfGasError
def calculate_gas_extend_memory(
memory: bytearray, extensions: List[Tuple[U256, U256]]
) -> ExtendMemory:
"""
Calculates the gas amount to extend memory
Parameters
----------
memory :
Memory contents of the EVM.
extensions:
List of extensions to be made to the memory.
Consists of a tuple of start position and size.
Returns
-------
extend_memory: `ExtendMemory`
"""
size_to_extend = Uint(0)
to_be_paid = Uint(0)
current_size = Uint(len(memory))
for start_position, size in extensions:
if size == 0:
continue
before_size = ceil32(current_size)
after_size = ceil32(Uint(start_position) + Uint(size))
if after_size <= before_size:
continue
size_to_extend += after_size - before_size
already_paid = calculate_memory_gas_cost(before_size)
total_cost = calculate_memory_gas_cost(after_size)
to_be_paid += total_cost - already_paid
current_size = after_size
return ExtendMemory(to_be_paid, size_to_extend)
def calculate_message_call_gas(
state: State, gas: Uint, to: Address, value: U256
) -> MessageCallGas:
"""
Calculates the gas amount for executing Opcodes `CALL` and `CALLCODE`.
Parameters
----------
state :
The current state.
gas :
The amount of gas provided to the message-call.
to:
The address of the recipient account.
value:
The amount of `ETH` that needs to be transferred.
Returns
-------
message_call_gas: `MessageCallGas`
"""
create_gas_cost = Uint(0) if account_exists(state, to) else GAS_NEW_ACCOUNT
transfer_gas_cost = Uint(0) if value == 0 else GAS_CALL_VALUE
cost = GAS_CALL + gas + create_gas_cost + transfer_gas_cost
stipend = gas if value == 0 else GAS_CALL_STIPEND + gas
return MessageCallGas(cost, stipend)