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

Rpc v0.3.0 update #494

Merged
merged 17 commits into from
Jun 1, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
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
6 changes: 3 additions & 3 deletions starknet_devnet/blueprints/rpc/call.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ async def call(request: RpcFunctionCall, block_id: BlockId) -> List[Felt]:
return [rpc_felt(res) for res in result["result"]]
except StarkException as ex:
if ex.code.name == "TRANSACTION_FAILED" and ex.code.value == 39:
raise RpcError.from_spec_name("INVALID_CALL_DATA") from ex
raise RpcError.from_spec_name("CONTRACT_ERROR") from ex
if (
f"Entry point {gateway_felt(request['entry_point_selector'])} not found"
in ex.message
):
raise RpcError.from_spec_name("INVALID_MESSAGE_SELECTOR") from ex
raise RpcError.from_spec_name("CONTRACT_ERROR") from ex
if "While handling calldata" in ex.message:
raise RpcError.from_spec_name("INVALID_CALL_DATA") from ex
raise RpcError.from_spec_name("CONTRACT_ERROR") from ex
raise RpcError(
code=PredefinedRpcErrorCode.INTERNAL_ERROR.value, message=ex.message
) from ex
557 changes: 481 additions & 76 deletions starknet_devnet/blueprints/rpc/rpc_spec.py

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions starknet_devnet/blueprints/rpc/rpc_spec_write.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@
"errors": [
{
"$ref": "#/components/errors/INVALID_CONTRACT_CLASS"
},
{
"$ref": "#/components/errors/CLASS_ALREADY_DECLARED"
}
]
},
Expand Down Expand Up @@ -149,6 +152,10 @@
},
"CLASS_HASH_NOT_FOUND": {
"$ref": "./api/starknet_api_openrpc.json#/components/errors/CLASS_HASH_NOT_FOUND"
},
"CLASS_ALREADY_DECLARED": {
"code": 51,
"message": "Class already declared"
}
}
}
Expand Down
12 changes: 11 additions & 1 deletion starknet_devnet/blueprints/rpc/structures/payloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ class StructMember(TypedDict):

name: str
type: str
offset: int
offset: Optional[int]


class FunctionAbiEntry(TypedDict):
Expand All @@ -595,6 +595,7 @@ class FunctionAbiEntry(TypedDict):
name: str
inputs: List[TypedParameter]
outputs: List[TypedParameter]
stateMutability: Optional[Literal["view"]]


class EventAbiEntry(TypedDict):
Expand Down Expand Up @@ -622,6 +623,15 @@ def function_abi_entry(abi_entry: AbiEntryType) -> FunctionAbiEntry:
"""
Convert function gateway abi entry to rpc FunctionAbiEntry
"""
if "stateMutability" in abi_entry:
return FunctionAbiEntry(
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
type=abi_entry["type"],
name=abi_entry["name"],
inputs=abi_entry["inputs"],
outputs=abi_entry["outputs"],
stateMutability=abi_entry["stateMutability"],
)

return FunctionAbiEntry(
type=abi_entry["type"],
name=abi_entry["name"],
Expand Down
16 changes: 14 additions & 2 deletions starknet_devnet/blueprints/rpc/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,18 @@ async def add_declare_transaction(
class_hash, transaction_hash = await state.starknet_wrapper.declare(
external_tx=make_declare(declare_transaction)
)
status_response = await state.starknet_wrapper.transactions.get_transaction_status(
hex(transaction_hash)
)

if status_response["tx_status"] == "REJECTED":
error_message = status_response["tx_failure_reason"].error_message
if (
"Class with hash" in error_message
and "is already declared" in error_message
):
raise RpcError.from_spec_name("CLASS_ALREADY_DECLARED")

return RpcDeclareTransactionResult(
transaction_hash=rpc_felt(transaction_hash),
class_hash=rpc_felt(class_hash),
Expand Down Expand Up @@ -193,9 +205,9 @@ async def estimate_fee(request: List[RpcBroadcastedTxn], block_id: BlockId) -> l
)
except StarkException as ex:
if "Entry point" in ex.message and "not found" in ex.message:
raise RpcError.from_spec_name("INVALID_MESSAGE_SELECTOR") from ex
raise RpcError.from_spec_name("CONTRACT_ERROR") from ex
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
if "While handling calldata" in ex.message:
raise RpcError.from_spec_name("INVALID_CALL_DATA") from ex
raise RpcError.from_spec_name("CONTRACT_ERROR") from ex
if "is not deployed" in ex.message:
raise RpcError.from_spec_name("CONTRACT_NOT_FOUND") from ex
raise RpcError(code=-1, message=ex.message) from ex
Expand Down
2 changes: 1 addition & 1 deletion test/rpc/test_rpc_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def test_call_raises_on_incorrect_selector(deploy_info):
},
)

assert ex["error"] == {"code": 21, "message": "Invalid message selector"}
assert ex["error"] == {"code": 40, "message": "Contract error"}


@pytest.mark.usefixtures("run_devnet_in_background")
Expand Down
91 changes: 28 additions & 63 deletions test/rpc/test_rpc_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from test.rpc.rpc_utils import rpc_call
from test.shared import (
ABI_1_PATH,
ABI_PATH,
CONTRACT_1_PATH,
PREDEPLOY_ACCOUNT_CLI_ARGS,
PREDEPLOYED_ACCOUNT_ADDRESS,
Expand Down Expand Up @@ -43,49 +44,6 @@
"L1_HANDLER": [],
}

# abi from ABI_PATH (test/shared.py), but with all stateMutability omitted
EXPECTED_ABI = [
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
{
"type": "struct",
"name": "Point",
"size": 2,
"members": [
{"name": "x", "offset": 0, "type": "felt"},
{"name": "y", "offset": 1, "type": "felt"},
],
},
{
"type": "constructor",
"name": "constructor",
"inputs": [{"name": "initial_balance", "type": "felt"}],
"outputs": [],
},
{
"type": "function",
"name": "increase_balance",
"inputs": [
{"name": "amount1", "type": "felt"},
{"name": "amount2", "type": "felt"},
],
"outputs": [],
},
{
"type": "function",
"name": "get_balance",
"inputs": [],
"outputs": [{"name": "res", "type": "felt"}],
},
{
"type": "function",
"name": "sum_point_array",
"inputs": [
{"name": "points_len", "type": "felt"},
{"name": "points", "type": "Point*"},
],
"outputs": [{"name": "res", "type": "Point"}],
},
]


def assert_correct_cairo_1_contract(contract_class):
"""
Expand All @@ -108,15 +66,17 @@ def test_get_deprecated_class(class_hash):
"""
Test get contract class
"""
resp = rpc_call(
"starknet_getClass", params={"block_id": "latest", "class_hash": class_hash}
)
contract_class = resp["result"]
with open(ABI_PATH, mode="r", encoding="utf-8") as expected_abi:
expected_abi_json = json.loads(expected_abi.read())
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
resp = rpc_call(
"starknet_getClass", params={"block_id": "latest", "class_hash": class_hash}
)
contract_class = resp["result"]

assert contract_class["entry_points_by_type"] == EXPECTED_ENTRY_POINTS
assert isinstance(contract_class["program"], str)
decompress_program(contract_class["program"])
assert contract_class["abi"] == EXPECTED_ABI
assert contract_class["entry_points_by_type"] == EXPECTED_ENTRY_POINTS
assert isinstance(contract_class["program"], str)
decompress_program(contract_class["program"])
assert contract_class["abi"] == expected_abi_json


@devnet_in_background(*PREDEPLOY_ACCOUNT_CLI_ARGS)
Expand Down Expand Up @@ -203,16 +163,21 @@ def test_get_deprecated_class_at(deploy_info):
"""
Test get contract class at given contract address
"""
contract_address: str = deploy_info["address"]
block_id: BlockId = "latest"

resp = rpc_call(
"starknet_getClassAt",
params={"contract_address": rpc_felt(contract_address), "block_id": block_id},
)
contract_class = resp["result"]
with open(ABI_PATH, mode="r", encoding="utf-8") as expected_abi:
expected_abi_json = json.loads(expected_abi.read())
contract_address: str = deploy_info["address"]
block_id: BlockId = "latest"

resp = rpc_call(
"starknet_getClassAt",
params={
"contract_address": rpc_felt(contract_address),
"block_id": block_id,
},
)
contract_class = resp["result"]

assert contract_class["entry_points_by_type"] == EXPECTED_ENTRY_POINTS
assert isinstance(contract_class["program"], str)
decompress_program(contract_class["program"])
assert contract_class["abi"] == EXPECTED_ABI
assert contract_class["entry_points_by_type"] == EXPECTED_ENTRY_POINTS
assert isinstance(contract_class["program"], str)
decompress_program(contract_class["program"])
assert contract_class["abi"] == expected_abi_json
4 changes: 2 additions & 2 deletions test/rpc/test_rpc_estimate_fee.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ def test_estimate_fee_with_invalid_call_data():
"starknet_estimateFee", {"request": [invoke_transaction], "block_id": "latest"}
)

assert ex["error"] == {"code": 22, "message": "Invalid call data"}
assert ex["error"] == {"code": 40, "message": "Contract error"}


@pytest.mark.usefixtures("run_devnet_in_background")
Expand Down Expand Up @@ -340,4 +340,4 @@ def test_estimate_fee_with_invalid_message_selector():
"starknet_estimateFee", {"request": [txn], "block_id": "latest"}
)

assert ex["error"] == {"code": 21, "message": "Invalid message selector"}
assert ex["error"] == {"code": 40, "message": "Contract error"}
11 changes: 0 additions & 11 deletions test/rpc/test_rpc_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,17 +226,6 @@ def test_get_events_malformed_request():
assert resp["error"]["code"] == PredefinedRpcErrorCode.INVALID_PARAMS.value


@pytest.mark.usefixtures("run_devnet_in_background")
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
def test_get_events_missing_parameter():
"""
Test RPC get_events with malformed request.
"""
params = create_get_events_filter()
del params["filter"]["address"]
resp = rpc_call("starknet_getEvents", params=params)
assert resp["error"]["code"] == PredefinedRpcErrorCode.INVALID_PARAMS.value


@pytest.mark.usefixtures("run_devnet_in_background")
def test_get_events_wrong_blockid_type():
"""
Expand Down
44 changes: 44 additions & 0 deletions test/rpc/test_rpc_transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,50 @@ def test_add_declare_transaction_v2():
assert is_felt(receipt["class_hash"])


@pytest.mark.usefixtures("devnet_with_account")
def test_declare_transaction_v2_already_declared():
"""Add declare transaction v2"""
contract_class, _, compiled_class_hash = load_cairo1_contract()

max_fee = int(4e16)

def declare_tx():
nonce = get_nonce(PREDEPLOYED_ACCOUNT_ADDRESS)

tx_hash = calculate_declare_transaction_hash(
contract_class=contract_class,
compiled_class_hash=compiled_class_hash,
chain_id=StarknetChainId.TESTNET.value,
sender_address=int(PREDEPLOYED_ACCOUNT_ADDRESS, 16),
max_fee=max_fee,
version=SUPPORTED_RPC_DECLARE_TX_VERSION,
nonce=nonce,
)

signature = _get_signature(tx_hash, PREDEPLOYED_ACCOUNT_PRIVATE_KEY)

declare_transaction = RpcBroadcastedDeclareTxnV2(
contract_class=rpc_contract_class(contract_class),
sender_address=PREDEPLOYED_ACCOUNT_ADDRESS,
compiled_class_hash=rpc_felt(compiled_class_hash),
type="DECLARE",
version=rpc_felt(SUPPORTED_RPC_DECLARE_TX_VERSION),
nonce=rpc_felt(nonce),
max_fee=rpc_felt(max_fee),
signature=list(map(rpc_felt, signature)),
)

return rpc_call(
"starknet_addDeclareTransaction",
params={"declare_transaction": declare_transaction},
)

declare_tx()
ex = declare_tx()

assert ex["error"] == {"code": 51, "message": "Class already declared"}


def _add_declare_transaction():
contract_class = load_contract_class(CONTRACT_PATH)
contract_class_dump = contract_class.dump()
Expand Down