Skip to content
This repository has been archived by the owner on Dec 15, 2023. It is now read-only.

Add L1 to L2 message mock endpoint #365

Merged
merged 35 commits into from
Dec 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
1339fe3
Add L1 to L2 message mock endpoint
mikiw Dec 8, 2022
cacc73d
Add tests and docs
mikiw Dec 9, 2022
b447eba
Update postman.py
mikiw Dec 9, 2022
fd736e4
formater
mikiw Dec 9, 2022
87e26c7
refactor to_int and to_int_array
mikiw Dec 11, 2022
bd1dfc8
Update test_endpoints.py
mikiw Dec 11, 2022
04742d9
Update test_endpoints.py
mikiw Dec 11, 2022
16f78de
refactor extract_hex_string to hex_converter
mikiw Dec 11, 2022
64d9c2b
refactor mock_message_to_l2
mikiw Dec 11, 2022
73cb0b6
Update postman.py
mikiw Dec 11, 2022
f17aafc
Update test_fee_token.py
mikiw Dec 12, 2022
0af2ab4
Add send_l1_to_l2
mikiw Dec 12, 2022
17f3c79
Update test_endpoints.py
mikiw Dec 12, 2022
fbcf227
InternalL1Handler refactor
mikiw Dec 12, 2022
444e10c
Alpha version of consume_message_from_l2
mikiw Dec 13, 2022
8f1e7bb
Add tx hash instead of call data
mikiw Dec 14, 2022
14687ac
Update test_endpoints.py
mikiw Dec 14, 2022
1cdf751
Remove json files in tests
mikiw Dec 14, 2022
28c4600
Refactor of function names
mikiw Dec 14, 2022
24058bf
Add send_message_to_l1 tests
mikiw Dec 14, 2022
e45ce0c
Update postman.md
mikiw Dec 14, 2022
b5d205f
Update postman.md
mikiw Dec 15, 2022
ab6b032
rename send_message_to_l1 to consume_message_from_l2
mikiw Dec 15, 2022
0f7272e
rename invoke_tx_hash to transaction_hash
mikiw Dec 15, 2022
c335b8f
Update test_endpoints.py
mikiw Dec 15, 2022
e1b2f1e
Add constants
mikiw Dec 15, 2022
5075e28
Add AssertionError
mikiw Dec 15, 2022
53c4ee8
after format
mikiw Dec 15, 2022
65846aa
Merge branch 'master' into l1-l2-endpoints
mikiw Dec 15, 2022
9352f56
fix of get_selector_from_name
mikiw Dec 15, 2022
fd7f3fb
Update doc
mikiw Dec 15, 2022
6d8fdc5
move endpoints to new file
mikiw Dec 15, 2022
5719688
Update test_l1_l2_mock_messaging.py
mikiw Dec 15, 2022
32a88f3
Update postman.md
mikiw Dec 15, 2022
9bc7567
Add L1_TO_L2_MESSAGE_ZEROED_COUNTER
mikiw Dec 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions page/docs/guide/postman.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,59 @@ constructor(MockStarknetMessaging mockStarknetMessaging_) public {
}
```

### Postman - l1 to l2 mock endpoint
FabijanC marked this conversation as resolved.
Show resolved Hide resolved

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 and it's used in L1 and L2. In this case, we need to provide it manually.


```
POST /postman/send_message_to_l2
```

Request:
```
{
"l2_contract_address":"0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2",
"entry_point_selector":"0xC73F681176FC7B3F9693986FD7B14581E8D540519E27400E88B8713932BE01",
"l1_contract_address":"0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
"payload":[
"0x1",
"0x2"
],
"nonce":"0x0"
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
}
```

Response:
```
{'transaction_hash': 1283711137402474683298514877824575801113282594306182191973277342794215646143}
```

### Postman - l2 to l1 mock endpoint

Sending mock transactions from L2 to L1.
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
Deployed L2 contract address `l2_contract_address` and `l1_contract_address` must be valid.

```
POST /postman/consume_message_from_l2
```

Request:
```
{
"l2_contract_address": "0x00285ddb7e5c777b310d806b9b2a0f7c7ba0a41f12b420219209d97a3b7f25b2",
"l1_contract_address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
"payload": ["0x0", "0x1", "0x3e8"],
}
```

Response:
```
{'message_hash': '0xae14f241131b524ac8d043d9cb4934253ac5c5589afef19f0d761816a9c7e26d'}
```

## Dumping

To preserve your Devnet instance for future use, there are several options:
Expand Down
18 changes: 11 additions & 7 deletions starknet_devnet/blueprints/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@

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__)

Expand Down Expand Up @@ -44,20 +48,20 @@ def extract_positive(request_json, prop_name: str):
return value


def extract_hex_string(request_json, prop_name: str) -> int:
"""Parse value from hex string to int"""
def hex_converter(request_json, prop_name: str, convert=custom_int) -> int:
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
"""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 int(value, 16)
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,
Expand Down Expand Up @@ -172,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)

Expand Down
48 changes: 47 additions & 1 deletion starknet_devnet/blueprints/postman.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
import json

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
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")

Expand Down Expand Up @@ -55,3 +58,46 @@ async def flush():

result_dict = await state.starknet_wrapper.postman_flush()
return jsonify(result_dict)


@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 {}

# Generate transactions
transaction = InternalL1Handler.create(
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
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)
return jsonify({"transaction_hash": result})


@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 {}

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)

try:
result = await state.starknet_wrapper.consume_message_from_l2(
from_address, to_address, payload
)
return jsonify({"message_hash": result})
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
except AssertionError as err:
raise StarknetDevnetException(
code=StarknetErrorCode.L1_TO_L2_MESSAGE_ZEROED_COUNTER,
message="Message is fully consumed or does not exist.",
status_code=500,
) from err
33 changes: 33 additions & 0 deletions starknet_devnet/starknet_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,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 (
Expand Down Expand Up @@ -547,6 +548,38 @@ 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, 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 = 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 transaction.hash_value

async def postman_flush(self) -> dict:
"""Handles all pending L1 <> L2 messages and sends them to the other layer."""

Expand Down
5 changes: 5 additions & 0 deletions starknet_devnet/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand Down
4 changes: 2 additions & 2 deletions test/test_fee_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,15 @@ 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():
"""Assert failure if mint address missing"""
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
Expand Down
Loading