UDT 交易



  • UDT 存储在 cells 中,这就要求它的所有者也需要持有一些 CKByte。因此 UDT 持有者也是 CKB 持有者。在交易前,Alice 持有 Z 个 UDTs 和 X 个 CKBytes,UDTs 存储在 CKBytes 中。在交易后,Bob 将拥有 Z 个 UDTs 和 Y 个 CKBytes,而 UDTs 同样存储在这些 CKBytes。

    CKBytes 的存储功能解决了状态爆炸的问题,但也使得 UDT 交易变得更加复杂。在这里,我描述了几种具有不同优缺点的设计模式,希望可以引起大家对 UDT 这种特殊设计的认识。

    1. 捆绑转让

    Alice 将 UDTs 连同包含该 UDT 的 cell 一同转给 Bob。

    假设我们要将 Z 个 bananas 币从 Alice 转账给 Bob。Alice 有一个 X CKBytes 的 cell 包含 Z 个 bananas 币。交易是这样的:

    {
        inputs: [
            {capacity: X, data: Z, type: Banana, lock: Alice}
        ],
        outputs: [
            {capacity: X, data: Z, type: Banana, lock: Bob}
        ]
    }
    

    在这里例子中,X 个 CKBytes 和 Z 个 bananas 币是同时转账给 Bob 的。这是最简单的模式,但在大部分情况下这样的设计对用户并不友好。

    2. 接收 Cell

    在此模式中,Bob 会预先准备一个接收 cell。它的 lock user_owner(Alice,Bob) 允许 Alice 将这个接收 cell 作为 input,只要 Alice 创建的 output 具有相同的 capacity 和一个特定的 lock (比如 Bob 的 lock)。Alice 在 Bob 的接收 cell 中只能写入 UDT 数据。

    {
        inputs: [
            {capacity: X, data: Z, type: Banana, lock: Alice},
            {capacity: Y, data: "", type: "", lock: user_owner(Alice, Bob)}
        ],
        outputs: [
            {capacity: X, data: "", type: "", lock: Alice},
            {capacity: Y, data: Z, type: Banana, lock: Bob}
        ]
    }
    

    这里的优点是 Alice 在 UDT 交易中,不需要放弃自己的 CKBytes。而缺点就是 Bob 需要提前获取一些 CKBytes。

    3. 共享接收 Cell

    在一个 n-pay-one(多方向一方进行支付)的场景中,比如向商家付款或者向交易所存款。我们可以提供方案 2 中接收方的 CKBytes 使用效率,这样接收方就不需要为每个用户准备单独的接收 cell。

    在一开始,Bob 显示需要支付多少 bananas 币的信息(可能编码在条形码中)以及如何与 Alice 建立一个链下通道。Alice 创建一个 open transaction,将她的 bananas 币作为输入,一个空的找零作为输出,然后通过已经建立的链下通道将这个 open transaction 发送给 Bob。Bob 在一段时间内(比如 5 秒)从多个用户这边收集这些 open transactions,并将这些 open transactions 和一个额外的共享接收 cell 聚合到一笔交易中去。

    // Alice's open transaction, off-chain
    {
        inputs: [
            {capacity: X1, data: Z1, type: Banana, lock: Alice}
        ],
        outputs: [
            {capacity: X1, data: "", type: "", lock: Alice}
        ]
    }
    
    // Charlie's open transaction, off-chain
    {
        inputs: [
            {capacity: X2, data: Z2, type: Banana, lock: Charlie}
        ],
        outputs: [
            {capacity: X2, data: "", type: "", lock: Charlie}
        ]
    }
    
    // Bob's aggregated transaction, on-chain
    {
        inputs: [
            {capacity: X1, data: Z1, type: Banana, lock: Alice},
            {capacity: X2, data: Z2, type: Banana, lock: Charlie},
            {capacity: Y, data: "", type: "", lock: Bob}
        ],
        outputs: [
            {capacity: X1, data: "", type: "", lock: Alice},
            {capacity: X2, data: "", type: "", lock: Charlie},
            {capacity: Y, data: Z1+Z2, type: Banana, lock: Bob}
        ]
    }
    

    请注意,open transaction 和它的链下传输是技术细节,这些是可以对用户隐藏的。对于用户来说,他们不需要知道钱包发送给商家的是一笔普通的链上交易还是一笔 open transaction。对于用户而言,使用体验是相同的。

    这样的模式不仅减少了商家对于 CKByte 的需求,还减少了链上交易的数量,有效地提高了网络的吞吐量。

    Griefing 攻击是一个问题:如果一个用户试图在 open transaction 中双花他/她的 input,那么聚合交易将会失败。当然如果收款人知道付款人的身份,一旦发生这样的敌对行为,收款人可以很容易地将付款人移入黑名单中。

    4. 多签收集

    Alice 将她的 bananas 币发送到一个 lock 是 user_owner(Bob,Alice) 的 1-of-2 多签输出中,该输出赋予 Alice 完全控制权以及 Bob 特权来修改它的数据和类型。Bob 扫描每一个区块,然后收集这些多签 cells 中的 bananas 币存储到他自己的 cell 中。

    // Alice's payment
    {
        inputs: [
            {capacity: X1, data: Z1, type: Banana, lock: Alice}
        ],
        outputs: [
            {capacity: X1, data: Z1, type: Banana, lock: user_owner(Bob, Alice)}
        ]
    }
    
    // Charlie's payment
    {
        inputs: [
            {capacity: X2, data: Z2, type: Banana, lock: Charlie}
        ],
        outputs: [
            {capacity: X2, data: Z2, type: Banana, lock: user_owner(Bob, Charlie)}
        ]
    }
    
    // Bob's collect transaction, refers to above two transactions
    {
        inputs: [
            {capacity: X1, data: Z1, type: Banana, lock: user_owner(Bob, Alice)},
            {capacity: X2, data: Z2, type: Banana, lock: user_owner(Bob, Charlie)},
            {capacity: Y, data: "", type: "", lock: Bob}
        ],
        outputs: [
            {capacity: X1, data: "", type: "", lock: user_owner(Bob, Alice)},
            {capacity: X2, data: "", type: "", lock: user_owner(Bob, Charlie)},
            {capacity: Y, data: Z1+Z2, type: Banana, lock: Bob}
        ]
    }
    

    这种模式是方案 3 的变体,使用 CKB 而不是链下通道作为通信通道,使得上述的 griefing 攻击不可能发生。因为转账有两个阶段,只有在 Bob 的收集交易在链上完成(并得到 X 个区块的确认)后,才能被视为完成。

    使用两个阶段完成转账的好处是,你不可能失去你的资金。如果 Alice 使用了一个错误的地址,她可以简单地将自己的 bananas 币收回,因为她总是可以访问多签输出。

    5. 时间锁-多签收集

    如果我们在方案 4 中,在 user_owner(Bob,Alice) lock 中加入一个时间锁,Bob 可以确保 Alice 不能再时间 T 之前取回她的 bananas 币。


Log in to reply