# Fiber 中心化分账的集成方案设计
## Metadata
**Status**:: #x
**Zettel**:: #zettel/literature
**Created**:: [[2024-10-14]]
%% (**Notion**:: [notion.site](https://cryptape.notion.site/Fiber-11b8f0d3781e80ee9a72e619ba7429e2?pvs=4)) %%
## Synopsis
Fiber 中心化分账的目的是在用户不方便使用全功能 Fiber 节点时,维护中心化的账本为用户提供代理接入 Fiber 的服务。后方中使用“代理商”来代称提供 Fiber 代理服务的服务商,使用“用户”来特指使用该代理服务的用户。
代理商运行 Fiber 节点,并维护账本来记录用户余额。账本可以是中心化的,比如保存在代理商的数据库中;也可以是去中心化的,比如通过链下状态更新链上仲裁的合约。这里不讨论账本的实现细节,会假设代理商已经实现该功能。
为了方便该场景,Fiber 需要提供以下功能:
1. 代理商的 Fiber 节点收到订单要有办法确认收款人是哪个用户
2. 代理商能从 Fiber 节点同步所有成功支付的订单信息,包括发出和收到的。也就是通过 RPC 能够不重复不遗漏的同步成功支付的订单信息。该同步过程能在 Fiber 节点或者代理商的同步服务重启后自动恢复。
## 整体设计
采用 [[Fiber 中心化分账的三种方案#方案三、LSP 记录 Payment Hash 和 LSP User 的映射]],由代理商在 Fiber 节点之外记录 Payment Hash 和用户的关联。
## Fiber 功能设计
### 支付同步
Fiber 需要提供按照 RPC 来查询 Invoice 和 Payment Hash 查询支付状态的 RPC,包括发送出去和收到的支付订单。
#### RPC `get_invoice`
查询通过 `new_invoice` 创建的 invoice 信息和状态。
Request:
- `payment_hash (H256)`
Response,如果存在对应的 invoice 则返回以下字段,否则返回 `null`.
- `invoice (CkbInvoice)`: invoice 信息
- `status (string)`: invoice 状态,one of "unpaid", "paid", "expired"
其它字段可参考 [cln listinvoices](https://docs.corelightning.org/reference/lightning-listinvoices) 和 [lnd invoice](https://lightning.engineering/api-docs/api/lnd/invoices/lookup-invoice-v2#lnrpcinvoice)
#### RPC `get_payment`
查询通过 `send_payment` 发送的 payment 信息和状态。
Request:
- `payment_hash (H256)`
Response,如果存在对应的 payment 则返回以下字段,否则返回 `null`.
- `status (string)`: payment 状态,one of "pending", "failed", "complete"
其它字段可参考 [cln listpays](https://docs.corelightning.org/reference/lightning-listpays) 和 [lnd payment](https://lightning.engineering/api-docs/api/lnd/lightning/list-payments#lnrpcpayment)
## 代理商功能设计
### 收款
用户需要收款时由代理商通过其 Fiber 节点创建 Invoice。该过程不需要用户在线。创建 Invoice 之后,代理商需要在其数据库的 invoices 表中保存以下 tuple (payment hash, status, user id) 然后
- payment hash: `new_invoice` RPC 返回的 payment hash
- status: invoice status,初始为 `unpaid`
- user id: 代理商内部的用户标识
代理商需要定时通过 `get_invoice` 同步状态。取出 invocies 表中所有 status 为 `unpaid` 的 (payment hash, status, user id):
- 通过 payment hash 调用 `get_invoice`
- 如果 invoice status 为 `expired`, 更新数据库中对应 invoice 的 status
- 如果 invoice status 为 `paid`,更新账本和数据库中对应 invoice 的 status。两个更新操作要保证原子性。
### 支付给同一代理商的用户
用户支付给同一代理商的用户时,代理商可以直接更新账本并添加支付者和收款者分别在 payments 和 invoices 表中的数据。而 Fiber 节点中的 invoice 可以放着不管,让其过期。
### 支付给 Invoice
用户可以选择支付给 Invoice,如果 Invoice 的收款节点就是该用户的代理商,则代理商可以在保证原子性的前提下完成以下更新:
- 更新账本中订单双方的余额。
- 将 invoices 表中对应 invoice 的 status 更新为 `paid`
- 在代理商数据库的 payments 表中保存以下 tuple (payment hash, status, user id)
- payment hash: invoice payment hash
- status: `complete`
- user id: 支付者的用户标识
如果 Invoice 的收款节点是其它节点,则代理商需要通过 Fiber 节点进行支付
- 使用 `send_payment` 支付
- 在代理商的数据库的 payments 表中保存以下 tuple (payment hash, status, user id)
- payment hash: `send_payment` 返回的 payment hash
- status: payment status, 初始为 `pending`
- user id: 代理商内部用户标识
代理商需要定时通过 `get_payment` 同步状态。取出 payments 表中所有 status 为 `pending` 的 (payment hash, status, user id):
- 通过 payment hash 调用 `get_payment`
- 如果 payment status 为 `failed`, 更新数据库中对应 payment 的 status
- 如果 payment status 为 `complete`,更新账本和数据库中对应 payment 的 status。两个更新操作要保证原子性。