Skip to content

Commit 3422cdf

Browse files
authored
Merge pull request #350 from lidofinance/feature/trace-tx-from-receipt-improvements
Enhance parsing of Voting events from tx receipt
2 parents 0891ed5 + e24e27e commit 3422cdf

File tree

3 files changed

+206
-8
lines changed

3 files changed

+206
-8
lines changed
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
from utils.agent import agent_forward
2+
from utils.voting import create_vote, bake_vote_items
3+
from utils.config import contracts, LDO_HOLDER_ADDRESS_FOR_TESTS, ARAGON_CALLS_SCRIPT, GATE_SEAL_FACTORY
4+
from utils.test.tx_tracing_helpers import group_voting_events_from_receipt, group_voting_events
5+
from utils.evm_script import encode_call_script
6+
from utils.easy_track import add_evmscript_factory, create_permissions
7+
from utils.allowed_recipients_registry import create_top_up_allowed_recipient_permission
8+
from utils.test.event_validators.easy_track import (
9+
validate_evmscript_factory_added_event,
10+
EVMScriptFactoryAdded,
11+
)
12+
13+
14+
def test_empty_voting(helpers, accounts):
15+
vote_id, _ = create_vote(bake_vote_items([], []), tx_params={"from": LDO_HOLDER_ADDRESS_FOR_TESTS})
16+
vote_execute_tx = helpers.execute_vote(accounts, vote_id, contracts.voting)
17+
events_from_receipt = group_voting_events_from_receipt(vote_execute_tx)
18+
19+
assert len(events_from_receipt) == 0
20+
21+
22+
def test_voting_with_empty_agent_forward(helpers, accounts):
23+
vote_id, _ = create_vote(
24+
bake_vote_items(["1. Item with empty aragon forward"], [agent_forward([])]),
25+
tx_params={"from": LDO_HOLDER_ADDRESS_FOR_TESTS},
26+
)
27+
vote_execute_tx = helpers.execute_vote(accounts, vote_id, contracts.voting)
28+
events_from_receipt = group_voting_events_from_receipt(vote_execute_tx)
29+
30+
assert len(events_from_receipt) == 1
31+
assert len(events_from_receipt[0]["LogScriptCall"]) == 1
32+
assert len(events_from_receipt[0]["ScriptResult"]) == 1
33+
34+
log_script_call_event = events_from_receipt[0]["LogScriptCall"]
35+
assert log_script_call_event["sender"] == vote_execute_tx.sender
36+
assert log_script_call_event["src"] == contracts.voting
37+
assert log_script_call_event["dst"] == contracts.agent
38+
39+
script_result_event = events_from_receipt[0]["ScriptResult"]
40+
assert script_result_event["executor"] == ARAGON_CALLS_SCRIPT
41+
assert script_result_event["script"] == "0x00000001"
42+
assert script_result_event["input"] == "0x00"
43+
assert script_result_event["returnData"] == "0x00"
44+
45+
46+
def test_calls_without_events(accounts, helpers):
47+
vote_desc_items, call_script_items = zip(
48+
(
49+
"View method without events from Voting",
50+
(contracts.ldo_token.address, contracts.ldo_token.totalSupply.encode_input()),
51+
),
52+
(
53+
"View method without events forward to Agent",
54+
agent_forward([(contracts.voting.address, contracts.voting.isForwarder.encode_input())]),
55+
),
56+
)
57+
vote_id, _ = create_vote(
58+
bake_vote_items(list(vote_desc_items), list(call_script_items)),
59+
tx_params={"from": LDO_HOLDER_ADDRESS_FOR_TESTS},
60+
)
61+
vote_execute_tx = helpers.execute_vote(accounts, vote_id, contracts.voting)
62+
events_from_receipt = group_voting_events_from_receipt(vote_execute_tx)
63+
64+
assert len(events_from_receipt) == 2
65+
66+
# first item of the vote, even though view method doesn't emit any event, service LogScriptCall
67+
# event was emitted with correct params
68+
69+
assert len(events_from_receipt[0]) == 1
70+
assert len(events_from_receipt[0]["LogScriptCall"]) == 1
71+
72+
voting_log_script_call_event = events_from_receipt[0]["LogScriptCall"]
73+
assert voting_log_script_call_event["sender"] == vote_execute_tx.sender
74+
assert voting_log_script_call_event["src"] == contracts.voting
75+
assert voting_log_script_call_event["dst"] == contracts.ldo_token
76+
77+
# second item of the vote, beside LogScriptCall from Voting contract it also includes
78+
# LogScriptCall and ScriptResult events from forward() call to Agent
79+
80+
assert len(events_from_receipt[1]) == 3
81+
assert len(events_from_receipt[1]["LogScriptCall"]) == 2
82+
assert len(events_from_receipt[1]["ScriptResult"]) == 1
83+
84+
voting_log_script_call_event = events_from_receipt[1]["LogScriptCall"][0]
85+
assert voting_log_script_call_event["sender"] == vote_execute_tx.sender
86+
assert voting_log_script_call_event["src"] == contracts.voting
87+
assert voting_log_script_call_event["dst"] == contracts.agent
88+
89+
agent_log_script_call_event = events_from_receipt[1]["LogScriptCall"][1]
90+
assert agent_log_script_call_event["sender"] == contracts.voting
91+
assert agent_log_script_call_event["src"] == contracts.agent
92+
assert agent_log_script_call_event["dst"] == contracts.voting
93+
94+
agent_script_result_event = events_from_receipt[1]["ScriptResult"]
95+
assert agent_script_result_event["executor"] == ARAGON_CALLS_SCRIPT
96+
assert agent_script_result_event["script"] == encode_call_script(
97+
[(contracts.voting.address, contracts.voting.isForwarder.encode_input())]
98+
)
99+
assert agent_script_result_event["input"] == "0x00"
100+
assert agent_script_result_event["returnData"] == "0x00"
101+
102+
103+
def test_multiple_events_happy_path(accounts, helpers, interface, chain):
104+
seconds_per_day = 24 * 60 * 60
105+
seal_duration = 7 * seconds_per_day
106+
expiry_timestamp = chain.time() + 30 * seconds_per_day
107+
108+
# brownie checks that address contain code during permissions build, so use agent contract as stub
109+
registry_stub_address = contracts.agent.address
110+
new_evm_script_factory_stub = accounts[-1]
111+
112+
gate_seal_factory = interface.GateSealFactory(GATE_SEAL_FACTORY)
113+
114+
vote_desc_items, call_script_items = zip(
115+
(
116+
"Add EVMScriptFactory from Voting",
117+
add_evmscript_factory(
118+
factory=new_evm_script_factory_stub,
119+
permissions=create_top_up_allowed_recipient_permission(registry_address=registry_stub_address),
120+
),
121+
),
122+
(
123+
"Deploy Gate Seal through Agent forwarding",
124+
agent_forward(
125+
[
126+
(
127+
gate_seal_factory.address,
128+
gate_seal_factory.create_gate_seal.encode_input(
129+
contracts.agent.address,
130+
seal_duration,
131+
[contracts.withdrawal_queue],
132+
expiry_timestamp,
133+
),
134+
)
135+
]
136+
),
137+
),
138+
)
139+
140+
vote_id, _ = create_vote(
141+
bake_vote_items(list(vote_desc_items), list(call_script_items)),
142+
tx_params={"from": LDO_HOLDER_ADDRESS_FOR_TESTS},
143+
)
144+
vote_execute_tx = helpers.execute_vote(accounts, vote_id, contracts.voting)
145+
events_from_receipt = group_voting_events_from_receipt(vote_execute_tx)
146+
147+
assert len(events_from_receipt) == 2
148+
149+
assert len(events_from_receipt[0]["LogScriptCall"]) == 1
150+
assert len(events_from_receipt[0]["EVMScriptFactoryAdded"]) == 1
151+
152+
validate_evmscript_factory_added_event(
153+
events_from_receipt[0],
154+
EVMScriptFactoryAdded(
155+
factory_addr=new_evm_script_factory_stub,
156+
permissions=create_permissions(contracts.finance, "newImmediatePayment")
157+
+ create_permissions(interface.AllowedRecipientRegistry(registry_stub_address), "updateSpentAmount")[2:],
158+
),
159+
)
160+
161+
assert len(events_from_receipt[1]["LogScriptCall"]) == 2
162+
assert len(events_from_receipt[1]["GateSealCreated"]) == 1
163+
assert len(events_from_receipt[1]["ScriptResult"]) == 1
164+
165+
voting_log_script_call_event = events_from_receipt[1]["LogScriptCall"][0]
166+
assert voting_log_script_call_event["sender"] == vote_execute_tx.sender
167+
assert voting_log_script_call_event["src"] == contracts.voting
168+
assert voting_log_script_call_event["dst"] == contracts.agent
169+
170+
agent_log_script_call_event = events_from_receipt[1]["LogScriptCall"][1]
171+
assert agent_log_script_call_event["sender"] == contracts.voting
172+
assert agent_log_script_call_event["src"] == contracts.agent
173+
assert agent_log_script_call_event["dst"] == gate_seal_factory
174+
175+
agent_script_result_event = events_from_receipt[1]["ScriptResult"]
176+
assert agent_script_result_event["executor"] == ARAGON_CALLS_SCRIPT
177+
assert agent_script_result_event["script"] == encode_call_script(
178+
[
179+
(
180+
gate_seal_factory.address,
181+
gate_seal_factory.create_gate_seal.encode_input(
182+
contracts.agent.address,
183+
seal_duration,
184+
[contracts.withdrawal_queue],
185+
expiry_timestamp,
186+
),
187+
)
188+
]
189+
)
190+
assert agent_script_result_event["input"] == "0x00"
191+
assert agent_script_result_event["returnData"] == "0x00"
192+
193+
# Additionally check that events from trace produce same result
194+
195+
events_from_trace = group_voting_events(vote_execute_tx)
196+
assert str(events_from_receipt) == str(events_from_trace)

utils/test/tx_tracing_helpers.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,21 +69,23 @@ def group_voting_events(tx: TransactionReceipt) -> List[EventDict]:
6969
def group_voting_events_from_receipt(tx: TransactionReceipt) -> List[EventDict]:
7070
events = tx_events_from_receipt(tx)
7171

72+
# Validate "service" Voting events are in the log
73+
assert len(events) >= 2, "Unexpected events count"
74+
assert events[-2]["address"] == VOTING and events[-2]["name"] == "ScriptResult", "Unexpected Voting service event"
75+
assert events[-1]["address"] == VOTING and events[-1]["name"] == "ExecuteVote", "Unexpected Voting service event"
76+
7277
groups = []
7378
current_group = None
7479

75-
for event in events:
80+
for event in events[:-2]:
7681
is_start_of_new_group = event["name"] == "LogScriptCall" and event["address"] == VOTING
7782

7883
if is_start_of_new_group:
7984
current_group = []
8085
groups.append(current_group)
8186

82-
current_group.append(event)
87+
assert current_group != None, "Unexpected events chain"
8388

84-
event_dict_groups = []
85-
for group in groups:
86-
events = EventDict(group)
87-
event_dict_groups.append(events)
89+
current_group.append(event)
8890

89-
return event_dict_groups
91+
return [EventDict(group) for group in groups]

utils/tx_tracing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def _find_fist_index_of_event_with_different_from_first_event_address(events):
7070
return len(events)
7171

7272

73-
def tx_events_from_receipt(tx: TransactionReceipt) -> Optional[List]:
73+
def tx_events_from_receipt(tx: TransactionReceipt) -> List:
7474
if not tx.status:
7575
raise "Tx has reverted status (set to 0)"
7676

0 commit comments

Comments
 (0)