def serialize_amount(amount: Amount) -> bytes: """ Serialize an Amount. An XRP Amount comes as a string. It must be serialized to 64 bits: 1 zero bit, 1 sign bit (zero for negative, one for positive), 62 bits of absolute value. A non-XRP Amount comes as a dictionary. It must be serialized thus: 64 bits of unsigned value; 160 bit currency code; 160 bit issuer AccountID. """ if isinstance(amount, str): value = int(amount) sign = int(value > 0) magnitude = abs(value) assert magnitude <= 10**17 return to_bytes(sign << 62 | magnitude, 8) if isinstance(amount, dict): value_bytes = serialize_amount_non_xrp(amount['value']) currency_bytes = serialize_currency(amount['currency']) address_bytes = DEFAULT_CODEC.decode_address( t.cast(Address, amount['issuer'])) return value_bytes + currency_bytes + address_bytes raise ValueError('Amount must be `str` or `{value, currency, issuer}`')
def derive_private_key(seed: bytes) -> int: sequence = 0 while True: buffer = seed + to_bytes(sequence, 4) private_key = from_bytes(hashes.sha512half(buffer)) if private_key != 0 and private_key < GROUP_ORDER: return private_key sequence += 1
def vl_encode(blob: bytes) -> bytes: """ Encode a variable length type. Prefixes an arbitrary byte array (a "blob") with an encoded length. The length of the prefix is 1 to 3 bytes depending on the length of the blob: <= 918744 bytes: prefix is 3 bytes <= 12480 bytes: prefix is 2 bytes <= 192 bytes: prefix is 1 byte """ length = len(blob) if length > 918744: raise ValueError( 'variable length field must not be longer than 918744 bytes') if length > 12480: prefix = to_bytes(length - 12481 + (241 << 16), 3) elif length > 192: prefix = to_bytes(length - 193 + (193 << 8), 2) else: prefix = bytes([length]) return prefix + blob
def deserialize_step(scanner: Scanner) -> Step: type_byte = scanner.take1() step = t.cast(Step, {}) if type_byte & 0x01: step['account'] = DEFAULT_CODEC.encode_address( t.cast(AccountId, scanner.take(20))) if type_byte & 0x10: step['currency'] = deserialize_currency(scanner) if type_byte & 0x20: step['issuer'] = DEFAULT_CODEC.encode_address( t.cast(AccountId, scanner.take(20))) step['type'] = type_byte step['type_hex'] = to_bytes(type_byte, 8).hex().upper() return step
def field_id(type_code, field_code): """ Encode a field ID from its type and field codes. The encoding occupies 1 to 3 bytes depending on whether the codes are "common" (<16) or "uncommon" (>=16). """ # Codes must be nonzero and fit in 1 byte. assert 0 < type_code < 256 assert 0 < field_code < 256 # Each code takes a nibble (4 bits) in the first byte. # If a code spills over the nibble (i.e. >=16), then the nibble is # filled with zeroes and the code is given a trailing byte. # Type always comes before field. if type_code < 16 and field_code < 16: return to_bytes(type_code << 4 | field_code, 1) if type_code >= 16 and field_code < 16: return to_bytes(field_code << 8 | type_code, 2) if type_code < 16 and field_code >= 16: return to_bytes(type_code << 12 | field_code, 2) return to_bytes(type_code << 8 | field_code, 3)
def derive_key_pair(seed: Seed) -> t.Tuple[PrivateKey, PublicKey]: root_private_key = derive_private_key(seed) root_public_point = keys.get_public_key(root_private_key, curve.secp256k1) root_public_key = compress_ecdsa_point(root_public_point) inter_private_key = derive_private_key(root_public_key + FAMILY) inter_public_point = keys.get_public_key(inter_private_key, curve.secp256k1) inter_public_key = compress_ecdsa_point(inter_public_point) master_private_key = to_bytes( (root_private_key + inter_private_key) % GROUP_ORDER, 32 ) master_public_point = root_public_point + inter_public_point master_public_key = compress_ecdsa_point(master_public_point) return ( t.cast(PrivateKey, master_private_key), t.cast(PublicKey, master_public_key), )
def serialize_amount_non_xrp(value: str) -> bytes: context = getcontext() context.prec = 15 context.Emin = EXPONENT_MIN context.Emax = EXPONENT_MAX decimal = Decimal(value) if decimal.is_zero(): return CANONICAL_ZERO # Convert components to integers. sign, digits, exponent = decimal.as_tuple() mantissa = int(''.join(str(d) for d in digits)) # Canonicalize to expected range. while mantissa < MANTISSA_MIN and exponent > EXPONENT_MIN: mantissa *= 10 exponent -= 1 while mantissa > MANTISSA_MAX: if exponent >= EXPONENT_MAX: raise ValueError('amount overflow') mantissa //= 10 exponent += 1 if exponent < EXPONENT_MIN or mantissa < MANTISSA_MIN: # Round to zero. return CANONICAL_ZERO if exponent > EXPONENT_MAX or mantissa > MANTISSA_MAX: raise ValueError('amount overflow') # Serialize to bytes. bits = 1 << 63 # "not XRP" bit if sign == 0: bits |= 1 << 62 # "is positive" bit bits |= ((exponent + 97) << 54) # 8 bits of exponent bits |= mantissa # 54 bits of mantissa return to_bytes(bits, 8)
def compress_ecdsa_point(point) -> PublicKey: prefix = b'\x03' if point.y % 2 else b'\x02' return t.cast(PublicKey, prefix + to_bytes(point.x, 32))
def serialize_uint(bits: int, value: int) -> bytes: assert 0 <= value < (1 << bits) return to_bytes(value, bits // 8)
def serialize_transaction_type(name: str) -> bytes: return to_bytes(TRANSACTION_TYPES_BY_NAME[name], 2)
if isinstance(amount, dict): value_bytes = serialize_amount_non_xrp(amount['value']) currency_bytes = serialize_currency(amount['currency']) address_bytes = DEFAULT_CODEC.decode_address( t.cast(Address, amount['issuer'])) return value_bytes + currency_bytes + address_bytes raise ValueError('Amount must be `str` or `{value, currency, issuer}`') """ Zero has a special-case canonical format: - one "not XRP" bit set to 1 - one sign bit set to 0 (for "negative") - sixty-two value bits set to 0 """ CANONICAL_ZERO = to_bytes(1 << 63, 8) MANTISSA_MIN = 10**15 MANTISSA_MAX = 10**16 - 1 EXPONENT_MIN = -96 EXPONENT_MAX = 80 def serialize_amount_non_xrp(value: str) -> bytes: context = getcontext() context.prec = 15 context.Emin = EXPONENT_MIN context.Emax = EXPONENT_MAX decimal = Decimal(value)