跳到主要内容

Epicbox中继协议

Epicbox是一个存储转发中继,用于在无法直接互达的钱包之间传递slate(交易协商数据),这使得两个位于NAT后的钱包之间的转账得以实现。

协议版本:3.0.0,当前所有钱包发送的常量(impls/src/adapters/epicbox.rs:77)。公共中继报告的中继软件为3.0.1,这是一个实现该协议的服务器构建产物,而非更新的协议。没有任何中继发布版本被打标签,也没有发布任何中继镜像。

中继的功能​

它接收以公钥为地址的已签名slate数据块,将其存储,并在接收方连接并证明对该密钥的所有权后进行投递。

它不接收任何密钥材料,其唯一的密码学操作是验证签名。

中继可以看到它所路由的公钥、通信时间以及连接方的IP地址。金额包含在slate的承诺(commitment)中,对中继不可见。

传输方式与寻址​

WebSocket over TLS,默认端口443。消息为带有type字段的JSON对象。

地址格式为<public_key>@<domain>[:<port>],其中public_key是52个base58-check字符,编码一个压缩的secp256k1公钥。参见地址。

密钥按账户派生,因此钱包为其每个账户持有一个epicbox地址,监听器以其打开的账户地址进行订阅(impls/src/adapters/epicbox.rs:152)。参见账户。

钱包的默认中继域名为epicbox.epiccash.com。

身份验证​

地址所有权通过对挑战值签名来证明。私钥不会离开钱包。

  1. 连接时以及之后按固定间隔,中继发送Challenge,附带一个随机字符串。
  2. 客户端回复Subscribe,附带其地址及对该挑战的签名。
  3. 中继验证签名。签名有效即证明所有权,客户端可接收该地址的队列消息。

挑战间隔默认为60秒,这决定了已排队的slate等待被推送给已连接客户端的最长时间。已连接并注册为监听器的客户端会立即接收slate,无需等待下一次挑战。

声明协议版本2.0.0或未声明版本的客户端,将收到固定挑战字符串而非随机字符串,以兼容3.5.2之前的旧版钱包。

消息类型​

客户端到中继​

类型字段用途
Subscribeaddress, ver, signature证明所有权并开始接收
Unsubscribeaddress停止接收并关闭
PostSlatefrom, to, str, signature提交slate以投递
Madeaddress, signature, ver, epicboxmsgid确认slate已处理
ClientDetailswallet_version, wallet_mode, protocol_version声明客户端身份
ping / pong无保活

Challenge、GetVersion和FastSend被接受以保持兼容性,不属于3.0.0的一部分。

声明wallet_mode为listener的客户端将被注册为即时投递,因此在其连接期间到达的slate会被直接推送。

中继到客户端​

类型字段用途
Challengestr签名此内容以订阅
Ok无已接受
Slatefrom, str, signature, challenge, ver, epicboxmsgid给你的slate
Errorkind, description已拒绝

错误类型:UnknownError、InvalidRequest、InvalidSignature、InvalidChallenge、TooManySubscriptions。

广播slate​

PostSlate在存储前经过两次验证:from和to的地址格式,以及针对from地址中密钥对载荷的签名。

目标域名和端口与中继自身配置匹配的slate将存储在本地。其他目标则通过新建出站WebSocket连接转发至对应中继,这就是跨中继投递的实现方式。

投递机制及其限制​

投递采用确认机制而非即发即忘,这是相对于协议2.0.0的主要变更。

  1. 收到有效的Subscribe后,中继发送该密钥最旧的未投递slate,并附带epicboxmsgid。
  2. 客户端处理后回复Made,对epicboxmsgid进行签名。
  3. 仅在收到有效的Made后,该slate才被标记为已投递,并释放下一条。

因此队列对每个接收方是串行的:同一时刻只有一条slate处于待确认状态。停止确认的客户端会阻塞自身队列。

适用两项限制,较短的那项并非存储过期时间:

存储过期时间,7天。 Slate保存604,800秒后无论状态如何均被删除。

确认限制,约九轮挑战。 中继按套接字统计发送尝试次数。三次未确认后重置计数器并开始新一轮,三轮之后(共九次尝试),中继丢弃该接收方所有未投递的slate,而不仅仅是失败的那一条(app_mongo.js:366)。在60秒挑战间隔下,约为九分钟。

An accepted post is not a delivered slate

两种限制均不通知发送方。从发送方角度看,slate已被接受后消失,而其输入(input)在转账取消前持续处于已预留状态。参见释放方式。

其他投递特性:

**投递状态按连接维护。**确认计数器与套接字绑定,因此重新连接会重置计数器,负载均衡器后端的多个中继实例之间不共享计数器。参考部署通过IP将客户端固定到同一实例。

**转发的slate不在本地存储。**发往其他中继的slate通过新建出站连接转发,接受该slate的中继不对其进行持久化存储。

客户端行为​

钱包的中继客户端:

  • 延迟5秒后重新连接,并持续重试。
  • 广播最终确认的交易后,每秒轮询一次节点的内存池(mempool),最长持续four分钟。
  • 使用slate V2,因此不携带支付证明或ttl_cutoff_height。参见选择合适的传输方式。

运行中继​

参考部署在nginx后端运行两个中继实例,使用MongoDB存储,并使用Rust辅助二进制文件进行签名和地址验证。

git clone https://github.com/EpicCash/epic-epicbox-docker.git
cd epic-epicbox-docker
git submodule update --init --recursive
EPICBOX_DOMAIN=epicbox.example.com docker compose up -d --build

将钱包指向该部署:

[epicbox]
epicbox_domain = "epicbox.example.com"
epicbox_port = 443

配置来自环境变量或default_config.json,环境变量优先。相关键:EPICBOX_DOMAIN、EPICBOX_PORT、MONGO_URL、CHALLENGE_INTERVAL(毫秒,默认值60000)、DEBUG、STATS。

生产环境前提条件:

MongoDB索引来自mongo-init.js。 compose文件将其挂载到/docker-entrypoint-initdb.d,因此全新的MongoDB卷会自动获得队列索引和createdat上的TTL索引。如果将中继指向已有的MongoDB,则需手动创建这些索引;相同的命令位于app_mongo.js顶部的注释块中。若无TTL索引,slate永不过期。

**将STATS设置为true。**默认关闭。它暴露的每小时计数器存储在内存中,重启后重置,如需保留历史数据请主动抓取。

参考compose文件将两个中继实例发布在宿主机端口8888和8889上,nginx发布在 8443. 在你自己的部署中将其映射到443,或直接编辑compose文件。

源码​

下一步​