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

genesis block, create block endpoint, performace optimizations, initial boot optimization, fix 188 #189

Merged
merged 30 commits into from
Aug 4, 2022
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0ec3578
fix(118) warning on zero division with normal response
tabaktoni Jul 21, 2022
3ea96c8
fix: test zero gas price
tabaktoni Jul 21, 2022
22aa29b
fix: replace lstrip with split for rpc method name
tabaktoni Jul 21, 2022
092aee8
Feature: do initialization on package run, this slows down initial st…
tabaktoni Jul 22, 2022
28d52f5
Feature create empty block
tabaktoni Jul 22, 2022
6cf5d47
feature: boot indicator, refactor wrapper async
tabaktoni Jul 22, 2022
17845f0
revert RPC fix
tabaktoni Jul 22, 2022
5ea9bc1
Merge branch 'master' into fix/188-zero-division-gas-price
tabaktoni Jul 22, 2022
9958e44
Fix: validate transaction_receipts exist, exstrast to fun
tabaktoni Jul 25, 2022
c254ea5
FIx: removed async call
tabaktoni Jul 25, 2022
06f2b43
Fix: Lints and Transaction_receipts in empty block instead of None tr…
tabaktoni Jul 25, 2022
3ee69da
Merge branch 'master' into fix/188-zero-division-gas-price
tabaktoni Jul 25, 2022
bcb6f24
Fix: Tests
tabaktoni Jul 25, 2022
a95e31a
Fix: Tests with Genesis block
tabaktoni Jul 27, 2022
34be494
Fix: remove time testing, lint, initialize in server main instead of …
tabaktoni Jul 27, 2022
b8457e5
Merge branch 'master' into fix/188-zero-division-gas-price
tabaktoni Jul 27, 2022
393a28a
Feat: create_block docs and test
tabaktoni Jul 27, 2022
06a9fc6
Fix: test function naming
tabaktoni Jul 27, 2022
935e5f5
fix: cleanup
tabaktoni Aug 2, 2022
e0d7d7f
fix: remove check by timestamp
tabaktoni Aug 2, 2022
5e51da4
feature: added pylint quotes extension
tabaktoni Aug 2, 2022
c6fe2c6
Fix: test enhanced
tabaktoni Aug 2, 2022
7e5943d
fix: post create_block
tabaktoni Aug 2, 2022
909ec2e
fix: tipfeler and quotes
tabaktoni Aug 2, 2022
c2f0fcb
Merge branch 'master' into fix/188-zero-division-gas-price
tabaktoni Aug 2, 2022
199f4dd
cleanup
tabaktoni Aug 2, 2022
1455f70
Merge branch 'master' into fix/188-zero-division-gas-price
tabaktoni Aug 3, 2022
9fcf2d5
Fix: test and clenup
tabaktoni Aug 3, 2022
d78e17e
Merge branch 'master' into fix/188-zero-division-gas-price
tabaktoni Aug 3, 2022
7d95ac2
fix: starknet cleanup after merge, test
tabaktoni Aug 3, 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
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,15 @@ docker run \

A local block explorer (Voyager), as noted [here](https://voyager.online/local-version/), apparently cannot be set up to work with Devnet. Read more in [this issue](https://github.com/Shard-Labs/starknet-devnet/issues/60).

## Block

Devnet start with genesis block.
tabaktoni marked this conversation as resolved.
Show resolved Hide resolved
You can create empty block without transaction.

```
GET /create_block
tabaktoni marked this conversation as resolved.
Show resolved Hide resolved
```

## Lite mode

To improve Devnet performance, instead of calculating the actual hash of deployment transactions and blocks, sequential numbering can be used (0x0, 0x1, 0x2, ...).
Expand Down
47 changes: 47 additions & 0 deletions starknet_devnet/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,50 @@ async def generate(
self.__state_updates[block_number] = state_update

return block

def generate_empty(
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
self, state: StarknetState, state_root: bytes, state_update = None
) -> StarknetBlock:
"""
Generate block without transaction
"""
block_number = self.get_number_of_blocks()
timestamp = state.state.block_info.block_timestamp

if block_number == 0:
parent_block_hash = 0
else:
last_block = self.__get_last_block()
parent_block_hash = last_block.block_hash

#Fake block number
block_hash = block_number

block = StarknetBlock.create(
block_hash=block_hash,
block_number=block_number,
state_root=state_root,
transactions=[],
timestamp=timestamp,
transaction_receipts=(),
status=BlockStatus.ACCEPTED_ON_L2,
gas_price=state.state.block_info.gas_price,
sequencer_address=state.general_config.sequencer_address,
parent_block_hash=parent_block_hash,
starknet_version=CAIRO_LANG_VERSION
)

self.__num2block[block_number] = block
self.__hash2num[block_hash] = block_number

if state_update is not None:
state_update = BlockStateUpdate(
block_hash=block_hash,
old_root=state_update.old_root,
new_root=state_update.new_root,
state_diff=state_update.state_diff,
)

self.__state_updates[block_number] = state_update

return block
6 changes: 6 additions & 0 deletions starknet_devnet/blueprints/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,9 @@ async def mint():

new_balance = await FeeToken.get_balance(address)
return jsonify({"new_balance": new_balance, "unit": "wei", "tx_hash": tx_hash})

@base.route("/create_block", methods=["GET"])
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
async def create_block():
"""Create empyt block"""
tabaktoni marked this conversation as resolved.
Show resolved Hide resolved
block = await state.starknet_wrapper.create_empty_block()
return Response(block.dumps(), status=200, mimetype="application/json")
29 changes: 16 additions & 13 deletions starknet_devnet/blueprints/feeder_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,21 @@ def _get_block_object(block_hash: str, block_number: int):

return block

def _get_block_transaction_traces(block):
traces = []
if block.transaction_receipts:
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
for transaction in block.transaction_receipts:
tx_hash = hex(transaction.transaction_hash)
trace = state.starknet_wrapper.transactions.get_transaction_trace(tx_hash)

# expected trace is equal to response of get_transaction, but with the hash property
trace_dict = trace.dump()
trace_dict["transaction_hash"] = tx_hash
traces.append(trace_dict)

# assert correct structure
return BlockTransactionTraces.load({ "traces": traces })

@feeder_gateway.route("/is_alive", methods=["GET"])
def is_alive():
"""Health check endpoint."""
Expand Down Expand Up @@ -86,19 +101,7 @@ def get_block_traces():
block_number = request.args.get("blockNumber", type=custom_int)

block = _get_block_object(block_hash=block_hash, block_number=block_number)

traces = []
for transaction in block.transaction_receipts:
tx_hash = hex(transaction.transaction_hash)
trace = state.starknet_wrapper.transactions.get_transaction_trace(tx_hash)

# expected trace is equal to response of get_transaction, but with the hash property
trace_dict = trace.dump()
trace_dict["transaction_hash"] = tx_hash
traces.append(trace_dict)

# assert correct structure
block_transaction_traces = BlockTransactionTraces.load({ "traces": traces })
block_transaction_traces = _get_block_transaction_traces(block)

return jsonify(block_transaction_traces.dump())

Expand Down
6 changes: 3 additions & 3 deletions starknet_devnet/blueprints/rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ async def call(contract_address: str, entry_point_selector: str, calldata: list,
raise RpcError(code=-1, message=ex.message) from ex


async def estimate_fee():
async def estimate_fee(request_body: dict):
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
"""
Get the estimate fee for the transaction
"""
Expand All @@ -304,7 +304,7 @@ async def chain_id() -> str:
"""
Return the currently configured StarkNet chain id
"""
devnet_state = await state.starknet_wrapper.get_state()
devnet_state = state.starknet_wrapper.get_state()
config = devnet_state.general_config
chain: int = config.chain_id.value
return hex(chain)
Expand Down Expand Up @@ -526,7 +526,7 @@ def new_root() -> str:
}
transactions: list = await mapping[requested_scope]()

devnet_state = await state.starknet_wrapper.get_state()
devnet_state = state.starknet_wrapper.get_state()
config = devnet_state.general_config

block: RpcBlock = {
Expand Down
3 changes: 3 additions & 0 deletions starknet_devnet/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from pickle import UnpicklingError
import sys
import asyncio

from flask import Flask, jsonify
from flask_cors import CORS
Expand Down Expand Up @@ -101,6 +102,8 @@ def main():
set_start_time(args)
set_gas_price(args)

asyncio.run(state.starknet_wrapper.initialize())

try:
meinheld.listen((args.host, args.port))
print(f" * Listening on http://{args.host}:{args.port}/ (Press CTRL+C to quit)")
Expand Down
74 changes: 44 additions & 30 deletions starknet_devnet/starknet_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
This module introduces `StarknetWrapper`, a wrapper class of
starkware.starknet.testing.starknet.Starknet.
"""

import dataclasses
from copy import deepcopy
from typing import Dict, List, Tuple, Union
Expand Down Expand Up @@ -82,39 +81,50 @@ def load(path: str) -> "StarknetWrapper":
async def initialize(self):
"""Initialize the underlying starknet instance, fee_token and accounts."""
if not self.__initialized:
starknet = await self.__get_starknet()
starknet = await self.__init_starknet()

await self.__deploy_fee_token()
await self.__deploy_accounts()

await self.__preserve_current_state(starknet.state.state)
await self.create_empty_block()
self.__initialized = True

async def create_empty_block(self):
"""create empty block"""
state_update = await self.__update_state()
state = self.get_state()
state_root = self.__get_state_root()
return self.blocks.generate_empty(state, state_root, state_update)

async def __preserve_current_state(self, state: CarriedState):
self.__current_carried_state = deepcopy(state)
self.__current_carried_state.shared_state = state.shared_state

async def __get_starknet(self):
async def __init_starknet(self):
"""
Returns the underlying Starknet instance, creating it first if necessary.
Create and return underlying Starknet instance
"""
if not self.__starknet:
self.__starknet = await Starknet.empty(general_config=DEFAULT_GENERAL_CONFIG)
return self.__starknet

async def get_state(self):
def get_starknet(self):
"""
Returns the underlying Starknet instance.
"""
return self.__starknet

def get_state(self):
"""
Returns the StarknetState of the underlyling Starknet instance,
creating the instance first if necessary.
Returns the StarknetState of the underlyling Starknet instance.
"""
starknet = await self.__get_starknet()
return starknet.state
return self.__starknet.state

async def __update_state(self):
previous_state = self.__current_carried_state
assert previous_state is not None
current_carried_state = (await self.get_state()).state
state = await self.get_state()
current_carried_state = self.get_state().state
state = self.get_state()

current_carried_state.block_info = self.block_info_generator.next_block(
block_info=current_carried_state.block_info,
Expand All @@ -138,9 +148,8 @@ async def __update_state(self):

return None

async def __get_state_root(self):
state = await self.get_state()
return state.state.shared_state.contract_states.root
def __get_state_root(self):
return self.get_state().state.shared_state.contract_states.root

async def __store_transaction(
self, transaction: DevnetTransaction, tx_hash: int,
Expand All @@ -155,8 +164,8 @@ async def __store_transaction(
assert error_message, "error_message must be present if tx rejected"
transaction.set_failure_reason(error_message)
else:
state = await self.get_state()
state_root = await self.__get_state_root()
state = self.get_state()
state_root = self.__get_state_root()

block = await self.blocks.generate(
transaction,
Expand All @@ -169,12 +178,12 @@ async def __store_transaction(
self.transactions.store(tx_hash, transaction)

async def __deploy_fee_token(self):
starknet = await self.__get_starknet()
starknet = self.get_starknet()
await FeeToken.deploy(starknet)
self.contracts.store(FeeToken.ADDRESS, ContractWrapper(FeeToken.contract, FeeToken.get_contract_class()))

async def __deploy_accounts(self):
starknet = await self.__get_starknet()
starknet = self.get_starknet()
for account in self.accounts:
contract = await account.deploy(starknet)
self.contracts.store(account.address, ContractWrapper(contract, Account.get_contract_class()))
Expand All @@ -192,7 +201,7 @@ async def declare(self, declare_transaction: Declare) -> Tuple[int, int]:
Returns (class_hash, transaction_hash)
"""

starknet = await self.__get_starknet()
starknet = self.get_starknet()
internal_declare: InternalDeclare = InternalDeclare.from_external(
declare_transaction,
starknet.state.general_config
Expand Down Expand Up @@ -227,7 +236,7 @@ async def deploy(self, deploy_transaction: Deploy) -> Tuple[int, int]:
Returns (contract_address, transaction_hash).
"""

state = await self.get_state()
state = self.get_state()
contract_class = deploy_transaction.contract_definition

internal_tx: InternalDeploy = InternalDeploy.from_external(deploy_transaction, state.general_config)
Expand All @@ -242,7 +251,7 @@ async def deploy(self, deploy_transaction: Deploy) -> Tuple[int, int]:
else:
tx_hash = internal_tx.hash_value

starknet = await self.__get_starknet()
starknet = self.get_starknet()

try:
contract = await starknet.deploy(
Expand Down Expand Up @@ -282,7 +291,7 @@ async def deploy(self, deploy_transaction: Deploy) -> Tuple[int, int]:

async def invoke(self, invoke_function: InvokeFunction):
"""Perform invoke according to specifications in `transaction`."""
state = await self.get_state()
state = self.get_state()
invoke_transaction: InternalInvokeFunction = InternalInvokeFunction.from_external(invoke_function, state.general_config)

try:
Expand Down Expand Up @@ -337,7 +346,7 @@ async def call(self, transaction: InvokeFunction):
async def __register_new_contracts(self, internal_calls: List[Union[FunctionInvocation, CallInfo]], tx_hash: int):
for internal_call in internal_calls:
if internal_call.entry_point_type == EntryPointType.CONSTRUCTOR:
state = await self.get_state()
state = self.get_state()
class_hash = to_bytes(internal_call.class_hash)
contract_class = state.state.get_contract_class(class_hash)

Expand All @@ -351,7 +360,7 @@ async def get_storage_at(self, contract_address: int, key: int) -> str:
Returns the storage identified by `key`
from the contract at `contract_address`.
"""
state = await self.get_state()
state = self.get_state()
contract_states = state.state.contract_states

contract_state = contract_states[contract_address]
Expand All @@ -361,18 +370,18 @@ async def get_storage_at(self, contract_address: int, key: int) -> str:

async def load_messaging_contract_in_l1(self, network_url: str, contract_address: str, network_id: str) -> dict:
"""Loads the messaging contract at `contract_address`"""
starknet = await self.__get_starknet()
starknet = self.get_starknet()
return self.l1l2.load_l1_messaging_contract(starknet, network_url, contract_address, network_id)

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

state = await self.get_state()
state = self.get_state()
return await self.l1l2.flush(state)

async def calculate_actual_fee(self, external_tx: InvokeFunction):
"""Calculates actual fee"""
state = await self.get_state()
state = self.get_state()
internal_tx = InternalInvokeFunction.from_external_query_tx(external_tx, state.general_config)

child_state = state.state.create_child_state_for_querying()
Expand All @@ -385,15 +394,20 @@ async def calculate_actual_fee(self, external_tx: InvokeFunction):
)
tabaktoni marked this conversation as resolved.
Show resolved Hide resolved

gas_price = state.state.block_info.gas_price
gas_usage = tx_fee // gas_price
gas_usage = tx_fee // gas_price if gas_price else 0

return {
result = {
"overall_fee": tx_fee,
"unit": "wei",
"gas_price": gas_price,
"gas_usage": gas_usage,
}

if state.state.block_info.block_timestamp == 0:
result["warning"] = "block isn't produced"
tabaktoni marked this conversation as resolved.
Show resolved Hide resolved
FabijanC marked this conversation as resolved.
Show resolved Hide resolved

return result

def increase_block_time(self, time_s: int):
"""Increases the block time by `time_s`."""
self.block_info_generator.increase_time(time_s)
Expand Down
4 changes: 2 additions & 2 deletions test/expected/deploy_receipt.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"actual_fee": "0x0",
"block_hash": "0x0",
"block_number": 0,
"block_hash": "0x1",
"block_number": 1,
"events": [],
"execution_resources": {
"builtin_instance_counter": {
Expand Down
4 changes: 2 additions & 2 deletions test/expected/deploy_receipt_auth.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"actual_fee": "0x0",
"block_hash": "0x0",
"block_number": 0,
"block_hash": "0x1",
"block_number": 1,
"events": [],
"execution_resources": {
"builtin_instance_counter": {
Expand Down
Loading