Exemple #1
0
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}`')
Exemple #2
0
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
Exemple #3
0
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
Exemple #4
0
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
Exemple #5
0
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)
Exemple #6
0
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),
    )
Exemple #7
0
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)
Exemple #8
0
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))
Exemple #9
0
def serialize_uint(bits: int, value: int) -> bytes:
    assert 0 <= value < (1 << bits)
    return to_bytes(value, bits // 8)
Exemple #10
0
def serialize_transaction_type(name: str) -> bytes:
    return to_bytes(TRANSACTION_TYPES_BY_NAME[name], 2)
Exemple #11
0
    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)