跳到主要内容

钱包:发送与接收

转账由双方共同构建,因此发送是一个序列而非单次调用。参见交互式交易

以下示例基于连接与读取中的客户端,并在examples目录下运行。

InitTxArgs包含每个字段

init_send_txprocess_invoice_tx均接受一个InitTxArgs对象,钱包反序列化时要求其全部十二个键(libwallet/src/api_impl/types.rs:53)。不完整的对象会被拒绝。Python客户端中的init_tx_args每次调用均返回完整对象:

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_nameamountmessagetarget_slate_version。字段含义见Owner API参考

让钱包驱动交易所交互

提供send_argsinit_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接受httpkeybaseepicboxapi/src/owner.rs:749)。在httpkeybase路径上,调用会阻塞直到对方响应,然后遵守finalizepost_txapi/src/owner.rs:787),因此应将其加入队列而非在请求线程上运行。在epicbox路径上,调用将slate发布到中继,锁定输入(input)并返回发送方的第一轮slate(api/src/owner.rs:761);其启动的监听器在对方回复后完成转账。

在CLI中,--min_conf默认值为10,--selection默认值为smallestsrc/cmd/wallet_args.rs:143),即selection_strategy_is_use_all: false。本示例使用的确认数下限请传入-c 3

预估手续费而不提交

estimate_only计算手续费并选择输入(input),不预留任何内容。

# 打印两种选择策略的手续费,不实际发送。
epic-wallet send -e -c 3 1.5

在Python中,send.py里的estimate_fee()是相同的调用,通过init_tx_args传入estimate_only=True

手续费是交易形态的函数,与需求无关:

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

不存在手续费市场,因此估算值是精确值而非竞价。参见发行与手续费

手动执行各步骤

当slate通过自有传输方式传递时使用此方式。这也是CLI所执行的序列。

  1. init_send_tx构建slate。
  2. tx_lock_outputs预留输入(input)。Can spend funds从此刻起,相关输出(output)不可用,直到转账完成或cancel_tx释放它们。
  3. 通过任意传输方式交付slate。
  4. 接收方返回已签名的slate。
  5. finalize_tx完成聚合签名。
  6. post_tx广播最终确认的slate中携带的交易。

tx_lock_outputs接受四个参数:tokenslateparticipant_idaddr_toapi/src/owner_rpc_s.rs:897)。发送方为参与者0。没有任何机制会按计时器释放输出(output),因此锁定之后的所有操作都应放在调用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,需要可达性

如需自行处理slate,Foreign API方法为receive_tx。该方法位于端口3415,不需要令牌,接受四个位置参数:slatedest_acct_namemessageaddr_fromapi/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传输slate 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返回两个布尔值,详见支付证明

下一步