Merge pull request #13 from jacqueswww/6_use_pc

Use PC
This commit is contained in:
Jacques Wagener 2018-10-12 12:40:03 +02:00 committed by GitHub
commit d044b2c38e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 148 additions and 19 deletions

View File

@ -4,18 +4,13 @@ import vyper
from collections import Counter from collections import Counter
from pprint import pprint from pprint import pprint
from vyper import compiler from vyper import compiler
from vyper.parser import (
parser,
)
from vdb.vdb import (
set_evm_opcode_debugger,
set_evm_opcode_pass
)
from vdb.source_map import ( from vdb.source_map import (
produce_source_map produce_source_map
) )
from eth_tester import ( from vdb.eth_tester_debug_backend import (
EthereumTester, PyEVMDebugBackend,
set_debug_info
) )
from web3.providers.eth_tester import ( from web3.providers.eth_tester import (
EthereumTesterProvider, EthereumTesterProvider,
@ -30,7 +25,6 @@ aparser.add_argument('call_list', help='call list, without parameters: func, wit
aparser.add_argument('-i', help='init args, comma separated', default=None, dest='init_args') aparser.add_argument('-i', help='init args, comma separated', default=None, dest='init_args')
args = aparser.parse_args() args = aparser.parse_args()
set_evm_opcode_pass() # by default just pass over the debug opcode.
def cast_types(args, abi_signature): def cast_types(args, abi_signature):
@ -43,10 +37,17 @@ def cast_types(args, abi_signature):
return newargs return newargs
def get_tester(): def get_tester(code):
tester = EthereumTester() from eth_tester import (
EthereumTester,
)
source_map = produce_source_map(code)
set_debug_info(code, source_map)
tester = EthereumTester(backend=PyEVMDebugBackend())
def zero_gas_price_strategy(web3, transaction_params=None): def zero_gas_price_strategy(web3, transaction_params=None):
return 0 # zero gas price makes testing simpler. return 0 # zero gas price makes testing simpler.
w3 = Web3(EthereumTesterProvider(tester)) w3 = Web3(EthereumTesterProvider(tester))
w3.eth.setGasPriceStrategy(zero_gas_price_strategy) w3.eth.setGasPriceStrategy(zero_gas_price_strategy)
return tester, w3 return tester, w3
@ -122,7 +123,7 @@ if __name__ == '__main__':
code = fh.read() code = fh.read()
# Patch in vdb. # Patch in vdb.
init_args = args.init_args.split(',') if args.init_args else [] init_args = args.init_args.split(',') if args.init_args else []
tester, w3 = get_tester() tester, w3 = get_tester(code)
# Built list of calls to make. # Built list of calls to make.
calls = [] calls = []
@ -146,7 +147,6 @@ if __name__ == '__main__':
init_abi = next(filter(lambda func: func["name"] == '__init__', abi)) init_abi = next(filter(lambda func: func["name"] == '__init__', abi))
init_args = cast_types(init_args, init_abi) init_args = cast_types(init_args, init_abi)
# Compile contract to chain. # Compile contract to chain.
contract = get_contract(w3, code, *init_args) contract = get_contract(w3, code, *init_args)
@ -167,10 +167,7 @@ if __name__ == '__main__':
res = getattr(contract.functions, func_name)(*cast_args).call({'gas': func_abi.get('gas', 0) + 50000}) res = getattr(contract.functions, func_name)(*cast_args).call({'gas': func_abi.get('gas', 0) + 50000})
source_map = produce_source_map(code)
set_evm_opcode_debugger(source_code=code, source_map=source_map)
tx_hash = getattr(contract.functions, func_name)(*cast_args).transact({'gas': func_abi.get('gas', 0) + 50000}) tx_hash = getattr(contract.functions, func_name)(*cast_args).transact({'gas': func_abi.get('gas', 0) + 50000})
set_evm_opcode_pass()
print('- Returns:') print('- Returns:')
res = getattr(contract.functions, func_name)(*cast_args).call({'gas': func_abi['gas'] + 92000}) res = getattr(contract.functions, func_name)(*cast_args).call({'gas': func_abi['gas'] + 92000})

70
vdb/debug_computation.py Normal file
View File

@ -0,0 +1,70 @@
from evm.vm.forks.byzantium.computation import (
ByzantiumComputation,
)
from evm.exceptions import (
Halt,
)
from vdb.vdb import VyperDebugCmd
class DebugComputation(ByzantiumComputation):
source_code = None
source_map = None
@classmethod
def run_debugger(self, computation, line_no):
VyperDebugCmd(
computation,
line_no=line_no,
source_code=self.source_code,
source_map=self.source_map,
stdin=None,
stdout=None
).cmdloop()
@classmethod
def get_line_no(cls, pc):
pc_pos_map = cls.source_map['line_number_map']['pc_pos_map']
if pc in pc_pos_map:
return pc_pos_map[pc][0]
@classmethod
def is_breakpoint(cls, pc):
breakpoint_lines = cls.source_map['line_number_map']['breakpoints']
line_no = cls.get_line_no(pc)
if line_no is not None:
return line_no in breakpoint_lines, line_no
return False, None
@classmethod
def apply_computation(cls, state, message, transaction_context):
with cls(state, message, transaction_context) as computation:
# Early exit on pre-compiles
if message.code_address in computation.precompiles:
computation.precompiles[message.code_address](computation)
return computation
for opcode in computation.code:
opcode_fn = computation.get_opcode_fn(opcode)
pc_to_execute = max(0, computation.code.pc - 1)
computation.logger.trace(
"OPCODE: 0x%x (%s) | pc: %s",
opcode,
opcode_fn.mnemonic,
pc_to_execute,
)
is_breakpoint, line_no = cls.is_breakpoint(pc_to_execute)
if is_breakpoint:
# import ipdb; ipdb.set_trace()
cls.run_debugger(computation, line_no)
try:
opcode_fn(computation=computation)
except Halt:
break
return computation

View File

@ -0,0 +1,57 @@
from evm.vm.forks.byzantium import ByzantiumVM
from evm.vm.forks.byzantium.state import ByzantiumState
from vdb.debug_computation import DebugComputation
from eth_tester.backends.pyevm.main import (
get_default_genesis_params,
generate_genesis_state,
get_default_genesis_params,
get_default_account_keys,
PyEVMBackend
)
class DebugState(ByzantiumState):
computation_class = DebugComputation
class DebugVM(ByzantiumVM):
_state_class = DebugState # type: Type[BaseState]
def _setup_tester_chain():
from evm.chains.tester import MainnetTesterChain
from evm.db import get_db_backend
class DebugNoProofVM(DebugVM):
"""Byzantium VM rules, without validating any miner proof of work"""
def validate_seal(self, header):
pass
class MainnetTesterNoProofChain(MainnetTesterChain):
vm_configuration = ((0, DebugNoProofVM), )
genesis_params = get_default_genesis_params()
account_keys = get_default_account_keys()
genesis_state = generate_genesis_state(account_keys)
base_db = get_db_backend()
chain = MainnetTesterNoProofChain.from_genesis(base_db, genesis_params, genesis_state)
return account_keys, chain
class PyEVMDebugBackend(PyEVMBackend):
def __init__(self, ):
super().__init__()
def reset_to_genesis(self):
self.account_keys, self.chain = _setup_tester_chain()
def set_debug_info(source_code, source_map):
setattr(DebugComputation, 'source_code', source_code)
setattr(DebugComputation, 'source_map', source_map)

View File

@ -10,6 +10,9 @@ from vyper.types import (
MappingType, MappingType,
TupleType TupleType
) )
from vyper import compile_lll
from vyper import optimizer
from vyper.parser.parser import parse_to_lll
def serialise_var_rec(var_rec): def serialise_var_rec(var_rec):
@ -36,9 +39,12 @@ def serialise_var_rec(var_rec):
def produce_source_map(code): def produce_source_map(code):
global_ctx = GlobalContext.get_global_context(parser.parse(code)) global_ctx = GlobalContext.get_global_context(parser.parse(code))
asm_list = compile_lll.compile_to_assembly(optimizer.optimize(parse_to_lll(code, runtime_only=True)))
c, line_number_map = compile_lll.assembly_to_evm(asm_list)
source_map = { source_map = {
'globals': {}, 'globals': {},
'locals': {} 'locals': {},
'line_number_map': line_number_map
} }
source_map['globals'] = { source_map['globals'] = {
name: serialise_var_rec(var_record) name: serialise_var_rec(var_record)

View File

@ -9,7 +9,6 @@ from eth_utils import (
import evm import evm
from evm import constants from evm import constants
from evm.vm.opcode import as_opcode from evm.vm.opcode import as_opcode
from vyper.opcodes import opcodes as vyper_opcodes from vyper.opcodes import opcodes as vyper_opcodes
from vdb.variables import ( from vdb.variables import (
parse_global, parse_global,