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

Test account fees #73

Merged
merged 5 commits into from
Apr 11, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
27 changes: 24 additions & 3 deletions starknet_devnet/starknet_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
from starkware.starknet.testing.objects import StarknetTransactionExecutionInfo
from starkware.starkware_utils.error_handling import StarkException
from starkware.starknet.services.api.feeder_gateway.block_hash import calculate_block_hash
from starkware.starknet.business_logic.transaction_fee import calculate_tx_fee_by_cairo_usage
from starkware.starknet.services.api.contract_definition import EntryPointType
from starkware.starknet.definitions import constants

from .origin import NullOrigin, Origin
from .general_config import DEFAULT_GENERAL_CONFIG
Expand Down Expand Up @@ -530,12 +533,30 @@ def get_state_update(self, block_hash=None, block_number=None):

return self.__last_state_update or self.origin.get_state_update()

async def calculate_actual_fee(self, transaction: InvokeFunction):
async def calculate_actual_fee(self, external_tx: InvokeFunction):
"""Calculates actual fee"""
state = await self.__get_state()
internal_tx = InternalInvokeFunction.from_external(transaction, state.general_config)
internal_tx = InternalInvokeFunction.create(
contract_address=external_tx.contract_address,
entry_point_selector=external_tx.entry_point_selector,
max_fee=external_tx.max_fee,
entry_point_type=EntryPointType.EXTERNAL,
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
calldata=external_tx.calldata,
signature=external_tx.signature,
nonce=None,
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
chain_id=state.general_config.chain_id.value,
# Need to set to 0 as it will be invoked in apply_state_updates
version=constants.TRANSACTION_VERSION,
)

state_copy = state.state._copy() # pylint: disable=protected-access
execution_info = await internal_tx.apply_state_updates(state_copy, state.general_config)

return execution_info.actual_fee
actual_fee = calculate_tx_fee_by_cairo_usage(
general_config=state.general_config,
cairo_resource_usage=execution_info.call_info.execution_resources.to_dict(),
l1_gas_usage=0,
gas_price=state.general_config.min_gas_price
)

return actual_fee
135 changes: 135 additions & 0 deletions test/account.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
"""
Account test functions and utilities.
"""


from starkware.cairo.common.hash_state import compute_hash_on_elements
from starkware.crypto.signature.signature import private_to_stark_key, sign
from starkware.starknet.public.abi import get_selector_from_name

from .util import deploy, call, invoke, estimate_fee

ACCOUNT_ARTIFACTS_PATH = "starknet_devnet/accounts_artifacts"
ACCOUNT_AUTHOR = "OpenZeppelin"
ACCOUNT_VERSION = "0.1.0"

ACCOUNT_PATH = f"{ACCOUNT_ARTIFACTS_PATH}/{ACCOUNT_AUTHOR}/{ACCOUNT_VERSION}/Account.cairo/Account.json"
ACCOUNT_ABI_PATH = f"{ACCOUNT_ARTIFACTS_PATH}/{ACCOUNT_AUTHOR}/{ACCOUNT_VERSION}/Account.cairo/Account_abi.json"

TRANSACTION_VERSION = 0

PRIVATE_KEY = 123456789987654321
PUBLIC_KEY = private_to_stark_key(PRIVATE_KEY)

def deploy_account_contract(salt=None):
"""Deploy account contract."""
return deploy(ACCOUNT_PATH, inputs=[str(PUBLIC_KEY)], salt=salt)

def get_nonce(account_address):
"""Get nonce."""
return call("get_nonce", account_address, ACCOUNT_ABI_PATH)

def get_execute_calldata(call_array, calldata, nonce):
"""Get calldata for __execute__."""
return [
len(call_array),
*[x for t in call_array for x in t],
len(calldata),
*calldata,
int(nonce)
]

def str_to_felt(text):
"""Converts string to felt."""
return int.from_bytes(bytes(text, "ascii"), "big")

def hash_multicall(sender, calls, nonce, max_fee):
"""desc"""
hash_array = []

for call_tuple in calls:
call_elements = [call_tuple[0], call_tuple[1], compute_hash_on_elements(call_tuple[2])]
hash_array.append(compute_hash_on_elements(call_elements))

return compute_hash_on_elements([
str_to_felt('StarkNet Transaction'),
sender,
compute_hash_on_elements(hash_array),
nonce,
max_fee,
TRANSACTION_VERSION
])


def get_signature(message_hash):
"""Get signature from message hash and private key."""
sig_r, sig_s = sign(message_hash, PRIVATE_KEY)
return [str(sig_r), str(sig_s)]

def from_call_to_call_array(calls):
"""Transforms calls to call_array and calldata."""
call_array = []
calldata = []

for call_tuple in calls:
assert len(call_tuple) == 3, "Invalid call parameters"

entry = (
call_tuple[0],
get_selector_from_name(call_tuple[1]),
len(calldata),
len(call_tuple[2])
)
call_array.append(entry)
calldata.extend(call_tuple[2])

return (call_array, calldata)

def adapt_inputs(execute_calldata):
"""Get stringified inputs from execute_calldata."""
return [str(v) for v in execute_calldata]

def get_execute_args(calls, account_address, nonce=None, max_fee=0):
"""Returns signature and execute calldata"""

if nonce is None:
nonce = get_nonce(account_address)

# get signature
calls_with_selector = [
(call[0], get_selector_from_name(call[1]), call[2]) for call in calls]
message_hash = hash_multicall(
int(account_address, 16), calls_with_selector, int(nonce), max_fee
)
signature = get_signature(message_hash)

# get execute calldata
(call_array, calldata) = from_call_to_call_array(calls)
execute_calldata = get_execute_calldata(call_array, calldata, nonce)

return signature, execute_calldata

def get_estimated_fee(calls, account_address, nonce=None):
"""Get estmated fee."""
signature, execute_calldata = get_execute_args(calls, account_address, nonce)

return estimate_fee(
"__execute__",
inputs=adapt_inputs(execute_calldata),
address=account_address,
abi_path=ACCOUNT_ABI_PATH,
signature=signature,
)

def execute(calls, account_address, nonce=None, max_fee=0):
"""Invoke __execute__ with correct calldata and signature."""
signature, execute_calldata = get_execute_args(calls, account_address, nonce, max_fee)

return invoke(
"__execute__",
inputs=adapt_inputs(execute_calldata),
address=account_address,
abi_path=ACCOUNT_ABI_PATH,
signature=signature,
max_fee=str(max_fee)
)
Loading