Skip to content

Commit 645aa70

Browse files
committed
Implement more of EIP-1559
- prefer exceptions for base gas fee to None, where feasible - update mainnet block number for London - increase default gas price in tests to 10 gwei to easily out-price the 1559 min gas fee of 1gwei - make old transactions understand max_priority_fee_per_gas and max_fee_per_gas (just return the gas_price, as the 1559 spec suggests) - validate the transaction nonce on new london transactions - enable creating a genesis London header (was hard-coded to pre-London-style) generally allow create_header_from_parent to accept a None parent, for genesis creation - use the correct VM's block header when creating a temp block for txn evaluation - update tests that were configured with a gas price of < 1 gwei - Drop support for Header.from_parent which is essentially always wrong. Should use vm_class.create_header_from_parent instead. - Make eth_call free again - when running a call, set the header's base gas fee to 0 - API update: state_in_temp_block -> in_costless_state to reference ^ - Correctly load uncles across fork boundary - Add test to validate uncles across chain VMs - Use a universal header sedes, since we can't load the VM before decoding the header - Decode block bodies with both London and pre-London uncles - Bonus bugfix: must double the header's gas limit at the VM boundary - Implement base fee adjustment, with tests - target must be half of limit - gas_used_delta bug when parent_gas_used > parent_gas_target: was subtracting base fee instead of target - Apply gas limit rules correctly Note that the 'diff' approach had a bug when 'diff' was negative. That approach needed a math.abs() to correctly validate the limit.
1 parent 5b205e6 commit 645aa70

35 files changed

+816
-422
lines changed

eth/_utils/headers.py

Lines changed: 69 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,85 @@
77

88
from eth.abc import BlockHeaderAPI
99
from eth.constants import (
10+
BLANK_ROOT_HASH,
11+
EMPTY_UNCLE_HASH,
12+
GENESIS_BLOCK_NUMBER,
1013
GENESIS_GAS_LIMIT,
14+
GENESIS_NONCE,
15+
GENESIS_PARENT_HASH,
1116
GAS_LIMIT_EMA_DENOMINATOR,
1217
GAS_LIMIT_ADJUSTMENT_FACTOR,
18+
GAS_LIMIT_MAXIMUM,
1319
GAS_LIMIT_MINIMUM,
1420
GAS_LIMIT_USAGE_ADJUSTMENT_NUMERATOR,
1521
GAS_LIMIT_USAGE_ADJUSTMENT_DENOMINATOR,
22+
ZERO_ADDRESS,
23+
ZERO_HASH32,
1624
)
1725
from eth.rlp.headers import (
1826
BlockHeader,
1927
)
2028

2129

22-
def compute_gas_limit_bounds(parent: BlockHeaderAPI) -> Tuple[int, int]:
30+
def fill_header_params_from_parent(
31+
parent: BlockHeaderAPI,
32+
gas_limit: int,
33+
difficulty: int,
34+
timestamp: int,
35+
coinbase: Address = ZERO_ADDRESS,
36+
nonce: bytes = None,
37+
extra_data: bytes = None,
38+
transaction_root: bytes = None,
39+
state_root: bytes = None,
40+
mix_hash: bytes = None,
41+
receipt_root: bytes = None) -> 'BlockHeaderAPI':
42+
43+
if parent is None:
44+
parent_hash = GENESIS_PARENT_HASH
45+
block_number = GENESIS_BLOCK_NUMBER
46+
if state_root is None:
47+
raise ValueError(f"Must set state root on genesis block")
48+
else:
49+
parent_hash = parent.hash
50+
block_number = parent.block_number + 1
51+
52+
if state_root is None:
53+
state_root = parent.state_root
54+
55+
header_kwargs: Dict[str, HeaderParams] = {
56+
'parent_hash': parent_hash,
57+
'coinbase': coinbase,
58+
'state_root': state_root,
59+
'gas_limit': gas_limit,
60+
'difficulty': difficulty,
61+
'block_number': block_number,
62+
'timestamp': timestamp,
63+
}
64+
if nonce is not None:
65+
header_kwargs['nonce'] = nonce
66+
if extra_data is not None:
67+
header_kwargs['extra_data'] = extra_data
68+
if transaction_root is not None:
69+
header_kwargs['transaction_root'] = transaction_root
70+
if receipt_root is not None:
71+
header_kwargs['receipt_root'] = receipt_root
72+
if mix_hash is not None:
73+
header_kwargs['mix_hash'] = mix_hash
74+
75+
return header_kwargs
76+
77+
78+
def compute_gas_limit_bounds(previous_limit: int) -> Tuple[int, int]:
2379
"""
2480
Compute the boundaries for the block gas limit based on the parent block.
2581
"""
26-
boundary_range = parent.gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
27-
upper_bound = parent.gas_limit + boundary_range
28-
lower_bound = max(GAS_LIMIT_MINIMUM, parent.gas_limit - boundary_range)
82+
boundary_range = previous_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
83+
upper_bound = min(GAS_LIMIT_MAXIMUM, previous_limit + boundary_range)
84+
lower_bound = max(GAS_LIMIT_MINIMUM, previous_limit - boundary_range)
2985
return lower_bound, upper_bound
3086

3187

32-
def compute_gas_limit(parent_header: BlockHeaderAPI, gas_limit_floor: int) -> int:
88+
def compute_gas_limit(parent_header: BlockHeaderAPI, genesis_gas_limit: int) -> int:
3389
"""
3490
A simple strategy for adjusting the gas limit.
3591
@@ -38,21 +94,24 @@ def compute_gas_limit(parent_header: BlockHeaderAPI, gas_limit_floor: int) -> in
3894
- decrease by 1/1024th of the gas limit from the previous block
3995
- increase by 50% of the total gas used by the previous block
4096
41-
If the value is less than the given `gas_limit_floor`:
97+
If the value is less than the given `genesis_gas_limit`:
4298
4399
- increase the gas limit by 1/1024th of the gas limit from the previous block.
44100
45101
If the value is less than the GAS_LIMIT_MINIMUM:
46102
47103
- use the GAS_LIMIT_MINIMUM as the new gas limit.
48104
"""
49-
if gas_limit_floor < GAS_LIMIT_MINIMUM:
105+
if genesis_gas_limit < GAS_LIMIT_MINIMUM:
50106
raise ValueError(
51-
"The `gas_limit_floor` value must be greater than the "
52-
f"GAS_LIMIT_MINIMUM. Got {gas_limit_floor}. Must be greater than "
107+
"The `genesis_gas_limit` value must be greater than the "
108+
f"GAS_LIMIT_MINIMUM. Got {genesis_gas_limit}. Must be greater than "
53109
f"{GAS_LIMIT_MINIMUM}"
54110
)
55111

112+
if parent_header is None:
113+
return genesis_gas_limit
114+
56115
decay = parent_header.gas_limit // GAS_LIMIT_EMA_DENOMINATOR
57116

58117
if parent_header.gas_used:
@@ -73,40 +132,7 @@ def compute_gas_limit(parent_header: BlockHeaderAPI, gas_limit_floor: int) -> in
73132

74133
if gas_limit < GAS_LIMIT_MINIMUM:
75134
return GAS_LIMIT_MINIMUM
76-
elif gas_limit < gas_limit_floor:
135+
elif gas_limit < genesis_gas_limit:
77136
return parent_header.gas_limit + decay
78137
else:
79138
return gas_limit
80-
81-
82-
def generate_header_from_parent_header(
83-
compute_difficulty_fn: Callable[[BlockHeaderAPI, int], int],
84-
parent_header: BlockHeaderAPI,
85-
coinbase: Address,
86-
timestamp: Optional[int] = None,
87-
extra_data: bytes = b'') -> BlockHeader:
88-
"""
89-
Generate BlockHeader from state_root and parent_header
90-
"""
91-
if timestamp is None:
92-
timestamp = max(int(time.time()), parent_header.timestamp + 1)
93-
elif timestamp <= parent_header.timestamp:
94-
raise ValueError(
95-
f"header.timestamp ({timestamp}) should be higher than"
96-
f"parent_header.timestamp ({parent_header.timestamp})"
97-
)
98-
header = BlockHeader(
99-
difficulty=compute_difficulty_fn(parent_header, timestamp),
100-
block_number=(parent_header.block_number + 1),
101-
gas_limit=compute_gas_limit(
102-
parent_header,
103-
gas_limit_floor=GENESIS_GAS_LIMIT,
104-
),
105-
timestamp=timestamp,
106-
parent_hash=parent_header.hash,
107-
state_root=parent_header.state_root,
108-
coinbase=coinbase,
109-
extra_data=extra_data,
110-
)
111-
112-
return header

eth/abc.py

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class MiningHeaderAPI(ABC):
7474
gas_used: int
7575
timestamp: int
7676
extra_data: bytes
77-
base_fee_per_gas: int # EIP-1559
77+
base_fee_per_gas: int # EIP-1559, set to None in pre-London header
7878

7979
@property
8080
@abstractmethod
@@ -337,7 +337,10 @@ def nonce(self) -> int:
337337

338338
@property
339339
@abstractmethod
340-
def gas_price(self) -> Optional[int]:
340+
def gas_price(self) -> int:
341+
"""
342+
Will raise :cls:`AttributeError` if get or set on a 1559 transaction.
343+
"""
341344
...
342345

343346
@property
@@ -1690,9 +1693,9 @@ def chain_id(self) -> int:
16901693

16911694
@property
16921695
@abstractmethod
1693-
def base_gas_fee(self) -> Optional[int]:
1696+
def base_fee_per_gas(self) -> Optional[int]:
16941697
"""
1695-
Return the base gas fee of the block
1698+
Return the base fee per gas of the block
16961699
"""
16971700
...
16981701

@@ -2877,8 +2880,9 @@ def get_computation(self,
28772880
#
28782881
# Transaction context
28792882
#
2883+
@classmethod
28802884
@abstractmethod
2881-
def get_transaction_context_class(self) -> Type[TransactionContextAPI]:
2885+
def get_transaction_context_class(cls) -> Type[TransactionContextAPI]:
28822886
"""
28832887
Return the :class:`~eth.vm.transaction_context.BaseTransactionContext` class that the
28842888
state class uses.
@@ -2990,6 +2994,36 @@ def get_fee_recipient(cls, header: BlockHeaderAPI) -> Address:
29902994
...
29912995

29922996

2997+
2998+
class BlockHeaderSedesAPI(ABC):
2999+
"""
3000+
Serialize and deserialize RLP for a header.
3001+
3002+
The header may be one of several definitions, like a London (EIP-1559) or
3003+
pre-London header.
3004+
"""
3005+
3006+
@classmethod
3007+
@abstractmethod
3008+
def deserialize(cls, encoded: List[bytes]) -> BlockHeaderAPI:
3009+
"""
3010+
Extract a header from an encoded RLP object.
3011+
3012+
This method is used by rlp.decode(..., sedes=TransactionBuilderAPI).
3013+
"""
3014+
...
3015+
3016+
@classmethod
3017+
@abstractmethod
3018+
def serialize(cls, obj: BlockHeaderAPI) -> List[bytes]:
3019+
"""
3020+
Encode a header to a series of bytes used by RLP.
3021+
3022+
This method is used by rlp.encode(obj).
3023+
"""
3024+
...
3025+
3026+
29933027
class VirtualMachineAPI(ConfigurableAPI):
29943028
"""
29953029
The :class:`~eth.abc.VirtualMachineAPI` class represents the Chain rules for a
@@ -3466,10 +3500,10 @@ def get_state_class(cls) -> Type[StateAPI]:
34663500
...
34673501

34683502
@abstractmethod
3469-
def state_in_temp_block(self) -> ContextManager[StateAPI]:
3503+
def in_costless_state(self) -> ContextManager[StateAPI]:
34703504
"""
34713505
Return a :class:`~typing.ContextManager` with the current state wrapped in a temporary
3472-
block.
3506+
block. In this state, the ability to pay gas costs is ignored.
34733507
"""
34743508
...
34753509

@@ -3932,13 +3966,6 @@ def validate_seal(self, header: BlockHeaderAPI) -> None:
39323966
"""
39333967
...
39343968

3935-
@abstractmethod
3936-
def validate_gaslimit(self, header: BlockHeaderAPI) -> None:
3937-
"""
3938-
Validate the gas limit on the given ``header``.
3939-
"""
3940-
...
3941-
39423969
@abstractmethod
39433970
def validate_uncles(self, block: BlockAPI) -> None:
39443971
"""

eth/chains/base.py

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
)
2323
from eth_utils.toolz import (
2424
concatv,
25+
keyfilter,
2526
sliding_window,
2627
)
2728

@@ -31,9 +32,6 @@
3132
from eth._utils.datatypes import (
3233
Configurable,
3334
)
34-
from eth._utils.headers import (
35-
compute_gas_limit_bounds,
36-
)
3735
from eth._utils.rlp import (
3836
validate_imported_block_unchanged,
3937
)
@@ -249,7 +247,7 @@ def from_genesis(cls,
249247
f"Expected {genesis_params['state_root']!r}"
250248
)
251249

252-
genesis_header = BlockHeader(**genesis_params)
250+
genesis_header = genesis_vm_class.create_header_from_parent(None, **genesis_params)
253251
return cls.from_genesis_header(base_db, genesis_header)
254252

255253
@classmethod
@@ -442,7 +440,7 @@ def get_transaction_result(
442440
transaction: SignedTransactionAPI,
443441
at_header: BlockHeaderAPI) -> bytes:
444442

445-
with self.get_vm(at_header).state_in_temp_block() as state:
443+
with self.get_vm(at_header).in_costless_state() as state:
446444
computation = state.costless_execute_transaction(transaction)
447445

448446
computation.raise_if_error()
@@ -454,7 +452,7 @@ def estimate_gas(
454452
at_header: BlockHeaderAPI = None) -> int:
455453
if at_header is None:
456454
at_header = self.get_canonical_head()
457-
with self.get_vm(at_header).state_in_temp_block() as state:
455+
with self.get_vm(at_header).in_costless_state() as state:
458456
return self.gas_estimator(state, transaction)
459457

460458
def import_block(self,
@@ -497,6 +495,7 @@ def persist_block(
497495
if perform_validation:
498496
self.validate_block(block)
499497

498+
vm = self.get_vm(block.header)
500499
(
501500
new_canonical_hashes,
502501
old_canonical_hashes,
@@ -541,28 +540,11 @@ def validate_block(self, block: BlockAPI) -> None:
541540
vm.validate_seal(block.header)
542541
vm.validate_seal_extension(block.header, ())
543542
self.validate_uncles(block)
544-
self.validate_gaslimit(block.header)
545543

546544
def validate_seal(self, header: BlockHeaderAPI) -> None:
547545
vm = self.get_vm(header)
548546
vm.validate_seal(header)
549547

550-
def validate_gaslimit(self, header: BlockHeaderAPI) -> None:
551-
parent_header = self.get_block_header_by_hash(header.parent_hash)
552-
low_bound, high_bound = compute_gas_limit_bounds(parent_header)
553-
if header.gas_limit < low_bound:
554-
raise ValidationError(
555-
f"The gas limit on block {encode_hex(header.hash)} "
556-
f"is too low: {header.gas_limit}. "
557-
f"It must be at least {low_bound}"
558-
)
559-
elif header.gas_limit > high_bound:
560-
raise ValidationError(
561-
f"The gas limit on block {encode_hex(header.hash)} "
562-
f"is too high: {header.gas_limit}. "
563-
f"It must be at most {high_bound}"
564-
)
565-
566548
def validate_uncles(self, block: BlockAPI) -> None:
567549
has_uncles = len(block.uncles) > 0
568550
should_have_uncles = block.header.uncles_hash != EMPTY_UNCLE_HASH

eth/chains/mainnet/constants.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@
5858
#
5959
BERLIN_MAINNET_BLOCK = BlockNumber(12244000)
6060

61-
6261
#
6362
# London Block
6463
#
65-
LONDON_MAINNET_BLOCK = BlockNumber(12244001) # TODO change to actual when known
64+
LONDON_MAINNET_BLOCK = BlockNumber(12965000)

0 commit comments

Comments
 (0)