Skip to content

Commit

Permalink
Bug/events should be linkable to emitting contract (kkrt-labs#603)
Browse files Browse the repository at this point in the history
copy of kkrt-labs#601

---------

Co-authored-by: johann makram salib bestowrous <[email protected]>
Co-authored-by: kakarot CI <[email protected]>
  • Loading branch information
3 people authored Jun 28, 2023
1 parent 5b2b9bf commit 1a52997
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
if: github.event_name == 'push'
run: |
git config --global user.name "${{ env.CI_COMMIT_AUTHOR }}"
git config --global user.email "sayajin[email protected]"
git config --global user.email "kkrt[email protected]"
git add -A
git diff-index --quiet HEAD || (git commit -m "${{ env.CI_COMMIT_MESSAGE }}" && git push)
- name: Compile all the cairo files
Expand Down
6 changes: 1 addition & 5 deletions scripts/deploy_kakarot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@
import logging
from asyncio import run

from scripts.constants import (
COMPILED_CONTRACTS,
ETH_TOKEN_ADDRESS,
EVM_ADDRESS,
)
from scripts.constants import COMPILED_CONTRACTS, ETH_TOKEN_ADDRESS, EVM_ADDRESS
from scripts.utils.starknet import (
declare,
deploy,
Expand Down
19 changes: 15 additions & 4 deletions src/kakarot/execution_context.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -312,27 +312,38 @@ namespace ExecutionContext {
}

// @notice Iterates through a list of events and emits them on the case that a context ran successfully (stopped and not reverted).
// @param evm_contract_address The execution context's evm contract address.
// @param events_len The length of the events array.
// @param events The array of Event structs that are emitted via the `emit_event` syscall.
func emit_events{
syscall_ptr: felt*,
pedersen_ptr: HashBuiltin*,
range_check_ptr,
bitwise_ptr: BitwiseBuiltin*,
}(events_len: felt, events: model.Event*) {
}(evm_contract_address: felt, events_len: felt, events: model.Event*) {
alloc_locals;

if (events_len == 0) {
return ();
}

let event: model.Event = [events];
let event_keys_felt = cast(event.keys, felt*);
// we add the operating evm_contract_address of an execution context
// as the final key of an event
// we track kakarot events as those emitted from the kkrt contract
// and map it to the kkrt contract via this convention
assert [event_keys_felt + events.keys_len] = evm_contract_address;
let updated_event_len = event.keys_len + 1;

emit_event(
keys_len=event.keys_len, keys=event.keys, data_len=event.data_len, data=event.data
keys_len=updated_event_len,
keys=event_keys_felt,
data_len=event.data_len,
data=event.data,
);
// we maintain the semantics of one event struct involves iterating a full event struct size recursively
emit_events(events_len - 1, events + 1 * model.Event.SIZE);
emit_events(evm_contract_address, events_len - 1, events + 1 * model.Event.SIZE);
return ();
}

Expand Down Expand Up @@ -364,7 +375,7 @@ namespace ExecutionContext {
// this is called after a top level check that a given context is stopped
// so this is the case of a stopped, non reverted context
// meaning events should be fired off!
emit_events(self.events_len, self.events);
emit_events(self.evm_contract_address, self.events_len, self.events);
return self;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/kakarot/model.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ namespace model {
// @param events_len The events length.
// @param events The events to be emitted upon a non-reverted stopped execution context.
// @param create_addresses_len The create_addresses length.
// @param events The addresses of contracts initialized by the create(2) opcodes that are deleted if the creating context is reverted.
// @param create_addresses The addresses of contracts initialized by the create(2) opcodes that are deleted if the creating context is reverted.
// @param revert_contract_state A dictionary that keeps track of the prior-to-first-write value of a contract storage key so it can be reverted to if the writing execution context reverts.
// @param read_only if set to true, context cannot do any state modifying instructions or send ETH in the sub context.
struct ExecutionContext {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ def event(self):

async def test_should_emit_log0_with_no_data(self, plain_opcodes, addresses):
await plain_opcodes.opcodeLog0(caller_address=addresses[0].starknet_address)
# the contract address is set at deploy time, we verify that event address is
# getting correctly set by asserting equality
expected_address = plain_opcodes.address
for log_receipt in plain_opcodes.raw_log_receipts:
assert log_receipt["address"] == expected_address
assert plain_opcodes.events.Log0 == [{}]

async def test_should_emit_log0_with_data(
Expand All @@ -106,23 +111,48 @@ async def test_should_emit_log0_with_data(
await plain_opcodes.opcodeLog0Value(
caller_address=addresses[0].starknet_address
)
# the contract address is set at deploy time, we verify that event address is
# getting correctly set by asserting equality
expected_address = plain_opcodes.address
for log_receipt in plain_opcodes.raw_log_receipts:
assert log_receipt["address"] == expected_address
assert plain_opcodes.events.Log0Value == [{"value": event["value"]}]

async def test_should_emit_log1(self, plain_opcodes, addresses, event):
await plain_opcodes.opcodeLog1(caller_address=addresses[0].starknet_address)
# the contract address is set at deploy time, we verify that event address is
# getting correctly set by asserting equality
expected_address = plain_opcodes.address
for log_receipt in plain_opcodes.raw_log_receipts:
assert log_receipt["address"] == expected_address
assert plain_opcodes.events.Log1 == [{"value": event["value"]}]

async def test_should_emit_log2(self, plain_opcodes, addresses, event):
await plain_opcodes.opcodeLog2(caller_address=addresses[0].starknet_address)
del event["spender"]
# the contract address is set at deploy time, we verify that event address is
# getting correctly set by asserting equality
expected_address = plain_opcodes.address
for log_receipt in plain_opcodes.raw_log_receipts:
assert log_receipt["address"] == expected_address
assert plain_opcodes.events.Log2 == [event]

async def test_should_emit_log3(self, plain_opcodes, addresses, event):
await plain_opcodes.opcodeLog3(caller_address=addresses[0].starknet_address)
# the contract address is set at deploy time, we verify that event address is
# getting correctly set by asserting equality
expected_address = plain_opcodes.address
for log_receipt in plain_opcodes.raw_log_receipts:
assert log_receipt["address"] == expected_address
assert plain_opcodes.events.Log3 == [event]

async def test_should_emit_log4(self, plain_opcodes, addresses, event):
await plain_opcodes.opcodeLog4(caller_address=addresses[0].starknet_address)
# the contract address is set at deploy time, we verify that event address is
# getting correctly set by asserting equality
expected_address = plain_opcodes.address
for log_receipt in plain_opcodes.raw_log_receipts:
assert log_receipt["address"] == expected_address
assert plain_opcodes.events.Log4 == [event]

class TestCreate2:
Expand Down
3 changes: 2 additions & 1 deletion tests/integration/test_kakarot.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ async def test_execute(
if events:
assert [
[
event.keys,
# we remove the key that is used to convey the emitting kkrt evm contract
event.keys[:-1],
event.data,
]
for event in sorted(res.call_info.events, key=lambda x: x.order)
Expand Down
8 changes: 6 additions & 2 deletions tests/utils/contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import web3
from eth_abi.exceptions import InsufficientDataBytes
from eth_utils.address import to_checksum_address
from starkware.starknet.testing.starknet import StarknetContract
from web3 import Web3
from web3._utils.abi import map_abi_data
Expand Down Expand Up @@ -118,9 +119,11 @@ async def _wrapped(self, *args, **kwargs):
for log_index, event in enumerate(res.raw_events):
# Using try/except as some events are emitted by cairo code and not LOG opcode
try:
# every kkrt evm event emission appends the emitting contract as the final value of the event key (as felt)
address = hex(event.keys[-1])
log_receipts.append(
LogReceipt(
address=self.address,
address=to_checksum_address(address),
blockHash=bytes(),
blockNumber=bytes(),
data=bytes(event.data),
Expand All @@ -134,7 +137,8 @@ async def _wrapped(self, *args, **kwargs):
# of the bytes32 topics. This recomputes the original topic
f"{(event.keys[i] + 2**128 * event.keys[i + 1]):064x}"
)
for i in range(0, len(event.keys), 2)
# every kkrt evm event emission appends the emitting contract as the final value of the event key (as felt), we skip those here
for i in range(0, len(event.keys) - 1, 2)
],
transactionHash=bytes(),
transactionIndex=0,
Expand Down

0 comments on commit 1a52997

Please sign in to comment.