用一个 HTML 页面构建应用
ZooBC 应用程序可以是一个背后没有服务器的单个 HTML 文件。页面从节点的 HTTP API 读取链,在浏览器中用用户的密钥构造并签署交易,然后提交签名后的字节。用户不需要运行节点,也没有需要申请的 API 密钥。官方钱包就是证明:它本身就是一个 HTML 文件,每个节点都在 / 和 /wallet 提供它(网页钱包)。
节点 API
每个节点都提供明文的 HTTP/1.1 API,默认端口为 8080,在 /api/v1/ 下有一百多个路由:区块、账户、代币、应用、交易所等(节点 API)。每个响应都带有以下响应头,OPTIONS 请求会立即得到 200 应答,因此托管在任何地方的页面都可以调用任何节点:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Node-Public-Key, X-Timestamp, X-Signature
节点本身没有 TLS。公共域名在网关处终止 TLS,网关会加上自己的 CORS 响应头。浏览器会阻止通过 HTTPS 加载的页面发起明文 HTTP 调用,因此公开页面要与网关通信。
除八个路由外,其余都是无需认证的读取;提交交易也不需要任何密钥,因为交易自带签名。限制按客户端地址计算:每分钟 300 次写请求,超出后返回 429 并附带 Retry-After 响应头;除非运营者设置了限制,读取不受限;5 分钟内 20 次签名认证失败会让该客户端被锁定 300 秒。
在浏览器中签名
页面签署一个把交易绑定到一条链的摘要:
digest = SHA3-256("ZBC-TX" ‖ genesis hash ‖ unsigned transaction bytes)
原生账户用 Ed25519 签署它。页面从 GET /api/v1/node/info 读取 genesis_hash 和 signing_version(2);由于创世哈希在摘要之内,为一条链做出的签名在另一条链上毫无用处(链绑定签名)。不需要 SDK:Ed25519、SHA3-256 和创世哈希就足够了,字节布局见 交易信封。公开工具还提供现成的库,包括一个无依赖、可用 script 标签引入的 JavaScript 文件(命令行工具)。密钥从不离开页面。
提交与跟踪
POST /api/v1/transactions → 202, with "transaction_hash"
GET /api/v1/transactions/<hash>/status → staging, mempool, confirmed (with block_height),
rejected (with the reason and stage), or 404 not_found
202 的意思是已接受,而不是已完成。节点已解析该交易,运行了初步检查,并把它收进暂存区;出块者仍需把它打包进区块。在状态显示为 confirmed 之前,没有任何余额发生变化。按每 15 秒一个区块计算,每隔几秒轮询一次就足够了。
如果回应丢失,就重发完全相同的已签名字节。交易的哈希是未签名字节与签名的 SHA3-256,因此已经持有该交易的节点会回应 202 并带上 "duplicate": true,交易进入区块后还会加上 "state": "confirmed",而交易只执行一次。用新的时间戳重新签名会生成另一笔不同的交易,重复付款就是这样发生的。
默认情况下,交易可以在其时间戳之后最多 3,600 秒内被打包进区块;版本 3 交易自行声明其窗口,最长 86,400 秒;时间戳超前未来 3,600 秒以上的交易会被拒绝(有效期与时间戳)。在凭一笔付款发货之前,先等待确认,再等待 finalized 标志:当一个联署的脊链区块覆盖交易所在的区块时,交易就获得该标志(最终性与回滚)。API 还会报告 finalized_height、每个区块的 block_seed_proof、交易的 expiry,以及位于 /spine/blocks/:height 的脊链区块。
应用的路由
应用路由由节点本身而不是归档服务提供:/apps(筛选参数为 status、limit 和 category,后者取 solo、pvp 或 party)、/apps/open、/apps/pool、/apps/stats、/apps/:id(若有结算则一并返回)和 /accounts/:a/apps。
检查清单
- 通过网关以 HTTPS 读取;通过提交已签名字节写入,密钥保留在页面中。
- 按哈希跟踪;重试时重发相同的字节,绝不重新签名。
- 遵守 429 和
Retry-After。 - 用
textContent显示链上文本(链上数据是不可信的输入)。 - 把“confirmed”视为已收到,把“finalized”视为已结清。