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

Adapt to Starknet / cairo-lang 0.12.1a0 #516

Merged
merged 16 commits into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from 10 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 poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ keywords = ["starknet", "cairo", "testnet", "local", "server"]
python = ">=3.9,<3.10"
Flask = {extras = ["async"], version = "~2.0.3"}
flask-cors = "~3.0.10"
cairo-lang = "0.12.0"
cairo-lang = "0.12.1a0"
Werkzeug = "~2.0.3"
cloudpickle = "~2.1.0"
crypto-cpp-py = "~1.4.0"
Expand Down
1 change: 1 addition & 0 deletions scripts/compile_cairo1.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ for contract in "test/contracts/cairo1"/*.cairo; do
cargo run --bin starknet-compile \
--manifest-path "$CAIRO_1_COMPILER_MANIFEST" \
-- \
--single-file \
"$contract" "$sierra_output"

# compile to casm
Expand Down
21 changes: 21 additions & 0 deletions scripts/compute_compiled_class_hash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Script for computing compiled class hash of a contract"""

import sys

from starkware.starknet.core.os.contract_class.compiled_class_hash import (
compute_compiled_class_hash,
)
from starkware.starknet.services.api.contract_class.contract_class import CompiledClass


def main():
"""Main function"""
casm_path = sys.argv[1]
with open(casm_path, encoding="utf-8") as casm_file:
casm = CompiledClass.loads(casm_file.read())
compiled_class_hash = compute_compiled_class_hash(casm)
print(hex(compiled_class_hash))


if __name__ == "__main__":
main()
58 changes: 58 additions & 0 deletions scripts/find_reverted.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""Find reverted txs in given range"""

import sys
import time

import requests

def extract_not_succeeded_from_block(block_number: int) -> list:
"""Check txs in a block"""
while True:
req = requests.get(f"https://external.integration.starknet.io/feeder_gateway/get_block?blockNumber={block_number}")
if req.status_code == 200:
break

print(f"Status code not OK; is: {req.status_code} ({req.text})")

sleep_secs = 1
print(f"Sleeping for {sleep_secs} s")
time.sleep(sleep_secs)

body = req.json()

not_succeeded = []

receipts = body["transaction_receipts"]
for receipt in receipts:
status = receipt["execution_status"]
if status != "SUCCEEDED":
print(f"{receipt['transaction_hash']} is {status}")
not_succeeded.append(receipt["transaction_hash"])

return not_succeeded

def main():
"""The main method"""
try:
from_block = int(sys.argv[1])
to_block =int(sys.argv[2])
except (IndexError, ValueError):
sys.exit(f"{__file__}: <FROM_BLOCK> <TO_BLOCK>")

print(f"Searching in [{from_block} to {to_block}]")

not_succeeded = []
for block_number in range(from_block, to_block + 1):
not_succeeded.extend(extract_not_succeeded_from_block(block_number))
time.sleep(0.2)

if block_number % 100 == 0:
print(f"At block {block_number}, found so far: {len(not_succeeded)}")

print()
print("Finished. Not succeeded txs:")
print(*not_succeeded, sep="\n")
print("Count:", len(not_succeeded))

if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion starknet_devnet/blueprints/rpc/structures/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ def status() -> TxnStatus:
mapping: Dict[TransactionStatus, TxnStatus] = {
TransactionStatus.ACCEPTED_ON_L2: "ACCEPTED_ON_L2",
TransactionStatus.ACCEPTED_ON_L1: "ACCEPTED_ON_L1",
TransactionStatus.RECEIVED: "PENDING",
TransactionStatus.RECEIVED: "PENDING", # TODO
TransactionStatus.REJECTED: "REJECTED",
}
return mapping[txr.status]
Expand Down
2 changes: 1 addition & 1 deletion starknet_devnet/blueprints/rpc/structures/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def rpc_block_status(block_status: str) -> RpcBlockStatus:
block_status_map = {
BlockStatus.PENDING.name: "PENDING",
BlockStatus.ABORTED.name: "REJECTED",
BlockStatus.REVERTED.name: "REJECTED",
BlockStatus.REVERTED.name: "REJECTED", # TODO
BlockStatus.ACCEPTED_ON_L2.name: "ACCEPTED_ON_L2",
BlockStatus.ACCEPTED_ON_L1.name: "ACCEPTED_ON_L1",
}
Expand Down
44 changes: 32 additions & 12 deletions starknet_devnet/blueprints/rpc/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@
RpcInvokeTransactionResult,
rpc_transaction_receipt,
)
from starknet_devnet.blueprints.rpc.structures.types import BlockId, RpcError, TxnHash
from starknet_devnet.blueprints.rpc.structures.types import (
BlockId,
PredefinedRpcErrorCode,
RpcError,
TxnHash,
)
from starknet_devnet.blueprints.rpc.utils import (
assert_block_id_is_valid,
get_block_by_block_id,
Expand Down Expand Up @@ -133,14 +138,20 @@ async def add_declare_transaction(
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
):
assert (
status_response["tx_status"] != "REJECTED"
), f"Invalid status response: {status_response}"

if status_response["tx_status"] == "REVERTED":
revert_reason = status_response["tx_revert_reason"]
if "is already declared" in revert_reason:
raise RpcError.from_spec_name("CLASS_ALREADY_DECLARED")

# an unknown error
raise RpcError(
code=PredefinedRpcErrorCode.INTERNAL_ERROR, message=revert_reason
)

return RpcDeclareTransactionResult(
transaction_hash=rpc_felt(transaction_hash),
class_hash=rpc_felt(class_hash),
Expand All @@ -162,11 +173,20 @@ async def add_deploy_account_transaction(
status_response = await state.starknet_wrapper.transactions.get_transaction_status(
hex(transaction_hash)
)
if (
status_response["tx_status"] == "REJECTED"
and "is not declared" in status_response["tx_failure_reason"].error_message
):
raise RpcError.from_spec_name("CLASS_HASH_NOT_FOUND")

assert (
status_response["tx_status"] != "REJECTED"
), f"Invalid status response: {status_response}"

if status_response["tx_status"] == "REVERTED":
revert_reason = status_response["tx_revert_reason"]
if "is not declared" in revert_reason:
raise RpcError.from_spec_name("CLASS_HASH_NOT_FOUND")

# an unknown error
raise RpcError(
code=PredefinedRpcErrorCode.INTERNAL_ERROR, message=revert_reason
)

return RpcDeployAccountTransactionResult(
transaction_hash=rpc_felt(transaction_hash),
Expand Down
4 changes: 2 additions & 2 deletions starknet_devnet/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@
OLD_SUPPORTED_VERSIONS = [0]

# account used by Starknet CLI; calculated using
# poetry run python scripts/compute_compiled_class_hash.py \
# poetry run python scripts/compute_deprecated_compiled_class_hash.py \
# ~/.cache/pypoetry/virtualenvs/<YOUR_VENV>/lib/python3.9/site-packages/starkware/starknet/third_party/open_zeppelin/account.json
STARKNET_CLI_ACCOUNT_CLASS_HASH = (
0x646A72E2AAB2FCA75D713FBE4A58F2D12CBD64105621B89DC9CE7045B5BF02B
0x495B30BEA9715F5BD596989103C4D609917FD343B935163D58F29EDB12E2472
)

# starkware.starknet.public.abi.get_selector_from_name("replace_class")
Expand Down
12 changes: 10 additions & 2 deletions starknet_devnet/origin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
FeederGatewayClient,
)
from starkware.starknet.services.api.feeder_gateway.response_objects import (
FinalityStatus,
StarknetBlock,
TransactionInfo,
TransactionReceipt,
Expand Down Expand Up @@ -80,18 +81,24 @@ class NullOrigin(Origin):
"""

async def get_transaction_status(self, transaction_hash: str):
return {"tx_status": TransactionStatus.NOT_RECEIVED.name}
return {
"tx_status": TransactionStatus.NOT_RECEIVED.name,
"finality_status": FinalityStatus.NOT_RECEIVED.name,
"execution_status": None,
}

async def get_transaction(self, transaction_hash: str) -> TransactionInfo:
return TransactionInfo.create(
status=TransactionStatus.NOT_RECEIVED,
finality_status=FinalityStatus.NOT_RECEIVED,
)

async def get_transaction_receipt(
self, transaction_hash: str
) -> TransactionReceipt:
return TransactionReceipt(
status=TransactionStatus.NOT_RECEIVED,
finality_status=FinalityStatus.NOT_RECEIVED,
execution_status=None,
transaction_hash=0, # testnet returns 0 instead of received hash
events=[],
l2_to_l1_messages=[],
Expand All @@ -101,6 +108,7 @@ async def get_transaction_receipt(
execution_resources=None,
actual_fee=None,
transaction_failure_reason=None,
revert_error=None,
l1_to_l2_consumed_message=None,
)

Expand Down
33 changes: 21 additions & 12 deletions starknet_devnet/starknet_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
BlockStateUpdate,
ClassHashPair,
ContractAddressHashPair,
ExecutionStatus,
FinalityStatus,
StarknetBlock,
StateDiff,
StorageEntry,
Expand Down Expand Up @@ -346,9 +348,13 @@ def _store_transaction(
error_message: str = None,
) -> StarknetBlock:
"""Stores the provided transaction in the transaction storage."""
if transaction.status == TransactionStatus.REJECTED:
assert error_message, "error_message must be present if tx rejected"
transaction.set_failure_reason(error_message)

# TODO is this checking and setting even necessary?
# if it needs to be done, can it be done earlier, outside of this function?

# if transaction.status in [TransactionStatus.REJECTED, TransactionStatus.REVERTED]:
# assert error_message, "error_message must be present if tx rejected"
# transaction.set_failure_reason(error_message)

self.transactions.store(transaction.transaction_hash, transaction)

Expand Down Expand Up @@ -470,7 +476,6 @@ async def __aexit__(
raise StarknetDevnetException(
code=StarknetErrorCode.UNEXPECTED_FAILURE, message=str(exc)
) from exc
status = TransactionStatus.REJECTED

# restore block info
self.starknet_wrapper.get_state().state.block_info = (
Expand All @@ -479,18 +484,19 @@ async def __aexit__(

transaction = DevnetTransaction(
internal_tx=self.internal_tx,
status=status,
status=TransactionStatus.REVERTED,
execution_status=ExecutionStatus.REVERTED, # TODO or reverted
finality_status=FinalityStatus.ACCEPTED_ON_L2,
execution_info=TransactionExecutionInfo.empty(),
transaction_hash=tx_hash,
block_number=None, # Rejected txs have no block number
transaction_index=None, # Rejected txs have no tx index
block_number=0, # Rejected txs have no block number
transaction_index=0, # Rejected txs have no tx index
revert_error=exc.message,
)
self.starknet_wrapper._store_transaction(
transaction, error_message=exc.message
)
else:
status = TransactionStatus.ACCEPTED_ON_L2

assert self.execution_info is not None
if self.execution_info.call_info:
await self.starknet_wrapper._register_new_contracts(
Expand All @@ -512,7 +518,9 @@ async def __aexit__(

transaction = DevnetTransaction(
internal_tx=self.internal_tx,
status=status,
status=TransactionStatus.ACCEPTED_ON_L2,
execution_status=ExecutionStatus.SUCCEEDED,
finality_status=FinalityStatus.ACCEPTED_ON_L2,
execution_info=self.execution_info,
transaction_hash=tx_hash,
block_number=next_block_number,
Expand Down Expand Up @@ -911,6 +919,7 @@ async def calculate_traces_and_fees(
execution_info.fee_transfer_info
),
signature=external_tx.signature,
revert_error=execution_info.revert_error,
)
traces.append(trace)

Expand Down Expand Up @@ -1037,9 +1046,9 @@ async def abort_blocks(self, starting_block: StarknetBlock) -> str:
hex(last_block.block_hash)
)

# Reject transactions.
# Revert transactions.
for transaction in last_block.transactions:
await self.transactions.reject_transaction(
await self.transactions.revert_transaction_in_aborted_block(
tx_hash=transaction.transaction_hash
)

Expand Down
Loading