add
This commit is contained in:
0
ccxt/static_dependencies/starknet/hash/__init__.py
Normal file
0
ccxt/static_dependencies/starknet/hash/__init__.py
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
79
ccxt/static_dependencies/starknet/hash/address.py
Normal file
79
ccxt/static_dependencies/starknet/hash/address.py
Normal file
@@ -0,0 +1,79 @@
|
||||
from typing import Sequence
|
||||
|
||||
from ..constants import CONTRACT_ADDRESS_PREFIX, L2_ADDRESS_UPPER_BOUND
|
||||
from .utils import (
|
||||
HEX_PREFIX,
|
||||
_starknet_keccak,
|
||||
compute_hash_on_elements,
|
||||
encode_uint,
|
||||
get_bytes_length,
|
||||
)
|
||||
|
||||
|
||||
def compute_address(
|
||||
*,
|
||||
class_hash: int,
|
||||
constructor_calldata: Sequence[int],
|
||||
salt: int,
|
||||
deployer_address: int = 0,
|
||||
) -> int:
|
||||
"""
|
||||
Computes the contract address in the Starknet network - a unique identifier of the contract.
|
||||
|
||||
:param class_hash: class hash of the contract
|
||||
:param constructor_calldata: calldata for the contract constructor
|
||||
:param salt: salt used to calculate contract address
|
||||
:param deployer_address: address of the deployer (if not provided default 0 is used)
|
||||
:return: Contract's address
|
||||
"""
|
||||
|
||||
constructor_calldata_hash = compute_hash_on_elements(data=constructor_calldata)
|
||||
raw_address = compute_hash_on_elements(
|
||||
data=[
|
||||
CONTRACT_ADDRESS_PREFIX,
|
||||
deployer_address,
|
||||
salt,
|
||||
class_hash,
|
||||
constructor_calldata_hash,
|
||||
],
|
||||
)
|
||||
|
||||
return raw_address % L2_ADDRESS_UPPER_BOUND
|
||||
|
||||
|
||||
def get_checksum_address(address: str) -> str:
|
||||
"""
|
||||
Outputs formatted checksum address.
|
||||
|
||||
Follows implementation of starknet.js. It is not compatible with EIP55 as it treats hex string as encoded number,
|
||||
instead of encoding it as ASCII string.
|
||||
|
||||
:param address: Address to encode
|
||||
:return: Checksum address
|
||||
"""
|
||||
if not address.lower().startswith(HEX_PREFIX):
|
||||
raise ValueError(f"{address} is not a valid hexadecimal address.")
|
||||
|
||||
int_address = int(address, 16)
|
||||
string_address = address[2:].zfill(64)
|
||||
|
||||
address_in_bytes = encode_uint(int_address, get_bytes_length(int_address))
|
||||
address_hash = _starknet_keccak(address_in_bytes)
|
||||
|
||||
result = "".join(
|
||||
(
|
||||
char.upper()
|
||||
if char.isalpha() and (address_hash >> 256 - 4 * i - 1) & 1
|
||||
else char
|
||||
)
|
||||
for i, char in enumerate(string_address)
|
||||
)
|
||||
|
||||
return f"{HEX_PREFIX}{result}"
|
||||
|
||||
|
||||
def is_checksum_address(address: str) -> bool:
|
||||
"""
|
||||
Checks if provided string is in a checksum address format.
|
||||
"""
|
||||
return get_checksum_address(address) == address
|
||||
@@ -0,0 +1,111 @@
|
||||
# File is copied from
|
||||
# https://github.com/starkware-libs/cairo-lang/blob/v0.13.1/src/starkware/starknet/core/os/contract_class/compiled_class_hash_objects.py
|
||||
|
||||
import dataclasses
|
||||
import itertools
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, List, Union
|
||||
|
||||
from poseidon_py.poseidon_hash import poseidon_hash_many
|
||||
|
||||
|
||||
class BytecodeSegmentStructure(ABC):
|
||||
"""
|
||||
Represents the structure of the bytecode to allow loading it partially into the OS memory.
|
||||
See the documentation of the OS function `bytecode_hash_node` in `compiled_class.cairo`
|
||||
for more details.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def hash(self) -> int:
|
||||
"""
|
||||
Computes the hash of the node.
|
||||
"""
|
||||
|
||||
def bytecode_with_skipped_segments(self):
|
||||
"""
|
||||
Returns the bytecode of the node.
|
||||
Skipped segments are replaced with [-1, -2, -2, -2, ...].
|
||||
"""
|
||||
res: List[int] = []
|
||||
self.add_bytecode_with_skipped_segments(res)
|
||||
return res
|
||||
|
||||
@abstractmethod
|
||||
def add_bytecode_with_skipped_segments(self, data: List[int]):
|
||||
"""
|
||||
Same as bytecode_with_skipped_segments, but appends the result to the given list.
|
||||
"""
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class BytecodeLeaf(BytecodeSegmentStructure):
|
||||
"""
|
||||
Represents a leaf in the bytecode segment tree.
|
||||
"""
|
||||
|
||||
data: List[int]
|
||||
|
||||
def hash(self) -> int:
|
||||
return poseidon_hash_many(self.data)
|
||||
|
||||
def add_bytecode_with_skipped_segments(self, data: List[int]):
|
||||
data.extend(self.data)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class BytecodeSegmentedNode(BytecodeSegmentStructure):
|
||||
"""
|
||||
Represents an internal node in the bytecode segment tree.
|
||||
Each child can be loaded into memory or skipped.
|
||||
"""
|
||||
|
||||
segments: List["BytecodeSegment"]
|
||||
|
||||
def hash(self) -> int:
|
||||
return (
|
||||
poseidon_hash_many(
|
||||
itertools.chain( # pyright: ignore
|
||||
*[
|
||||
(node.segment_length, node.inner_structure.hash())
|
||||
for node in self.segments
|
||||
]
|
||||
)
|
||||
)
|
||||
+ 1
|
||||
)
|
||||
|
||||
def add_bytecode_with_skipped_segments(self, data: List[int]):
|
||||
for segment in self.segments:
|
||||
if segment.is_used:
|
||||
segment.inner_structure.add_bytecode_with_skipped_segments(data)
|
||||
else:
|
||||
data.append(-1)
|
||||
data.extend(-2 for _ in range(segment.segment_length - 1))
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class BytecodeSegment:
|
||||
"""
|
||||
Represents a child of BytecodeSegmentedNode.
|
||||
"""
|
||||
|
||||
# The length of the segment.
|
||||
segment_length: int
|
||||
# Should the segment (or part of it) be loaded to memory.
|
||||
# In other words, is the segment used during the execution.
|
||||
# Note that if is_used is False, the entire segment is not loaded to memory.
|
||||
# If is_used is True, it is possible that part of the segment will be skipped (according
|
||||
# to the "is_used" field of the child segments).
|
||||
is_used: bool
|
||||
# The inner structure of the segment.
|
||||
inner_structure: BytecodeSegmentStructure
|
||||
|
||||
def __post_init__(self):
|
||||
assert (
|
||||
self.segment_length > 0
|
||||
), f"Invalid segment length: {self.segment_length}."
|
||||
|
||||
|
||||
# Represents a nested list of integers. E.g., [1, [2, [3], 4], 5, 6].
|
||||
NestedIntList = Union[int, List[Any]]
|
||||
16
ccxt/static_dependencies/starknet/hash/selector.py
Normal file
16
ccxt/static_dependencies/starknet/hash/selector.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from ..constants import (
|
||||
DEFAULT_ENTRY_POINT_NAME,
|
||||
DEFAULT_ENTRY_POINT_SELECTOR,
|
||||
DEFAULT_L1_ENTRY_POINT_NAME,
|
||||
)
|
||||
from ..hash.utils import _starknet_keccak
|
||||
|
||||
|
||||
def get_selector_from_name(func_name: str) -> int:
|
||||
"""
|
||||
Returns the selector of a contract's function name.
|
||||
"""
|
||||
if func_name in [DEFAULT_ENTRY_POINT_NAME, DEFAULT_L1_ENTRY_POINT_NAME]:
|
||||
return DEFAULT_ENTRY_POINT_SELECTOR
|
||||
|
||||
return _starknet_keccak(data=func_name.encode("ascii"))
|
||||
12
ccxt/static_dependencies/starknet/hash/storage.py
Normal file
12
ccxt/static_dependencies/starknet/hash/storage.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from functools import reduce
|
||||
|
||||
from constants import ADDR_BOUND
|
||||
from hash.utils import _starknet_keccak, pedersen_hash
|
||||
|
||||
|
||||
def get_storage_var_address(var_name: str, *args: int) -> int:
|
||||
"""
|
||||
Returns the storage address of a Starknet storage variable given its name and arguments.
|
||||
"""
|
||||
res = _starknet_keccak(var_name.encode("ascii"))
|
||||
return reduce(pedersen_hash, args, res) % ADDR_BOUND
|
||||
78
ccxt/static_dependencies/starknet/hash/utils.py
Normal file
78
ccxt/static_dependencies/starknet/hash/utils.py
Normal file
@@ -0,0 +1,78 @@
|
||||
import functools
|
||||
from typing import List, Optional, Sequence
|
||||
|
||||
from ... import keccak
|
||||
|
||||
from ..common import int_from_bytes
|
||||
from ..constants import EC_ORDER
|
||||
from ...starkware.crypto.signature import (
|
||||
ECSignature,
|
||||
private_to_stark_key,
|
||||
sign
|
||||
# verify
|
||||
)
|
||||
from ...starkware.crypto.fast_pedersen_hash import (
|
||||
pedersen_hash
|
||||
)
|
||||
|
||||
MASK_250 = 2**250 - 1
|
||||
HEX_PREFIX = "0x"
|
||||
|
||||
|
||||
def _starknet_keccak(data: bytes) -> int:
|
||||
"""
|
||||
A variant of eth-keccak that computes a value that fits in a Starknet field element.
|
||||
"""
|
||||
return int_from_bytes(keccak.SHA3(data)) & MASK_250
|
||||
|
||||
|
||||
# def pedersen_hash(left: int, right: int) -> int:
|
||||
# """
|
||||
# One of two hash functions (along with _starknet_keccak) used throughout Starknet.
|
||||
# """
|
||||
# return cpp_hash(left, right)
|
||||
|
||||
|
||||
def compute_hash_on_elements(data: Sequence) -> int:
|
||||
"""
|
||||
Computes a hash chain over the data, in the following order:
|
||||
h(h(h(h(0, data[0]), data[1]), ...), data[n-1]), n).
|
||||
|
||||
The hash is initialized with 0 and ends with the data length appended.
|
||||
The length is appended in order to avoid collisions of the following kind:
|
||||
H([x,y,z]) = h(h(x,y),z) = H([w, z]) where w = h(x,y).
|
||||
"""
|
||||
return functools.reduce(pedersen_hash, [*data, len(data)], 0)
|
||||
|
||||
|
||||
def message_signature(
|
||||
msg_hash: int, priv_key: int, seed: Optional[int] = 32
|
||||
) -> ECSignature:
|
||||
"""
|
||||
Signs the message with private key.
|
||||
"""
|
||||
return sign(msg_hash, priv_key, seed)
|
||||
|
||||
|
||||
# def verify_message_signature(
|
||||
# msg_hash: int, signature: List[int], public_key: int
|
||||
# ) -> bool:
|
||||
# """
|
||||
# Verifies ECDSA signature of a given message hash with a given public key.
|
||||
# Returns true if public_key signs the message.
|
||||
# """
|
||||
# sig_r, sig_s = signature
|
||||
# # sig_w = pow(sig_s, -1, EC_ORDER)
|
||||
# return verify(msg_hash=msg_hash, r=sig_r, s=sig_s, public_key=public_key)
|
||||
|
||||
|
||||
def encode_uint(value: int, bytes_length: int = 32) -> bytes:
|
||||
return value.to_bytes(bytes_length, byteorder="big")
|
||||
|
||||
|
||||
def encode_uint_list(data: List[int]) -> bytes:
|
||||
return b"".join(encode_uint(x) for x in data)
|
||||
|
||||
|
||||
def get_bytes_length(value: int) -> int:
|
||||
return (value.bit_length() + 7) // 8
|
||||
Reference in New Issue
Block a user