From 1339fe363b959edadf8f4f7bb16c73dc75320f95 Mon Sep 17 00:00:00 2001 From: mikiw Date: Thu, 8 Dec 2022 13:30:17 +0100 Subject: [PATCH 01/34] Add L1 to L2 message mock endpoint [skip ci] --- starknet_devnet/blueprints/base.py | 16 ++++++++++++-- starknet_devnet/blueprints/postman.py | 22 ++++++++++++++++++ starknet_devnet/starknet_wrapper.py | 32 +++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/starknet_devnet/blueprints/base.py b/starknet_devnet/blueprints/base.py index a61097fd3..ad34178de 100644 --- a/starknet_devnet/blueprints/base.py +++ b/starknet_devnet/blueprints/base.py @@ -1,6 +1,8 @@ """ Base routes """ +from typing import List + from flask import Blueprint, Response, jsonify, request from starkware.starkware_utils.error_handling import StarkErrorCode @@ -44,7 +46,17 @@ def extract_positive(request_json, prop_name: str): return value -def extract_hex_string(request_json, prop_name: str) -> int: +def to_int(value) -> int: + """Convert to int value""" + return int(value, 16) + + +def to_int_array(value) -> List[int]: + """Convert to List of ints""" + return [int(numeric, 16) for numeric in value] + + +def extract_hex_string(request_json, prop_name: str, fun=to_int) -> int: """Parse value from hex string to int""" value = request_json.get(prop_name) if value is None: @@ -55,7 +67,7 @@ def extract_hex_string(request_json, prop_name: str) -> int: ) try: - return int(value, 16) + return fun(value) except (ValueError, TypeError) as error: message = f"{prop_name} value must be a hex string." raise StarknetDevnetException( diff --git a/starknet_devnet/blueprints/postman.py b/starknet_devnet/blueprints/postman.py index c097d3ede..53b4cc394 100644 --- a/starknet_devnet/blueprints/postman.py +++ b/starknet_devnet/blueprints/postman.py @@ -7,6 +7,7 @@ from flask import Blueprint, jsonify, request from starkware.starkware_utils.error_handling import StarkErrorCode +from starknet_devnet.blueprints.base import extract_hex_string, to_int_array from starknet_devnet.state import state from starknet_devnet.util import StarknetDevnetException @@ -55,3 +56,24 @@ async def flush(): result_dict = await state.starknet_wrapper.postman_flush() return jsonify(result_dict) + + +@postman.route("/l1_to_l2", methods=["POST"]) +async def l1_to_l2(): + """L1 to L2 message mock endpoint""" + request_json = request.json or {} + l1_contract_address = extract_hex_string(request_json, "l1_contract_address") + l2_contract_address = extract_hex_string(request_json, "l2_contract_address") + entry_point_selector = extract_hex_string(request_json, "entry_point_selector") + payload = extract_hex_string(request_json, "payload", to_int_array) + nonce = extract_hex_string(request_json, "nonce") + + result = await state.starknet_wrapper.postman_l1_to_l2( + l1_contract_address=l1_contract_address, + l2_contract_address=l2_contract_address, + entry_point_selector=entry_point_selector, + payload=payload, + nonce=nonce, + ) + + return jsonify({"execution_info_calldata": result}) diff --git a/starknet_devnet/starknet_wrapper.py b/starknet_devnet/starknet_wrapper.py index e6f78c65f..01612ba18 100644 --- a/starknet_devnet/starknet_wrapper.py +++ b/starknet_devnet/starknet_wrapper.py @@ -79,6 +79,7 @@ # pylint: disable=too-many-instance-attributes # pylint: disable=too-many-public-methods +# pylint: disable=too-many-arguments class StarknetWrapper: """ Wraps a Starknet instance and stores data to be returned by the server: @@ -547,6 +548,37 @@ async def load_messaging_contract_in_l1( self.starknet, network_url, contract_address, network_id ) + async def postman_l1_to_l2( + self, + l1_contract_address: int, + l2_contract_address: int, + entry_point_selector: int, + payload: List[int], + nonce: int, + ) -> dict: + """Handles L1 to L2 message mock endpoint""" + + state = self.get_state() + + # Generate transactions + transaction = InternalL1Handler.create( + contract_address=l2_contract_address, + entry_point_selector=entry_point_selector, + calldata=[l1_contract_address, *payload], + nonce=nonce, + chain_id=self.starknet.state.general_config.chain_id.value, + ) + + # Execute transactions inside StarknetWrapper + 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 tx_handler.execution_info.call_info.calldata + async def postman_flush(self) -> dict: """Handles all pending L1 <> L2 messages and sends them to the other layer.""" From cacc73ddb86fe7d88e2fafd4af6c7c7956f01ab9 Mon Sep 17 00:00:00 2001 From: mikiw Date: Fri, 9 Dec 2022 10:19:45 +0100 Subject: [PATCH 02/34] Add tests and docs --- page/docs/guide/postman.md | 22 ++++++++++ starknet_devnet/blueprints/postman.py | 7 ++- test/test_endpoints.py | 63 +++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/page/docs/guide/postman.md b/page/docs/guide/postman.md index ceede745f..72be347e2 100644 --- a/page/docs/guide/postman.md +++ b/page/docs/guide/postman.md @@ -50,6 +50,28 @@ constructor(MockStarknetMessaging mockStarknetMessaging_) public { } ``` +### Postman - l1 to l2 mock endpoint + +``` +POST /postman/l1_to_l2 +``` +Sending mock transactions from L1 to L2 without the need for running L1. +Deployed L2 contract address `l2_contract_address` and `entry_point_selector` must be valid otherwise new block will not be created. + +Example POST json: +``` +{ + "l2_contract_address":"0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", + "entry_point_selector":"0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", + "l1_contract_address":"0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "payload":[ + "0x1", + "0x2" + ], + "nonce":"0x0" +} +``` + ## Dumping To preserve your Devnet instance for future use, there are several options: diff --git a/starknet_devnet/blueprints/postman.py b/starknet_devnet/blueprints/postman.py index 53b4cc394..d23d53c46 100644 --- a/starknet_devnet/blueprints/postman.py +++ b/starknet_devnet/blueprints/postman.py @@ -76,4 +76,9 @@ async def l1_to_l2(): nonce=nonce, ) - return jsonify({"execution_info_calldata": result}) + try: + result_iterator = iter(result) + return jsonify({"execution_info_calldata": list(map(hex, result_iterator))}) + except TypeError: + return jsonify({"execution_info_calldata": str(StarkErrorCode.INVALID_TRANSACTION)}) + diff --git a/test/test_endpoints.py b/test/test_endpoints.py index 977236e8e..b0f723e46 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -7,6 +7,7 @@ import pytest import requests from starkware.starknet.definitions.error_codes import StarknetErrorCode +from starkware.starkware_utils.error_handling import StarkErrorCode from starknet_devnet.constants import DEFAULT_GAS_PRICE from starknet_devnet.server import app @@ -16,6 +17,7 @@ FAILING_CONTRACT_PATH, GENESIS_BLOCK_HASH, GENESIS_BLOCK_NUMBER, + L1L2_CONTRACT_PATH, STORAGE_CONTRACT_PATH, ) from .support.assertions import assert_valid_schema @@ -350,3 +352,64 @@ def test_get_transaction_trace_of_rejected(): resp_body = resp.json() assert resp_body["code"] == str(StarknetErrorCode.NO_TRACE) assert resp.status_code == 500 + + +@devnet_in_background() +def test_post_l1_to_l2_deploy_execute(): + """Test POST l1 to l2 deploy contract and execute transaction""" + # Deploy L1L2 contract + deploy_info = deploy(contract=L1L2_CONTRACT_PATH) + l1_contract_address = "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" + payload = ["0x1", "0x2"] + + # Create l1 to l2 mock transaction + response = requests.post( + f"{APP_URL}/postman/l1_to_l2", + json={ + "l2_contract_address": deploy_info["address"], + "entry_point_selector": "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", + "l1_contract_address": l1_contract_address, + "payload": payload, + "nonce": "0x0", + }, + ) + assert response.status_code == 200 + assert response.json().get("execution_info_calldata") == [ + hex.lower() for hex in [l1_contract_address, *payload] + ] + + +@devnet_in_background() +def test_post_l1_to_l2_execute_without_data(): + """Test POST l1 to l2 without data""" + # Create l1 to l2 mock transaction + response = requests.post( + f"{APP_URL}/postman/l1_to_l2", + json={ + "l2_contract_address": "", + "entry_point_selector": "", + "l1_contract_address": "", + "payload": "", + "nonce": "", + }, + ) + assert response.status_code == 400 + assert response.json().get("code") == str(StarkErrorCode.MALFORMED_REQUEST) + + +@devnet_in_background() +def test_post_l1_to_l2_execute_without_deploy(): + """Test POST l1 to l2 without deploy""" + # Create l1 to l2 mock transaction + response = requests.post( + f"{APP_URL}/postman/l1_to_l2", + json={ + "l2_contract_address": "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", + "entry_point_selector": "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", + "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "payload": ["0x1", "0x2"], + "nonce": "0x0", + }, + ) + assert response.status_code == 200 + assert response.json().get("execution_info_calldata") == str(StarkErrorCode.INVALID_TRANSACTION) From b447ebaf3eb55a15cac173e10ee25aea17137d3c Mon Sep 17 00:00:00 2001 From: mikiw Date: Fri, 9 Dec 2022 10:23:11 +0100 Subject: [PATCH 03/34] Update postman.py --- starknet_devnet/blueprints/postman.py | 1 - 1 file changed, 1 deletion(-) diff --git a/starknet_devnet/blueprints/postman.py b/starknet_devnet/blueprints/postman.py index d23d53c46..d14a41fed 100644 --- a/starknet_devnet/blueprints/postman.py +++ b/starknet_devnet/blueprints/postman.py @@ -81,4 +81,3 @@ async def l1_to_l2(): return jsonify({"execution_info_calldata": list(map(hex, result_iterator))}) except TypeError: return jsonify({"execution_info_calldata": str(StarkErrorCode.INVALID_TRANSACTION)}) - From fd736e4868b6ef3f11a76a5be806fb6e702c6d42 Mon Sep 17 00:00:00 2001 From: mikiw Date: Fri, 9 Dec 2022 10:24:41 +0100 Subject: [PATCH 04/34] formater --- starknet_devnet/blueprints/postman.py | 4 +++- test/test_endpoints.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/starknet_devnet/blueprints/postman.py b/starknet_devnet/blueprints/postman.py index d14a41fed..f1eb00c72 100644 --- a/starknet_devnet/blueprints/postman.py +++ b/starknet_devnet/blueprints/postman.py @@ -80,4 +80,6 @@ async def l1_to_l2(): result_iterator = iter(result) return jsonify({"execution_info_calldata": list(map(hex, result_iterator))}) except TypeError: - return jsonify({"execution_info_calldata": str(StarkErrorCode.INVALID_TRANSACTION)}) + return jsonify( + {"execution_info_calldata": str(StarkErrorCode.INVALID_TRANSACTION)} + ) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index b0f723e46..1f462aa57 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -412,4 +412,6 @@ def test_post_l1_to_l2_execute_without_deploy(): }, ) assert response.status_code == 200 - assert response.json().get("execution_info_calldata") == str(StarkErrorCode.INVALID_TRANSACTION) + assert response.json().get("execution_info_calldata") == str( + StarkErrorCode.INVALID_TRANSACTION + ) From 87e26c74de56dc31fe8525e410937c189a9d0c28 Mon Sep 17 00:00:00 2001 From: mikiw Date: Sun, 11 Dec 2022 01:03:17 +0100 Subject: [PATCH 05/34] refactor to_int and to_int_array --- starknet_devnet/blueprints/base.py | 22 +++++++--------------- starknet_devnet/blueprints/postman.py | 4 ++-- starknet_devnet/util.py | 5 +++++ 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/starknet_devnet/blueprints/base.py b/starknet_devnet/blueprints/base.py index ad34178de..e0033c241 100644 --- a/starknet_devnet/blueprints/base.py +++ b/starknet_devnet/blueprints/base.py @@ -1,14 +1,16 @@ """ Base routes """ -from typing import List - from flask import Blueprint, Response, jsonify, request from starkware.starkware_utils.error_handling import StarkErrorCode from starknet_devnet.fee_token import FeeToken from starknet_devnet.state import state -from starknet_devnet.util import StarknetDevnetException, check_valid_dump_path +from starknet_devnet.util import ( + StarknetDevnetException, + check_valid_dump_path, + custom_int, +) base = Blueprint("base", __name__) @@ -46,17 +48,7 @@ def extract_positive(request_json, prop_name: str): return value -def to_int(value) -> int: - """Convert to int value""" - return int(value, 16) - - -def to_int_array(value) -> List[int]: - """Convert to List of ints""" - return [int(numeric, 16) for numeric in value] - - -def extract_hex_string(request_json, prop_name: str, fun=to_int) -> int: +def extract_hex_string(request_json, prop_name: str, convert=custom_int) -> int: """Parse value from hex string to int""" value = request_json.get(prop_name) if value is None: @@ -67,7 +59,7 @@ def extract_hex_string(request_json, prop_name: str, fun=to_int) -> int: ) try: - return fun(value) + return convert(value) except (ValueError, TypeError) as error: message = f"{prop_name} value must be a hex string." raise StarknetDevnetException( diff --git a/starknet_devnet/blueprints/postman.py b/starknet_devnet/blueprints/postman.py index f1eb00c72..0f79303de 100644 --- a/starknet_devnet/blueprints/postman.py +++ b/starknet_devnet/blueprints/postman.py @@ -7,9 +7,9 @@ from flask import Blueprint, jsonify, request from starkware.starkware_utils.error_handling import StarkErrorCode -from starknet_devnet.blueprints.base import extract_hex_string, to_int_array +from starknet_devnet.blueprints.base import extract_hex_string from starknet_devnet.state import state -from starknet_devnet.util import StarknetDevnetException +from starknet_devnet.util import StarknetDevnetException, to_int_array postman = Blueprint("postman", __name__, url_prefix="/postman") diff --git a/starknet_devnet/util.py b/starknet_devnet/util.py index 82bd0125b..ea98aa97a 100644 --- a/starknet_devnet/util.py +++ b/starknet_devnet/util.py @@ -34,6 +34,11 @@ def fixed_length_hex(arg: int) -> str: return f"0x{arg:064x}" +def to_int_array(values: List[str]) -> List[int]: + """Convert to List of ints""" + return [int(numeric, 16) for numeric in values] + + @dataclass class Uint256: """Abstraction of Uint256 type""" From bd1dfc87c28816d26a7981148f6c5609991e2260 Mon Sep 17 00:00:00 2001 From: mikiw Date: Sun, 11 Dec 2022 01:14:16 +0100 Subject: [PATCH 06/34] Update test_endpoints.py [skip ci] --- test/test_endpoints.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index 1f462aa57..6863c04d5 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -359,6 +359,7 @@ def test_post_l1_to_l2_deploy_execute(): """Test POST l1 to l2 deploy contract and execute transaction""" # Deploy L1L2 contract deploy_info = deploy(contract=L1L2_CONTRACT_PATH) + # Just a random example of an l1 contract address and an example payload l1_contract_address = "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" payload = ["0x1", "0x2"] From 04742d966eb7f5c9866a22e8e271aa5eacd7a90b Mon Sep 17 00:00:00 2001 From: mikiw Date: Sun, 11 Dec 2022 01:26:42 +0100 Subject: [PATCH 07/34] Update test_endpoints.py [skip ci] --- test/test_endpoints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index 6863c04d5..dda49ea8b 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -400,7 +400,7 @@ def test_post_l1_to_l2_execute_without_data(): @devnet_in_background() def test_post_l1_to_l2_execute_without_deploy(): - """Test POST l1 to l2 without deploy""" + """Test POST l1 to l2 without the target contract being deploy""" # Create l1 to l2 mock transaction response = requests.post( f"{APP_URL}/postman/l1_to_l2", From 16f78dee7af6445a6db974f3a7475ae62e2d7ffb Mon Sep 17 00:00:00 2001 From: mikiw Date: Sun, 11 Dec 2022 01:53:23 +0100 Subject: [PATCH 08/34] refactor extract_hex_string to hex_converter --- starknet_devnet/blueprints/base.py | 10 +++++----- starknet_devnet/blueprints/postman.py | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/starknet_devnet/blueprints/base.py b/starknet_devnet/blueprints/base.py index e0033c241..623f0c123 100644 --- a/starknet_devnet/blueprints/base.py +++ b/starknet_devnet/blueprints/base.py @@ -48,20 +48,20 @@ def extract_positive(request_json, prop_name: str): return value -def extract_hex_string(request_json, prop_name: str, convert=custom_int) -> int: - """Parse value from hex string to int""" +def hex_converter(request_json, prop_name: str, convert=custom_int) -> int: + """Parse value from hex string to int, or values from hex strings to ints""" value = request_json.get(prop_name) if value is None: raise StarknetDevnetException( code=StarkErrorCode.MALFORMED_REQUEST, status_code=400, - message=f"{prop_name} value must be provided.", + message=f"{prop_name} value or values must be provided.", ) try: return convert(value) except (ValueError, TypeError) as error: - message = f"{prop_name} value must be a hex string." + message = f"{prop_name} value or values must be a hex string." raise StarknetDevnetException( code=StarkErrorCode.MALFORMED_REQUEST, status_code=400, @@ -176,7 +176,7 @@ async def mint(): """Mint token and transfer to the provided address""" request_json = request.json or {} - address = extract_hex_string(request_json, "address") + address = hex_converter(request_json, "address") amount = extract_positive(request_json, "amount") is_lite = request_json.get("lite", False) diff --git a/starknet_devnet/blueprints/postman.py b/starknet_devnet/blueprints/postman.py index 0f79303de..19fd09da5 100644 --- a/starknet_devnet/blueprints/postman.py +++ b/starknet_devnet/blueprints/postman.py @@ -7,7 +7,7 @@ from flask import Blueprint, jsonify, request from starkware.starkware_utils.error_handling import StarkErrorCode -from starknet_devnet.blueprints.base import extract_hex_string +from starknet_devnet.blueprints.base import hex_converter from starknet_devnet.state import state from starknet_devnet.util import StarknetDevnetException, to_int_array @@ -62,11 +62,11 @@ async def flush(): async def l1_to_l2(): """L1 to L2 message mock endpoint""" request_json = request.json or {} - l1_contract_address = extract_hex_string(request_json, "l1_contract_address") - l2_contract_address = extract_hex_string(request_json, "l2_contract_address") - entry_point_selector = extract_hex_string(request_json, "entry_point_selector") - payload = extract_hex_string(request_json, "payload", to_int_array) - nonce = extract_hex_string(request_json, "nonce") + l1_contract_address = hex_converter(request_json, "l1_contract_address") + l2_contract_address = hex_converter(request_json, "l2_contract_address") + entry_point_selector = hex_converter(request_json, "entry_point_selector") + payload = hex_converter(request_json, "payload", to_int_array) + nonce = hex_converter(request_json, "nonce") result = await state.starknet_wrapper.postman_l1_to_l2( l1_contract_address=l1_contract_address, From 64d9c2b81bdd8a55d5b93d9d52923a670860ca0a Mon Sep 17 00:00:00 2001 From: mikiw Date: Sun, 11 Dec 2022 02:00:23 +0100 Subject: [PATCH 09/34] refactor mock_message_to_l2 --- starknet_devnet/blueprints/postman.py | 2 +- starknet_devnet/starknet_wrapper.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/starknet_devnet/blueprints/postman.py b/starknet_devnet/blueprints/postman.py index 19fd09da5..b9674ee24 100644 --- a/starknet_devnet/blueprints/postman.py +++ b/starknet_devnet/blueprints/postman.py @@ -68,7 +68,7 @@ async def l1_to_l2(): payload = hex_converter(request_json, "payload", to_int_array) nonce = hex_converter(request_json, "nonce") - result = await state.starknet_wrapper.postman_l1_to_l2( + result = await state.starknet_wrapper.mock_message_to_l2( l1_contract_address=l1_contract_address, l2_contract_address=l2_contract_address, entry_point_selector=entry_point_selector, diff --git a/starknet_devnet/starknet_wrapper.py b/starknet_devnet/starknet_wrapper.py index 01612ba18..4bab880c0 100644 --- a/starknet_devnet/starknet_wrapper.py +++ b/starknet_devnet/starknet_wrapper.py @@ -548,7 +548,7 @@ async def load_messaging_contract_in_l1( self.starknet, network_url, contract_address, network_id ) - async def postman_l1_to_l2( + async def mock_message_to_l2( self, l1_contract_address: int, l2_contract_address: int, From 73cb0b6a24cec193377f8eedc2e7ddaf90177a8a Mon Sep 17 00:00:00 2001 From: mikiw Date: Sun, 11 Dec 2022 02:06:17 +0100 Subject: [PATCH 10/34] Update postman.py --- starknet_devnet/blueprints/postman.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/starknet_devnet/blueprints/postman.py b/starknet_devnet/blueprints/postman.py index b9674ee24..719366a6e 100644 --- a/starknet_devnet/blueprints/postman.py +++ b/starknet_devnet/blueprints/postman.py @@ -77,8 +77,7 @@ async def l1_to_l2(): ) try: - result_iterator = iter(result) - return jsonify({"execution_info_calldata": list(map(hex, result_iterator))}) + return jsonify({"execution_info_calldata": [hex(r) for r in result]}) except TypeError: return jsonify( {"execution_info_calldata": str(StarkErrorCode.INVALID_TRANSACTION)} From f17aafca1956ee0e35001d3bc302dc03de6f53eb Mon Sep 17 00:00:00 2001 From: mikiw Date: Mon, 12 Dec 2022 09:22:21 +0100 Subject: [PATCH 11/34] Update test_fee_token.py --- test/test_fee_token.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_fee_token.py b/test/test_fee_token.py index 5ca44aed4..a2898e49c 100644 --- a/test/test_fee_token.py +++ b/test/test_fee_token.py @@ -99,7 +99,7 @@ def test_wrong_mint_address_format(): resp = mint_client({"amount": 10, "address": "invalid_address"}) assert resp.status_code == 400 - assert resp.json["message"] == "address value must be a hex string." + assert resp.json["message"] == "address value or values must be a hex string." def test_missing_mint_address(): @@ -107,7 +107,7 @@ def test_missing_mint_address(): resp = mint_client({"amount": 10}) assert resp.status_code == 400 - assert resp.json["message"] == "address value must be provided." + assert resp.json["message"] == "address value or values must be provided." @pytest.mark.fee_token From 0af2ab49afb539751f93c98877c6949657a3c104 Mon Sep 17 00:00:00 2001 From: mikiw Date: Mon, 12 Dec 2022 12:12:41 +0100 Subject: [PATCH 12/34] Add send_l1_to_l2 --- test/l1_l2.json | 7 +++++ test/l1_l2_empty.json | 7 +++++ test/test_endpoints.py | 58 +++++++++++++++++------------------------- 3 files changed, 37 insertions(+), 35 deletions(-) create mode 100644 test/l1_l2.json create mode 100644 test/l1_l2_empty.json diff --git a/test/l1_l2.json b/test/l1_l2.json new file mode 100644 index 000000000..2c5254d17 --- /dev/null +++ b/test/l1_l2.json @@ -0,0 +1,7 @@ +{ + "l2_contract_address": "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", + "entry_point_selector": "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", + "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "payload": ["0x1", "0x2"], + "nonce": "0x0" +} \ No newline at end of file diff --git a/test/l1_l2_empty.json b/test/l1_l2_empty.json new file mode 100644 index 000000000..43a4a54d5 --- /dev/null +++ b/test/l1_l2_empty.json @@ -0,0 +1,7 @@ +{ + "l2_contract_address": "", + "entry_point_selector": "", + "l1_contract_address": "", + "payload": "", + "nonce": "" +} \ No newline at end of file diff --git a/test/test_endpoints.py b/test/test_endpoints.py index dda49ea8b..78b99ecc9 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -25,6 +25,8 @@ DEPLOY_CONTENT = load_file_content("deploy.json") INVOKE_CONTENT = load_file_content("invoke.json") +L1_L2_CONTENT = load_file_content("l1_l2.json") +L1_L2_EMPTY = load_file_content("l1_l2_empty.json") CALL_CONTENT = load_file_content("call.json") INVALID_HASH = "0x58d4d4ed7580a7a98ab608883ec9fe722424ce52c19f2f369eeea301f535914" INVALID_ADDRESS = "0x123" @@ -48,6 +50,14 @@ def send_call(req_dict: dict): ) +def send_l1_to_l2(req_dict: dict): + """Sends the dict in a POST request and returns the response data.""" + return requests.post( + f"{APP_URL}/postman/l1_to_l2", + json=req_dict, + ) + + def assert_deploy_resp(resp: bytes): """Asserts the validity of deploy response body.""" resp_dict = json.loads(resp.data.decode("utf-8")) @@ -359,24 +369,16 @@ def test_post_l1_to_l2_deploy_execute(): """Test POST l1 to l2 deploy contract and execute transaction""" # Deploy L1L2 contract deploy_info = deploy(contract=L1L2_CONTRACT_PATH) - # Just a random example of an l1 contract address and an example payload - l1_contract_address = "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" - payload = ["0x1", "0x2"] - + # Create l1 to l2 mock transaction - response = requests.post( - f"{APP_URL}/postman/l1_to_l2", - json={ - "l2_contract_address": deploy_info["address"], - "entry_point_selector": "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", - "l1_contract_address": l1_contract_address, - "payload": payload, - "nonce": "0x0", - }, - ) + req_dict = json.loads(L1_L2_CONTENT) + req_dict["l2_contract_address"] = deploy_info["address"] + response = send_l1_to_l2(req_dict) + assert response.status_code == 200 + # l1_contract_address is a random example of an l1 contract address and an example payload assert response.json().get("execution_info_calldata") == [ - hex.lower() for hex in [l1_contract_address, *payload] + hex.lower() for hex in [req_dict["l1_contract_address"], *req_dict["payload"]] ] @@ -384,16 +386,9 @@ def test_post_l1_to_l2_deploy_execute(): def test_post_l1_to_l2_execute_without_data(): """Test POST l1 to l2 without data""" # Create l1 to l2 mock transaction - response = requests.post( - f"{APP_URL}/postman/l1_to_l2", - json={ - "l2_contract_address": "", - "entry_point_selector": "", - "l1_contract_address": "", - "payload": "", - "nonce": "", - }, - ) + req_dict = json.loads(L1_L2_EMPTY) + response = send_l1_to_l2(req_dict) + assert response.status_code == 400 assert response.json().get("code") == str(StarkErrorCode.MALFORMED_REQUEST) @@ -402,16 +397,9 @@ def test_post_l1_to_l2_execute_without_data(): def test_post_l1_to_l2_execute_without_deploy(): """Test POST l1 to l2 without the target contract being deploy""" # Create l1 to l2 mock transaction - response = requests.post( - f"{APP_URL}/postman/l1_to_l2", - json={ - "l2_contract_address": "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", - "entry_point_selector": "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", - "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", - "payload": ["0x1", "0x2"], - "nonce": "0x0", - }, - ) + req_dict = json.loads(L1_L2_CONTENT) + response = send_l1_to_l2(req_dict) + assert response.status_code == 200 assert response.json().get("execution_info_calldata") == str( StarkErrorCode.INVALID_TRANSACTION From 17f3c798d8d174d249455f52fdb5384aed5a28ae Mon Sep 17 00:00:00 2001 From: mikiw Date: Mon, 12 Dec 2022 12:18:12 +0100 Subject: [PATCH 13/34] Update test_endpoints.py --- test/test_endpoints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index 78b99ecc9..26f5163ef 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -369,7 +369,7 @@ def test_post_l1_to_l2_deploy_execute(): """Test POST l1 to l2 deploy contract and execute transaction""" # Deploy L1L2 contract deploy_info = deploy(contract=L1L2_CONTRACT_PATH) - + # Create l1 to l2 mock transaction req_dict = json.loads(L1_L2_CONTENT) req_dict["l2_contract_address"] = deploy_info["address"] From fbcf227239fcd2da27dfbde8d95b46ddd310e018 Mon Sep 17 00:00:00 2001 From: mikiw Date: Mon, 12 Dec 2022 13:26:35 +0100 Subject: [PATCH 14/34] InternalL1Handler refactor --- starknet_devnet/blueprints/postman.py | 26 ++++++++++++++------------ starknet_devnet/starknet_wrapper.py | 21 ++------------------- 2 files changed, 16 insertions(+), 31 deletions(-) diff --git a/starknet_devnet/blueprints/postman.py b/starknet_devnet/blueprints/postman.py index 719366a6e..f9f6b8584 100644 --- a/starknet_devnet/blueprints/postman.py +++ b/starknet_devnet/blueprints/postman.py @@ -5,6 +5,7 @@ import json from flask import Blueprint, jsonify, request +from starkware.starknet.business_logic.transaction.objects import InternalL1Handler from starkware.starkware_utils.error_handling import StarkErrorCode from starknet_devnet.blueprints.base import hex_converter @@ -62,20 +63,21 @@ async def flush(): async def l1_to_l2(): """L1 to L2 message mock endpoint""" request_json = request.json or {} - l1_contract_address = hex_converter(request_json, "l1_contract_address") - l2_contract_address = hex_converter(request_json, "l2_contract_address") - entry_point_selector = hex_converter(request_json, "entry_point_selector") - payload = hex_converter(request_json, "payload", to_int_array) - nonce = hex_converter(request_json, "nonce") - - result = await state.starknet_wrapper.mock_message_to_l2( - l1_contract_address=l1_contract_address, - l2_contract_address=l2_contract_address, - entry_point_selector=entry_point_selector, - payload=payload, - nonce=nonce, + + # Generate transactions + transaction = InternalL1Handler.create( + contract_address=hex_converter(request_json, "l2_contract_address"), + entry_point_selector=hex_converter(request_json, "entry_point_selector"), + calldata=[ + hex_converter(request_json, "l1_contract_address"), + *hex_converter(request_json, "payload", to_int_array), + ], + nonce=hex_converter(request_json, "nonce"), + chain_id=state.starknet_wrapper.get_state().general_config.chain_id.value, ) + result = await state.starknet_wrapper.mock_message_to_l2(transaction) + try: return jsonify({"execution_info_calldata": [hex(r) for r in result]}) except TypeError: diff --git a/starknet_devnet/starknet_wrapper.py b/starknet_devnet/starknet_wrapper.py index 4bab880c0..f1e597676 100644 --- a/starknet_devnet/starknet_wrapper.py +++ b/starknet_devnet/starknet_wrapper.py @@ -79,7 +79,6 @@ # pylint: disable=too-many-instance-attributes # pylint: disable=too-many-public-methods -# pylint: disable=too-many-arguments class StarknetWrapper: """ Wraps a Starknet instance and stores data to be returned by the server: @@ -548,30 +547,14 @@ async def load_messaging_contract_in_l1( self.starknet, network_url, contract_address, network_id ) - async def mock_message_to_l2( - self, - l1_contract_address: int, - l2_contract_address: int, - entry_point_selector: int, - payload: List[int], - nonce: int, - ) -> dict: + async def mock_message_to_l2(self, call: CallL1Handler) -> dict: """Handles L1 to L2 message mock endpoint""" state = self.get_state() - # Generate transactions - transaction = InternalL1Handler.create( - contract_address=l2_contract_address, - entry_point_selector=entry_point_selector, - calldata=[l1_contract_address, *payload], - nonce=nonce, - chain_id=self.starknet.state.general_config.chain_id.value, - ) - # Execute transactions inside StarknetWrapper async with self.__get_transaction_handler() as tx_handler: - tx_handler.internal_tx = transaction + tx_handler.internal_tx = call tx_handler.execution_info = await state.execute_tx(tx_handler.internal_tx) tx_handler.internal_calls = ( tx_handler.execution_info.call_info.internal_calls From 444e10c83796f54afd77fbbca216d58943a6de97 Mon Sep 17 00:00:00 2001 From: mikiw Date: Tue, 13 Dec 2022 16:25:09 +0100 Subject: [PATCH 15/34] Alpha version of consume_message_from_l2 [skip ci] --- starknet_devnet/blueprints/postman.py | 13 ++++++++++ starknet_devnet/starknet_wrapper.py | 17 +++++++++++++ test/test_endpoints.py | 36 +++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/starknet_devnet/blueprints/postman.py b/starknet_devnet/blueprints/postman.py index f9f6b8584..95d88d9bb 100644 --- a/starknet_devnet/blueprints/postman.py +++ b/starknet_devnet/blueprints/postman.py @@ -84,3 +84,16 @@ async def l1_to_l2(): return jsonify( {"execution_info_calldata": str(StarkErrorCode.INVALID_TRANSACTION)} ) + + +@postman.route("/l2_to_l1", methods=["POST"]) +async def l2_to_l1(): + """L2 to L1 message mock endpoint""" + request_json = request.json or {} + + from_address = hex_converter(request_json, "l2_contract_address") + to_address = hex_converter(request_json, "l1_contract_address") + payload = hex_converter(request_json, "payload", to_int_array) + + result = await state.starknet_wrapper.consume_message_from_l2(from_address, to_address, payload) + return jsonify({"message_hash": result}) \ No newline at end of file diff --git a/starknet_devnet/starknet_wrapper.py b/starknet_devnet/starknet_wrapper.py index f1e597676..39d64f363 100644 --- a/starknet_devnet/starknet_wrapper.py +++ b/starknet_devnet/starknet_wrapper.py @@ -7,6 +7,7 @@ from typing import Dict, List, Optional, Set, Tuple, Type, Union import cloudpickle as pickle +from starkware.starknet.services.api.messages import StarknetMessageToL1 from starkware.starknet.business_logic.state.state import BlockInfo, CachedState from starkware.starknet.business_logic.transaction.fee import calculate_tx_fee from starkware.starknet.business_logic.transaction.objects import ( @@ -547,6 +548,22 @@ async def load_messaging_contract_in_l1( self.starknet, network_url, contract_address, network_id ) + async def consume_message_from_l2(self, from_address: int, to_address: int, payload: List[int]) -> str: + """ + Mocks the L1 contract function consumeMessageFromL2. + """ + state = self.get_state() + + starknet_message = StarknetMessageToL1( + from_address=from_address, + to_address=to_address, + payload=payload, + ) + message_hash = starknet_message.get_hash() + state.consume_message_hash(message_hash=message_hash) + return message_hash + + async def mock_message_to_l2(self, call: CallL1Handler) -> dict: """Handles L1 to L2 message mock endpoint""" diff --git a/test/test_endpoints.py b/test/test_endpoints.py index 26f5163ef..e1d324412 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -12,6 +12,7 @@ from starknet_devnet.constants import DEFAULT_GAS_PRICE from starknet_devnet.server import app +from .account import invoke from .settings import APP_URL from .shared import ( FAILING_CONTRACT_PATH, @@ -19,6 +20,9 @@ GENESIS_BLOCK_NUMBER, L1L2_CONTRACT_PATH, STORAGE_CONTRACT_PATH, + PREDEPLOYED_ACCOUNT_ADDRESS, + PREDEPLOY_ACCOUNT_CLI_ARGS, + PREDEPLOYED_ACCOUNT_PRIVATE_KEY, ) from .support.assertions import assert_valid_schema from .util import create_empty_block, deploy, devnet_in_background, load_file_content @@ -30,6 +34,7 @@ CALL_CONTENT = load_file_content("call.json") INVALID_HASH = "0x58d4d4ed7580a7a98ab608883ec9fe722424ce52c19f2f369eeea301f535914" INVALID_ADDRESS = "0x123" +USER_ID = 1 def send_transaction(req_dict: dict): @@ -404,3 +409,34 @@ def test_post_l1_to_l2_execute_without_deploy(): assert response.json().get("execution_info_calldata") == str( StarkErrorCode.INVALID_TRANSACTION ) + +@devnet_in_background(*PREDEPLOY_ACCOUNT_CLI_ARGS) +def test_post_l2_to_l1(): + """Test POST l2 to l1""" + + deploy_info = deploy(L1L2_CONTRACT_PATH) + + # increase and withdraw balance + invoke( + calls=[(deploy_info["address"], "increase_balance", [USER_ID, 3333])], + account_address=PREDEPLOYED_ACCOUNT_ADDRESS, + private_key=PREDEPLOYED_ACCOUNT_PRIVATE_KEY, + ) + contract_address_int = int("0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", 16) + invoke( + calls=[(deploy_info["address"], "withdraw", [USER_ID, 1000, contract_address_int])], + account_address=PREDEPLOYED_ACCOUNT_ADDRESS, + private_key=PREDEPLOYED_ACCOUNT_PRIVATE_KEY, + ) + + response = requests.post( + f"{APP_URL}/postman/l2_to_l1", + json={ + "l2_contract_address": deploy_info["address"], + "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "payload": ["0x0", "0x1", "0x3e8"], + }, + ) + + assert response.status_code == 200 + # assert response.json().get("message_hash") == "0x20fac3dc93f9391a6be225691ec1042e65f4c44b4dd8bedcc53ac337909ae964" From 8f1e7bb41cb060144d981dd6b122638b828b103f Mon Sep 17 00:00:00 2001 From: mikiw Date: Wed, 14 Dec 2022 14:05:16 +0100 Subject: [PATCH 16/34] Add tx hash instead of call data --- starknet_devnet/blueprints/postman.py | 16 ++++------- starknet_devnet/starknet_wrapper.py | 15 +++++----- test/l1_l2.json | 2 +- test/test_endpoints.py | 41 ++++++++++++++++++--------- 4 files changed, 43 insertions(+), 31 deletions(-) diff --git a/starknet_devnet/blueprints/postman.py b/starknet_devnet/blueprints/postman.py index 95d88d9bb..c2a5ae102 100644 --- a/starknet_devnet/blueprints/postman.py +++ b/starknet_devnet/blueprints/postman.py @@ -77,13 +77,7 @@ async def l1_to_l2(): ) result = await state.starknet_wrapper.mock_message_to_l2(transaction) - - try: - return jsonify({"execution_info_calldata": [hex(r) for r in result]}) - except TypeError: - return jsonify( - {"execution_info_calldata": str(StarkErrorCode.INVALID_TRANSACTION)} - ) + return jsonify({"invoke_tx_hash": result}) @postman.route("/l2_to_l1", methods=["POST"]) @@ -94,6 +88,8 @@ async def l2_to_l1(): from_address = hex_converter(request_json, "l2_contract_address") to_address = hex_converter(request_json, "l1_contract_address") payload = hex_converter(request_json, "payload", to_int_array) - - result = await state.starknet_wrapper.consume_message_from_l2(from_address, to_address, payload) - return jsonify({"message_hash": result}) \ No newline at end of file + + result = await state.starknet_wrapper.consume_message_from_l2( + from_address, to_address, payload + ) + return jsonify({"message_hash": result}) diff --git a/starknet_devnet/starknet_wrapper.py b/starknet_devnet/starknet_wrapper.py index 39d64f363..bc247fa01 100644 --- a/starknet_devnet/starknet_wrapper.py +++ b/starknet_devnet/starknet_wrapper.py @@ -7,7 +7,6 @@ from typing import Dict, List, Optional, Set, Tuple, Type, Union import cloudpickle as pickle -from starkware.starknet.services.api.messages import StarknetMessageToL1 from starkware.starknet.business_logic.state.state import BlockInfo, CachedState from starkware.starknet.business_logic.transaction.fee import calculate_tx_fee from starkware.starknet.business_logic.transaction.objects import ( @@ -46,6 +45,7 @@ DeployAccount, InvokeFunction, ) +from starkware.starknet.services.api.messages import StarknetMessageToL1 from starkware.starknet.testing.objects import FunctionInvocation from starkware.starknet.testing.starknet import Starknet from starkware.starknet.third_party.open_zeppelin.starknet_contracts import ( @@ -548,12 +548,14 @@ async def load_messaging_contract_in_l1( self.starknet, network_url, contract_address, network_id ) - async def consume_message_from_l2(self, from_address: int, to_address: int, payload: List[int]) -> str: + async def consume_message_from_l2( + self, from_address: int, to_address: int, payload: List[int] + ) -> str: """ Mocks the L1 contract function consumeMessageFromL2. """ state = self.get_state() - + starknet_message = StarknetMessageToL1( from_address=from_address, to_address=to_address, @@ -563,21 +565,20 @@ async def consume_message_from_l2(self, from_address: int, to_address: int, payl state.consume_message_hash(message_hash=message_hash) return message_hash - - async def mock_message_to_l2(self, call: CallL1Handler) -> dict: + async def mock_message_to_l2(self, transaction: InternalL1Handler) -> dict: """Handles L1 to L2 message mock endpoint""" state = self.get_state() # Execute transactions inside StarknetWrapper async with self.__get_transaction_handler() as tx_handler: - tx_handler.internal_tx = call + 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 tx_handler.execution_info.call_info.calldata + return transaction.hash_value async def postman_flush(self) -> dict: """Handles all pending L1 <> L2 messages and sends them to the other layer.""" diff --git a/test/l1_l2.json b/test/l1_l2.json index 2c5254d17..81a1cca94 100644 --- a/test/l1_l2.json +++ b/test/l1_l2.json @@ -2,6 +2,6 @@ "l2_contract_address": "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", "entry_point_selector": "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", - "payload": ["0x1", "0x2"], + "payload": ["0x1", "0x1"], "nonce": "0x0" } \ No newline at end of file diff --git a/test/test_endpoints.py b/test/test_endpoints.py index e1d324412..204cfa294 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -18,14 +18,22 @@ FAILING_CONTRACT_PATH, GENESIS_BLOCK_HASH, GENESIS_BLOCK_NUMBER, + L1L2_ABI_PATH, L1L2_CONTRACT_PATH, - STORAGE_CONTRACT_PATH, - PREDEPLOYED_ACCOUNT_ADDRESS, PREDEPLOY_ACCOUNT_CLI_ARGS, + PREDEPLOYED_ACCOUNT_ADDRESS, PREDEPLOYED_ACCOUNT_PRIVATE_KEY, + STORAGE_CONTRACT_PATH, ) from .support.assertions import assert_valid_schema -from .util import create_empty_block, deploy, devnet_in_background, load_file_content +from .util import ( + assert_tx_status, + call, + create_empty_block, + deploy, + devnet_in_background, + load_file_content, +) DEPLOY_CONTENT = load_file_content("deploy.json") INVOKE_CONTENT = load_file_content("invoke.json") @@ -380,11 +388,17 @@ def test_post_l1_to_l2_deploy_execute(): req_dict["l2_contract_address"] = deploy_info["address"] response = send_l1_to_l2(req_dict) + # Check balace of user + value = call( + function="get_balance", + address=deploy_info["address"], + abi_path=L1L2_ABI_PATH, + inputs=[str(USER_ID)], + ) + + assert int(value) == 1 assert response.status_code == 200 - # l1_contract_address is a random example of an l1 contract address and an example payload - assert response.json().get("execution_info_calldata") == [ - hex.lower() for hex in [req_dict["l1_contract_address"], *req_dict["payload"]] - ] + assert_tx_status(hex(response.json().get("invoke_tx_hash")), "ACCEPTED_ON_L2") @devnet_in_background() @@ -406,16 +420,15 @@ def test_post_l1_to_l2_execute_without_deploy(): response = send_l1_to_l2(req_dict) assert response.status_code == 200 - assert response.json().get("execution_info_calldata") == str( - StarkErrorCode.INVALID_TRANSACTION - ) + assert_tx_status(hex(response.json().get("invoke_tx_hash")), "REJECTED") + @devnet_in_background(*PREDEPLOY_ACCOUNT_CLI_ARGS) def test_post_l2_to_l1(): """Test POST l2 to l1""" deploy_info = deploy(L1L2_CONTRACT_PATH) - + # increase and withdraw balance invoke( calls=[(deploy_info["address"], "increase_balance", [USER_ID, 3333])], @@ -424,12 +437,14 @@ def test_post_l2_to_l1(): ) contract_address_int = int("0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", 16) invoke( - calls=[(deploy_info["address"], "withdraw", [USER_ID, 1000, contract_address_int])], + calls=[ + (deploy_info["address"], "withdraw", [USER_ID, 1000, contract_address_int]) + ], account_address=PREDEPLOYED_ACCOUNT_ADDRESS, private_key=PREDEPLOYED_ACCOUNT_PRIVATE_KEY, ) - response = requests.post( + response = requests.post( f"{APP_URL}/postman/l2_to_l1", json={ "l2_contract_address": deploy_info["address"], From 14687aca1cbf086e1b451bbfe8fe24db273b3cc1 Mon Sep 17 00:00:00 2001 From: mikiw Date: Wed, 14 Dec 2022 14:07:34 +0100 Subject: [PATCH 17/34] Update test_endpoints.py [skip ci] --- test/test_endpoints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index 204cfa294..c1bf2c48c 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -414,7 +414,7 @@ def test_post_l1_to_l2_execute_without_data(): @devnet_in_background() def test_post_l1_to_l2_execute_without_deploy(): - """Test POST l1 to l2 without the target contract being deploy""" + """Test POST l1 to l2 without the target contract being deployed""" # Create l1 to l2 mock transaction req_dict = json.loads(L1_L2_CONTENT) response = send_l1_to_l2(req_dict) From 1cdf75151d2cbdbf5b062111d8c934c4172a8a49 Mon Sep 17 00:00:00 2001 From: mikiw Date: Wed, 14 Dec 2022 14:16:58 +0100 Subject: [PATCH 18/34] Remove json files in tests [skip ci] --- test/l1_l2.json | 7 ------- test/l1_l2_empty.json | 7 ------- test/test_endpoints.py | 38 ++++++++++++++++++++++++++++---------- 3 files changed, 28 insertions(+), 24 deletions(-) delete mode 100644 test/l1_l2.json delete mode 100644 test/l1_l2_empty.json diff --git a/test/l1_l2.json b/test/l1_l2.json deleted file mode 100644 index 81a1cca94..000000000 --- a/test/l1_l2.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "l2_contract_address": "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", - "entry_point_selector": "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", - "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", - "payload": ["0x1", "0x1"], - "nonce": "0x0" -} \ No newline at end of file diff --git a/test/l1_l2_empty.json b/test/l1_l2_empty.json deleted file mode 100644 index 43a4a54d5..000000000 --- a/test/l1_l2_empty.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "l2_contract_address": "", - "entry_point_selector": "", - "l1_contract_address": "", - "payload": "", - "nonce": "" -} \ No newline at end of file diff --git a/test/test_endpoints.py b/test/test_endpoints.py index c1bf2c48c..6aa77305e 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -37,8 +37,6 @@ DEPLOY_CONTENT = load_file_content("deploy.json") INVOKE_CONTENT = load_file_content("invoke.json") -L1_L2_CONTENT = load_file_content("l1_l2.json") -L1_L2_EMPTY = load_file_content("l1_l2_empty.json") CALL_CONTENT = load_file_content("call.json") INVALID_HASH = "0x58d4d4ed7580a7a98ab608883ec9fe722424ce52c19f2f369eeea301f535914" INVALID_ADDRESS = "0x123" @@ -384,9 +382,13 @@ def test_post_l1_to_l2_deploy_execute(): deploy_info = deploy(contract=L1L2_CONTRACT_PATH) # Create l1 to l2 mock transaction - req_dict = json.loads(L1_L2_CONTENT) - req_dict["l2_contract_address"] = deploy_info["address"] - response = send_l1_to_l2(req_dict) + response = send_l1_to_l2({ + "l2_contract_address": deploy_info["address"], + "entry_point_selector": "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", + "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "payload": ["0x1", "0x1"], + "nonce": "0x0" + }) # Check balace of user value = call( @@ -405,8 +407,13 @@ def test_post_l1_to_l2_deploy_execute(): def test_post_l1_to_l2_execute_without_data(): """Test POST l1 to l2 without data""" # Create l1 to l2 mock transaction - req_dict = json.loads(L1_L2_EMPTY) - response = send_l1_to_l2(req_dict) + response = send_l1_to_l2({ + "l2_contract_address": "", + "entry_point_selector": "", + "l1_contract_address": "", + "payload": "", + "nonce": "" + }) assert response.status_code == 400 assert response.json().get("code") == str(StarkErrorCode.MALFORMED_REQUEST) @@ -416,8 +423,13 @@ def test_post_l1_to_l2_execute_without_data(): def test_post_l1_to_l2_execute_without_deploy(): """Test POST l1 to l2 without the target contract being deployed""" # Create l1 to l2 mock transaction - req_dict = json.loads(L1_L2_CONTENT) - response = send_l1_to_l2(req_dict) + response = send_l1_to_l2({ + "l2_contract_address": "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", + "entry_point_selector": "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", + "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "payload": ["0x1", "0x1"], + "nonce": "0x0" + }) assert response.status_code == 200 assert_tx_status(hex(response.json().get("invoke_tx_hash")), "REJECTED") @@ -443,6 +455,8 @@ def test_post_l2_to_l1(): account_address=PREDEPLOYED_ACCOUNT_ADDRESS, private_key=PREDEPLOYED_ACCOUNT_PRIVATE_KEY, ) + + # TODO: check balance response = requests.post( f"{APP_URL}/postman/l2_to_l1", @@ -453,5 +467,9 @@ def test_post_l2_to_l1(): }, ) + print("message_hash") + print(response.json().get("message_hash")) + assert response.status_code == 200 - # assert response.json().get("message_hash") == "0x20fac3dc93f9391a6be225691ec1042e65f4c44b4dd8bedcc53ac337909ae964" + +#TODO: Add new test \ No newline at end of file From 28c4600564e0d6d863a79f59b5b178831c8fa70e Mon Sep 17 00:00:00 2001 From: mikiw Date: Wed, 14 Dec 2022 14:28:14 +0100 Subject: [PATCH 19/34] Refactor of function names --- starknet_devnet/blueprints/postman.py | 8 +++--- test/test_endpoints.py | 36 ++++++++++++++------------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/starknet_devnet/blueprints/postman.py b/starknet_devnet/blueprints/postman.py index c2a5ae102..281cee56a 100644 --- a/starknet_devnet/blueprints/postman.py +++ b/starknet_devnet/blueprints/postman.py @@ -59,8 +59,8 @@ async def flush(): return jsonify(result_dict) -@postman.route("/l1_to_l2", methods=["POST"]) -async def l1_to_l2(): +@postman.route("/send_message_to_l2", methods=["POST"]) +async def send_message_to_l2(): """L1 to L2 message mock endpoint""" request_json = request.json or {} @@ -80,8 +80,8 @@ async def l1_to_l2(): return jsonify({"invoke_tx_hash": result}) -@postman.route("/l2_to_l1", methods=["POST"]) -async def l2_to_l1(): +@postman.route("/send_message_to_l1", methods=["POST"]) +async def send_message_to_l1(): """L2 to L1 message mock endpoint""" request_json = request.json or {} diff --git a/test/test_endpoints.py b/test/test_endpoints.py index 6aa77305e..6f6f6ecb7 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -61,10 +61,10 @@ def send_call(req_dict: dict): ) -def send_l1_to_l2(req_dict: dict): +def send_message_to_l2(req_dict: dict): """Sends the dict in a POST request and returns the response data.""" return requests.post( - f"{APP_URL}/postman/l1_to_l2", + f"{APP_URL}/postman/send_message_to_l2", json=req_dict, ) @@ -376,13 +376,13 @@ def test_get_transaction_trace_of_rejected(): @devnet_in_background() -def test_post_l1_to_l2_deploy_execute(): +def test_send_message_to_l2_deploy_execute(): """Test POST l1 to l2 deploy contract and execute transaction""" # Deploy L1L2 contract deploy_info = deploy(contract=L1L2_CONTRACT_PATH) # Create l1 to l2 mock transaction - response = send_l1_to_l2({ + response = send_message_to_l2({ "l2_contract_address": deploy_info["address"], "entry_point_selector": "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", @@ -404,10 +404,10 @@ def test_post_l1_to_l2_deploy_execute(): @devnet_in_background() -def test_post_l1_to_l2_execute_without_data(): +def test_send_message_to_l2_execute_without_data(): """Test POST l1 to l2 without data""" # Create l1 to l2 mock transaction - response = send_l1_to_l2({ + response = send_message_to_l2({ "l2_contract_address": "", "entry_point_selector": "", "l1_contract_address": "", @@ -420,10 +420,10 @@ def test_post_l1_to_l2_execute_without_data(): @devnet_in_background() -def test_post_l1_to_l2_execute_without_deploy(): +def test_send_message_to_l2_execute_without_deploy(): """Test POST l1 to l2 without the target contract being deployed""" # Create l1 to l2 mock transaction - response = send_l1_to_l2({ + response = send_message_to_l2({ "l2_contract_address": "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", "entry_point_selector": "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", @@ -436,7 +436,7 @@ def test_post_l1_to_l2_execute_without_deploy(): @devnet_in_background(*PREDEPLOY_ACCOUNT_CLI_ARGS) -def test_post_l2_to_l1(): +def test_send_message_to_l1(): """Test POST l2 to l1""" deploy_info = deploy(L1L2_CONTRACT_PATH) @@ -455,11 +455,17 @@ def test_post_l2_to_l1(): account_address=PREDEPLOYED_ACCOUNT_ADDRESS, private_key=PREDEPLOYED_ACCOUNT_PRIVATE_KEY, ) - - # TODO: check balance + # Check balace of user + balance = call( + function="get_balance", + address=deploy_info["address"], + abi_path=L1L2_ABI_PATH, + inputs=[str(USER_ID)], + ) + response = requests.post( - f"{APP_URL}/postman/l2_to_l1", + f"{APP_URL}/postman/send_message_to_l1", json={ "l2_contract_address": deploy_info["address"], "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", @@ -467,9 +473,5 @@ def test_post_l2_to_l1(): }, ) - print("message_hash") - print(response.json().get("message_hash")) - + assert int(balance) == 2333 assert response.status_code == 200 - -#TODO: Add new test \ No newline at end of file From 24058bf56f1af7188ef72407dc6e9b990316f0f8 Mon Sep 17 00:00:00 2001 From: mikiw Date: Wed, 14 Dec 2022 15:22:29 +0100 Subject: [PATCH 20/34] Add send_message_to_l1 tests --- test/test_endpoints.py | 128 ++++++++++++++++++++++++++++------------- 1 file changed, 89 insertions(+), 39 deletions(-) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index 6f6f6ecb7..32418ed34 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -69,6 +69,14 @@ def send_message_to_l2(req_dict: dict): ) +def send_message_to_l1(req_dict: dict): + """Sends the dict in a POST request and returns the response data.""" + return requests.post( + f"{APP_URL}/postman/send_message_to_l1", + json=req_dict, + ) + + def assert_deploy_resp(resp: bytes): """Asserts the validity of deploy response body.""" resp_dict = json.loads(resp.data.decode("utf-8")) @@ -382,13 +390,15 @@ def test_send_message_to_l2_deploy_execute(): deploy_info = deploy(contract=L1L2_CONTRACT_PATH) # Create l1 to l2 mock transaction - response = send_message_to_l2({ - "l2_contract_address": deploy_info["address"], - "entry_point_selector": "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", - "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", - "payload": ["0x1", "0x1"], - "nonce": "0x0" - }) + response = send_message_to_l2( + { + "l2_contract_address": deploy_info["address"], + "entry_point_selector": "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", + "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "payload": ["0x1", "0x1"], + "nonce": "0x0", + } + ) # Check balace of user value = call( @@ -407,13 +417,15 @@ def test_send_message_to_l2_deploy_execute(): def test_send_message_to_l2_execute_without_data(): """Test POST l1 to l2 without data""" # Create l1 to l2 mock transaction - response = send_message_to_l2({ - "l2_contract_address": "", - "entry_point_selector": "", - "l1_contract_address": "", - "payload": "", - "nonce": "" - }) + response = send_message_to_l2( + { + "l2_contract_address": "", + "entry_point_selector": "", + "l1_contract_address": "", + "payload": "", + "nonce": "", + } + ) assert response.status_code == 400 assert response.json().get("code") == str(StarkErrorCode.MALFORMED_REQUEST) @@ -423,22 +435,23 @@ def test_send_message_to_l2_execute_without_data(): def test_send_message_to_l2_execute_without_deploy(): """Test POST l1 to l2 without the target contract being deployed""" # Create l1 to l2 mock transaction - response = send_message_to_l2({ - "l2_contract_address": "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", - "entry_point_selector": "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", - "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", - "payload": ["0x1", "0x1"], - "nonce": "0x0" - }) + response = send_message_to_l2( + { + "l2_contract_address": "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", + "entry_point_selector": "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", + "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "payload": ["0x1", "0x1"], + "nonce": "0x0", + } + ) assert response.status_code == 200 assert_tx_status(hex(response.json().get("invoke_tx_hash")), "REJECTED") @devnet_in_background(*PREDEPLOY_ACCOUNT_CLI_ARGS) -def test_send_message_to_l1(): - """Test POST l2 to l1""" - +def test_send_message_to_l1_deploy_execute(): + """Test POST l2 to l1 deploy contract and execute transaction""" deploy_info = deploy(L1L2_CONTRACT_PATH) # increase and withdraw balance @@ -447,31 +460,68 @@ def test_send_message_to_l1(): account_address=PREDEPLOYED_ACCOUNT_ADDRESS, private_key=PREDEPLOYED_ACCOUNT_PRIVATE_KEY, ) - contract_address_int = int("0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", 16) invoke( calls=[ - (deploy_info["address"], "withdraw", [USER_ID, 1000, contract_address_int]) + ( + deploy_info["address"], + "withdraw", + [USER_ID, 1000, 0xE7F1725E7734CE288F8367E1BB143E90BB3F0512], + ) ], account_address=PREDEPLOYED_ACCOUNT_ADDRESS, private_key=PREDEPLOYED_ACCOUNT_PRIVATE_KEY, ) - # Check balace of user - balance = call( - function="get_balance", - address=deploy_info["address"], - abi_path=L1L2_ABI_PATH, - inputs=[str(USER_ID)], - ) - - response = requests.post( - f"{APP_URL}/postman/send_message_to_l1", - json={ + response = send_message_to_l1( + { "l2_contract_address": deploy_info["address"], "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", "payload": ["0x0", "0x1", "0x3e8"], - }, + } ) - assert int(balance) == 2333 assert response.status_code == 200 + + +@devnet_in_background(*PREDEPLOY_ACCOUNT_CLI_ARGS) +def test_send_message_to_l1_deploy_execute_without_withdraw(): + """Test POST l2 to l1 deploy contract and try to execute transaction without calling withdraw""" + deploy_info = deploy(L1L2_CONTRACT_PATH) + response = send_message_to_l1( + { + "l2_contract_address": deploy_info["address"], + "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "payload": ["0x0", "0x1", "0x3e8"], + } + ) + + assert response.status_code == 500 + + +@devnet_in_background() +def test_send_message_to_l1_execute_without_data(): + """Test POST l2 to l1 deploy without data""" + response = send_message_to_l1( + { + "l2_contract_address": "", + "l1_contract_address": "", + "payload": "", + } + ) + + assert response.status_code == 400 + assert response.json().get("code") == str(StarkErrorCode.MALFORMED_REQUEST) + + +@devnet_in_background() +def test_send_message_to_l1_execute_without_deploy(): + """Test POST l2 to l1 without contract deploy""" + response = send_message_to_l1( + { + "l2_contract_address": "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", + "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "payload": ["0x0", "0x1", "0x3e8"], + } + ) + + assert response.status_code == 500 From e45ce0cb24ef22929ea03c2739425c6230b7ada5 Mon Sep 17 00:00:00 2001 From: mikiw Date: Wed, 14 Dec 2022 15:50:31 +0100 Subject: [PATCH 21/34] Update postman.md [skip ci] --- page/docs/guide/postman.md | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/page/docs/guide/postman.md b/page/docs/guide/postman.md index 72be347e2..efbea4152 100644 --- a/page/docs/guide/postman.md +++ b/page/docs/guide/postman.md @@ -53,7 +53,7 @@ constructor(MockStarknetMessaging mockStarknetMessaging_) public { ### Postman - l1 to l2 mock endpoint ``` -POST /postman/l1_to_l2 +POST /postman/send_message_to_l2 ``` Sending mock transactions from L1 to L2 without the need for running L1. Deployed L2 contract address `l2_contract_address` and `entry_point_selector` must be valid otherwise new block will not be created. @@ -61,14 +61,31 @@ Deployed L2 contract address `l2_contract_address` and `entry_point_selector` mu Example POST json: ``` { - "l2_contract_address":"0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", - "entry_point_selector":"0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", - "l1_contract_address":"0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", - "payload":[ + "l2_contract_address":"0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", + "entry_point_selector":"0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", + "l1_contract_address":"0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "payload":[ "0x1", "0x2" - ], - "nonce":"0x0" + ], + "nonce":"0x0" +} +``` + +### Postman - l2 to l1 mock endpoint + +``` +POST /postman/send_message_to_l1 +``` +Sending mock transactions from L2 to L1. +Deployed L2 contract address `l2_contract_address` and `l1_contract_address` must be valid. + +Example POST json: +``` +{ + "l2_contract_address": "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", + "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "payload": ["0x0", "0x1", "0x3e8"], } ``` From b5d205f8c4258f6f91aa009792fb57c8b3656b45 Mon Sep 17 00:00:00 2001 From: mikiw Date: Thu, 15 Dec 2022 09:38:37 +0100 Subject: [PATCH 22/34] Update postman.md [skip ci] --- page/docs/guide/postman.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/page/docs/guide/postman.md b/page/docs/guide/postman.md index efbea4152..aff7f2b43 100644 --- a/page/docs/guide/postman.md +++ b/page/docs/guide/postman.md @@ -55,8 +55,9 @@ constructor(MockStarknetMessaging mockStarknetMessaging_) public { ``` POST /postman/send_message_to_l2 ``` -Sending mock transactions from L1 to L2 without the need for running L1. -Deployed L2 contract address `l2_contract_address` and `entry_point_selector` must be valid otherwise new block will not be created. +Sending mock transactions from L1 to L2 without the need for running L1. Deployed L2 contract address `l2_contract_address` and `entry_point_selector` must be valid otherwise new block will not be created. + +Normally `nonce` is calculated by l1 StarknetContract but in this case, we need to provide it manually. Example POST json: ``` From ab6b0321b6f97970bfa31bce95af24e24b1edeff Mon Sep 17 00:00:00 2001 From: mikiw Date: Thu, 15 Dec 2022 09:42:20 +0100 Subject: [PATCH 23/34] rename send_message_to_l1 to consume_message_from_l2 --- page/docs/guide/postman.md | 2 +- starknet_devnet/blueprints/postman.py | 4 ++-- test/test_endpoints.py | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/page/docs/guide/postman.md b/page/docs/guide/postman.md index aff7f2b43..a5142daa1 100644 --- a/page/docs/guide/postman.md +++ b/page/docs/guide/postman.md @@ -76,7 +76,7 @@ Example POST json: ### Postman - l2 to l1 mock endpoint ``` -POST /postman/send_message_to_l1 +POST /postman/consume_message_from_l2 ``` Sending mock transactions from L2 to L1. Deployed L2 contract address `l2_contract_address` and `l1_contract_address` must be valid. diff --git a/starknet_devnet/blueprints/postman.py b/starknet_devnet/blueprints/postman.py index 281cee56a..5e03ba40c 100644 --- a/starknet_devnet/blueprints/postman.py +++ b/starknet_devnet/blueprints/postman.py @@ -80,8 +80,8 @@ async def send_message_to_l2(): return jsonify({"invoke_tx_hash": result}) -@postman.route("/send_message_to_l1", methods=["POST"]) -async def send_message_to_l1(): +@postman.route("/consume_message_from_l2", methods=["POST"]) +async def consume_message_from_l2(): """L2 to L1 message mock endpoint""" request_json = request.json or {} diff --git a/test/test_endpoints.py b/test/test_endpoints.py index 32418ed34..c4a51cca7 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -69,10 +69,10 @@ def send_message_to_l2(req_dict: dict): ) -def send_message_to_l1(req_dict: dict): +def consume_message_from_l2(req_dict: dict): """Sends the dict in a POST request and returns the response data.""" return requests.post( - f"{APP_URL}/postman/send_message_to_l1", + f"{APP_URL}/postman/consume_message_from_l2", json=req_dict, ) @@ -450,7 +450,7 @@ def test_send_message_to_l2_execute_without_deploy(): @devnet_in_background(*PREDEPLOY_ACCOUNT_CLI_ARGS) -def test_send_message_to_l1_deploy_execute(): +def test_consume_message_from_l2_deploy_execute(): """Test POST l2 to l1 deploy contract and execute transaction""" deploy_info = deploy(L1L2_CONTRACT_PATH) @@ -472,7 +472,7 @@ def test_send_message_to_l1_deploy_execute(): private_key=PREDEPLOYED_ACCOUNT_PRIVATE_KEY, ) - response = send_message_to_l1( + response = consume_message_from_l2( { "l2_contract_address": deploy_info["address"], "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", @@ -484,10 +484,10 @@ def test_send_message_to_l1_deploy_execute(): @devnet_in_background(*PREDEPLOY_ACCOUNT_CLI_ARGS) -def test_send_message_to_l1_deploy_execute_without_withdraw(): +def test_consume_message_from_l2_deploy_execute_without_withdraw(): """Test POST l2 to l1 deploy contract and try to execute transaction without calling withdraw""" deploy_info = deploy(L1L2_CONTRACT_PATH) - response = send_message_to_l1( + response = consume_message_from_l2( { "l2_contract_address": deploy_info["address"], "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", @@ -499,9 +499,9 @@ def test_send_message_to_l1_deploy_execute_without_withdraw(): @devnet_in_background() -def test_send_message_to_l1_execute_without_data(): +def test_consume_message_from_l2_execute_without_data(): """Test POST l2 to l1 deploy without data""" - response = send_message_to_l1( + response = consume_message_from_l2( { "l2_contract_address": "", "l1_contract_address": "", @@ -514,9 +514,9 @@ def test_send_message_to_l1_execute_without_data(): @devnet_in_background() -def test_send_message_to_l1_execute_without_deploy(): +def test_consume_message_from_l2_execute_without_deploy(): """Test POST l2 to l1 without contract deploy""" - response = send_message_to_l1( + response = consume_message_from_l2( { "l2_contract_address": "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", From 0f7272e7f5ea2c140e9b2ae160de4275b4795a7f Mon Sep 17 00:00:00 2001 From: mikiw Date: Thu, 15 Dec 2022 10:03:42 +0100 Subject: [PATCH 24/34] rename invoke_tx_hash to transaction_hash --- starknet_devnet/blueprints/postman.py | 2 +- test/test_endpoints.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/starknet_devnet/blueprints/postman.py b/starknet_devnet/blueprints/postman.py index 5e03ba40c..8e790a5bf 100644 --- a/starknet_devnet/blueprints/postman.py +++ b/starknet_devnet/blueprints/postman.py @@ -77,7 +77,7 @@ async def send_message_to_l2(): ) result = await state.starknet_wrapper.mock_message_to_l2(transaction) - return jsonify({"invoke_tx_hash": result}) + return jsonify({"transaction_hash": result}) @postman.route("/consume_message_from_l2", methods=["POST"]) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index c4a51cca7..d3eb53bad 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -410,7 +410,7 @@ def test_send_message_to_l2_deploy_execute(): assert int(value) == 1 assert response.status_code == 200 - assert_tx_status(hex(response.json().get("invoke_tx_hash")), "ACCEPTED_ON_L2") + assert_tx_status(hex(response.json().get("transaction_hash")), "ACCEPTED_ON_L2") @devnet_in_background() @@ -446,7 +446,7 @@ def test_send_message_to_l2_execute_without_deploy(): ) assert response.status_code == 200 - assert_tx_status(hex(response.json().get("invoke_tx_hash")), "REJECTED") + assert_tx_status(hex(response.json().get("transaction_hash")), "REJECTED") @devnet_in_background(*PREDEPLOY_ACCOUNT_CLI_ARGS) From c335b8f006564ab1abc181b814f97077eae0a451 Mon Sep 17 00:00:00 2001 From: mikiw Date: Thu, 15 Dec 2022 10:06:13 +0100 Subject: [PATCH 25/34] Update test_endpoints.py [skip ci] --- test/test_endpoints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index d3eb53bad..254bad585 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -400,7 +400,7 @@ def test_send_message_to_l2_deploy_execute(): } ) - # Check balace of user + # Check balance of user value = call( function="get_balance", address=deploy_info["address"], From e1b2f1ecdbdfccee04592077d2fbb241be9cf982 Mon Sep 17 00:00:00 2001 From: mikiw Date: Thu, 15 Dec 2022 10:31:16 +0100 Subject: [PATCH 26/34] Add constants --- test/test_endpoints.py | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index 254bad585..dac1fd573 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -41,7 +41,12 @@ INVALID_HASH = "0x58d4d4ed7580a7a98ab608883ec9fe722424ce52c19f2f369eeea301f535914" INVALID_ADDRESS = "0x123" USER_ID = 1 - +L1_CONTRACT_ADDRESS = 0xE7F1725E7734CE288F8367E1BB143E90BB3F0512 +L2_CONTRACT_ADDRESS = "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2" +ENTRY_POINT_SELECTOR = "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01" +CONSUME_PAYLOAD = ["0x0", "0x1", "0x3e8"] +MESSAGE_TO_l2_PAYLOAD = ["0x1", "0x1"] +MESSAGE_TO_l2_NONCE = "0x0" def send_transaction(req_dict: dict): """Sends the dict in a POST request and returns the response data.""" @@ -393,10 +398,10 @@ def test_send_message_to_l2_deploy_execute(): response = send_message_to_l2( { "l2_contract_address": deploy_info["address"], - "entry_point_selector": "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", - "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", - "payload": ["0x1", "0x1"], - "nonce": "0x0", + "entry_point_selector": ENTRY_POINT_SELECTOR, + "l1_contract_address": str(L1_CONTRACT_ADDRESS), + "payload": MESSAGE_TO_l2_PAYLOAD, + "nonce": MESSAGE_TO_l2_NONCE, } ) @@ -407,7 +412,6 @@ def test_send_message_to_l2_deploy_execute(): abi_path=L1L2_ABI_PATH, inputs=[str(USER_ID)], ) - assert int(value) == 1 assert response.status_code == 200 assert_tx_status(hex(response.json().get("transaction_hash")), "ACCEPTED_ON_L2") @@ -437,11 +441,11 @@ def test_send_message_to_l2_execute_without_deploy(): # Create l1 to l2 mock transaction response = send_message_to_l2( { - "l2_contract_address": "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", - "entry_point_selector": "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01", - "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", - "payload": ["0x1", "0x1"], - "nonce": "0x0", + "l2_contract_address": L2_CONTRACT_ADDRESS, + "entry_point_selector": ENTRY_POINT_SELECTOR, + "l1_contract_address": str(L1_CONTRACT_ADDRESS), + "payload": MESSAGE_TO_l2_PAYLOAD, + "nonce": MESSAGE_TO_l2_NONCE, } ) @@ -475,8 +479,8 @@ def test_consume_message_from_l2_deploy_execute(): response = consume_message_from_l2( { "l2_contract_address": deploy_info["address"], - "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", - "payload": ["0x0", "0x1", "0x3e8"], + "l1_contract_address": str(L1_CONTRACT_ADDRESS), + "payload": CONSUME_PAYLOAD, } ) @@ -490,8 +494,8 @@ def test_consume_message_from_l2_deploy_execute_without_withdraw(): response = consume_message_from_l2( { "l2_contract_address": deploy_info["address"], - "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", - "payload": ["0x0", "0x1", "0x3e8"], + "l1_contract_address": str(L1_CONTRACT_ADDRESS), + "payload": CONSUME_PAYLOAD, } ) @@ -518,9 +522,9 @@ def test_consume_message_from_l2_execute_without_deploy(): """Test POST l2 to l1 without contract deploy""" response = consume_message_from_l2( { - "l2_contract_address": "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", - "l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", - "payload": ["0x0", "0x1", "0x3e8"], + "l2_contract_address": L2_CONTRACT_ADDRESS, + "l1_contract_address": str(L1_CONTRACT_ADDRESS), + "payload": CONSUME_PAYLOAD, } ) From 5075e280efd28e04a85054785c9cd48ecc7fbe16 Mon Sep 17 00:00:00 2001 From: mikiw Date: Thu, 15 Dec 2022 11:27:22 +0100 Subject: [PATCH 27/34] Add AssertionError --- starknet_devnet/blueprints/postman.py | 16 ++++++++++++---- test/test_endpoints.py | 18 ++++++++++++------ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/starknet_devnet/blueprints/postman.py b/starknet_devnet/blueprints/postman.py index 8e790a5bf..c75223d4f 100644 --- a/starknet_devnet/blueprints/postman.py +++ b/starknet_devnet/blueprints/postman.py @@ -88,8 +88,16 @@ async def consume_message_from_l2(): from_address = hex_converter(request_json, "l2_contract_address") to_address = hex_converter(request_json, "l1_contract_address") payload = hex_converter(request_json, "payload", to_int_array) + result = "" - result = await state.starknet_wrapper.consume_message_from_l2( - from_address, to_address, payload - ) - return jsonify({"message_hash": result}) + try: + result = await state.starknet_wrapper.consume_message_from_l2( + from_address, to_address, payload + ) + return jsonify({"message_hash": result}) + except AssertionError as err: + raise StarknetDevnetException( + code=StarkErrorCode.MALFORMED_REQUEST, + message=f"Message of hash {result} is fully consumed or does not exist.", + status_code=500, + )from err diff --git a/test/test_endpoints.py b/test/test_endpoints.py index dac1fd573..03d8d5ffa 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -44,9 +44,13 @@ L1_CONTRACT_ADDRESS = 0xE7F1725E7734CE288F8367E1BB143E90BB3F0512 L2_CONTRACT_ADDRESS = "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2" ENTRY_POINT_SELECTOR = "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01" +MESSAGE_TO_L2_NONCE = "0x0" + +# from_address, user and amount for L2 contract CONSUME_PAYLOAD = ["0x0", "0x1", "0x3e8"] -MESSAGE_TO_l2_PAYLOAD = ["0x1", "0x1"] -MESSAGE_TO_l2_NONCE = "0x0" + +# user and amount for L1 contract +MESSAGE_TO_L2_PAYLOAD = ["0x1", "0x1"] def send_transaction(req_dict: dict): """Sends the dict in a POST request and returns the response data.""" @@ -400,8 +404,8 @@ def test_send_message_to_l2_deploy_execute(): "l2_contract_address": deploy_info["address"], "entry_point_selector": ENTRY_POINT_SELECTOR, "l1_contract_address": str(L1_CONTRACT_ADDRESS), - "payload": MESSAGE_TO_l2_PAYLOAD, - "nonce": MESSAGE_TO_l2_NONCE, + "payload": MESSAGE_TO_L2_PAYLOAD, + "nonce": MESSAGE_TO_L2_NONCE, } ) @@ -444,8 +448,8 @@ def test_send_message_to_l2_execute_without_deploy(): "l2_contract_address": L2_CONTRACT_ADDRESS, "entry_point_selector": ENTRY_POINT_SELECTOR, "l1_contract_address": str(L1_CONTRACT_ADDRESS), - "payload": MESSAGE_TO_l2_PAYLOAD, - "nonce": MESSAGE_TO_l2_NONCE, + "payload": MESSAGE_TO_L2_PAYLOAD, + "nonce": MESSAGE_TO_L2_NONCE, } ) @@ -500,6 +504,7 @@ def test_consume_message_from_l2_deploy_execute_without_withdraw(): ) assert response.status_code == 500 + assert response.json().get("code") == str(StarkErrorCode.MALFORMED_REQUEST) @devnet_in_background() @@ -529,3 +534,4 @@ def test_consume_message_from_l2_execute_without_deploy(): ) assert response.status_code == 500 + assert response.json().get("code") == str(StarkErrorCode.MALFORMED_REQUEST) From 53c4ee8b0993637d98d886a8e28ca7884dc4cf65 Mon Sep 17 00:00:00 2001 From: mikiw Date: Thu, 15 Dec 2022 11:44:49 +0100 Subject: [PATCH 28/34] after format --- starknet_devnet/blueprints/postman.py | 2 +- test/test_endpoints.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/starknet_devnet/blueprints/postman.py b/starknet_devnet/blueprints/postman.py index c75223d4f..05b8938a1 100644 --- a/starknet_devnet/blueprints/postman.py +++ b/starknet_devnet/blueprints/postman.py @@ -100,4 +100,4 @@ async def consume_message_from_l2(): code=StarkErrorCode.MALFORMED_REQUEST, message=f"Message of hash {result} is fully consumed or does not exist.", status_code=500, - )from err + ) from err diff --git a/test/test_endpoints.py b/test/test_endpoints.py index 03d8d5ffa..fe27d4efd 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -7,6 +7,7 @@ import pytest import requests from starkware.starknet.definitions.error_codes import StarknetErrorCode +from starkware.starknet.public.abi import get_selector_from_name from starkware.starkware_utils.error_handling import StarkErrorCode from starknet_devnet.constants import DEFAULT_GAS_PRICE @@ -42,8 +43,10 @@ INVALID_ADDRESS = "0x123" USER_ID = 1 L1_CONTRACT_ADDRESS = 0xE7F1725E7734CE288F8367E1BB143E90BB3F0512 -L2_CONTRACT_ADDRESS = "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2" -ENTRY_POINT_SELECTOR = "0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01" +L2_CONTRACT_ADDRESS = ( + "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2" +) +ENTRY_POINT_SELECTOR = get_selector_from_name("deposit") MESSAGE_TO_L2_NONCE = "0x0" # from_address, user and amount for L2 contract @@ -52,6 +55,7 @@ # user and amount for L1 contract MESSAGE_TO_L2_PAYLOAD = ["0x1", "0x1"] + def send_transaction(req_dict: dict): """Sends the dict in a POST request and returns the response data.""" return app.test_client().post( From 9352f56b1ec043869cdd8a80325a6859d7cc7710 Mon Sep 17 00:00:00 2001 From: mikiw Date: Thu, 15 Dec 2022 13:01:42 +0100 Subject: [PATCH 29/34] fix of get_selector_from_name --- test/test_endpoints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_endpoints.py b/test/test_endpoints.py index fe27d4efd..84f365605 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -46,7 +46,7 @@ L2_CONTRACT_ADDRESS = ( "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2" ) -ENTRY_POINT_SELECTOR = get_selector_from_name("deposit") +ENTRY_POINT_SELECTOR = hex(get_selector_from_name("deposit")) MESSAGE_TO_L2_NONCE = "0x0" # from_address, user and amount for L2 contract From fd7f3fbc4471a6a585599c5cf45b48cd70645430 Mon Sep 17 00:00:00 2001 From: mikiw Date: Thu, 15 Dec 2022 13:14:10 +0100 Subject: [PATCH 30/34] Update doc [skip ci] --- page/docs/guide/postman.md | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/page/docs/guide/postman.md b/page/docs/guide/postman.md index a5142daa1..7b9571862 100644 --- a/page/docs/guide/postman.md +++ b/page/docs/guide/postman.md @@ -52,14 +52,15 @@ constructor(MockStarknetMessaging mockStarknetMessaging_) public { ### Postman - l1 to l2 mock endpoint -``` -POST /postman/send_message_to_l2 -``` Sending mock transactions from L1 to L2 without the need for running L1. Deployed L2 contract address `l2_contract_address` and `entry_point_selector` must be valid otherwise new block will not be created. Normally `nonce` is calculated by l1 StarknetContract but in this case, we need to provide it manually. -Example POST json: +``` +POST /postman/send_message_to_l2 +``` + +Request: ``` { "l2_contract_address":"0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", @@ -73,15 +74,21 @@ Example POST json: } ``` +Response: +``` +{'transaction_hash': 1283711137402474683298514877824575801113282594306182191973277342794215646143} +``` + ### Postman - l2 to l1 mock endpoint +Sending mock transactions from L2 to L1. +Deployed L2 contract address `l2_contract_address` and `l1_contract_address` must be valid. + ``` POST /postman/consume_message_from_l2 ``` -Sending mock transactions from L2 to L1. -Deployed L2 contract address `l2_contract_address` and `l1_contract_address` must be valid. -Example POST json: +Request: ``` { "l2_contract_address": "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2", @@ -90,6 +97,11 @@ Example POST json: } ``` +Response: +``` +{'message_hash': '0xae14f241131b524ac8d043d9cb4934253ac5c5589afef19f0d761816a9c7e26d'} +``` + ## Dumping To preserve your Devnet instance for future use, there are several options: From 6d8fdc50f67c4cd944e5dd60e3b9f6197a96ff20 Mon Sep 17 00:00:00 2001 From: mikiw Date: Thu, 15 Dec 2022 13:35:04 +0100 Subject: [PATCH 31/34] move endpoints to new file --- test/test_endpoints.py | 191 +--------------------------- test/test_l1_l2_mock_messaging.py | 204 ++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+), 190 deletions(-) create mode 100644 test/test_l1_l2_mock_messaging.py diff --git a/test/test_endpoints.py b/test/test_endpoints.py index 84f365605..977236e8e 100644 --- a/test/test_endpoints.py +++ b/test/test_endpoints.py @@ -7,53 +7,25 @@ import pytest import requests from starkware.starknet.definitions.error_codes import StarknetErrorCode -from starkware.starknet.public.abi import get_selector_from_name -from starkware.starkware_utils.error_handling import StarkErrorCode from starknet_devnet.constants import DEFAULT_GAS_PRICE from starknet_devnet.server import app -from .account import invoke from .settings import APP_URL from .shared import ( FAILING_CONTRACT_PATH, GENESIS_BLOCK_HASH, GENESIS_BLOCK_NUMBER, - L1L2_ABI_PATH, - L1L2_CONTRACT_PATH, - PREDEPLOY_ACCOUNT_CLI_ARGS, - PREDEPLOYED_ACCOUNT_ADDRESS, - PREDEPLOYED_ACCOUNT_PRIVATE_KEY, STORAGE_CONTRACT_PATH, ) from .support.assertions import assert_valid_schema -from .util import ( - assert_tx_status, - call, - create_empty_block, - deploy, - devnet_in_background, - load_file_content, -) +from .util import create_empty_block, deploy, devnet_in_background, load_file_content DEPLOY_CONTENT = load_file_content("deploy.json") INVOKE_CONTENT = load_file_content("invoke.json") CALL_CONTENT = load_file_content("call.json") INVALID_HASH = "0x58d4d4ed7580a7a98ab608883ec9fe722424ce52c19f2f369eeea301f535914" INVALID_ADDRESS = "0x123" -USER_ID = 1 -L1_CONTRACT_ADDRESS = 0xE7F1725E7734CE288F8367E1BB143E90BB3F0512 -L2_CONTRACT_ADDRESS = ( - "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2" -) -ENTRY_POINT_SELECTOR = hex(get_selector_from_name("deposit")) -MESSAGE_TO_L2_NONCE = "0x0" - -# from_address, user and amount for L2 contract -CONSUME_PAYLOAD = ["0x0", "0x1", "0x3e8"] - -# user and amount for L1 contract -MESSAGE_TO_L2_PAYLOAD = ["0x1", "0x1"] def send_transaction(req_dict: dict): @@ -74,22 +46,6 @@ def send_call(req_dict: dict): ) -def send_message_to_l2(req_dict: dict): - """Sends the dict in a POST request and returns the response data.""" - return requests.post( - f"{APP_URL}/postman/send_message_to_l2", - json=req_dict, - ) - - -def consume_message_from_l2(req_dict: dict): - """Sends the dict in a POST request and returns the response data.""" - return requests.post( - f"{APP_URL}/postman/consume_message_from_l2", - json=req_dict, - ) - - def assert_deploy_resp(resp: bytes): """Asserts the validity of deploy response body.""" resp_dict = json.loads(resp.data.decode("utf-8")) @@ -394,148 +350,3 @@ def test_get_transaction_trace_of_rejected(): resp_body = resp.json() assert resp_body["code"] == str(StarknetErrorCode.NO_TRACE) assert resp.status_code == 500 - - -@devnet_in_background() -def test_send_message_to_l2_deploy_execute(): - """Test POST l1 to l2 deploy contract and execute transaction""" - # Deploy L1L2 contract - deploy_info = deploy(contract=L1L2_CONTRACT_PATH) - - # Create l1 to l2 mock transaction - response = send_message_to_l2( - { - "l2_contract_address": deploy_info["address"], - "entry_point_selector": ENTRY_POINT_SELECTOR, - "l1_contract_address": str(L1_CONTRACT_ADDRESS), - "payload": MESSAGE_TO_L2_PAYLOAD, - "nonce": MESSAGE_TO_L2_NONCE, - } - ) - - # Check balance of user - value = call( - function="get_balance", - address=deploy_info["address"], - abi_path=L1L2_ABI_PATH, - inputs=[str(USER_ID)], - ) - assert int(value) == 1 - assert response.status_code == 200 - assert_tx_status(hex(response.json().get("transaction_hash")), "ACCEPTED_ON_L2") - - -@devnet_in_background() -def test_send_message_to_l2_execute_without_data(): - """Test POST l1 to l2 without data""" - # Create l1 to l2 mock transaction - response = send_message_to_l2( - { - "l2_contract_address": "", - "entry_point_selector": "", - "l1_contract_address": "", - "payload": "", - "nonce": "", - } - ) - - assert response.status_code == 400 - assert response.json().get("code") == str(StarkErrorCode.MALFORMED_REQUEST) - - -@devnet_in_background() -def test_send_message_to_l2_execute_without_deploy(): - """Test POST l1 to l2 without the target contract being deployed""" - # Create l1 to l2 mock transaction - response = send_message_to_l2( - { - "l2_contract_address": L2_CONTRACT_ADDRESS, - "entry_point_selector": ENTRY_POINT_SELECTOR, - "l1_contract_address": str(L1_CONTRACT_ADDRESS), - "payload": MESSAGE_TO_L2_PAYLOAD, - "nonce": MESSAGE_TO_L2_NONCE, - } - ) - - assert response.status_code == 200 - assert_tx_status(hex(response.json().get("transaction_hash")), "REJECTED") - - -@devnet_in_background(*PREDEPLOY_ACCOUNT_CLI_ARGS) -def test_consume_message_from_l2_deploy_execute(): - """Test POST l2 to l1 deploy contract and execute transaction""" - deploy_info = deploy(L1L2_CONTRACT_PATH) - - # increase and withdraw balance - invoke( - calls=[(deploy_info["address"], "increase_balance", [USER_ID, 3333])], - account_address=PREDEPLOYED_ACCOUNT_ADDRESS, - private_key=PREDEPLOYED_ACCOUNT_PRIVATE_KEY, - ) - invoke( - calls=[ - ( - deploy_info["address"], - "withdraw", - [USER_ID, 1000, 0xE7F1725E7734CE288F8367E1BB143E90BB3F0512], - ) - ], - account_address=PREDEPLOYED_ACCOUNT_ADDRESS, - private_key=PREDEPLOYED_ACCOUNT_PRIVATE_KEY, - ) - - response = consume_message_from_l2( - { - "l2_contract_address": deploy_info["address"], - "l1_contract_address": str(L1_CONTRACT_ADDRESS), - "payload": CONSUME_PAYLOAD, - } - ) - - assert response.status_code == 200 - - -@devnet_in_background(*PREDEPLOY_ACCOUNT_CLI_ARGS) -def test_consume_message_from_l2_deploy_execute_without_withdraw(): - """Test POST l2 to l1 deploy contract and try to execute transaction without calling withdraw""" - deploy_info = deploy(L1L2_CONTRACT_PATH) - response = consume_message_from_l2( - { - "l2_contract_address": deploy_info["address"], - "l1_contract_address": str(L1_CONTRACT_ADDRESS), - "payload": CONSUME_PAYLOAD, - } - ) - - assert response.status_code == 500 - assert response.json().get("code") == str(StarkErrorCode.MALFORMED_REQUEST) - - -@devnet_in_background() -def test_consume_message_from_l2_execute_without_data(): - """Test POST l2 to l1 deploy without data""" - response = consume_message_from_l2( - { - "l2_contract_address": "", - "l1_contract_address": "", - "payload": "", - } - ) - - assert response.status_code == 400 - assert response.json().get("code") == str(StarkErrorCode.MALFORMED_REQUEST) - - -@devnet_in_background() -def test_consume_message_from_l2_execute_without_deploy(): - """Test POST l2 to l1 without contract deploy""" - response = consume_message_from_l2( - { - "l2_contract_address": L2_CONTRACT_ADDRESS, - "l1_contract_address": str(L1_CONTRACT_ADDRESS), - "payload": CONSUME_PAYLOAD, - } - ) - - assert response.status_code == 500 - assert response.json().get("code") == str(StarkErrorCode.MALFORMED_REQUEST) diff --git a/test/test_l1_l2_mock_messaging.py b/test/test_l1_l2_mock_messaging.py new file mode 100644 index 000000000..d4d88ff9a --- /dev/null +++ b/test/test_l1_l2_mock_messaging.py @@ -0,0 +1,204 @@ + +import json + +import requests +from starkware.starknet.public.abi import get_selector_from_name +from starkware.starkware_utils.error_handling import StarkErrorCode + +from starknet_devnet.server import app + +from .account import invoke +from .settings import APP_URL +from .shared import ( + L1L2_ABI_PATH, + L1L2_CONTRACT_PATH, + PREDEPLOY_ACCOUNT_CLI_ARGS, + PREDEPLOYED_ACCOUNT_ADDRESS, + PREDEPLOYED_ACCOUNT_PRIVATE_KEY, +) +from .util import ( + assert_tx_status, + call, + deploy, + devnet_in_background, + load_file_content, +) + +DEPLOY_CONTENT = load_file_content("deploy.json") +INVOKE_CONTENT = load_file_content("invoke.json") +CALL_CONTENT = load_file_content("call.json") +INVALID_HASH = "0x58d4d4ed7580a7a98ab608883ec9fe722424ce52c19f2f369eeea301f535914" +INVALID_ADDRESS = "0x123" +USER_ID = 1 +L1_CONTRACT_ADDRESS = 0xE7F1725E7734CE288F8367E1BB143E90BB3F0512 +L2_CONTRACT_ADDRESS = ( + "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2" +) +ENTRY_POINT_SELECTOR = hex(get_selector_from_name("deposit")) +MESSAGE_TO_L2_NONCE = "0x0" + +# from_address, user and amount for L2 contract +CONSUME_PAYLOAD = ["0x0", "0x1", "0x3e8"] + +# user and amount for L1 contract +MESSAGE_TO_L2_PAYLOAD = ["0x1", "0x1"] + +def send_message_to_l2(req_dict: dict): + """Sends the dict in a POST request and returns the response data.""" + return requests.post( + f"{APP_URL}/postman/send_message_to_l2", + json=req_dict, + ) + + +def consume_message_from_l2(req_dict: dict): + """Sends the dict in a POST request and returns the response data.""" + return requests.post( + f"{APP_URL}/postman/consume_message_from_l2", + json=req_dict, + ) + + +@devnet_in_background() +def test_send_message_to_l2_deploy_execute(): + """Test POST l1 to l2 deploy contract and execute transaction""" + # Deploy L1L2 contract + deploy_info = deploy(contract=L1L2_CONTRACT_PATH) + + # Create l1 to l2 mock transaction + response = send_message_to_l2( + { + "l2_contract_address": deploy_info["address"], + "entry_point_selector": ENTRY_POINT_SELECTOR, + "l1_contract_address": str(L1_CONTRACT_ADDRESS), + "payload": MESSAGE_TO_L2_PAYLOAD, + "nonce": MESSAGE_TO_L2_NONCE, + } + ) + + # Check balance of user + value = call( + function="get_balance", + address=deploy_info["address"], + abi_path=L1L2_ABI_PATH, + inputs=[str(USER_ID)], + ) + assert int(value) == 1 + assert response.status_code == 200 + assert_tx_status(hex(response.json().get("transaction_hash")), "ACCEPTED_ON_L2") + + +@devnet_in_background() +def test_send_message_to_l2_execute_without_data(): + """Test POST l1 to l2 without data""" + # Create l1 to l2 mock transaction + response = send_message_to_l2( + { + "l2_contract_address": "", + "entry_point_selector": "", + "l1_contract_address": "", + "payload": "", + "nonce": "", + } + ) + + assert response.status_code == 400 + assert response.json().get("code") == str(StarkErrorCode.MALFORMED_REQUEST) + + +@devnet_in_background() +def test_send_message_to_l2_execute_without_deploy(): + """Test POST l1 to l2 without the target contract being deployed""" + # Create l1 to l2 mock transaction + response = send_message_to_l2( + { + "l2_contract_address": L2_CONTRACT_ADDRESS, + "entry_point_selector": ENTRY_POINT_SELECTOR, + "l1_contract_address": str(L1_CONTRACT_ADDRESS), + "payload": MESSAGE_TO_L2_PAYLOAD, + "nonce": MESSAGE_TO_L2_NONCE, + } + ) + + assert response.status_code == 200 + assert_tx_status(hex(response.json().get("transaction_hash")), "REJECTED") + + +@devnet_in_background(*PREDEPLOY_ACCOUNT_CLI_ARGS) +def test_consume_message_from_l2_deploy_execute(): + """Test POST l2 to l1 deploy contract and execute transaction""" + deploy_info = deploy(L1L2_CONTRACT_PATH) + + # increase and withdraw balance + invoke( + calls=[(deploy_info["address"], "increase_balance", [USER_ID, 3333])], + account_address=PREDEPLOYED_ACCOUNT_ADDRESS, + private_key=PREDEPLOYED_ACCOUNT_PRIVATE_KEY, + ) + invoke( + calls=[ + ( + deploy_info["address"], + "withdraw", + [USER_ID, 1000, 0xE7F1725E7734CE288F8367E1BB143E90BB3F0512], + ) + ], + account_address=PREDEPLOYED_ACCOUNT_ADDRESS, + private_key=PREDEPLOYED_ACCOUNT_PRIVATE_KEY, + ) + + response = consume_message_from_l2( + { + "l2_contract_address": deploy_info["address"], + "l1_contract_address": str(L1_CONTRACT_ADDRESS), + "payload": CONSUME_PAYLOAD, + } + ) + + assert response.status_code == 200 + + +@devnet_in_background(*PREDEPLOY_ACCOUNT_CLI_ARGS) +def test_consume_message_from_l2_deploy_execute_without_withdraw(): + """Test POST l2 to l1 deploy contract and try to execute transaction without calling withdraw""" + deploy_info = deploy(L1L2_CONTRACT_PATH) + response = consume_message_from_l2( + { + "l2_contract_address": deploy_info["address"], + "l1_contract_address": str(L1_CONTRACT_ADDRESS), + "payload": CONSUME_PAYLOAD, + } + ) + + assert response.status_code == 500 + assert response.json().get("code") == str(StarkErrorCode.MALFORMED_REQUEST) + + +@devnet_in_background() +def test_consume_message_from_l2_execute_without_data(): + """Test POST l2 to l1 deploy without data""" + response = consume_message_from_l2( + { + "l2_contract_address": "", + "l1_contract_address": "", + "payload": "", + } + ) + + assert response.status_code == 400 + assert response.json().get("code") == str(StarkErrorCode.MALFORMED_REQUEST) + + +@devnet_in_background() +def test_consume_message_from_l2_execute_without_deploy(): + """Test POST l2 to l1 without contract deploy""" + response = consume_message_from_l2( + { + "l2_contract_address": L2_CONTRACT_ADDRESS, + "l1_contract_address": str(L1_CONTRACT_ADDRESS), + "payload": CONSUME_PAYLOAD, + } + ) + + assert response.status_code == 500 + assert response.json().get("code") == str(StarkErrorCode.MALFORMED_REQUEST) From 57196888f35eab0b6d05c5a1a97811c54e9f1906 Mon Sep 17 00:00:00 2001 From: mikiw Date: Thu, 15 Dec 2022 13:49:05 +0100 Subject: [PATCH 32/34] Update test_l1_l2_mock_messaging.py --- test/test_l1_l2_mock_messaging.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_l1_l2_mock_messaging.py b/test/test_l1_l2_mock_messaging.py index d4d88ff9a..32cfcee5c 100644 --- a/test/test_l1_l2_mock_messaging.py +++ b/test/test_l1_l2_mock_messaging.py @@ -1,12 +1,11 @@ - -import json +""" +Test l1 l2 mock messaging. +""" import requests from starkware.starknet.public.abi import get_selector_from_name from starkware.starkware_utils.error_handling import StarkErrorCode -from starknet_devnet.server import app - from .account import invoke from .settings import APP_URL from .shared import ( @@ -43,6 +42,7 @@ # user and amount for L1 contract MESSAGE_TO_L2_PAYLOAD = ["0x1", "0x1"] + def send_message_to_l2(req_dict: dict): """Sends the dict in a POST request and returns the response data.""" return requests.post( From 32a88f3eec02b63b0ab9415525d1e74c4aa03043 Mon Sep 17 00:00:00 2001 From: mikiw Date: Thu, 15 Dec 2022 14:42:53 +0100 Subject: [PATCH 33/34] Update postman.md [skip ci] --- page/docs/guide/postman.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/page/docs/guide/postman.md b/page/docs/guide/postman.md index 7b9571862..fb1f1d2cf 100644 --- a/page/docs/guide/postman.md +++ b/page/docs/guide/postman.md @@ -54,7 +54,8 @@ constructor(MockStarknetMessaging mockStarknetMessaging_) public { Sending mock transactions from L1 to L2 without the need for running L1. Deployed L2 contract address `l2_contract_address` and `entry_point_selector` must be valid otherwise new block will not be created. -Normally `nonce` is calculated by l1 StarknetContract but in this case, we need to provide it manually. +Normally `nonce` is calculated by l1 StarknetContract and it's used in L1 and L2. In this case, we need to provide it manually. + ``` POST /postman/send_message_to_l2 From 9bc7567c0d7fe9f28646171becc248c4aa96ab6b Mon Sep 17 00:00:00 2001 From: mikiw Date: Thu, 15 Dec 2022 15:53:02 +0100 Subject: [PATCH 34/34] Add L1_TO_L2_MESSAGE_ZEROED_COUNTER --- starknet_devnet/blueprints/postman.py | 6 +++--- test/test_l1_l2_mock_messaging.py | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/starknet_devnet/blueprints/postman.py b/starknet_devnet/blueprints/postman.py index 05b8938a1..4830f3673 100644 --- a/starknet_devnet/blueprints/postman.py +++ b/starknet_devnet/blueprints/postman.py @@ -6,6 +6,7 @@ from flask import Blueprint, jsonify, request from starkware.starknet.business_logic.transaction.objects import InternalL1Handler +from starkware.starknet.definitions.error_codes import StarknetErrorCode from starkware.starkware_utils.error_handling import StarkErrorCode from starknet_devnet.blueprints.base import hex_converter @@ -88,7 +89,6 @@ async def consume_message_from_l2(): from_address = hex_converter(request_json, "l2_contract_address") to_address = hex_converter(request_json, "l1_contract_address") payload = hex_converter(request_json, "payload", to_int_array) - result = "" try: result = await state.starknet_wrapper.consume_message_from_l2( @@ -97,7 +97,7 @@ async def consume_message_from_l2(): return jsonify({"message_hash": result}) except AssertionError as err: raise StarknetDevnetException( - code=StarkErrorCode.MALFORMED_REQUEST, - message=f"Message of hash {result} is fully consumed or does not exist.", + code=StarknetErrorCode.L1_TO_L2_MESSAGE_ZEROED_COUNTER, + message="Message is fully consumed or does not exist.", status_code=500, ) from err diff --git a/test/test_l1_l2_mock_messaging.py b/test/test_l1_l2_mock_messaging.py index 32cfcee5c..2ce6fa657 100644 --- a/test/test_l1_l2_mock_messaging.py +++ b/test/test_l1_l2_mock_messaging.py @@ -3,6 +3,7 @@ """ import requests +from starkware.starknet.definitions.error_codes import StarknetErrorCode from starkware.starknet.public.abi import get_selector_from_name from starkware.starkware_utils.error_handling import StarkErrorCode @@ -171,7 +172,9 @@ def test_consume_message_from_l2_deploy_execute_without_withdraw(): ) assert response.status_code == 500 - assert response.json().get("code") == str(StarkErrorCode.MALFORMED_REQUEST) + assert response.json().get("code") == str( + StarknetErrorCode.L1_TO_L2_MESSAGE_ZEROED_COUNTER + ) @devnet_in_background() @@ -201,4 +204,6 @@ def test_consume_message_from_l2_execute_without_deploy(): ) assert response.status_code == 500 - assert response.json().get("code") == str(StarkErrorCode.MALFORMED_REQUEST) + assert response.json().get("code") == str( + StarknetErrorCode.L1_TO_L2_MESSAGE_ZEROED_COUNTER + )