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

Introduce forking #328

Merged
merged 53 commits into from
Nov 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
76f0a74
Begin implementation [skip ci]
FabijanC Oct 28, 2022
563cdc9
Add cli options; default to origin [WIP] [skip ci]
FabijanC Nov 2, 2022
c8ab6e9
Simplify ForkedStateReader [WIP] [skip ci]
FabijanC Nov 3, 2022
5f12ce8
Fix conversion to hex [WIP] [skip ci]
FabijanC Nov 3, 2022
bb93087
Refactor call; stop storing deployment hash [WIP] [skip ci]
FabijanC Nov 4, 2022
0444503
Revert version in __init__.py [skip ci]
FabijanC Nov 4, 2022
4a32c5b
Refactor class and contract storing [WIP] [skip ci]
FabijanC Nov 4, 2022
acd083f
Make tests pass [skip ci]
FabijanC Nov 7, 2022
17c8148
Suppress deprecation warnings [skip ci]
FabijanC Nov 7, 2022
bc53846
Refactor [WIP] [skip ci]
FabijanC Nov 7, 2022
35a706a
Refactor [WIP] [skip ci]
FabijanC Nov 8, 2022
4b85731
Enable deployment [skip ci]
FabijanC Nov 9, 2022
aeb56dc
Merge branch 'master' into forking [skip ci]
FabijanC Nov 9, 2022
13357d0
Add test_fork.sh [skip ci]
FabijanC Nov 9, 2022
d4b2cbd
Fix test_fork.sh [skip ci]
FabijanC Nov 9, 2022
2f1c42c
Adapt Origin [skip ci]
FabijanC Nov 9, 2022
0737694
Expand deprecation warning suppression [skip ci]
FabijanC Nov 10, 2022
56e025f
Remove debugging lines [skip ci]
FabijanC Nov 10, 2022
75626e0
Remove lite mode deployment [skip ci]
FabijanC Nov 10, 2022
bc77beb
Fix unused imports [skip ci]
FabijanC Nov 10, 2022
23c4ee8
Use official fee token address [skip ci]
FabijanC Nov 10, 2022
ff7f3ff
Fix test_fork.sh [skip ci]
FabijanC Nov 10, 2022
4e2c1db
Start testing [skip ci]
FabijanC Nov 10, 2022
67031db
Support forking alpha-goerli-2 [skip ci]
FabijanC Nov 11, 2022
943ab11
Make calls to origin async [skip ci]
FabijanC Nov 11, 2022
69c04c7
Fix handling block number in fork [skip ci]
FabijanC Nov 11, 2022
890a376
Fix fee token handling if already deployed [skip ci]
FabijanC Nov 11, 2022
4dd779c
Fix block info generation [skip ci]
FabijanC Nov 11, 2022
74c64c6
Remove ContractWrapper [skip ci]
FabijanC Nov 11, 2022
d8a34e5
Expand tests [skip ci]
FabijanC Nov 11, 2022
832101d
Expand testing (add cli) [skip ci]
FabijanC Nov 14, 2022
3cf4b88
Expand tests [skip ci]
FabijanC Nov 14, 2022
6ae36ed
Fix predefined network specification test [skip ci]
FabijanC Nov 15, 2022
7fad115
Test deploying on fork [skip ci]
FabijanC Nov 15, 2022
20d6e6e
Add balance check to tests [skip ci]
FabijanC Nov 15, 2022
7c91caa
Add feeder gateway testing skeleton [skip ci]
FabijanC Nov 15, 2022
76029ef
Rename alpha-goerli-2 to alpha-goerli2 [skip ci]
FabijanC Nov 16, 2022
f762e03
Test feeder_gateway responses [WIP] [skip ci]
FabijanC Nov 16, 2022
b6761e1
Add contract tests [skip ci]
FabijanC Nov 16, 2022
85dae3a
Fix get_transaction_trace [skip ci]
FabijanC Nov 16, 2022
007bb90
Add declare test [skip ci]
FabijanC Nov 16, 2022
2bdec34
Add nonce testing [skip ci]
FabijanC Nov 16, 2022
723bcce
Fix error handling [skip ci]
FabijanC Nov 16, 2022
427422a
Fix wrong network msg [skip ci]
FabijanC Nov 17, 2022
0959fb1
Fix checking if stark exception [skip ci]
FabijanC Nov 17, 2022
cfa1168
Test mainnet [skip ci]
FabijanC Nov 17, 2022
68b8903
Warn in yellow [skip ci]
FabijanC Nov 17, 2022
a221c3a
Test block response [skip ci]
FabijanC Nov 17, 2022
496458a
Add missing tests [skip ci]
FabijanC Nov 17, 2022
d419f4f
Merge branch 'master' into forking [skip ci]
FabijanC Nov 17, 2022
3d79875
Add missing import [skip ci]
FabijanC Nov 17, 2022
9d02c86
Fix lint errors
FabijanC Nov 17, 2022
9dcc50e
Update docs [skip ci]
FabijanC Nov 17, 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
2 changes: 1 addition & 1 deletion page/docs/guide/development.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 17
sidebar_position: 18
---

# Development
Expand Down
2 changes: 1 addition & 1 deletion page/docs/guide/devnet-speed-up.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 16
sidebar_position: 17
---
# Devnet speed-up troubleshooting

Expand Down
19 changes: 19 additions & 0 deletions page/docs/guide/fork.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
sidebar_position: 16
---

# Fork

To interact with contracts deployed on mainnet or testnet, you can use the forking feature to copy the remote origin and experiment with it locally with no changes to the origin.

```
starknet-devnet --fork-network <NAME|URL> [--fork-block <BLOCK_NUMBER>]
```

The value of `--fork-network` can either be a network name (`alpha-goerli`, `alpha-goerli2`, or `alpha-mainnet`) or a URL (e.g. `https://alpha4.starknet.io`).

The `--fork-block` parameter is optional and its value should be the block number from which the forking is done. If none is provided, defaults to the `"latest"` block at the time of Devnet's start-up.

All calls will first try Devnet's state and then fall back to the forking block.

If you are forking another Devnet instance, keep in mind that it doesn't support polling specific blocks, but will always fall back to the currently latest block.
4 changes: 2 additions & 2 deletions page/docs/guide/mint-token.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ sidebar_position: 14

Other than using prefunded predeployed accounts, you can also add funds to an account that you deployed yourself.

The ERC20 contract used for minting ETH tokens and charging fees is at: `0x62230ea046a9a5fbc261ac77d03c8d41e5d442db2284587570ab46455fd2488`
The ERC20 contract used for minting ETH tokens and charging fees is at: `0x49D36570D4E46F48E99674BD3FCC84644DDD6B96F7C741B1562B82F9E004DC7`

## Query fee token address

Expand All @@ -19,7 +19,7 @@ Response:
```
{
"symbol":"ETH",
"address":"0x62230ea046a9a5fbc261ac77d03c8d41e5d442db2284587570ab46455fd2488",
"address":"0x...",
}
```

Expand Down
12 changes: 7 additions & 5 deletions page/docs/guide/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Installing the package adds the `starknet-devnet` command.
```text
usage: starknet-devnet [-h] [-v] [--host HOST] [--port PORT] [--load-path LOAD_PATH] [--dump-path DUMP_PATH] [--dump-on DUMP_ON] [--lite-mode] [--accounts ACCOUNTS]
[--initial-balance INITIAL_BALANCE] [--seed SEED] [--hide-predeployed-accounts] [--start-time START_TIME] [--gas-price GAS_PRICE] [--timeout TIMEOUT]
[--account-class ACCOUNT_CLASS]
[--account-class ACCOUNT_CLASS] [--fork-network FORK_NETWORK] [--fork-block FORK_BLOCK]

Run a local instance of StarkNet Devnet

Expand All @@ -23,7 +23,7 @@ optional arguments:
--dump-path DUMP_PATH
Specify the path to dump to
--dump-on DUMP_ON Specify when to dump; can dump on: exit, transaction
--lite-mode Introduces speed-up by skipping block hash and deploy transaction hash calculation - applies sequential numbering instead (0x0, 0x1, 0x2, ...).
--lite-mode Introduces speed-up by skipping block hash calculation - applies sequential numbering instead (0x0, 0x1, 0x2, ...).
--accounts ACCOUNTS Specify the number of accounts to be predeployed; defaults to 10
--initial-balance INITIAL_BALANCE, -e INITIAL_BALANCE
Specify the initial balance of accounts to be predeployed; defaults to 1e+21
Expand All @@ -37,9 +37,11 @@ optional arguments:
--timeout TIMEOUT, -t TIMEOUT
Specify the server timeout in seconds; defaults to 60
--account-class ACCOUNT_CLASS
Specify the account implementation to be used for predeploying;
should be a path to the compiled JSON artifact;
defaults to OpenZeppelin v0.5.0
Specify the account implementation to be used for predeploying; should be a path to the compiled JSON artifact; defaults to OpenZeppelin v0.5.0
--fork-network FORK_NETWORK
Specify the network to fork: can be a URL (e.g. https://alpha-mainnet.starknet.io) or network name (valid names: alpha-goerli, alpha-goerli2, alpha-mainnet)
--fork-block FORK_BLOCK
Specify the block number where the --fork-network is forked; defaults to latest
```

You can run `starknet-devnet` in a separate shell, or you can run it in background with `starknet-devnet &`.
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,5 @@ asyncio_mode="strict"
filterwarnings=[
"ignore::DeprecationWarning:lark.*:",
"ignore::DeprecationWarning:frozendict.*:",
"ignore::DeprecationWarning:eth_abi.codec.*:",
]
9 changes: 9 additions & 0 deletions scripts/mint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash

set -eu

address=$1

curl localhost:5050/mint \
-H "Content-Type: application/json" \
-d "{ \"address\": \"$address\", \"amount\": 1000000000000000000, \"lite\": false }"
88 changes: 88 additions & 0 deletions scripts/test_fork.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/bin/bash

set -eu

trap 'for killable in $(jobs -p); do kill $killable; done' EXIT

HOST=localhost
PORT1=5049
DEVNET1_URL="http://$HOST:$PORT1"
PORT2=5050
DEVNET2_URL="http://$HOST:$PORT2"


poetry run starknet-devnet --host "$HOST" --port "$PORT1" --seed 42 --accounts 1 --hide-predeployed-accounts &
DEVNET1_PID=$!
curl --retry 20 --retry-delay 1 --retry-connrefused -s -o /dev/null "$DEVNET1_URL/is_alive"
echo "Started up devnet1; pid: $DEVNET1_PID"

poetry run starknet-devnet --host "$HOST" --port "$PORT2" --fork-network "$DEVNET1_URL" --accounts 0 &
DEVNET2_PID=$!
curl --retry 20 --retry-delay 1 --retry-connrefused -s -o /dev/null "$DEVNET2_URL/is_alive"
echo "Started up devnet2; pid: $DEVNET2_PID"

# # get public key of predeployed account
# for port in "$PORT1" "$PORT2"; do
# echo "Polling devnet at :$port"
# poetry run starknet get_storage_at \
# --feeder_gateway_url "http://$HOST:$port" \
# --contract_address 0x347be35996a21f6bf0623e75dbce52baba918ad5ae8d83b6f416045ab22961a \
# --key 550557492744938365112574611882025123252567779123164597803728068558738016655
# done

source ~/venvs/cairo_venv-0.10.1-pre/bin/activate
DEPLOYMENT_URL="$DEVNET1_URL"
starknet deploy \
--contract test/artifacts/contracts/cairo/contract.cairo/contract.json \
--inputs 10 \
--gateway_url "$DEPLOYMENT_URL" \
--feeder_gateway_url "$DEPLOYMENT_URL" \
--salt 0x99 \
--no_wallet
echo "Deployed contract"
CONTRACT_ADDRESS=0x07c80f5573d4c636960b56b02a01514d487c6e6a2c6f9242490280c932a32f71

export STARKNET_WALLET="starkware.starknet.wallets.open_zeppelin.OpenZeppelinAccount"
ACCOUNT_DIR="."
rm -rf ./starknet_open_zeppelin_accounts.json*

starknet new_account --network alpha-goerli --account_dir "$ACCOUNT_DIR"

ACCOUNT_DEPLOYMENT_URL="$DEVNET1_URL"
starknet deploy_account \
--gateway_url "$ACCOUNT_DEPLOYMENT_URL" \
--feeder_gateway_url "$ACCOUNT_DEPLOYMENT_URL" \
--network alpha-goerli \
--account_dir "$ACCOUNT_DIR" \
--max_fee 0
echo "Deployed account"

INVOKE_URL="$DEVNET1_URL"
INVOKE_HASH=$(starknet invoke \
--abi test/artifacts/contracts/cairo/contract.cairo/contract_abi.json \
--function increase_balance \
--inputs 10 20 \
--address "$CONTRACT_ADDRESS" \
--gateway_url "$INVOKE_URL" \
--feeder_gateway_url "$INVOKE_URL" \
--max_fee 0 \
--network_id alpha-goerli \
--chain_id 0x534e5f474f45524c49 \
--account_dir "$ACCOUNT_DIR" | sed -rn 's/^Transaction hash: (.*)$/\1/p'
)
echo "Invoked contract on $INVOKE_URL"

echo "Transaction on $DEVNET1_URL"
starknet get_transaction --hash "$INVOKE_HASH" --feeder_gateway_url "$DEVNET1_URL"
echo "Transaction on $DEVNET2_URL"
starknet get_transaction --hash "$INVOKE_HASH" --feeder_gateway_url "$DEVNET2_URL"
echo "Block on $DEVNET2_URL"
starknet get_block --feeder_gateway "$DEVNET2_URL"

for url in "$DEVNET1_URL" "$DEVNET2_URL"; do
starknet call \
--abi test/artifacts/contracts/cairo/contract.cairo/contract_abi.json \
--feeder_gateway_url "$url" \
--address "$CONTRACT_ADDRESS" \
--function get_balance
done
11 changes: 0 additions & 11 deletions starknet_devnet/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,3 @@ async def deploy(self) -> StarknetContract:
await starknet.state.state.set_storage_at(
fee_token_address, balance_address + 1, initial_balance_uint256.high
)

contract = StarknetContract(
state=starknet.state,
abi=contract_class.abi,
contract_address=self.address,
deploy_call_info=None,
)

await self.starknet_wrapper.store_contract(
self.address, contract, contract_class
)
3 changes: 2 additions & 1 deletion starknet_devnet/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from starkware.crypto.signature.signature import private_to_stark_key

from .account import Account
from .util import warn


class Accounts:
Expand Down Expand Up @@ -77,7 +78,7 @@ def __print(self):

print(f"Initial balance of each account: {self.__initial_balance} WEI")
print("Seed to replicate this account sequence:", self.__seed)
print(
warn(
"WARNING: Use these accounts and their keys ONLY for local testing. "
"DO NOT use them on mainnet or other live networks because you will LOSE FUNDS.\n",
file=sys.stderr,
Expand Down
46 changes: 26 additions & 20 deletions starknet_devnet/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
BlockStatus,
)
from starkware.starknet.services.api.feeder_gateway.response_objects import (
BlockIdentifier,
BlockStateUpdate,
)
from starkware.starkware_utils.error_handling import StarkErrorCode
Expand All @@ -33,22 +34,16 @@ def __init__(self, origin: Origin, lite=False) -> None:
self.__state_updates: Dict[int, BlockStateUpdate] = {}
self.__hash2num: Dict[str, int] = {}

def get_last_block(self) -> StarknetBlock:
async def get_last_block(self) -> StarknetBlock:
"""Returns the last block stored so far."""
number_of_blocks = self.get_number_of_blocks()
return self.get_by_number(number_of_blocks - 1)
return await self.get_by_number(number_of_blocks - 1)

def get_number_of_blocks(self) -> int:
"""Returns the number of blocks stored so far."""
return len(self.__num2block) + self.origin.get_number_of_blocks()

def get_by_number(self, block_number: int) -> StarknetBlock:
"""Returns the block whose block_number is provided"""
if block_number is None:
if self.__num2block:
return self.get_last_block()
return self.origin.get_block_by_number(block_number)

def __assert_block_number_in_range(self, block_number: BlockIdentifier):
if block_number < 0:
message = (
f"Block number must be a non-negative integer; got: {block_number}."
Expand All @@ -63,24 +58,34 @@ def get_by_number(self, block_number: int) -> StarknetBlock:
code=StarknetErrorCode.BLOCK_NOT_FOUND, message=message
)

async def get_by_number(self, block_number: BlockIdentifier) -> StarknetBlock:
"""Returns the block whose block_number is provided"""
if block_number is None:
if self.__num2block:
return await self.get_last_block()
return await self.origin.get_block_by_number(block_number)

self.__assert_block_number_in_range(block_number)
if block_number in self.__num2block:
return self.__num2block[block_number]

return self.origin.get_block_by_number(block_number)
return await self.origin.get_block_by_number(block_number)

def get_by_hash(self, block_hash: str) -> StarknetBlock:
async def get_by_hash(self, block_hash: str) -> StarknetBlock:
"""
Returns the block with the given block hash.
"""
numeric_hash = int(block_hash, 16)

if numeric_hash in self.__hash2num:
block_number = self.__hash2num[int(block_hash, 16)]
return self.get_by_number(block_number)
return await self.get_by_number(block_number)

return self.origin.get_block_by_hash(block_hash)
return await self.origin.get_block_by_hash(block_hash)

def get_state_update(self, block_hash=None, block_number=None) -> BlockStateUpdate:
async def get_state_update(
self, block_hash=None, block_number=None
) -> BlockStateUpdate:
"""
Returns state update for the provided block hash or block number.
It will return the last state update if block is not provided.
Expand All @@ -89,19 +94,20 @@ def get_state_update(self, block_hash=None, block_number=None) -> BlockStateUpda
numeric_hash = int(block_hash, 16)

if numeric_hash not in self.__hash2num:
return self.origin.get_state_update(block_hash=block_hash)
return await self.origin.get_state_update(block_hash=block_hash)

block_number = self.__hash2num[numeric_hash]

if block_number is not None:
if block_number not in self.__state_updates:
return self.origin.get_state_update(block_number=block_number)
self.__assert_block_number_in_range(block_number)
if block_number in self.__state_updates:
return self.__state_updates[block_number]

return self.__state_updates[block_number]
return await self.origin.get_state_update(block_number=block_number)

return (
self.__state_updates.get(self.get_number_of_blocks() - 1)
or self.origin.get_state_update()
or await self.origin.get_state_update()
)

async def generate(
Expand All @@ -122,7 +128,7 @@ async def generate(
if block_number == 0:
parent_block_hash = 0
else:
last_block = self.get_last_block()
last_block = await self.get_last_block()
parent_block_hash = last_block.block_hash

if is_empty_block:
Expand Down
Loading