Skip to content

Commit

Permalink
Store L1L2 handle transactions (0xSpaceShard#319)
Browse files Browse the repository at this point in the history
  • Loading branch information
mikiw authored Oct 27, 2022
1 parent 48516b0 commit a057931
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 10 deletions.
80 changes: 76 additions & 4 deletions starknet_devnet/postman_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@

from starkware.solidity.utils import load_nearby_contract
from starkware.starknet.definitions.error_codes import StarknetErrorCode
from starkware.starknet.testing.postman import Postman
from starkware.starknet.testing.starknet import Starknet
from starkware.eth.eth_test_utils import EthAccount, EthContract
from starkware.starknet.business_logic.transaction.objects import InternalL1Handler

from .constants import L1_MESSAGE_CANCELLATION_DELAY, TIMEOUT_FOR_WEB3_REQUESTS
from .util import fixed_length_hex, StarknetDevnetException
Expand Down Expand Up @@ -116,9 +116,12 @@ async def flush(self, state) -> dict:
postman.n_consumed_l2_to_l1_messages :
]

await self.__postman_wrapper.flush()
transactions_to_execute = await self.__postman_wrapper.flush()

return self.__parse_l1_l2_messages(l1_to_l2_messages, l2_to_l1_messages)
return (
self.__parse_l1_l2_messages(l1_to_l2_messages, l2_to_l1_messages),
transactions_to_execute,
)


class PostmanWrapper(ABC):
Expand All @@ -138,7 +141,7 @@ def load_mock_messaging_contract_in_l1(self, starknet, contract_address):

async def flush(self):
"""Handles the L1 <> L2 message exchange"""
await self.postman.flush()
return await self.postman.flush()


class LocalPostmanWrapper(PostmanWrapper):
Expand Down Expand Up @@ -170,3 +173,72 @@ def load_mock_messaging_contract_in_l1(self, starknet, contract_address):
self.l1_to_l2_message_filter = self.mock_starknet_messaging_contract.w3_contract.events.LogMessageToL2.createFilter(
fromBlock="latest"
)


class Postman:
"""
Postman class copied from starknet code base.
https://github.com/starkware-libs/cairo-lang/blob/v0.10.1/src/starkware/starknet/testing/postman.py
Modifications were made in _handle_l1_to_l2_messages function.
"""

def __init__(
self,
mock_starknet_messaging_contract: EthContract,
starknet: Starknet,
):
self.mock_starknet_messaging_contract = mock_starknet_messaging_contract
self.starknet = starknet
self.n_consumed_l2_to_l1_messages = 0

# Create a filter to collect LogMessageToL2 events.
w3_contract = self.mock_starknet_messaging_contract.w3_contract
self.message_to_l2_filter = w3_contract.events.LogMessageToL2.createFilter(
fromBlock="latest"
)

async def _handle_l1_to_l2_messages(self):
transactions_to_execute = []
for event in self.message_to_l2_filter.get_new_entries():
args = event.args
transaction = InternalL1Handler.create(
contract_address=args["toAddress"],
entry_point_selector=args["selector"],
calldata=[int(args["fromAddress"], 16), *args["payload"]],
nonce=args["nonce"],
chain_id=self.starknet.state.general_config.chain_id.value,
)
transactions_to_execute.append(transaction)
self.mock_starknet_messaging_contract.mockConsumeMessageToL2.transact(
int(args["fromAddress"], 16),
args["toAddress"],
args["selector"],
args["payload"],
args["nonce"],
)

return transactions_to_execute

def _handle_l2_to_l1_messages(self):
l2_to_l1_messages_log = self.starknet.state.l2_to_l1_messages_log
assert len(l2_to_l1_messages_log) >= self.n_consumed_l2_to_l1_messages
for message in l2_to_l1_messages_log[self.n_consumed_l2_to_l1_messages :]:
self.mock_starknet_messaging_contract.mockSendMessageFromL2.transact(
message.from_address, message.to_address, message.payload
)
self.starknet.consume_message_from_l2(
from_address=message.from_address,
to_address=message.to_address,
payload=message.payload,
)
self.n_consumed_l2_to_l1_messages = len(l2_to_l1_messages_log)

async def flush(self):
"""
Handles all messages and sends them to the other layer.
"""
# Need to handle L1 to L2 first in case that those messages will create L2 to L1 messages.
transactions_to_execute = await self._handle_l1_to_l2_messages()
self._handle_l2_to_l1_messages()
return transactions_to_execute
16 changes: 15 additions & 1 deletion starknet_devnet/starknet_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,21 @@ async def postman_flush(self) -> dict:
"""Handles all pending L1 <> L2 messages and sends them to the other layer."""

state = self.get_state()
return await self.l1l2.flush(state)
# Generate transactions in PostmanWrapper
parsed_l1_l2_messages, transactions_to_execute = await self.l1l2.flush(state)

# Execute transactions inside StarknetWrapper
for transaction in transactions_to_execute:
async with self.__get_transaction_handler() as tx_handler:
tx_handler.internal_tx = transaction
tx_handler.execution_info = await state.execute_tx(
tx_handler.internal_tx
)
tx_handler.internal_calls = (
tx_handler.execution_info.call_info.internal_calls
)

return parsed_l1_l2_messages

async def calculate_trace_and_fee(self, external_tx: InvokeFunction):
"""Calculates trace and fee by simulating tx on state copy."""
Expand Down
6 changes: 5 additions & 1 deletion test/contracts/cairo/l1l2.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ func withdraw{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
return ();
}

@event
func l1_handler_test_event(user: felt, new_balance: felt) {
}

@l1_handler
func deposit{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
from_address: felt, user: felt, amount: felt
Expand All @@ -67,6 +71,6 @@ func deposit{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
// Compute and update the new balance.
tempvar new_balance = res + amount;
balance.write(user, new_balance);

l1_handler_test_event.emit(user, new_balance);
return ();
}
4 changes: 1 addition & 3 deletions test/test_block_number.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ def test_block_number_incremented(expected_tx_hash):
"""
Tests how block number is incremented in regular mode and lite mode.
In regular mode with salt "0x42" our expected hash is
0x4f1ea446f67c1be47619444eae4d8118f6e017d0e6fe16e89b3df03da38606d.
In lite mode we expect 0x4f1ea446f67c1be47619444eae4d8118f6e017d0e6fe16e89b3df03da38606d
transaction hash because currently, we can't disable tx hash calculations.
0x4506fb016a309c8694a5c862625ba743a3ed2e248bca1ba5aa174ca06381f0f.
"""

deploy_info = deploy(BLOCK_NUMBER_CONTRACT_PATH, salt="0x42")
Expand Down
9 changes: 8 additions & 1 deletion test/test_postman.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
devnet_in_background,
ensure_server_alive,
estimate_message_fee,
get_block,
load_file_content,
terminate_and_wait,
)
Expand Down Expand Up @@ -288,6 +289,13 @@ def _l1_l2_message_exchange(web3, l1l2_example_contract, l2_contract_address):

assert l2_balance == "2933"

# Check if last block contains L1_HANDLER transaction and event contains the correct balance
latest_block = get_block(parse=True)
assert latest_block["transactions"][0]["type"] == "L1_HANDLER"
assert latest_block["transaction_receipts"][0]["events"][0]["data"][1] == hex(
int(l2_balance)
)


@pytest.mark.web3_messaging
@devnet_in_background(*PREDEPLOY_ACCOUNT_CLI_ARGS)
Expand All @@ -314,7 +322,6 @@ def test_postman():

# Test initializing the l2 example contract
l2_contract_address = _init_l2_contract(l1l2_example_contract.address)

_l1_l2_message_exchange(web3, l1l2_example_contract, l2_contract_address)


Expand Down

0 comments on commit a057931

Please sign in to comment.