Перейти к основному содержимому

Wallet: отправка и получение

Перевод строится совместно обеими сторонами, поэтому отправка представляет собой последовательность вызовов, а не один вызов. См. интерактивные транзакции.

Эти примеры основаны на клиенте из connect and read и запускаются из каталога examples.

InitTxArgs содержит все поля

init_send_tx и process_invoice_tx принимают объект InitTxArgs, и все двенадцать его ключей обязательны при десериализации кошельком (libwallet/src/api_impl/types.rs:53). Неполный объект отклоняется. init_tx_args в Python-клиенте возвращает полный объект при каждом вызове:

examples/python/tx_args.py
"""Argument builders for the wallet Owner API transfer calls.

`InitTxArgs` and `IssueInvoiceTxArgs` derive Deserialize without serde defaults, so every key is
required on the wire and a partial object is rejected with a missing-field error. These builders
return every key on every call, which is why the examples pass them rather than hand-written dicts.

Imported by send.py, send_manual.py, invoice.py and payment_proof.py, and re-exported from
epic_wallet.py.
"""

from __future__ import annotations

from typing import Any

EPIC = 100_000_000
"""Freemen in one EPIC. Every amount on the API is an integer count of freemen."""


def init_tx_args(
amount: int,
*,
src_acct_name: str | None = None,
minimum_confirmations: int = 3,
max_outputs: int = 500,
num_change_outputs: int = 1,
selection_strategy_is_use_all: bool = False,
message: str | None = None,
target_slate_version: int | None = None,
ttl_blocks: int | None = None,
payment_proof_recipient_address: str | None = None,
estimate_only: bool = False,
send_args: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""Build a complete InitTxArgs object for init_send_tx and process_invoice_tx.

All twelve keys are required when the wallet deserializes the object, so this
returns every one of them on every call. `amount` is in freemen.
"""
return {
"src_acct_name": src_acct_name,
"amount": amount,
"minimum_confirmations": minimum_confirmations,
"max_outputs": max_outputs,
"num_change_outputs": num_change_outputs,
"selection_strategy_is_use_all": selection_strategy_is_use_all,
"message": message,
"target_slate_version": target_slate_version,
"ttl_blocks": ttl_blocks,
"payment_proof_recipient_address": payment_proof_recipient_address,
"estimate_only": estimate_only,
"send_args": send_args,
}


def invoice_tx_args(
amount: int,
*,
dest_acct_name: str | None = None,
message: str | None = None,
target_slate_version: int | None = None,
) -> dict[str, Any]:
"""Build a complete IssueInvoiceTxArgs object. All four keys are required."""
return {
"dest_acct_name": dest_acct_name,
"amount": amount,
"message": message,
"target_slate_version": target_slate_version,
}

IssueInvoiceTxArgs работает аналогично и требует четыре обязательных ключа: dest_acct_name, amount, message и target_slate_version. Значения полей описаны в справочнике Owner API.

Передать управление обменом кошельку

Укажите send_args, и init_send_tx выполнит обмен за вас.

epic-wallet send -m epicbox -d 'esYQ...52chars@epicbox.epiccash.com' -c 3 1.5

# По HTTP к доступному слушателю
epic-wallet send -m http -d http://receiver.example:3415 -c 3 1.5

method принимает http, keybase и epicbox (api/src/owner.rs:749). На путях http и keybase вызов блокируется до ответа контрагента, затем учитывает finalize и post_tx (api/src/owner.rs:787) — ставьте его в очередь, а не запускайте в потоке запроса. На пути epicbox вызов публикует слейт (slate) на релей, блокирует входы (inputs) и возвращает слейт первого раунда отправителя (api/src/owner.rs:761); запущенный им слушатель завершает перевод, когда контрагент отвечает.

В CLI --min_conf по умолчанию равно 10, а --selectionsmallest (src/cmd/wallet_args.rs:143), что соответствует selection_strategy_is_use_all: false. Передайте -c 3 для минимального порога подтверждений, используемого в этих примерах.

Рассчитать комиссию без подтверждения

estimate_only вычисляет комиссию и выбирает входы, не резервируя их.

# Выводит комиссию для обеих стратегий выбора, ничего не отправляет.
epic-wallet send -e -c 3 1.5

В Python estimate_fee() в send.py — это тот же вызов с estimate_only=True, переданным через init_tx_args.

Комиссия зависит от формы транзакции, а не от спроса:

fee = max(4 × outputs + kernels − inputs, 1) × 0.001 EPIC

Рынка комиссий нет, поэтому оценка точная, а не ставка. См. эмиссия и комиссии.

Управление шагами вручную

Используйте это, когда слейт передаётся по вашему собственному транспорту. Это также последовательность, которую выполняет CLI.

  1. init_send_tx собирает слейт.
  2. tx_lock_outputs резервирует входы. Can spend funds С этого момента выходы (outputs) недоступны до завершения перевода или до тех пор, пока cancel_tx не освободит их.
  3. Вы доставляете слейт выбранным транспортом.
  4. Получатель возвращает его подписанным.
  5. finalize_tx завершает агрегированную подпись.
  6. post_tx транслирует в сеть транзакцию, содержащуюся в финализированном слейте.

tx_lock_outputs принимает четыре параметра: token, slate, participant_id и addr_to (api/src/owner_rpc_s.rs:897). Отправитель — участник 0. Ничто не освобождает выходы по таймеру, поэтому всё после блокировки должно находиться внутри обработчика ошибок, вызывающего cancel_tx. См. что освобождает их.

examples/shell/send-file.sh
#!/usr/bin/env bash
# Sender side of a file-transport transfer, with the cancel branch wired in.
# Needs epic-wallet and jq. epic-wallet prompts for the password at each step.
# Usage: bash send-file.sh <amount in EPIC> [slate file]
# See https://devdocs.epiccash.com/examples/send-receive
set -euo pipefail

AMOUNT="${1:?usage: send-file.sh <amount in EPIC> [slate file]}"
SLATE="${2:-slate.tx}"
MIN_CONF="${MIN_CONF:-3}"
TIMEOUT="${TIMEOUT:-600}"

command -v jq >/dev/null || { echo "this script needs jq" >&2; exit 1; }

# Round one. Writes the slate file and reserves the inputs.
epic-wallet send -m file -d "$SLATE" -c "$MIN_CONF" -s smallest "$AMOUNT"

# tr -d '\r' because a Windows jq writes CRLF. Harmless on Linux and macOS.
slate_id=$(jq -r '.id' "$SLATE" | tr -d '\r')
echo "slate $slate_id written to $SLATE, inputs reserved"

# The receiver runs `epic-wallet receive -m file -i <slate>` and returns <slate>.response.
echo "waiting up to ${TIMEOUT}s for $SLATE.response"
deadline=$(( $(date +%s) + TIMEOUT ))
while [ ! -f "$SLATE.response" ]; do
if [ "$(date +%s)" -ge "$deadline" ]; then
echo "no response, releasing the reserved inputs" >&2
epic-wallet cancel -t "$slate_id"
exit 1
fi
sleep 5
done

epic-wallet finalize -m file -i "$SLATE.response"
echo "transfer $slate_id posted"

Получение

Слушатель обрабатывает сторону получателя:

epic-wallet listen -m epicbox # ставится в очередь, пока вы офлайн
epic-wallet listen # HTTP, требует доступности

Чтобы обработать слейт самостоятельно, используйте метод Foreign API receive_tx. Он работает на порту 3415, не требует токена и принимает четыре позиционных параметра: slate, dest_acct_name, message и addr_from (api/src/foreign_rpc.rs:359).

curl -s -H 'Content-Type: application/json' \
-d "{\"jsonrpc\":\"2.0\",\"method\":\"receive_tx\",\"params\":[$(cat slate.json),null,null,null],\"id\":1}" \
http://127.0.0.1:3415/v2/foreign | jq '.result.Ok'

Foreign API не требует учётных данных. Ограничьте доступ к нему на уровне прокси и см. аутентификация.

Поиск и устранение застрявших переводов

epic-wallet info # если доступно к трате меньше общего — что-то зарезервировано
epic-wallet txs # искать Sent (Created) с Confirmed? false
epic-wallet cancel -i 42 # освободить входы этого перевода
epic-wallet scan # пересканировать, если цифры по-прежнему расходятся с цепочкой

Для отмены необходим доступный узел и перевод, который ещё не подтверждён. Алгоритм принятия решения описан в застрявшие транзакции.

Инвойсы

Получатель запрашивает сумму, а плательщик её финансирует. Криптография та же; меняется инициатор. finalize_invoice_tx — метод Foreign API, поэтому получатель финализирует там и публикует через Owner API.

epic-wallet invoice -d invoice.tx 2 # payee
epic-wallet pay -c 3 -i invoice.tx # плательщик, записывает invoice.tx.response
epic-wallet finalize -i invoice.tx.response # payee

Доказательства платежа

Запрашивайте доказательство платежа при отправке — добавить его впоследствии невозможно. Epicbox передаёт слейт версии V2, а доказательство платежа является полем V3, поэтому при необходимости отправляйте через HTTP. См. доказательства платежа.

epic-wallet send --request_payment_proof \
--proof_address <recipient proof address> \
-m http -d http://receiver.example:3415 -c 3 1

epic-wallet export_proof -i 42 proof.json
epic-wallet verify_proof proof.json

verify_payment_proof возвращает два булевых значения, описанных в доказательства платежа.

Далее