一个去信任的本地交易所交易系统

author img

Amitabh Saxena

2019.05.29

blog photo

本地交易所交易系统(LETS)旨在发展当地经济,并且通常由在彼此附近的当地人使用。 在此链接中描述了一个管理LETS的委员会。 我们将此类系统称为管理或许可,因为它取决于受信任的委员会。 在这里,我们描述了一个去信任的LETS系统,即没有管理委员会的系统。

概要

LETS涉及多个同意使用某种形式的“本地货币”的党派,通常以1:1的比率与该国的主要货币挂钩。假设我们的LETS位于欧洲国家,货币为欧元,交换以“当地欧元”进行,被认为相当于国家欧元。

LETS中的每个用户都有一个帐户,其中包含该用户的LETS余额(以本地欧元为单位)。在加入时,每个用户的余额为零。余额存储在(可能是去中心化的)分类帐中。 LETS的一个有趣特征是零余额的用户也可以“提款”,但仅用于支付另一个LETS用户。在任何时候,所有用户的LETS余额总和为零。

例如,零余额的Alice希望从Bob那里以2欧元购买一升牛奶,Bob也是零余额的LETS成员。她将2欧元从她的账户转到Bob's,她的余额为-2,Bob为+2。然后,Bob可以将他的部分或全部余额转移给另一个LETS用户,以换取商品或服务。

去信任的LETS

由于我们希望获得去信任的LETS,因此我们不能依赖任何受信任的群体来接纳用户。我们只假设又一个有信任的预言机(oracle)由一些全球账号和一个包含这个账号的一个代币的单例币箱标识。此币箱还包含在任何给定时间段内尔格币与欧元的比率。通过使用此币箱并使用新费率创建另一个单例币箱来更新费率。

在任何情况下,我们的LETS由包含一些LETS成员资格代币的全局LETS币箱来定义。此币箱受以下脚本保护。代币账号唯一地定义了所使用的LETS的属性,例如位置,货币单位,费率预言机(oracle)账号等。一个或多个用户可以花费该币箱并创建他们各自的LETS币箱作为交易的输出。该币箱最初以例如10000 LETS会员代币开始。

一个LETS币箱表示一个LETS成员,必须用于LETS交易。 LETS交易发生在两个LETS成员之间,一个是发送者而另一个是接收者,这样发送者将一些正数量的LETS货币(本地欧元)转移给接收者。这样的交易消耗成员的币箱并将其重新创建为具有更新余额的输出。

基本变体

为了防止垃圾信息和分布式拒绝服务攻击,我们要求至少将一些最小数量的尔格币(minErgsToJoin)锁定在新创建的成员的币箱中。 尔格币将被锁定,直到至少minWithdrawTime数量的区块被挖掘为止。 允许一个币箱的LETS余额为负值,直至锁定的尔格币可以覆盖的金额(使用交易时的汇率)。

// 一个币箱存储会员的代币
val tokenBox = OUTPUTS(0) // first output contains remaining LETS tokens
def isLets(b:Box) = {
   // 一个LETS 币箱必须在代币中有正好一个会员资格代币(0)
   b.tokens(0)._1 == letsTokenID && b.tokens(0)._2 == 1 &&
   blake2b256(b.propositionBytes) == memberBoxScriptHash &&
   SELF.R4[Long].get == 0 && // 从0 LETS结余中开始一个币箱
   b.value >= minErgsToJoin && // 币箱必须包含一些最小数量的尔格币
   b.R6[Long].get <= HEIGHT // 将创建高度存储在R6中
}

// 在tx中创建了多少lets币箱
val numLetsBoxes = OUTPUTS.filter({(b:Box) => isLets(b)}).size

// 在交易中,为币箱保留了以下内容 ...
tokenBox.tokens(0)._1 == SELF.tokens(0)._1 &&                // 代币账号
tokenBox.tokens(0)._2 == SELF.tokens(0)._2 - numLetsBoxes && //  数量
tokenBox.propositionBytes == SELF.propositionBytes           //  脚本

LETS成员的币框受以下脚本的保护,其哈希memberBoxScriptHash(成员币箱脚本哈希)被以上使用。

val validRateOracle = CONTEXT.dataInputs(0).tokens(0)._1 == rateTokenID
val rate = CONTEXT.dataInputs(0).R4[Int].get
val inBalance = SELF.R4[Long].get    // 当前输入下的LETS结余
val pubKey = SELF.R5[SigmaProp].get  // 当前输入的拥有者
val createdAt = SELF.R6[Long].get    // 当前输入开采的高度

val index = getVar[Int](0).get       // 相关输出的指数
val out = OUTPUTS(index)
val outBalance = out.R4[Long].get    // 输出的LETS结余

// LETS币箱与当前币箱具有相同的脚本
val isMemberBox = {(b:Box) => b.propositionBytes == SELF.propositionBytes}
val letsInputs = INPUTS.filter(isMemberBox)    // 所有LETS输入币箱
val letsOutputs = OUTPUTS.filter(isMemberBox)  // 所有LETS输出币箱

// 如果LETS余额增加,则当前输入属于接收器
// 接收器的输入币箱中可能有一些尔格币,我们需要确保
// 接收器的输出币箱也包含与输入相同数量的尔格币
val receiver = outBalance > inBalance && out.value == SELF.value

val getBalance = {(b:Box) => b.R4[Long].get} // 回到币箱的LETS结余

val letsBalIn = letsInputs.map(getBalance).fold(0L, {(l:Long, r:Long) => l + r})
val letsBalOut = letsOutputs.map(getBalance).fold(0L, {(l:Long, r:Long) => l + r})

// 发送人的币箱可以包含较少量的尔格币(发送人可以撤回提供的尔格币
// 发送者的任何负LETS余额都有足够的尔格币支持)
val correctErgs = out.value >= -outBalance * rate && (
  out.value >= SELF.value || SELF.R6[Long].get + minWithdrawTime > HEIGHT
)

// 对于接受者来说,我们并不接触尔格币的结余
// 因为接受者没有积极参与交易

inBalance != outBalance && // 交易应该发生;结余必须变化
SELF.tokens(0)._1 == letsTokenID && // 当前输入有正确的代币
out.tokens(0)._1 == letsTokenID && // 对应地,输出也有正确的代币
validRateOracle &&          // 预言机(oracle)提供费率具备正确的“费率代币”
letsBalIn == letsBalOut &&  // 总共的LETS结余被保存在交易中
letsInputs.size == 2 && letsOutputs.size == 2 &&  // 只有两个LETS输入,输出
out.propositionBytes == SELF.propositionBytes &&  // 输出是一个LETS币箱 ...
out.R5[SigmaProp].get == pubKey &&                // ... 用正确的公钥
out.R6[Long].get == SELF.R6[Long].get &&          // ... 和创建高度
(receiver ||              // ...要么当前输入属于接受者
  (pubKey && correctErgs) // ... 要么输出有正确的尔格币,同时tx有签名
)

使用上述脚本支付币箱的交易需要:

  • 保留了输入和输出的LETS结余之和
  • 有两个LETS输入和两个LETS输出
  • 公钥(存储在R5中)保存在相应的输出中
  • 创建高度(存储在R6中)保留在相应的输出中

我们说如果输出的LETS结余高于其输入的LETS结余,则某些公钥是接收器。

最后一个条件要求输入(和输出)币箱属于接收器(以便保留尔格币的数量),或者如果LETS余额为负,则输出由所需数量的尔格币支持。此外,它要求发送者的尔格币结余不能减少,直到在尔格币被锁定之后已经挖掘了minWithdrawTime的区块数量。

与托管LETS相比,上述系统有以下不同之处:

  • 没有会员记录:与托管LETS不同,我们不在此处存储任何会员信息。
  • 多个币箱:一个人可以创建多个会员币箱,这是允许的。我们只要求每个币箱都锁定最小数量的尔格币。

LETS-1:零和,有抵押

以上是我们称之为LETS-1的基本变体。 它具有以下功能:

  • 有时间限制的加入费:为了防止垃圾信息攻击,会员必须在加入时支付一定的最低费用。 此费用可退还,但仅限于预定数量的区块后。
  • 零和:所有成员币箱的LETS余额总和为零。 只要在一定限度内,会员币箱就可以有负余额。
  • 抵押品:对于发送人的输入,尔格币用作抵押品以支付当前汇率的负LETS余额。

以下是LETS-1的一些变体。

LETS-2:零和,无抵押

这是LETS-1的略微变化如下:

  • 不可退还的加入费:与LETS-1类似,需要加入费用以防止垃圾信息攻击。但是,与LETS-1不同,此费用不可退还,必须发送给某个预定义的管理委员会。
  • 零和:与LETS-1一样。

LETS-3:正和,有抵押

以上两种变体要求总LETS余额始终为零。在这里,我们考虑这个总和的正值。特别是,此变体具有以下属性:

  • 时间锁定加入费:与LETS-1相同。
  • 正和:每个成员的LETS余额必须始终为非负数。这确保了所有成员币箱的LETS余额总和为正。初始LETS余额根据当前汇率的加入费设置为正值,上限为某个最大值。
  • 抵押品:发送方的尔格币余额的任何减少必须伴随当前汇率的相应LETS余额的减少。

我们还可以通过添加等量的尔格币来允许在交易期间补足LETS余额。

LETS-4:正和,无抵押

这类似于LETS-3,但有一些小的变化:

  • 不可退还的加入费:与LETS-2相同
  • 正和:与LETS-3一样

下表总结了这些变体:

零和正和
有抵押LETS-1LETS-3
无抵押LETS-2LETS-4

分享转发

相关阅读