import decimal
import hashlib
from web3 import Web3
from dydx_constants import ASSET_RESOLUTION
from starkex.constants import ORDER_FIELD_BIT_LENGTHS
from starkex.starkex_resources.python_signature import (
get_random_private_key
)
from starkex.starkex_resources.python_signature import (
private_key_to_ec_point_on_stark_curve,
)
from starkex.starkex_resources.python_signature import (
private_to_stark_key
)
BIT_MASK_250 = (2 ** 250) - 1
NONCE_UPPER_BOUND_EXCLUSIVE = 1 << ORDER_FIELD_BIT_LENGTHS['nonce']
DECIMAL_CTX_ROUND_DOWN = decimal.Context(rounding=decimal.ROUND_DOWN)
DECIMAL_CTX_ROUND_UP = decimal.Context(rounding=decimal.ROUND_UP)
DECIMAL_CTX_EXACT = decimal.Context(
traps=[
decimal.Inexact,
decimal.DivisionByZero,
decimal.InvalidOperation,
decimal.Overflow,
],
)
def strip_hex_prefix(input):
if input.startswith('0x'):
return input[2:]
return input
def bytes_to_int(x):
return int(x.hex(), 16)
def int_to_hex_32(x):
padded_hex = hex(x)[2:].rjust(64, '0')
if len(padded_hex) != 64:
raise ValueError('Input does not fit in 32 bytes')
return padded_hex
def serialize_signature(r, s):
return int_to_hex_32(r) + int_to_hex_32(s)
def deserialize_signature(signature):
if len(signature) != 128:
raise ValueError(
'Invalid serialized signature, expected hex string of length 128',
)
return int(signature[:64], 16), int(signature[64:], 16)
def to_quantums_exact(human_amount, asset):
return _to_quantums_helper(human_amount, asset, DECIMAL_CTX_EXACT)
def to_quantums_round_down(human_amount, asset):
return _to_quantums_helper(human_amount, asset, DECIMAL_CTX_ROUND_DOWN)
def to_quantums_round_up(human_amount, asset):
return _to_quantums_helper(human_amount, asset, DECIMAL_CTX_ROUND_UP)
def _to_quantums_helper(human_amount, asset, ctx):
try:
amount_dec = ctx.create_decimal(human_amount)
resolution_dec = ctx.create_decimal(ASSET_RESOLUTION[asset])
quantums = (amount_dec * resolution_dec).to_integral_exact(context=ctx)
except decimal.Inexact:
raise ValueError(
'Amount {} is not a multiple of the quantum size {}'.format(
human_amount,
1 / float(ASSET_RESOLUTION[asset]),
),
)
return int(quantums)
def nonce_from_client_id(client_id):
message = hashlib.sha256()
message.update(client_id.encode()) return int(message.digest().hex(), 16) % NONCE_UPPER_BOUND_EXCLUSIVE
def fact_to_condition(fact_registry_address, fact):
if not isinstance(fact, bytes):
raise ValueError('fact must be a byte-string')
data = bytes.fromhex(strip_hex_prefix(fact_registry_address)) + fact
return int(Web3.keccak(data).hex(), 16) & BIT_MASK_250
def message_to_hash(message_string):
message = hashlib.sha256()
message.update(message_string.encode()) return int(message.digest().hex(), 16) >> 5
def generate_private_key_hex_unsafe():
return hex(get_random_private_key())
def private_key_from_bytes(data):
if not isinstance(data, bytes):
raise ValueError('Input must be a byte-string')
return hex(int(Web3.keccak(data).hex(), 16) >> 5)
def private_key_to_public_hex(private_key_hex):
private_key_int = int(private_key_hex, 16)
return hex(private_to_stark_key(private_key_int))
def private_key_to_public_key_pair_hex(private_key_hex):
private_key_int = int(private_key_hex, 16)
x, y = private_key_to_ec_point_on_stark_curve(private_key_int)
return [hex(x), hex(y)]