Non-blocking Chained Transaction and its applications in CKB

1. Introduction

Due to the off-chain determinism of the Cell model, even if the dependent cell is not confirmed, a chained transaction can be constructed and then quickly submitted to the transaction pool without waiting.

This is useful in many scenarios, for example, many people accuse “AMM in the blockchain of the UTXO model can only have at most one transaction per block”, but this is not correct, when Layer1 guarantees the validity of the transaction, but the transaction ordering is handled by another consensus mechanism (PoS), the AMM experience for UTXO can be as smooth as the account model.

I have a rough look at the transaction verification code of ckb. ckb supports transactions referencing cells in the pending state, which makes it possible to use this cell immediately without waiting for a new cell to be generated, so that the transaction does not need to block to wait for the confirmation of the previous transaction.

To verify ckb’s support for chained transactions, I used rust-sdk to send many chained transactions to ckb node in testnet, but currently many checks of rust-sdk hinder this, I had to remove many checks and add some code, and finally successfully sent a non-blocking chain transaction on CKB.

In the following six transactions, the three input cells of each transaction depend on the output of the previous transaction, but they are packaged into the same block 5586924.

0xdf4e50350b325964177f66034dd9d348901d7e3f87c71e04f87eef5bcf1cf0e3

0x75860ab2c8214cef99d045683565a7e9321b5009c320ed01f8bd29041a566f3d

0x98fd512e5fe9ab4535a430e618cf73acf9ad0bb61f229b2cb5f2e020ba3bbc37

0xbaa24d56fef4f956b0725a23a8731fdd0512f0de4df76e82a57f242dfbcd7f7f

0x992f11a0bd81a2ab3a7f3ac045e6dbae885835f70422572daa8b1ca5bdcb7cc0

0x02d5c2005fe2a68a5b043764ac1ee3965c7baa6c2033951c83853202833f3742

In addition to the fact that rust-sdk is not well adapted to the construction and transmission of chained-tx, because the the input cell at least exists in the pending pool, I can only send transactions serially, which does not achieve perfect non-blocking.

2. Applications

2.1 Fee Provider

In some dApps, the service provider may pay the tx fee for all users. If the number of users using at the same time is greater than the number of cells currently available to the service provider, when Non-blocking chained tx is not used, some users may have larger delay, and with Non-blocking Chained Tx, even if only one cell provides tx fees, it can give users silky feedback.

2.2 NFT buying and exchange withdraw

When using the NFT platform, users may deposit a large amount of assets in a single cell for the purchase of NFTs. When chained tx is not used, if users quickly purchase multiple NFTs, it will appear that the account balance is sufficient but cannot be used, which will make users very confused and will greatly affect the experience of using the Cell model, while using chained tx will not Will have this trouble again. Just like using Metamask, Metamask caches the user’s nonce, so that the user can continue to send transactions to ETH without waiting for the completion of the previous transaction. Using ChainedTX has the same experience.

Another similar scenario is the withdrawal of the exchange. Since the exchange often collects funds, a large amount of funds often exist in a few cells. In addition to constructing multiple outputs within a transaction, ChainedTX can also be used to speed up withdrawals.

2.3 Global State

Although intuitively, the cell model has no global state, but after using chainedTX, if a certain consensus mechanism (such as DPoS, PoS) is adopted, the global state can be perfectly realized. Such a global state design can be a timestamp that an application depends on, or it can be a global state where users help the application calculate interest (for example, a ckb renting dapp).

2.4 Access List and account model

Through ChainedTX plus access list and an additional consensus mechanism, various account models can be simulated on the cell model, and there is no problem of “one block can only have one transaction”, which will enable Defi applications such as swap and lending available on layer1 for everyone to use.

2.5 Speed up rollup confirmation

For Layer 1.5 dApps and Rollup, since the transactions on them are decoupled from Layer 1, their block generation speed is often faster than that of Layer 1. Through ChainedTX, such applications can generate blocks without blocking, while there is no need to consider the block rhythm of Layer1.

At the same time, when the block is reorganized and the transaction is not replayed, this type of application can also use ChainedTX to speed up the confirmation speed of its block.

3. Suggestions

3.1 Optimize RPC methods

When testing ChainedTX, I can only send the next transaction after the previous transaction is verified, so that ChainedTX can only be verified serially, but this is not logically necessary.

If there is a send_chained_transaction rpc method that can send transactions in batches at a time, then these transactions can be verified in parallel, thereby speeding up the submission of ChainedTX. At the same time, if the application adopts such transactions on a large scale, it is also very good to optimize the broadcast of such transactions in the P2P layer, but adding an rpc method does not require a hard fork, so it is better to implement, while modifying the P2P layer may need to introduce hard fork.

3.2 ChainedTX friendly SDK

The current SDK, at least the Rust SDK I use, can’t successfully build ChainedTX. I don’t know if the SDKs of other languages are optimized at this point, but I think this is a very important feature, in order to allow dApps to use it, the SDK must first be well supported.

3.3 dApps should use chainedTx to optimize the experience

After the SDK supports ChainedTx well, dApps should try to adopt the design of ChainedTX to optimize the user experience. Of course, it stands to reason that this should be done by Signer, just like in the ETH ecosystem, nonce is cached in metamask.

2 Likes

1. 介绍

由于Cell模型的链下确定性,在未上链的时候,就可以构建一个链式交易,然后快速提交到交易池,而无需等待Cell完全Confirmed。这在许多场景都是有用的,比如说许多人指责“UTXO模型的区块链里的AMM一个区块最多只能有一笔交易”,但这并不正确,当设计一个AMM,让交易的安全性验证交给底层链,而把交易排序交给另一个准入机制的时候,UTXO的AMM体验可以和账户模型一样丝滑。

我粗略过了一遍CKB的交易验证代码,CKB支持交易引用pending状态的Cell,这使得无需等待新Cell生成,可以立刻使用这个Cell,从而使交易无需阻塞等待上一笔交易的敲定,为了验证CKB对链式交易的支持,我使用rust-sdk往CKB上发送了许多笔链式交易,但是当前rust-sdk的许多检查阻碍了这一点,我不得不删去许多检查,同时添加一些代码,最后成功在CKB上发送了无阻塞的链式交易。

下面六笔交易,每一笔交易的三个Cell的输入都依赖于前一笔的输出,但是它们被打包进了同一个区块5586924

0xdf4e50350b325964177f66034dd9d348901d7e3f87c71e04f87eef5bcf1cf0e3

0x75860ab2c8214cef99d045683565a7e9321b5009c320ed01f8bd29041a566f3d

0x98fd512e5fe9ab4535a430e618cf73acf9ad0bb61f229b2cb5f2e020ba3bbc37

0xbaa24d56fef4f956b0725a23a8731fdd0512f0de4df76e82a57f242dfbcd7f7f

0x992f11a0bd81a2ab3a7f3ac045e6dbae885835f70422572daa8b1ca5bdcb7cc0

0x02d5c2005fe2a68a5b043764ac1ee3965c7baa6c2033951c83853202833f3742

除了rust-sdk未良好适配chained-tx的构造与发送之外,由于引用或者输入的Cell至少存在于pending池中,导致我只能串行发送交易,没有达到完美的非阻塞。

2. 应用

2.1 手续费提供者

在某些应用中,应用服务商可能会为所有的用户代付手续费,如果同时使用的用户大于服务商当前可用的Cell数,在不使用Non-blocking Chained Tx时,对于某些用户可能会有较大的delay,而借助Non-blocking Chained Tx,即使只有一个cell提供手续费,也可以给用户丝滑的反馈。

2.2 NFT购买和交易所提款

在使用NFT平台时,用户可能会一次往平台地址充值大额资金,存储在单个Cell内,在未使用Chained TX时,用户快速购买多个NFT,将会出现令用户十分疑惑的现象,明明账户余额充足,当前却无法使用,这将大大影响Cell模型的使用体验,而使用了Chained TX则不会再有这个困扰。就像在使用Metamask一样,Metamask缓存了用户的nonce,从而使用用户无需等待上一笔交易完成,即可持续往ETH发送交易,使用ChainedTX拥有同样的体验。

另一个与之类似的场景是交易所的提现,由于交易所经常会进行资金归集,从而使得大量资金往往存在于少数几个Cell中,在有大量用户提款时,除了可以使用在一笔交易内构造多个输出外,也可以使用ChainedTX以加速提款。

2.3 全局状态

在使用ChainedTX后,如果再采用一定的准入机制(如DPoS,PoS),则可以完美得实现全局状态,它可以是某个应用依赖得时间戳,也可以是某个租赁平台辅助利息计算的全局状态(如CKB租赁)。

2.4 访问列表和账户模型

通过ChainedTX加上Access List以及一个额外的准入机制,可以在Cell模型上模拟各种账户模型,同时并不存在”一个区块最多一笔交易“的问题,这将使得swap、借贷等多种Defi应用可以存在于Layer1上。

2.5 加速Rollup的确认

对于Layer1.5以及Rollup类型的Dapp来说,由于其上的交易是与Layer1解耦的,则它的出块速度往往快于Layer1,通过ChainedTX,可以使得该类应用在链下无阻塞地出块,而无需考虑Layer1的出块节奏。

同时,在区块重组同时交易没有重放的时候,这类应用也可以使用ChainedTX加速其区块的确认速度。

3. 建议

3.1 优化RPC方法

在测试ChainedTX时,我只能等待上一笔交易通过验证以后,才能发送下一笔交易,导致ChainedTX只能被串行验证,但这并不是逻辑上必须的,如果存在一个send_chained_transaction的rpc方法,可以一次批量发送交易,那么这些交易是可以被并行验证,从而加速ChainedTX的提交的,同时如果应用大范围采用这类交易,那么在P2P层优化这类交易的广播也是可以的,不过增加rpc接口并不需要硬分叉,所以比较好实施,而修改P2P层则可能需要引入硬分叉。

3.2 Chained Tx友好的SDK

目前的SDK,至少我使用的Rust SDK并不能成功构建出ChainedTX,我不知道其他语言的sdk是否在这一点上做了优化,不过我觉得这是一个很重要的特性,为了能让Dapps使用上,SDK必须首先支持得很好才行。

3.3 dApps应采用以优化体验

在SDK很好地支持ChainedTx以后,dApps应尽量采用ChainedTX的设计以优化用户体验,当然,按理说这件事应该交由Signer来完成,就像在ETH生态,nonce是在metamask里缓存的一样。

1 Like

Agree. I think off-chain transaction chain construction is a very useful pattern for CKB dapps. RPC and SDK developers should keep this use case in mind and provide support for chained transactions construction and broadcast.

If CKB can loosen the check in send_transaction and allow the transaction to refer to the cells in the transaction pool (off-chain transaction), it could solve parts of the problem. But CKB may need to take more care about miner’s strategies regarding how to order or choose transaction in each block period

CKB already allows refer to cells in the transaction pool, and I have constructed such transaction samples in the article. But to fully utilize this design requires more support.

This is ground-breaking: seems simple to integrate and with possibly a ten-fold UX improvement, brilliant!! :clap:

@orange-xc I definitely have a use case for Chained Transactions in the iCKB deposit phase, what’s the current status? Is there any Github issue where I can keep track of this?

CKB itself supports this mode ( CKB Fee Bumping Mechanism Proposal - HackMD), but the sdk may not support it. You need to determine which language sdk to use first, and then make some modifications, including caching the unconfirmed cells locally and using them as the input of the transaction.

I modified ckb-sdk-rust ( nervosnetwork/ckb-sdk-rust: Rust SDK for Nervos CKB (github.com)) for testing, and it needs minimal modifications to support chained-tx.

xcshuan/chained_tx (github.com)

xcshuan/ckb-sdk-rust: Rust SDK for Nervos CKB (github.com)

there is a simpe example of chained-tx.

1 Like

I see why it’s not yet integrated, while those checks prevent chained transactions, they are in place to prevent more common developer errors.

A naive solution would be to add a global variable or flags to function calls to allow for these modifications when needed, but the result wouldn’t be pretty.

A more realistic solution would be to just soften the checks so that an input cell can be either already committed or in the pool waiting to be committed.

I think this should be a feature of all ckb-sdk, use a middleware to handle all of this, but I don’t know the plan that when the feature added to sdk.

1 Like

any updates for the chained txs until now? this is a really practical feature to most of DApps on CKB

Yes, SDK developers are implementing this feature now. check:
Indexer Support for Chained Transactions · Issue #3950 · nervosnetwork/ckb (github.com)
Support Chained Transactions · Issue #501 · ckb-js/lumos (github.com)