Skip to content

Commit

Permalink
Remove gas_used from state, and related cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
carver committed May 3, 2018
1 parent 3aa8f53 commit 08ed4e3
Show file tree
Hide file tree
Showing 12 changed files with 79 additions and 58 deletions.
2 changes: 1 addition & 1 deletion evm/chains/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ def apply_transaction(self, transaction):
vm = self.get_vm()
base_block = vm.block

new_header, receipt, computation = vm.apply_transaction(transaction)
new_header, receipt, computation = vm.apply_transaction(base_block.header, transaction)

transactions = base_block.transactions + (transaction, )
receipts = base_block.get_receipts(self.chaindb) + (receipt, )
Expand Down
67 changes: 44 additions & 23 deletions evm/vm/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ def __init__(self, header, chaindb):
db=self.chaindb.db,
execution_context=self.block.header.create_execution_context(self.previous_hashes),
state_root=self.block.header.state_root,
gas_used=self.block.header.gas_used,
)

#
Expand Down Expand Up @@ -123,29 +122,63 @@ def execute_bytecode(self,
)

@abstractmethod
def make_receipt(self, transaction, computation, state):
def make_receipt(self, base_header, transaction, computation, state):
"""
Make receipt.
Generate the receipt resulting from applying the transaction.
:param base_header: the header of the block before the transaction was applied.
:param transaction: the transaction used to generate the receipt
:param computation: the result of running the transaction computation
:param state: the resulting state, after executing the computation
:return: receipt
"""
raise NotImplementedError("Must be implemented by subclasses")

def apply_transaction(self, transaction):
@abstractmethod
def validate_transaction_against_header(self, base_header, transaction):
"""
Validate that the given transaction is valid to apply to the given header.
:param base_header: header before applying the transaction
:param transaction: the transaction to validate
:raises: ValidationError if the transaction is not valid to apply
"""
raise NotImplementedError("Must be implemented by subclasses")

def apply_transaction(self, header, transaction):
"""
Apply the transaction to the vm in the current block.
:param transaction: to apply
:param header: header of the block before application
"""
self.validate_transaction_against_header(header, transaction)
state_root, computation = self.state.apply_transaction(transaction)
receipt = self.make_receipt(transaction, computation, self.state)
# TODO: remove this mutation.
self.state.gas_used = receipt.gas_used
receipt = self.make_receipt(header, transaction, computation, self.state)

new_header = self.block.header.copy(
bloom=int(BloomFilter(self.block.header.bloom) | receipt.bloom),
new_header = header.copy(
bloom=int(BloomFilter(header.bloom) | receipt.bloom),
gas_used=receipt.gas_used,
state_root=state_root,
)

return new_header, receipt, computation

def _apply_all_transactions(self, transactions, base_header):
receipts = []
previous_header = base_header
result_header = base_header

for transaction in transactions:
result_header, receipt, _ = self.apply_transaction(previous_header, transaction)

previous_header = result_header
receipts.append(receipt)

return result_header, receipts

#
# Mining
#
Expand All @@ -167,25 +200,14 @@ def import_block(self, block):
db=self.chaindb.db,
execution_context=self.block.header.create_execution_context(self.previous_hashes),
state_root=self.block.header.state_root,
gas_used=self.block.header.gas_used,
)

# run all of the transactions.
execution_data = [
self.apply_transaction(transaction)
for transaction
in block.transactions
]
if execution_data:
headers, receipts, _ = zip(*execution_data)
header_with_txns = headers[-1]
else:
receipts = tuple()
header_with_txns = self.block.header
last_header, receipts = self._apply_all_transactions(block.transactions, self.block.header)

self.block = self.set_block_transactions(
self.block,
header_with_txns,
last_header,
block.transactions,
receipts,
)
Expand Down Expand Up @@ -271,7 +293,6 @@ def state_in_temp_block(self):
db=self.chaindb.db,
execution_context=temp_block.header.create_execution_context(prev_hashes),
state_root=temp_block.header.state_root,
gas_used=0,
)

snapshot = state.snapshot()
Expand Down
18 changes: 5 additions & 13 deletions evm/vm/forks/byzantium/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
from evm.rlp.receipts import (
Receipt,
)
from evm.vm.forks.spurious_dragon import SpuriousDragonVM
from evm.vm.forks.frontier import make_frontier_receipt

Expand All @@ -16,20 +13,15 @@
from .state import ByzantiumState


def make_byzantium_receipt(transaction, computation, state):
old_receipt = make_frontier_receipt(transaction, computation, state)
def make_byzantium_receipt(base_header, transaction, computation, state):
frontier_receipt = make_frontier_receipt(base_header, transaction, computation, state)

if computation.is_error:
state_root = EIP658_TRANSACTION_STATUS_CODE_FAILURE
success_code = EIP658_TRANSACTION_STATUS_CODE_FAILURE
else:
state_root = EIP658_TRANSACTION_STATUS_CODE_SUCCESS
success_code = EIP658_TRANSACTION_STATUS_CODE_SUCCESS

receipt = Receipt(
state_root=state_root,
gas_used=old_receipt.gas_used,
logs=old_receipt.logs,
)
return receipt
return frontier_receipt.copy(state_root=success_code)


ByzantiumVM = SpuriousDragonVM.configure(
Expand Down
8 changes: 5 additions & 3 deletions evm/vm/forks/frontier/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
compute_frontier_difficulty,
configure_frontier_header,
)
from .validation import validate_frontier_transaction_against_header


def make_frontier_receipt(transaction, computation, state):
def make_frontier_receipt(base_header, transaction, computation, state):
# Reusable for other forks

logs = [
Expand All @@ -33,7 +34,7 @@ def make_frontier_receipt(transaction, computation, state):
gas_refund,
(transaction.gas - gas_remaining) // 2,
)
gas_used = state.gas_used + tx_gas_used
gas_used = base_header.gas_used + tx_gas_used

receipt = Receipt(
state_root=state.state_root,
Expand All @@ -55,5 +56,6 @@ def make_frontier_receipt(transaction, computation, state):
create_header_from_parent=staticmethod(create_frontier_header_from_parent),
compute_difficulty=staticmethod(compute_frontier_difficulty),
configure_header=configure_frontier_header,
make_receipt=staticmethod(make_frontier_receipt)
make_receipt=staticmethod(make_frontier_receipt),
validate_transaction_against_header=validate_frontier_transaction_against_header,
)
2 changes: 1 addition & 1 deletion evm/vm/forks/frontier/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ class FrontierState(BaseState, FrontierTransactionExecutor):
account_db_class = AccountDB # Type[BaseAccountDB]

def validate_transaction(self, transaction):
validate_frontier_transaction(self, transaction)
validate_frontier_transaction(self.account_db, transaction)

@staticmethod
def get_block_reward():
Expand Down
20 changes: 14 additions & 6 deletions evm/vm/forks/frontier/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
)


def validate_frontier_transaction(state, transaction):
def validate_frontier_transaction(account_db, transaction):
gas_cost = transaction.gas * transaction.gas_price
sender_balance = state.account_db.get_balance(transaction.sender)
sender_balance = account_db.get_balance(transaction.sender)

if sender_balance < gas_cost:
raise ValidationError(
Expand All @@ -17,8 +17,16 @@ def validate_frontier_transaction(state, transaction):
if sender_balance < total_cost:
raise ValidationError("Sender account balance cannot afford txn")

if state.gas_used + transaction.gas > state.gas_limit:
raise ValidationError("Transaction exceeds gas limit")

if state.account_db.get_nonce(transaction.sender) != transaction.nonce:
if account_db.get_nonce(transaction.sender) != transaction.nonce:
raise ValidationError("Invalid transaction nonce")


def validate_frontier_transaction_against_header(_vm, base_header, transaction):
if base_header.gas_used + transaction.gas > base_header.gas_limit:
raise ValidationError(
"Transaction exceeds gas limit: using {}, bringing total to {}, but limit is {}".format(
transaction.gas,
base_header.gas_used + transaction.gas,
base_header.gas_limit,
)
)
2 changes: 1 addition & 1 deletion evm/vm/forks/homestead/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ class HomesteadState(FrontierState):
computation_class = HomesteadComputation

def validate_transaction(self, transaction):
validate_homestead_transaction(self, transaction)
validate_homestead_transaction(self.account_db, transaction)
4 changes: 2 additions & 2 deletions evm/vm/forks/homestead/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
)


def validate_homestead_transaction(evm, transaction):
def validate_homestead_transaction(account_db, transaction):
if transaction.s > SECPK1_N // 2 or transaction.s == 0:
raise ValidationError("Invalid signature S value")

validate_frontier_transaction(evm, transaction)
validate_frontier_transaction(account_db, transaction)
4 changes: 1 addition & 3 deletions evm/vm/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,16 @@ class BaseState(Configurable, metaclass=ABCMeta):
_chaindb = None
execution_context = None
state_root = None
gas_used = None

block_class = None # type: Type[BaseBlock]
computation_class = None # type: Type[BaseComputation]
transaction_context_class = None # type: Type[BaseTransactionContext]
account_db_class = None # type: Type[BaseAccountDB]

def __init__(self, db, execution_context, state_root, gas_used):
def __init__(self, db, execution_context, state_root):
self._db = db
self.execution_context = execution_context
self.account_db = self.get_account_db_class()(self._db, state_root)
self.gas_used = gas_used

#
# Logging
Expand Down
6 changes: 3 additions & 3 deletions tests/core/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,16 @@ def fill_block(chain, from_, key, gas, data):
amount = 100

vm = chain.get_vm()
assert vm.state.gas_used == 0
assert vm.block.header.gas_used == 0

while True:
tx = new_transaction(chain.get_vm(), from_, recipient, amount, key, gas=gas, data=data)
try:
chain.apply_transaction(tx)
except ValidationError as exc:
if "Transaction exceeds gas limit" == str(exc):
if str(exc).startswith("Transaction exceeds gas limit"):
break
else:
raise exc

assert chain.get_vm().state.gas_used > 0
assert chain.get_vm().block.header.gas_used > 0
2 changes: 1 addition & 1 deletion tests/core/vm/test_vm.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def test_apply_transaction(
amount = 100
from_ = funded_address
tx = new_transaction(vm, from_, recipient, amount, funded_address_private_key)
new_header, _, computation = vm.apply_transaction(tx)
new_header, _, computation = vm.apply_transaction(vm.block.header, tx)

assert not computation.is_error
tx_gas = tx.gas_price * constants.GAS_TX
Expand Down
2 changes: 1 addition & 1 deletion tests/json-fixtures/test_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ def test_state_fixtures(fixture, fixture_vm_class):
)

try:
header, receipt, computation = vm.apply_transaction(transaction)
header, receipt, computation = vm.apply_transaction(vm.block.header, transaction)
transactions = vm.block.transactions + (transaction, )
receipts = vm.block.get_receipts(chaindb) + (receipt, )
block = vm.set_block_transactions(vm.block, header, transactions, receipts)
Expand Down

0 comments on commit 08ed4e3

Please sign in to comment.