commit
d044b2c38e
|
@ -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})
|
||||||
|
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue