Skip to content

Commit

Permalink
Merge branch 'master' into kb/cli-build-with-scarb
Browse files Browse the repository at this point in the history
  • Loading branch information
Karol Bisztyga committed Jun 28, 2023
2 parents 5dce670 + 0074e1a commit d4670b6
Show file tree
Hide file tree
Showing 21 changed files with 741 additions and 775 deletions.
12 changes: 8 additions & 4 deletions cast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,14 @@ pub fn get_block_id(value: &str) -> Result<BlockId> {
match value {
"pending" => Ok(BlockId::Tag(Pending)),
"latest" => Ok(BlockId::Tag(Latest)),
_ => Err(anyhow::anyhow!(
"No such block id {}! Possible values are pending and latest for now.",
value
)),
_ if value.starts_with("0x") => Ok(BlockId::Hash(FieldElement::from_hex_be(value)?)),
_ => match value.parse::<u64>() {
Ok(value) => Ok(BlockId::Number(value)),
Err(_) => Err(anyhow::anyhow!(
"No such block id {}! Possible values are pending, latest, block hash (hex) and block number (u64).",
value
)),
},
}
}

Expand Down
3 changes: 2 additions & 1 deletion cast/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ async fn main() -> Result<()> {

let declared_contract = starknet_commands::declare::declare(
&declare.contract,
declare.max_fee,
&mut account,
)
.await?;
Expand Down Expand Up @@ -111,7 +112,7 @@ async fn main() -> Result<()> {
Ok(())
}
Commands::Call(call) => {
let block_id = get_block_id(&call.block_id).unwrap();
let block_id = get_block_id(&call.block_id)?;

let result = starknet_commands::call::call(
call.contract_address.as_ref(),
Expand Down
4 changes: 3 additions & 1 deletion cast/src/starknet_commands/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ pub struct Call {
#[clap(short = 'c', long = "calldata", value_delimiter = ' ')]
pub calldata: Vec<String>,

/// Block identifier on which call should be performed
/// Block identifier on which call should be performed.
/// Possible values: pending, latest, block hash (0x prefixed string)
/// and block number (u64)
#[clap(short = 'b', long = "block-id", default_value = "pending")]
pub block_id: String,
}
Expand Down
16 changes: 14 additions & 2 deletions cast/src/starknet_commands/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use anyhow::{Context, Result};
use cast::wait_for_tx;
use clap::Args;
use starknet::accounts::ConnectedAccount;
use starknet::core::types::FieldElement;
use starknet::{
accounts::{Account, SingleOwnerAccount},
core::types::{
Expand All @@ -17,12 +18,17 @@ use std::process::Command;
#[derive(Args)]
#[command(about = "Declare a contract to starknet", long_about = None)]
pub struct Declare {
/// Path to the sierra compiled contract
/// contract name
pub contract: String,

/// Max fee for the transaction. If not provided, max fee will be automatically estimated
#[clap(short, long)]
pub max_fee: Option<u128>,
}

pub async fn declare(
contract: &str,
max_fee: Option<u128>,
account: &mut SingleOwnerAccount<&JsonRpcClient<HttpTransport>, LocalWallet>,
) -> Result<DeclareTransactionResult> {
let _ = Command::new("scarb")
Expand Down Expand Up @@ -74,7 +80,13 @@ pub async fn declare(
let casm_class_hash = casm_contract_definition.class_hash()?;

let declaration = account.declare(Arc::new(contract_definition.flatten()?), casm_class_hash);
let declared = declaration.send().await?;
let execution = if let Some(max_fee) = max_fee {
declaration.max_fee(FieldElement::from(max_fee))
} else {
declaration
};

let declared = execution.send().await?;

wait_for_tx(account.provider(), declared.transaction_hash).await;

Expand Down
1,067 changes: 395 additions & 672 deletions poetry.lock

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions protostar.spec
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from PyInstaller.utils.hooks import collect_data_files, collect_submodules

import crypto_cpp_py
import poseidon_py

block_cipher = None
extra_files = [
Expand All @@ -10,12 +11,13 @@ extra_files = [
("cairo-protostar/Cargo.toml", "cairo"),
('templates', 'templates'),
('constants.json', 'info'),
] + collect_data_files('starkware')
(f'{poseidon_py.__path__[0]}/../lib_pos.*', '.')
] + collect_data_files('starkware') + collect_data_files('starknet_py') + collect_data_files('poseidon_py')
# Extra imports which are necessary for executing hints
extra_imports = [
"eth_hash.auto",
"certifi",
] + collect_submodules('starkware')
] + collect_submodules('starkware') + collect_submodules('starknet_py') + collect_submodules('poseidon_py')

binaries = [(f"{crypto_cpp_py.__path__[0]}/../libcrypto_c_exports.*", ".")]

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from typing import Callable

from starknet_py.utils.data_transformer.data_transformer import CairoData

from protostar.cheatable_starknet.controllers.expect_call_controller import (
ExpectCallController,
ExpectedCall,
Expand All @@ -11,6 +9,7 @@
)
from protostar.starknet.selector import Selector
from protostar.starknet import RawAddress, Address
from protostar.starknet.data_transformer import CairoData


class AssertExpectCallHintLocal(CallableHintLocal):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from typing import Callable

from starknet_py.utils.data_transformer.data_transformer import CairoData

from protostar.cheatable_starknet.controllers.expect_call_controller import (
ExpectCallController,
Expand All @@ -11,6 +10,7 @@
)
from protostar.starknet import RawAddress, Address
from protostar.starknet.selector import Selector
from protostar.starknet.data_transformer import CairoData


class ExpectCallHintLocal(CallableHintLocal):
Expand Down
4 changes: 2 additions & 2 deletions protostar/cli/signable_command_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ def get_signer(

try:
key_pair = KeyPair.from_private_key(private_key)
except AssertionError as a_err:
except (AssertionError, OverflowError) as err:
raise ProtostarException(
"Invalid private key value. Please provide a valid private key."
) from a_err
) from err

try:
signer = StarkCurveSigner(
Expand Down
2 changes: 1 addition & 1 deletion protostar/git/git_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def run_git(*args: Union[str, Path], cwd: Path):

def has_user_git_credentials():
try:
subprocess.run(["git", "config", "user.name"], check=True, **SHARED_KWARGS)
subprocess.run(["git", "config", "user.name"], check=True, **SHARED_KWARGS) # type: ignore
except subprocess.CalledProcessError:
return False
return True
Expand Down
128 changes: 83 additions & 45 deletions protostar/starknet/data_transformer.py
Original file line number Diff line number Diff line change
@@ -1,94 +1,132 @@
from typing import Any, Callable, Dict, Union
from typing import Any, Callable, Union
from typing_extensions import Literal

from marshmallow import ValidationError

from starknet_py.serialization import serializer_for_payload, CairoSerializerException
from starknet_py.abi import Abi, AbiParsingError, AbiParser

from starknet_py.utils.data_transformer.data_transformer import (
CairoSerializer,
CairoData,
)
from starkware.starknet.public.abi import AbiType
from starkware.starknet.public.abi_structs import identifier_manager_from_abi
from typing_extensions import Literal

from protostar.starknet.abi import find_abi_item
from protostar.protostar_exception import ProtostarException


class DataTransformerException(ProtostarException):
pass


PythonData = Dict[str, Any]
CairoData = list[int]
PythonData = dict[str, Any]
CairoOrPythonData = Union[CairoData, PythonData]
FromPythonTransformer = Callable[[PythonData], CairoData]
ToPythonTransformer = Callable[[CairoData], PythonData]


def get_function_from_abi(abi: Abi, fn_name: str) -> Abi.Function:
if abi.constructor is not None and fn_name == "constructor":
return abi.constructor
if abi.l1_handler is not None and abi.l1_handler.name == fn_name:
return abi.l1_handler
if fn_name in abi.functions:
return abi.functions[fn_name]
raise DataTransformerException(f"`{fn_name}` not found in ABI")


def from_python_transformer(
contract_abi: AbiType, fn_name: str, mode: Literal["inputs", "outputs"]
) -> FromPythonTransformer:
fn_abi_item = find_abi_item(contract_abi, fn_name)
structure_transformer = CairoSerializer(identifier_manager_from_abi(contract_abi))
def transform(data: PythonData):
try:
abi = AbiParser(contract_abi).parse()
except (AbiParsingError, ValidationError) as ex:
raise DataTransformerException("Invalid ABI") from ex

function = get_function_from_abi(abi, fn_name)

if mode == "inputs":
serializer = serializer_for_payload(function.inputs)
else:
serializer = serializer_for_payload(function.outputs)

def transform(data: PythonData) -> CairoData:
try:
for data_item_name, data_item_value in data.items():
for item in fn_abi_item[mode]:
if (
data_item_name == item["name"]
and isinstance(data_item_value, dict)
and item["type"] == "felt*"
):
raise TypeError(
f"invalid type 'dict' for felt* used for argument {data_item_name}"
)
return structure_transformer.from_python(fn_abi_item[mode], **data)[0]
except (TypeError, ValueError) as ex:
raise DataTransformerException(str(ex)) from ex
return serializer.serialize(data)
except (CairoSerializerException, KeyError) as ex:
raise DataTransformerException("Data transformer error") from ex

return transform


def from_python_events_transformer(
contract_abi: AbiType, event_name: str
) -> FromPythonTransformer:
event_abi_item = find_abi_item(contract_abi, event_name)
structure_transformer = CairoSerializer(identifier_manager_from_abi(contract_abi))
def transform(data: PythonData):
try:
abi = AbiParser(contract_abi).parse()
except (AbiParsingError, ValidationError) as ex:
raise DataTransformerException("Invalid ABI") from ex

def transform(data: PythonData) -> CairoData:
try:
return structure_transformer.from_python(event_abi_item["data"], **data)[0]
except (TypeError, ValueError) as ex:
raise DataTransformerException(str(ex)) from ex
event = abi.events[event_name]
except KeyError as ex:
raise DataTransformerException(
f"Event name `{event_name}` not in ABI"
) from ex

serializer = serializer_for_payload(event.data)

try:
return serializer.serialize(data)
except (CairoSerializerException, KeyError) as ex:
raise DataTransformerException("Data transformer error") from ex

return transform


def to_python_transformer(
contract_abi: AbiType, fn_name: str, mode: Literal["inputs", "outputs"]
) -> ToPythonTransformer:
fn_abi_item = find_abi_item(contract_abi, fn_name)
structure_transformer = CairoSerializer(identifier_manager_from_abi(contract_abi))
def transform(data: CairoData):
try:
abi = AbiParser(contract_abi).parse()
except (AbiParsingError, ValidationError) as ex:
raise DataTransformerException("Invalid ABI") from ex

function = get_function_from_abi(abi, fn_name)

if mode == "inputs":
serializer = serializer_for_payload(function.inputs)
else:
serializer = serializer_for_payload(function.outputs)

def transform(data: CairoData) -> PythonData:
try:
return structure_transformer.to_python(fn_abi_item[mode], data)._asdict()
except (TypeError, ValueError) as ex:
raise DataTransformerException(str(ex)) from ex
return serializer.deserialize(data).as_dict()
except CairoSerializerException as ex:
raise DataTransformerException("Data transformer error") from ex

return transform


def to_python_events_transformer(
contract_abi: AbiType, event_name: str
) -> ToPythonTransformer:
event_abi_item = find_abi_item(contract_abi, event_name)
structure_transformer = CairoSerializer(identifier_manager_from_abi(contract_abi))
def transform(data: CairoData):
try:
abi = AbiParser(contract_abi).parse()
except (AbiParsingError, ValidationError) as ex:
raise DataTransformerException("Invalid ABI") from ex

try:
event = abi.events[event_name]
except KeyError as ex:
raise DataTransformerException(
f"Event name `{event_name}` not in ABI"
) from ex

serializer = serializer_for_payload(event.data)

def transform(data: CairoData) -> PythonData:
try:
return structure_transformer.to_python(
event_abi_item["data"], data
)._asdict()
except (TypeError, ValueError) as ex:
raise DataTransformerException(str(ex)) from ex
return serializer.deserialize(data).as_dict()
except CairoSerializerException as ex:
raise DataTransformerException("Data transformer error") from ex

return transform
4 changes: 2 additions & 2 deletions protostar/starknet_gateway/gateway_facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
from starknet_py.net.signer import BaseSigner
from starknet_py.net.udc_deployer.deployer import Deployer, ContractDeployment
from starknet_py.net.client_models import Call, CasmClass
from starknet_py.transaction_exceptions import (
from starknet_py.transaction_errors import (
TransactionFailedError,
TransactionRejectedError,
)
from starknet_py.utils.data_transformer.errors import CairoSerializerException
from starknet_py.serialization.errors import CairoSerializerException
from typing_extensions import Self, TypeGuard

from protostar.protostar_exception import ProtostarException
Expand Down
2 changes: 1 addition & 1 deletion protostar/upgrader/upgrade_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def _pull_tarball(
# pylint: disable=missing-timeout
with requests.get(tar_url, stream=True) as request:
with open(tarball_path, "wb") as file:
shutil.copyfileobj(request.raw, file)
shutil.copyfileobj(request.raw, file) # type: ignore

def _install_new_version(
self, latest_version: Version, tarball_path: Path, protostar_dir_path: Path
Expand Down
Loading

0 comments on commit d4670b6

Please sign in to comment.