实现原理

实时结算的帐本

一个使用智能合约实现的实时结算帐本。帐本能实时结算的前提是交易逐笔发生,有确定的执行顺序,交易发生时间真实可靠等。区块链满足这些特性,为帐本自动结算提供基础。

当一个交易发生时账本会对账目进行一次结算,此时结算利息会更新到账目余额中。等到下次交易事件发生时,会再次触发这样的结算处理并更新余额。

利率模型

一个银行的简单模型就是通过借款产生营收,营收作为存款用户的利息。简化 利率模型,不设定浮动的借款利率,不考虑盈利,只保证账目借贷平衡,有:

借款营收 = 存款利息
其中:
借款营收 = 借款总额 * 借款利率 * 时间
存款利息 = 存款总额 * 存款利率 * 时间
=>
借款总额 * 借款利率 * 时间 = 存款总额 * 存款利率 * 时间
=>
借款总额 * 借款利率 = 存款总额 * 存款利率
复制代码
Loan Profit = Deposit Interest
Loan Profit = Total loan * Loan interest rate * Time
Deposit Interest = Total deposit * Deposit interest rate * Time
=>
Total deposit * Deposit interest rate * Time =Total loan * Loan interest rate * Time
=>
Total deposit * Deposit interest rate =Total loan * Loan interest rate

根据公式有:

  1. 借款总额为零(没有人进行借款),此时没有营收产生,存款利率为零

  2. 借款总额增大,产生营收增多,存款利率也会提高

  3. 借款总额不变(营收不变),存款总额增大,存款利率降低

结论:利率随着借款总额和存款总额的变动而变动。

帐本的变化

定义交易事件为:存款、提现、借款、还款。

如果没有任何交易事件发生,存款总额、借款总额就不会发生变化,利率在这个段时间里也会一直保持不变。随着交易事件的产生,存款/借款总额会发生变化,这会引起利率发生改变。

假定借款利率是 0.05,下面状态图中圆圈代表帐本和利率的状态,箭头代表事件:

图中 a 状态无借款,无营收,存款利率为 0。事件 1.借50 发生,根据公式,可得新的存款利率为 0.025。

存款利率 =(借款总额 * 借款利率)/ 存款总额
= (50 * 0.05)/ 100 = 0.025
Deposit interest rate = ( total loan * laon interest rate)/ total deposit
= (50 * 0.05)/ 100 = 0.025

事件 2、3 导致的帐本状态也可以根据公式计算。

结论:交易事件引起利率变化。

营收和时间的关系

上节的状态变化并没有包含结算环节。随着时间的推移,会有营收(利息)产生。

对于存款:

For deposits,

新的存款总额 = 存款总额 +(存款总额 * 存款利率 * 时间)
New total deposit = total deposit + (total deposit * deposit interest rate * time)

对于贷款:

新的贷款总额 = 贷款总额 +(贷款总额 * 贷款利率 * 时间)
New total loan = total loan + (total loan * loan interest rate * time)

假设借款利率 5% 为日利率(明显是高利贷,但便于计算),叠加时间后进行结算的状态图如下:

黄色箭头代表上一状态的持续时间,当事件发生后,状态更新并进入下一个时间段。

可以看出,考虑营收和时间的关系后,利率的变化变得更加复杂,但计算过程仍然清晰。

状态 a 持续了 1 天,由于借款为 0,存款利率为 0,发生事件 1 进行结算后存款没有产生变化,事件 1 增加了借款总额。重新计算利率可以得到新的存款利率 0.025。

事件 2 触发,状态 b 持续了 2 天,在进行结算时,可以推算出新的存款和借款总额:

## 结算
新的存款总额 = 100 +(100 * 0.025 * 2)= 105
新的借款总额 = 50 +(50 * 0.05 * 2) = 55
## Settlement
New total deposit = 100 +(100 * 0.025 * 2)= 105
New total loan = 50 +(50 * 0.05 * 2) = 55

结算后,存款总额再增加事件 2 存入的 50,结果为 105 + 50 = 155。根据存款总额 155 和借款总额 55 计算出新的存款利率为 0.01774。

事件 3 触发,状态 c 持续了 1 天:

## 结算
新的存款总额 = 155 +(155 * 0.01774 * 1) = 157.75
新的借款总额 = 55 + (55 * 0.05 * 1) = 57.75
## Settlement
New total deposit = 155 +(155 * 0.01774 * 1) = 157.75
New total loan = 55 + (55 * 0.05 * 1) = 57.75

由于还款为 20,此时借款总额是 57.75 - 20 = 37.75。重新计算出存款利率为 0.012。

结论:交易事件发生时进行结算,结算结束后按事件调整余额并引起利率变化。

每一笔明细帐

上述过程已经具有一定的复杂性,但由事件触发状态变化这个过程是很明确的。在实际生产中,存款和借款总额并不是由一个单一账户产生的,而是由无数的小账目汇聚而成的。比如 Alice 存入 50,Bob 存入了 30,存款总额是 80。这里就产生了更多问题,由于 Alice 和 Bob 的存款时间不同,它们的利率也不一样。借款也与之类似。因此每一笔帐都要单独进行结算,它们的利率根据总帐额度的变化而变化。

我们把状态 a 的存款总额 100 归为其他存款。在 2 天后,Alice 存入 50,结算后其他存款更新为 105。Alice 的存款增加了存款总额,使总额增长到 155,最终存款利率计算为 0.01774。

1 天后,Bob 也存入 50,此时 Alice 存款和其他存款以 0.01774 利率进行结算。结算结果如状态 c 所示。

通过上述分析,可以发现每次事件产生,需要对每一笔明细帐进行结算。这样随着存款/借款的用户增多,账目会越来越多,每次结算的计算量也会越来越大。不过细心观察可以发现,只要记录了历史利率,事件发生不需要对所有账户结算。我们直接根据各明细帐的初始的状态计算图中状态 c:

其他存款 = 100 +(100 * 0.025 * 2)+((100 +(100 * 0.025 * 2))* 0.01774 * 1)
Alice存款 = 50 + (50 * 0.01774 * 1)
Bob存款 = 50
Other Deposits = 100 +(100 * 0.025 * 2)+((100 +(100 * 0.025 * 2))* 0.01774 * 1)
Alice's deposit = 50 + (50 * 0.01774 * 1)
Bob's deposit = 50

其中 100 是其他存款的初始额度,50 是 Alice存款的初始额度。0.025 是第一期利率,0.01774 是第二期利率。可以看出,只要有历史利率就可以通过迭代运算计算出每个明细账户的当前余额。所以在进行结算操作时只需要对事件操作的明细帐进行结算,其他账户可以暂时不用结算,直到它们被操作时再计算即可。

结论:每次结算只需要计算余额受影响的明细帐,并更新总帐。其他账目可以等到被操作时再进行计算。

此部分说明采编于网络,感谢原作者outprog 链接:https://juejin.cn/post/6844903774620745742