Beispiel #1
0
async def _memo(ctx: Context, w: bytearray, msg: StellarSignTx) -> None:
    writers.write_uint32(w, msg.memo_type)
    if msg.memo_type == StellarMemoType.NONE:
        # nothing is serialized
        memo_confirm_text = ""
    elif msg.memo_type == StellarMemoType.TEXT:
        # Text: 4 bytes (size) + up to 28 bytes
        if msg.memo_text is None:
            raise DataError("Stellar: Missing memo text")
        if len(msg.memo_text) > 28:
            raise ProcessError(
                "Stellar: max length of a memo text is 28 bytes")
        writers.write_string(w, msg.memo_text)
        memo_confirm_text = msg.memo_text
    elif msg.memo_type == StellarMemoType.ID:
        # ID: 64 bit unsigned integer
        if msg.memo_id is None:
            raise DataError("Stellar: Missing memo id")
        writers.write_uint64(w, msg.memo_id)
        memo_confirm_text = str(msg.memo_id)
    elif msg.memo_type in (StellarMemoType.HASH, StellarMemoType.RETURN):
        # Hash/Return: 32 byte hash
        if msg.memo_hash is None:
            raise DataError("Stellar: Missing memo hash")
        writers.write_bytes_fixed(w, bytearray(msg.memo_hash), 32)
        memo_confirm_text = hexlify(msg.memo_hash).decode()
    else:
        raise ProcessError("Stellar invalid memo type")
    await layout.require_confirm_memo(ctx, msg.memo_type, memo_confirm_text)
Beispiel #2
0
def validate(msg: NEMSignTx):
    if msg.transaction is None:
        raise ProcessError("No common provided")

    _validate_single_tx(msg)
    _validate_common(msg.transaction)

    if msg.multisig:
        _validate_common(msg.multisig, True)
        _validate_multisig(msg.multisig, msg.transaction.network)
    if not msg.multisig and msg.cosigning:
        raise ProcessError("No multisig transaction to cosign")

    if msg.transfer:
        _validate_transfer(msg.transfer, msg.transaction.network)
    if msg.provision_namespace:
        _validate_provision_namespace(msg.provision_namespace, msg.transaction.network)
    if msg.mosaic_creation:
        _validate_mosaic_creation(msg.mosaic_creation, msg.transaction.network)
    if msg.supply_change:
        _validate_supply_change(msg.supply_change)
    if msg.aggregate_modification:
        _validate_aggregate_modification(
            msg.aggregate_modification, msg.multisig is None
        )
    if msg.importance_transfer:
        _validate_importance_transfer(msg.importance_transfer)
Beispiel #3
0
def _validate_common(common: NEMTransactionCommon, inner: bool = False):
    common.network = validate_network(common.network)

    err = None
    if common.timestamp is None:
        err = "timestamp"
    if common.fee is None:
        err = "fee"
    if common.deadline is None:
        err = "deadline"

    if not inner and common.signer:
        raise ProcessError("Signer not allowed in outer transaction")

    if inner and common.signer is None:
        err = "signer"

    if err:
        if inner:
            raise ProcessError("No %s provided in inner transaction" % err)
        else:
            raise ProcessError("No %s provided" % err)

    if common.signer is not None:
        _validate_public_key(
            common.signer, "Invalid signer public key in inner transaction"
        )
Beispiel #4
0
async def _memo(ctx, w: bytearray, msg: StellarSignTx):
    if msg.memo_type is None:
        msg.memo_type = consts.MEMO_TYPE_NONE
    writers.write_uint32(w, msg.memo_type)
    if msg.memo_type == consts.MEMO_TYPE_NONE:
        # nothing is serialized
        memo_confirm_text = ""
    elif msg.memo_type == consts.MEMO_TYPE_TEXT:
        # Text: 4 bytes (size) + up to 28 bytes
        if len(msg.memo_text) > 28:
            raise ProcessError(
                "Stellar: max length of a memo text is 28 bytes")
        writers.write_string(w, msg.memo_text)
        memo_confirm_text = msg.memo_text
    elif msg.memo_type == consts.MEMO_TYPE_ID:
        # ID: 64 bit unsigned integer
        writers.write_uint64(w, msg.memo_id)
        memo_confirm_text = str(msg.memo_id)
    elif msg.memo_type in (consts.MEMO_TYPE_HASH, consts.MEMO_TYPE_RETURN):
        # Hash/Return: 32 byte hash
        writers.write_bytes_unchecked(w, bytearray(msg.memo_hash))
        memo_confirm_text = hexlify(msg.memo_hash).decode()
    else:
        raise ProcessError("Stellar invalid memo type")
    await layout.require_confirm_memo(ctx, msg.memo_type, memo_confirm_text)
def _validate_common(common: NEMTransactionCommon, inner: bool = False):
    common.network = validate_network(common.network)

    err = None
    if common.timestamp is None:
        err = 'timestamp'
    if common.fee is None:
        err = 'fee'
    if common.deadline is None:
        err = 'deadline'

    if not inner and common.signer:
        raise ProcessError('Signer not allowed in outer transaction')

    if inner and common.signer is None:
        err = 'signer'

    if err:
        if inner:
            raise ProcessError('No %s provided in inner transaction' % err)
        else:
            raise ProcessError('No %s provided' % err)

    if common.signer is not None:
        _validate_public_key(common.signer, 'Invalid signer public key in inner transaction')
Beispiel #6
0
def _validate_single_tx(msg: NEMSignTx):
    # ensure exactly one transaction is provided
    tx_count = (bool(msg.transfer) + bool(msg.provision_namespace) +
                bool(msg.mosaic_creation) + bool(msg.supply_change) +
                bool(msg.aggregate_modification) +
                bool(msg.importance_transfer))
    if tx_count == 0:
        raise ProcessError("No transaction provided")
    if tx_count > 1:
        raise ProcessError("More than one transaction provided")
def _validate_provision_namespace(provision_namespace: NEMProvisionNamespace, network: int):
    if provision_namespace.namespace is None:
        raise ProcessError('No namespace provided')
    if provision_namespace.sink is None:
        raise ProcessError('No rental sink provided')
    if provision_namespace.fee is None:
        raise ProcessError('No rental sink fee provided')

    if not nem.validate_address(provision_namespace.sink, network):
        raise ProcessError('Invalid rental sink address')
Beispiel #8
0
def _validate_transfer(transfer: NEMTransfer, network: int):
    if transfer.recipient is None:
        raise ProcessError("No recipient provided")
    if transfer.amount is None:
        raise ProcessError("No amount provided")

    if transfer.public_key is not None:
        _validate_public_key(transfer.public_key, "Invalid recipient public key")
        if transfer.payload is None:
            raise ProcessError("Public key provided but no payload to encrypt")

    if transfer.payload:
        if len(transfer.payload) > NEM_MAX_PLAIN_PAYLOAD_SIZE:
            raise ProcessError("Payload too large")
        if (
            transfer.public_key
            and len(transfer.payload) > NEM_MAX_ENCRYPTED_PAYLOAD_SIZE
        ):
            raise ProcessError("Payload too large")

    if not nem.validate_address(transfer.recipient, network):
        raise ProcessError("Invalid recipient address")

    for m in transfer.mosaics:
        if m.namespace is None:
            raise ProcessError("No mosaic namespace provided")
        if m.mosaic is None:
            raise ProcessError("No mosaic name provided")
        if m.quantity is None:
            raise ProcessError("No mosaic quantity provided")
def _validate_supply_change(supply_change: NEMMosaicSupplyChange):
    if supply_change.namespace is None:
        raise ProcessError('No namespace provided')
    if supply_change.mosaic is None:
        raise ProcessError('No mosaic provided')
    if supply_change.type is None:
        raise ProcessError('No type provided')
    elif supply_change.type not in [NEMSupplyChangeType.SupplyChange_Decrease, NEMSupplyChangeType.SupplyChange_Increase]:
        raise ProcessError('Invalid supply change type')
    if supply_change.delta is None:
        raise ProcessError('No delta provided')
Beispiel #10
0
def validate(msg: RippleSignTx):
    if None in (msg.fee, msg.sequence, msg.payment) or (
        msg.payment and None in (msg.payment.amount, msg.payment.destination)
    ):
        raise ProcessError(
            "Some of the required fields are missing (fee, sequence, payment.amount, payment.destination)"
        )
    if msg.payment.amount < 0:
        raise ProcessError("Only non-negative amounts are allowed.")
    if msg.payment.amount > helpers.MAX_ALLOWED_AMOUNT:
        raise ProcessError("Amount exceeds maximum allowed amount.")
def _validate_aggregate_modification(
        aggregate_modification: NEMAggregateModification,
        creation: bool = False) -> None:

    if creation and not aggregate_modification.modifications:
        raise ProcessError("No modifications provided")

    for m in aggregate_modification.modifications:
        if creation and m.type == NEMModificationType.CosignatoryModification_Delete:
            raise ProcessError(
                "Cannot remove cosignatory when converting account")
        _validate_public_key(m.public_key,
                             "Invalid cosignatory public key provided")
Beispiel #12
0
def write_manage_data_op(w, msg: StellarManageDataOp):
    if len(msg.key) > 64:
        raise ProcessError("Stellar: max length of a key is 64 bytes")
    writers.write_string(w, msg.key)
    writers.write_bool(w, bool(msg.value))
    if msg.value:
        writers.write_string(w, msg.value)
Beispiel #13
0
def validate_network(network: int) -> int:
    if network is None:
        return NEM_NETWORK_MAINNET
    if network not in (NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET,
                       NEM_NETWORK_MIJIN):
        raise ProcessError("Invalid NEM network")
    return network
Beispiel #14
0
def validate(msg: RippleSignTx):
    if None in (msg.fee, msg.sequence, msg.payment) or (
            msg.payment
            and None in (msg.payment.amount, msg.payment.destination)):
        raise ProcessError(
            "Some of the required fields are missing (fee, sequence, payment.amount, payment.destination)"
        )
def _validate_transfer(transfer: NEMTransfer, network: int) -> None:
    if transfer.public_key is not None:
        _validate_public_key(transfer.public_key,
                             "Invalid recipient public key")
        if not transfer.payload:
            raise ProcessError("Public key provided but no payload to encrypt")

    if transfer.payload:
        if len(transfer.payload) > NEM_MAX_PLAIN_PAYLOAD_SIZE:
            raise ProcessError("Payload too large")
        if (transfer.public_key
                and len(transfer.payload) > NEM_MAX_ENCRYPTED_PAYLOAD_SIZE):
            raise ProcessError("Payload too large")

    if not nem.validate_address(transfer.recipient, network):
        raise ProcessError("Invalid recipient address")
Beispiel #16
0
async def confirm_set_options_op(ctx: Context,
                                 op: StellarSetOptionsOp) -> None:
    if op.inflation_destination_account:
        await confirm_address(
            ctx,
            "Inflation",
            op.inflation_destination_account,
            description="Destination:",
            br_type="op_inflation",
        )

    if op.clear_flags:
        t = _format_flags(op.clear_flags)
        await confirm_text(ctx, "op_set_options", "Clear flags", data=t)

    if op.set_flags:
        t = _format_flags(op.set_flags)
        await confirm_text(ctx, "op_set_options", "Set flags", data=t)

    thresholds = _format_thresholds(op)
    if thresholds:
        await confirm_properties(ctx,
                                 "op_thresholds",
                                 "Account Thresholds",
                                 props=thresholds)

    if op.home_domain:
        await confirm_text(ctx, "op_home_domain", "Home Domain",
                           op.home_domain)

    if op.signer_type is not None:
        if op.signer_key is None or op.signer_weight is None:
            raise DataError("Stellar: invalid signer option data.")

        if op.signer_weight > 0:
            title = "Add Signer"
        else:
            title = "Remove Signer"
        data: str | bytes = ""
        if op.signer_type == StellarSignerType.ACCOUNT:
            description = "Account:"
            data = helpers.address_from_public_key(op.signer_key)
        elif op.signer_type == StellarSignerType.PRE_AUTH:
            description = "Pre-auth transaction:"
            data = op.signer_key
        elif op.signer_type == StellarSignerType.HASH:
            description = "Hash:"
            data = op.signer_key
        else:
            raise ProcessError("Stellar: invalid signer type")

        await confirm_blob(
            ctx,
            "op_signer",
            title=title,
            description=description,
            data=data,
        )
def _validate_aggregate_modification(
        aggregate_modification: NEMAggregateModification,
        creation: bool = False):

    if creation and not aggregate_modification.modifications:
        raise ProcessError('No modifications provided')

    for m in aggregate_modification.modifications:
        if not m.type:
            raise ProcessError('No modification type provided')
        if m.type not in (
            NEMModificationType.CosignatoryModification_Add,
            NEMModificationType.CosignatoryModification_Delete
        ):
            raise ProcessError('Unknown aggregate modification')
        if creation and m.type == NEMModificationType.CosignatoryModification_Delete:
            raise ProcessError('Cannot remove cosignatory when converting account')
        _validate_public_key(m.public_key, 'Invalid cosignatory public key provided')
Beispiel #18
0
def _format_flags(flags: int) -> tuple:
    if flags > consts.FLAGS_MAX_SIZE:
        raise ProcessError("Stellar: invalid flags")
    text = ()
    if flags & consts.FLAG_AUTH_REQUIRED:
        text += ("AUTH_REQUIRED", )
    if flags & consts.FLAG_AUTH_REVOCABLE:
        text += ("AUTH_REVOCABLE", )
    if flags & consts.FLAG_AUTH_IMMUTABLE:
        text += ("AUTH_IMMUTABLE", )
    return text
Beispiel #19
0
def _format_flags(flags: int) -> str:
    if flags > consts.FLAGS_MAX_SIZE:
        raise ProcessError("Stellar: invalid flags")
    flags_set = []
    if flags & consts.FLAG_AUTH_REQUIRED:
        flags_set.append("AUTH_REQUIRED\n")
    if flags & consts.FLAG_AUTH_REVOCABLE:
        flags_set.append("AUTH_REVOCABLE\n")
    if flags & consts.FLAG_AUTH_IMMUTABLE:
        flags_set.append("AUTH_IMMUTABLE\n")
    return "".join(flags_set)
Beispiel #20
0
def _serialize_asset_code(w, asset_type: int, asset_code: str):
    code = bytearray(asset_code)
    if asset_type == consts.ASSET_TYPE_NATIVE:
        return  # nothing is needed
    elif asset_type == consts.ASSET_TYPE_ALPHANUM4:
        # pad with zeros to 4 chars
        writers.write_bytes(w, code + bytearray([0] * (4 - len(code))))
    elif asset_type == consts.ASSET_TYPE_ALPHANUM12:
        # pad with zeros to 12 chars
        writers.write_bytes(w, code + bytearray([0] * (12 - len(code))))
    else:
        raise ProcessError("Stellar: invalid asset type")
def _validate_common(common: NEMTransactionCommon,
                     inner: bool = False) -> None:
    validate_network(common.network)

    err = None

    if not inner and common.signer:
        raise ProcessError("Signer not allowed in outer transaction")

    if inner and common.signer is None:
        err = "signer"

    if err:
        if inner:
            raise ProcessError(f"No {err} provided in inner transaction")
        else:
            raise ProcessError(f"No {err} provided")

    if common.signer is not None:
        _validate_public_key(common.signer,
                             "Invalid signer public key in inner transaction")
Beispiel #22
0
async def confirm_set_options_op(ctx, op: StellarSetOptionsOp):
    if op.inflation_destination_account:
        text = Text("Confirm operation", ui.ICON_CONFIRM, ui.GREEN)
        text.bold("Set Inflation Destination")
        text.mono(*split(op.inflation_destination_account))
        await require_confirm(ctx, text, ButtonRequestType.ConfirmOutput)
    if op.clear_flags:
        t = _format_flags(op.clear_flags)
        text = Text("Confirm operation", ui.ICON_CONFIRM, ui.GREEN)
        text.bold("Clear Flags")
        text.mono(*t)
        await require_confirm(ctx, text, ButtonRequestType.ConfirmOutput)
    if op.set_flags:
        t = _format_flags(op.set_flags)
        text = Text("Confirm operation", ui.ICON_CONFIRM, ui.GREEN)
        text.bold("Set Flags")
        text.mono(*t)
        await require_confirm(ctx, text, ButtonRequestType.ConfirmOutput)
    thresholds = _format_thresholds(op)
    if thresholds:
        text = Text("Confirm operation", ui.ICON_CONFIRM, ui.GREEN)
        text.bold("Account Thresholds")
        text.mono(*thresholds)
        await require_confirm(ctx, text, ButtonRequestType.ConfirmOutput)
    if op.home_domain:
        text = Text("Confirm operation", ui.ICON_CONFIRM, ui.GREEN)
        text.bold("Home Domain")
        text.mono(*split(op.home_domain))
        await require_confirm(ctx, text, ButtonRequestType.ConfirmOutput)
    if op.signer_type is not None:
        if op.signer_weight > 0:
            t = "Add Signer (%s)"
        else:
            t = "Remove Signer (%s)"
        if op.signer_type == consts.SIGN_TYPE_ACCOUNT:
            text = Text("Confirm operation", ui.ICON_CONFIRM, ui.GREEN)
            text.bold(t % "acc")
            text.mono(*split(helpers.address_from_public_key(op.signer_key)))
            await require_confirm(ctx, text, ButtonRequestType.ConfirmOutput)
        elif op.signer_type in (consts.SIGN_TYPE_PRE_AUTH,
                                consts.SIGN_TYPE_HASH):
            if op.signer_type == consts.SIGN_TYPE_PRE_AUTH:
                signer_type = "auth"
            else:
                signer_type = "hash"
            text = Text("Confirm operation", ui.ICON_CONFIRM, ui.GREEN)
            text.bold(t % signer_type)
            text.mono(*split(hexlify(op.signer_key).decode()))
            await require_confirm(ctx, text, ButtonRequestType.ConfirmOutput)
        else:
            raise ProcessError("Stellar: invalid signer type")
Beispiel #23
0
def write_set_options_op(w, msg: StellarSetOptionsOp):
    # inflation destination
    if msg.inflation_destination_account is None:
        writers.write_bool(w, False)
    else:
        writers.write_bool(w, True)
        writers.write_pubkey(w, msg.inflation_destination_account)

    # clear flags
    _write_set_options_int(w, msg.clear_flags)
    # set flags
    _write_set_options_int(w, msg.set_flags)
    # account thresholds
    _write_set_options_int(w, msg.master_weight)
    _write_set_options_int(w, msg.low_threshold)
    _write_set_options_int(w, msg.medium_threshold)
    _write_set_options_int(w, msg.high_threshold)

    # home domain
    if msg.home_domain is None:
        writers.write_bool(w, False)
    else:
        writers.write_bool(w, True)
        if len(msg.home_domain) > 32:
            raise ProcessError(
                "Stellar: max length of a home domain is 32 bytes")
        writers.write_string(w, msg.home_domain)

    # signer
    if msg.signer_type is None:
        writers.write_bool(w, False)
    elif msg.signer_type in consts.SIGN_TYPES:
        writers.write_bool(w, True)
        writers.write_uint32(w, msg.signer_type)
        writers.write_bytes_fixed(w, msg.signer_key, 32)
        writers.write_uint32(w, msg.signer_weight)
    else:
        raise ProcessError("Stellar: unknown signer type")
Beispiel #24
0
async def _init(ctx, w: bytearray, pubkey: bytes, msg: StellarSignTx):
    network_passphrase_hash = sha256(msg.network_passphrase).digest()
    writers.write_bytes(w, network_passphrase_hash)
    writers.write_bytes(w, consts.TX_TYPE)

    address = helpers.address_from_public_key(pubkey)
    writers.write_pubkey(w, address)
    if helpers.public_key_from_address(msg.source_account) != pubkey:
        raise ProcessError("Stellar: source account does not match address_n")
    writers.write_uint32(w, msg.fee)
    writers.write_uint64(w, msg.sequence_number)

    # confirm init
    await layout.require_confirm_init(ctx, address, msg.network_passphrase)
Beispiel #25
0
def serialize_set_options_op(w, msg: StellarSetOptionsOp):
    # inflation destination
    writers.write_bool(w, bool(msg.inflation_destination_account))
    if msg.inflation_destination_account:
        writers.write_pubkey(w, msg.inflation_destination_account)

    # clear flags
    writers.write_bool(w, bool(msg.clear_flags))
    if msg.clear_flags:
        writers.write_uint32(w, msg.clear_flags)

    # set flags
    writers.write_bool(w, bool(msg.set_flags))
    if msg.set_flags:
        writers.write_uint32(w, msg.set_flags)

    # account thresholds
    writers.write_bool(w, bool(msg.master_weight))
    if msg.master_weight:
        writers.write_uint32(w, msg.master_weight)

    writers.write_bool(w, bool(msg.low_threshold))
    if msg.low_threshold:
        writers.write_uint32(w, msg.low_threshold)

    writers.write_bool(w, bool(msg.medium_threshold))
    if msg.medium_threshold:
        writers.write_uint32(w, msg.medium_threshold)

    writers.write_bool(w, bool(msg.high_threshold))
    if msg.high_threshold:
        writers.write_uint32(w, msg.high_threshold)

    # home domain
    writers.write_bool(w, bool(msg.home_domain))
    if msg.home_domain:
        if len(msg.home_domain) > 32:
            raise ProcessError(
                "Stellar: max length of a home domain is 32 bytes")
        writers.write_string(w, msg.home_domain)

    # signer
    writers.write_bool(w, bool(msg.signer_type))
    if msg.signer_type:
        # signer type
        writers.write_uint32(w, msg.signer_type)
        writers.write_bytes(w, msg.signer_key)
        writers.write_uint32(w, msg.signer_weight)
Beispiel #26
0
async def sign_tx(ctx, msg: StellarSignTx):
    if msg.num_operations == 0:
        raise ProcessError("Stellar: At least one operation is required")

    node = await seed.derive_node(ctx, msg.address_n, consts.STELLAR_CURVE)
    pubkey = seed.remove_ed25519_prefix(node.public_key())

    w = bytearray()
    await _init(ctx, w, pubkey, msg)
    _timebounds(w, msg.timebounds_start, msg.timebounds_end)
    await _memo(ctx, w, msg)
    await _operations(ctx, w, msg.num_operations)
    await _final(ctx, w, msg)

    # sign
    digest = sha256(w).digest()
    signature = ed25519.sign(node.private_key(), digest)

    # Add the public key for verification that the right account was used for signing
    return StellarSignedTx(pubkey, signature)
Beispiel #27
0
def _write_asset_code(w: Writer, asset_type: StellarAssetType,
                      asset_code: str | None) -> None:
    if asset_type == StellarAssetType.NATIVE:
        return  # nothing is needed

    if asset_code is None:
        raise DataError("Stellar: invalid asset")

    code = asset_code.encode()
    if asset_type == StellarAssetType.ALPHANUM4:
        if len(code) > 4:
            raise DataError("Stellar: asset code too long for ALPHANUM4")
        # pad with zeros to 4 chars
        writers.write_bytes_fixed(w, code + bytes([0] * (4 - len(code))), 4)
    elif asset_type == StellarAssetType.ALPHANUM12:
        if len(code) > 12:
            raise DataError("Stellar: asset code too long for ALPHANUM12")
        # pad with zeros to 12 chars
        writers.write_bytes_fixed(w, code + bytes([0] * (12 - len(code))), 12)
    else:
        raise ProcessError("Stellar: invalid asset type")
Beispiel #28
0
def write_set_options_op(w: Writer, msg: StellarSetOptionsOp) -> None:
    # inflation destination
    if msg.inflation_destination_account is None:
        writers.write_bool(w, False)
    else:
        writers.write_bool(w, True)
        writers.write_pubkey(w, msg.inflation_destination_account)

    # clear flags
    _write_set_options_int(w, msg.clear_flags)
    # set flags
    _write_set_options_int(w, msg.set_flags)
    # account thresholds
    _write_set_options_int(w, msg.master_weight)
    _write_set_options_int(w, msg.low_threshold)
    _write_set_options_int(w, msg.medium_threshold)
    _write_set_options_int(w, msg.high_threshold)

    # home domain
    if msg.home_domain is None:
        writers.write_bool(w, False)
    else:
        writers.write_bool(w, True)
        if len(msg.home_domain) > 32:
            raise ProcessError(
                "Stellar: max length of a home domain is 32 bytes")
        writers.write_string(w, msg.home_domain)

    # signer
    if msg.signer_type is None:
        writers.write_bool(w, False)
    else:
        if msg.signer_key is None or msg.signer_weight is None:
            raise DataError(
                "Stellar: signer_type, signer_key, signer_weight must be set together"
            )
        writers.write_bool(w, True)
        writers.write_uint32(w, msg.signer_type)
        writers.write_bytes_fixed(w, msg.signer_key, 32)
        writers.write_uint32(w, msg.signer_weight)
Beispiel #29
0
async def sign_tx(ctx, msg: StellarSignTx, keychain):
    await paths.validate_path(ctx, keychain, msg.address_n)

    node = keychain.derive(msg.address_n)
    pubkey = seed.remove_ed25519_prefix(node.public_key())

    if msg.num_operations == 0:
        raise ProcessError("Stellar: At least one operation is required")

    w = bytearray()
    await _init(ctx, w, pubkey, msg)
    await _timebounds(ctx, w, msg.timebounds_start, msg.timebounds_end)
    await _memo(ctx, w, msg)
    await _operations(ctx, w, msg.num_operations)
    await _final(ctx, w, msg)

    # sign
    digest = sha256(w).digest()
    signature = ed25519.sign(node.private_key(), digest)

    # Add the public key for verification that the right account was used for signing
    return StellarSignedTx(public_key=pubkey, signature=signature)
Beispiel #30
0
def check_fee(fee: int):
    if fee < helpers.MIN_FEE or fee > helpers.MAX_FEE:
        raise ProcessError("Fee must be in the range of 10 to 10,000 drops")