前言
現今以太坊的手續費過於昂貴,交易者需要付出高額的手續費才可以完成交易。 這次的 Ethereum London Hard Fork (倫敦硬分叉)所做的更新,包含了 EIP-1559、EIP-3198、EIP-3238 三個提案, 其中 EIP-1559 也是這次比較備受關注的部分。
EIP 是什麼?
EIP(Ethereum Improvement Proposals),是為以太坊指定潛在新功能或流程的標準,包含提議更改的技術規範。通過 EIP 流程討論和開發以太坊的網絡升級和應用標準。
提案一: EIP-1559(區塊鏈交易費用模型)
EIP-1559 是對用戶在以太坊網絡上支付 gas 費用方式的更改。(由於現在以太坊交易手續費過高,降低對用戶的吸引力)
- 以往:礦工可以賺的收入「挖礦獎勵」跟「 100% Gas fee 」
- EIP 1559 : gas fee 分為基礎費(base fee)和小費兩種,給礦工的是「小費」; base fee 的 50% 會直接銷毀。
EIP-1559 也導入目標塊容量機制,每個區塊的基本費用根據網絡需求而變化。如果一個區塊的交易量超過 50%,則基本費用將增加,低於 50% 的話基本費則會下降。 而基本費可能會隨著交易加入 block 時發生改變,可以先將 price 設定成願意支付的上限,最後沒用到的部分會退還。
這個提案重點在於基本費會被銷毀,並不會所有的 gas fee 都讓礦工拿走。而也藉由『銷毀』機制,讓以太幣的供應量下降,避免通貨膨脹。
交易格式的差異
以下部分段程式碼是從 EIP-1559 擷取。
新交易格式內的 base_fee_per_gas & priority_fee_per_gas 取代了以往的 Legacy Ethereum transactions 的 gas_price。 但 EIP-1559 可以相容 Legacy Ethereum transactions 的格式。 雖然現有的交易格式仍然有效且也被包含在 block 中,不過 Legacy transactions 沒辦法享有新的 pricing system 所帶來的優點。
@dataclass
class TransactionLegacy:
signer_nonce: int = 0
gas_price: int = 0
gas_limit: int = 0
destination: int = 0
amount: int = 0
payload: bytes = bytes()
v: int = 0
r: int = 0
s: int = 0
@dataclass
class Transaction1559Payload:
chain_id: int = 0
signer_nonce: int = 0
max_priority_fee_per_gas: int = 0
max_fee_per_gas: int = 0
gas_limit: int = 0
destination: int = 0
amount: int = 0
payload: bytes = bytes()
access_list: List[Tuple[int, List[int]]] = field(default_factory=list)
signature_y_parity: bool = False
signature_r: int = 0
signature_s: int = 0
---
def normalize_transaction(self, transaction: Transaction, signer_address: int) -> NormalizedTransaction:
# legacy transactions
if isinstance(transaction, TransactionLegacy):
return NormalizedTransaction(
signer_address = signer_address,
signer_nonce = transaction.signer_nonce,
gas_limit = transaction.gas_limit,
max_priority_fee_per_gas = transaction.gas_price,
max_fee_per_gas = transaction.gas_price,
destination = transaction.destination,
amount = transaction.amount,
payload = transaction.payload,
access_list = [],
)
# 1559 transactions
elif isinstance(transaction, Transaction1559Envelope):
return NormalizedTransaction(
signer_address = signer_address,
signer_nonce = transaction.payload.signer_nonce,
gas_limit = transaction.payload.gas_limit,
max_priority_fee_per_gas = transaction.payload.max_priority_fee_per_gas,
max_fee_per_gas = transaction.payload.max_fee_per_gas,
destination = transaction.payload.destination,
amount = transaction.payload.amount,
payload = transaction.payload.payload,
access_list = transaction.payload.access_list,
)
else:
raise Exception('invalid transaction: unexpected number of items')
計算 gas fee
gas fee 會分為基礎費和小費兩種,base fee 會直接銷毀,「小費」會給礦工,沒有用到的部分則會返回給交易者。
這邊第一步會判斷地址的 balance 是否有辦法負擔 base fee,如果有的話就會繼續往下計算 fee_per_gas 以及 priority_fee_per_gas,這邊就不逐一解釋程式碼了。
for unnormalized_transaction in transactions:
# Note: this validates transaction signature and chain ID which must happen before we normalize below since normalized transactions don't include signature or chain ID
signer_address = self.validate_and_recover_signer_address(unnormalized_transaction)
transaction = self.normalize_transaction(unnormalized_transaction, signer_address)
signer = self.account(signer_address)
signer.balance -= transaction.amount
assert signer.balance >= 0, 'invalid transaction: signer does not have enough ETH to cover attached value'
# ensure that the user was willing to at least pay the base fee
assert transaction.max_fee_per_gas >= block.base_fee_per_gas
# The first two of these four rules are implicit due to the next two rules
# Prevent impossibly large numbers
assert transaction.max_fee_per_gas < 2**256
# Prevent impossibly large numbers
assert transaction.max_priority_fee_per_gas < 2**256
# The total must be the larger of the two
assert transaction.max_fee_per_gas >= transaction.max_priority_fee_per_gas
# the signer must be able to afford the transaction
assert signer.balance >= transaction.gas_limit * transaction.max_fee_per_gas
# priority fee is capped because the base fee is filled first
priority_fee_per_gas = min(transaction.max_priority_fee_per_gas, transaction.max_fee_per_gas - block.base_fee_per_gas)
# signer pays both the priority fee and the base fee
effective_gas_price = priority_fee_per_gas + block.base_fee_per_gas
signer.balance -= transaction.gas_limit * effective_gas_price
assert signer.balance >= 0, 'invalid transaction: signer does not have enough ETH to cover gas'
gas_used = self.execute_transaction(transaction, effective_gas_price)
gas_refund = transaction.gas_limit - gas_used
cumulative_transaction_gas_used += gas_used
# signer gets refunded for unused gas
signer.balance += gas_refund * effective_gas_price
# miner only receives the priority fee; note that the base fee is not given to anyone (it is burned)
self.account(block.author).balance += gas_used * priority_fee_per_gas
交易順序
多數的人不會在 priority fees 去做競爭,所以基本上以相同優先度會依照交易時間來進行排序。
提案二: EIP-3198(BASEFEE 操作碼)
固定費用 base fee 的實際原始碼。
提案三: EIP-3238(Difficulty Bomb Delay)
難度炸彈是以太坊在 2015 年加入的一段程式碼,透過逐步增加區塊鏈挖礦難度,主要是為了減低以太坊被開發的速度。 EIP-3238 主要是延遲難度炸彈,原本炸彈難題這次將預定在 2021/7 發生,這次預計延到 2022 年第二季度左右才會發生。 過去以太坊也曾爆過三次炸彈難題,並手動調整把難度調回爆炸前的程度。
小結
由於本身是開發人員,不是投資人或是礦工,所以比較關心交易格式有沒有發生變化,是否可以相容,對於此次更新對於以太坊生態的影響可能沒有各界大佬清楚,還請見諒XD
References
- EIP-3198
- EIP-3238
- EIP-1559
- Introduction to Ethereum Improvement Proposals (EIPs)