Skip to content
This repository was archived by the owner on May 23, 2023. It is now read-only.

debug_traceTransaction support #445

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion ethereum/config.py
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@
# Genesis block difficulty
GENESIS_DIFFICULTY=131072,
# Genesis block gas limit
GENESIS_GAS_LIMIT=3141592,
GENESIS_GAS_LIMIT=8000000, #4712388,
# Genesis block prevhash, coinbase, nonce
GENESIS_PREVHASH=b'\x00' * 32,
GENESIS_COINBASE=b'\x00' * 20,
@@ -66,6 +66,8 @@
# Anti-DoS fork
ANTI_DOS_FORK_BLKNUM=2457000,
CLEARING_FORK_BLKNUM=2 ** 98,
# Trace
TRACE_TRANSACTIONS=True
)
assert default_config['NEPHEW_REWARD'] == \
default_config['BLOCK_REWARD'] // 32
32 changes: 22 additions & 10 deletions ethereum/processblock.py
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
from ethereum import specials
from ethereum import bloom
from ethereum import vm as vm
from ethereum.trace import Trace
from ethereum.exceptions import InvalidNonce, InsufficientStartGas, UnsignedTransaction, \
BlockGasLimitReached, InsufficientBalance, VerificationFailed
from ethereum.utils import safe_ord, normalize_address, mk_contract_address, \
@@ -22,6 +23,7 @@
log_msg = get_logger('eth.pb.msg')
log_state = get_logger('eth.pb.msg.state')


TT255 = 2 ** 255
TT256 = 2 ** 256
TT256M1 = 2 ** 256 - 1
@@ -167,11 +169,16 @@ def rp(what, actual, target):

# MESSAGE
ext = VMExt(block, tx)
tr = Trace()
if tr.enabled:
oldStorage = ext.get_storage(tx.to)
if tx.to and tx.to != CREATE_CONTRACT_ADDRESS:
result, gas_remained, data = apply_msg(ext, message)
result, gas_remained, data, trc = apply_msg(ext, message)
#We receive bytesarray for data
log_tx.debug('_res_', result=result, gas_remained=gas_remained, data=lazy_safe_encode(data))
else: # CREATE
result, gas_remained, data = create_contract(ext, message)
result, gas_remained, data, trc = create_contract(ext, message)
#We receive address for data
assert utils.is_numeric(gas_remained)
log_tx.debug('_create_', result=result, gas_remained=gas_remained, data=lazy_safe_encode(data))

@@ -184,6 +191,7 @@ def rp(what, actual, target):
log_tx.debug('TX FAILED', reason='out of gas',
startgas=tx.startgas, gas_remained=gas_remained)
block.gas_used += tx.startgas
gas_used = tx.startgas
block.delta_balance(block.coinbase, tx.gasprice * tx.startgas)
output = b''
success = 0
@@ -214,6 +222,9 @@ def rp(what, actual, target):
block.del_account(s)
block.add_transaction_to_list(tx)
block.logs = []
if trc and tr.enabled:
tr.addTrace(tx.hash.encode('hex'), { "returnValue":output, "gas":gas_used, "structLogs":trc })
tr.addStorage(ext.block_number, tx.hash.encode('hex'), oldStorage);
return success, output


@@ -230,6 +241,7 @@ def __init__(self, block, tx):
self.get_nonce = block.get_nonce
self.set_nonce = block.set_nonce
self.set_storage_data = block.set_storage_data
self.get_storage = block.get_storage
self.get_storage_data = block.get_storage_data
self.log_storage = lambda x: block.account_to_dict(x)['storage']
self.add_suicide = lambda x: block.suicides.append(x)
@@ -282,7 +294,7 @@ def _apply_msg(ext, msg, code):
if msg.code_address in specials.specials:
res, gas, dat = specials.specials[msg.code_address](ext, msg)
else:
res, gas, dat = vm.vm_execute(ext, msg, code)
res, gas, dat, trc = vm.vm_execute(ext, msg, code)
# gas = int(gas)
# assert utils.is_numeric(gas)
if trace_msg:
@@ -299,8 +311,8 @@ def _apply_msg(ext, msg, code):
if res == 0:
log_msg.debug('REVERTING')
ext._block.revert(snapshot)

return res, gas, dat
return res, gas, dat, trc


def create_contract(ext, msg):
@@ -332,12 +344,12 @@ def create_contract(ext, msg):
# assert not ext.get_code(msg.to)
msg.data = vm.CallData([], 0, 0)
snapshot = ext._block.snapshot()
res, gas, dat = _apply_msg(ext, msg, code)
res, gas, dat, trc = _apply_msg(ext, msg, code)
assert utils.is_numeric(gas)

if res:
if not len(dat):
return 1, gas, msg.to
return 1, gas, msg.to, trc
gcost = len(dat) * opcodes.GCONTRACTBYTE
if gas >= gcost:
gas -= gcost
@@ -346,8 +358,8 @@ def create_contract(ext, msg):
log_msg.debug('CONTRACT CREATION OOG', have=gas, want=gcost, block_number=ext._block.number)
if ext._block.number >= ext._block.config['HOMESTEAD_FORK_BLKNUM']:
ext._block.revert(snapshot)
return 0, 0, b''
return 0, 0, b'', trc
ext._block.set_code(msg.to, b''.join(map(ascii_chr, dat)))
return 1, gas, msg.to
return 1, gas, msg.to, trc
else:
return 0, gas, b''
return 0, gas, b'', trc
51 changes: 40 additions & 11 deletions ethereum/tester.py
Original file line number Diff line number Diff line change
@@ -8,12 +8,13 @@
import rlp
from rlp.utils import ascii_chr

from ethereum import blocks, db, opcodes, processblock, transactions
from ethereum import blocks, db, opcodes, transactions, processblock
from ethereum.abi import ContractTranslator
from ethereum.config import Env
from ethereum.slogging import LogRecorder
from ethereum._solidity import get_solidity
from ethereum.utils import to_string, sha3, privtoaddr, int_to_addr
from ethereum.trace import Trace

TRACE_LVL_MAP = [
':info',
@@ -24,21 +25,50 @@
'eth.vm.storage:trace,eth.vm.memory:trace'
]

GAS_LIMIT = 3141592
GAS_PRICE = 1
GAS_LIMIT = 8000000
GAS_PRICE = 20000000000

# pylint: disable=invalid-name

gas_limit = GAS_LIMIT
gas_price = GAS_PRICE

accounts = []
keys = []
accounts = [
u"ac7bebd558c734fe105d09167860b230fb0a218d".decode('hex'),
u"6e5fd1741e45c966a76a077af9132627c07b0dc1".decode('hex'),
u"ef057953c56855f16e658bf8fd0d2e300961fc1f".decode('hex'),
u"2c284ef5a0d50dda177bd8c9fdf20610f6fdac09".decode('hex'),
u"bd3c601b59f46cc59be3446ba29c66b9182a70b6".decode('hex'),
u"e6e6033428cfc58af1585c26a823916c8120ca73".decode('hex'),
u"e2c628c146a9d40c9ed4c5c3e29cd0a609f7c6f1".decode('hex'),
u"3e2ff0583a5dec1bd3ac0f7b8d26fa96b759fe92".decode('hex'),
u"2827a89f78d70c422452528634cfe522b5c668c6".decode('hex'),
u"f56ae85523c6f4773954fe0b25ba1f52e1183689".decode('hex'),
u"7703aCa0f4ee937C3073ec80c7608B6f7cE2426B".decode('hex'),
u"153ee6aD2e7e665b8a07ff37d93271d6E5FDc6d4".decode('hex'),
u"4fe1ead95d882580561b704938ecbd5cb9450ab6".decode('hex'),
]
keys = [
u"43df74be7858da13bb08c1289fe488b9843c555538dd1638203725b18a19863e".decode('hex'),
u"0af37c53fdc5b97bf1f84d30e84f09c84f733e467a3b26c1ce6c5d448f9d7cec".decode('hex'),
u"0d5c1bd818a4086f28314415cb375a937593efab66f8f7d2903bf2a13ed35070".decode('hex'),
u"17029bda254fdf118125741e80d2d43e8ac1ffa8cca20c51683bc0735c802e5b".decode('hex'),
u"8d2fd08f91550712ec0db96bdbb849ec88a560e200c58f05954827b2593cf9e7".decode('hex'),
u"803ae2f3b0030390092910e0f1e8ec15dbc975d6422ab2274b175c74eed589fb".decode('hex'),
u"0c71a0e6e4bf22677a5750c123aaf988270e1c4025f80d82b7a18f2efe295cbf".decode('hex'),
u"9ca1d29731e6302e3e7d7f0ebaf2b4fa48d7b7fd4f5c82f59b3983b0a2160d7e".decode('hex'),
u"e2631c2443b12abdfc5e70e3b7643f2d340eb49c6102c66835d0c7286903b009".decode('hex'),
u"f7a590340d042a107ec3c9e82f81d2301ecc5b79f959f7a09d3c7fb7ab7f1024".decode('hex'),
u"9e256901c752616c231904281333e5dd11fa48fdfd7ffac3515923b1fe60a28e".decode('hex'),
u"b10ec925fccdaf155fa0b56bd55cf6ce3cc8927b304c0a563476529d925755d5".decode('hex'),
u"71c52b034f6405fbabb6ad5e91997dd2ea1f43d48502e10dd47d5a4a8a02cbdf".decode('hex'),
]

languages = {}

for account_number in range(10):
keys.append(sha3(to_string(account_number)))
accounts.append(privtoaddr(keys[-1]))
#for account_number in range(10):
# keys.append(sha3(to_string(account_number)))
# accounts.append(privtoaddr(keys[-1]))

k0, k1, k2, k3, k4, k5, k6, k7, k8, k9 = keys[:10]
a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 = accounts[:10]
@@ -157,13 +187,12 @@ def kall(self, *args, **kwargs):


class state(object):

def __init__(self, num_accounts=len(keys)):
self.temp_data_dir = tempfile.mkdtemp()
self.db = db.EphemDB()
self.env = Env(self.db)
self.last_tx = None

initial_balances = {}

for i in range(num_accounts):
@@ -293,7 +322,7 @@ def _send(self, sender, to, value, evmdata='', funid=None, abi=None, # pylint:
(success, output) = processblock.apply_transaction(self.block, transaction)

if not success:
raise TransactionFailed()
raise TransactionFailed(transaction.hash.encode('hex'))

out = {
'output': output,
2 changes: 1 addition & 1 deletion ethereum/testutils.py
Original file line number Diff line number Diff line change
@@ -220,7 +220,7 @@ def blkhash(n):
time_pre = time.time()
if profiler:
profiler.enable()
success, gas_remained, output = vm.vm_execute(ext, msg, code)
success, gas_remained, output, trc = vm.vm_execute(ext, msg, code)
if profiler:
profiler.disable()
pb.apply_msg = orig_apply_msg
53 changes: 53 additions & 0 deletions ethereum/trace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
from ethereum.config import Env
from ethereum import config

class Trace(object):
storages = {}
transactions = {}
enabled = None
def __init__(self):
self.enabled = config.default_config['TRACE_TRANSACTIONS']

def getTrace(self, tx_hash):
if not self.enabled: raise Exception('Trace transaction is disabled!')
if tx_hash in self.transactions:
return self.transactions[tx_hash]
else:
raise Exception('Transaction not found!')

def addTrace(self, tx_hash, tx_trace):
if self.enabled:
if tx_hash.lower()[:2] != "0x": tx_hash = "0x"+tx_hash
if tx_hash in self.transactions:
# extend!
self.transactions[tx_hash]["structLogs"].extend(tx_trace["structLogs"])
self.transactions[tx_hash]["returnValue"] = tx_trace["returnValue"]
self.transactions[tx_hash]["gas"] = tx_trace["gas"]
else:
self.transactions[tx_hash] = tx_trace
return True
return False

def addStorage(self, block_num, tx_hash, storage):
if self.enabled:
str = {}
storage = storage.to_dict()
for a in storage:
str[a.encode('hex')] = storage[a].encode('hex')
if not block_num in self.storages: self.storages[block_num] = []
tmp = [tx_hash in i for i in self.storages[block_num]]
if (True in tmp):
self.storages[block_num][tmp.index(True)] = str
else:
self.storages[block_num].append({ tx_hash:str })
return True
return False

def getStorage(self, block_num, tx_num, contract_address, stor_start, stor_end, limit):
# stor_start, stor_end, limit not yet implemented
if self.enabled:
if not block_num in self.storages: raise Exception('Block not found!')
if tx_num >= len(self.storages[block_num]): raise Exception('TX not found!')
return { "complete": True, "storage": self.storages[block_num][tx_num] }
raise Exception('Trace is disabled!')
Loading