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

Commit

Permalink
Test account fees (#73) [skip ci]
Browse files Browse the repository at this point in the history
* Move account functions to new file

* Add estimate fee test

* Disable too many arguments

* Add max fee test
  • Loading branch information
badurinantun authored Apr 11, 2022
1 parent a102549 commit 3119f2c
Show file tree
Hide file tree
Showing 4 changed files with 262 additions and 124 deletions.
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,
calldata=external_tx.calldata,
signature=external_tx.signature,
nonce=None,
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

0 comments on commit 3119f2c

Please sign in to comment.