Closed doitian closed 1 year ago
The reason why OTX is required for payment scenarios is that CKB’s original one-time transfer transactions, such as Alice to Bob, have the following limitations:
The basic idea to solve the above two transfer restrictions is: Open Transaction, referred to as OTX. It can provide an offline collaboration mechanism that enables buyers and sellers to complete a transaction collaboratively and dynamically. It can also be said that OTX gives the payee a space for scheduling, allowing it to convert concurrent, multiple small transfers into a single transfer transaction.
Note: For UDT transfer, CoTA has its own built-in solution to the ACP cell competition problem, that is, using proof in witness. It is equivalent to giving the payee online space to schedule collections, and converting concurrent collections into serial collections. This is somewhat similar to OTX.
This document gives a user case in the payment scenario, and considers the form and mechanism of OTX by solving the problems encountered.
OTX not only solves the problem of payment scenarios, it should be a more general mechanism and standard, and hopes to be widely adopted by wallets, exchanges and dApps in the future. The final OTX standard and mechanism is the result of the integration and further abstraction of various common application scenarios PoC. It should be generic.
In the payment scenario, at least the following questions should be raised and considered:
Basic standard:
Known issues:
Error handling:
Alice, Bob and Carol are all users of video content provider Z. The three of them don't know each other, but both Alice and Bob choose to renew their memberships on Double 11, and Carol also wants to buy a movie that has just been released on this day.
Z's payment system is built on CKB and supports CKB payment. The platform has also established a UDT-based reward points system and supports UDT renewals. The membership price on Double 11 is 50 CKB per year, or 50 UDT per year. Z platform also supports "CKB to buy reward points" or "reward points to CKB".
Alice clicks the renewal button on the TV, and a payment QR code pops up. Then Alice took out her mobile wallet and scanned the QR code to confirm the payment of 50 CKB and 1 CKB transaction fee. At this time, the TV screen displays "Payment in progress", and the mobile wallet displays "Waiting for the transaction to be confirmed on the chain" > "Transaction confirmed on the chain progress 1/24" > ... > "Payment completed". The TV screen then shows that the payment is completed, and Alice's membership expiration time is updated.
Bob obtained 60 UDTs by completing multiple game tasks on the Z platform. But he didn't buy CKB, not even a mobile wallet. Bob clicks the UDT renewal button on the webpage, and confirms the payment of 50 UDT and 1 UDT transaction fee. At this time, a dialog box pops up: "Wait" > "Progress Confirmation 1/24" > ... > "Payment Complete". At this time, Bob sees that his membership validity period has been updated.
The movie that Carol wants to buy is more discounted with UDT reward points, but he still lacks 1 UDT. So he entered the "CKB buy UDT" page. He scanned the pop-up QR code with his mobile wallet and confirmed the payment of buying 1 UDT with 1 CKB without transaction fees.
In this user case, a perfect result is that there is only one transaction on the chain, and its form is:
{
inputs: [
{capacity: 151, data: "", type: "", lock: Alice},
{capacity: 144, data: 51, type: xudt z, lock: Bob},
{capacity: 145, data: 9, type: xudt z, lock: Carol},
{capacity: 142, data: 100, type: xudt z, lock: Z}
],
outputs: [
{capacity: 151-51, data: "", type: "", lock: Alice},
{capacity: 144, data: 51-51, type: xudt z, lock: Bob},
{capacity: 145-1, data: 9+1, type: xudt z, lock: Carol},
{capacity: 142+50+1, data: 100+50, type: xudt z, lock: Z} ]
}
This transaction shows that platform Z received 51 CKB and 50 UDT, and the miner received 1 CKB. Platform Z aggregated the OTXs of Alice, bob and carol, and gave one of the 2 ckb transaction fees to miner, and left one CKB for itself as a reward for aggregating otx.
It should be noted that the transaction fee in OTX is not for miners, but for the party that aggregates OTXs. How to aggregate OTXs and how much transaction fees to miners are determined by the OTXs aggregator.
At this point we may all be wondering, what should Alice, Bob, and Carol submit OTX look like?
In the payment scenario above, the QR code is dynamically generated by platform Z based on the user's purchase information, and must contain at least the following information:
tx_hash_call_back_url
is mainly for ordinary payment transfers, that is, transfers of more than 61 CKB. Users do not need to use OTX, but need to return tx hash so that merchants can associate orders.
Alice's mobile wallet scans the QR code of the Z platform to obtain the following information:
order_id:Z0x0001
asset_type: CKB
amount:50
address:Z lock
tx_type:OTX
tx_hash_call_back: null
otx_fee:1
otx_url:https://z.tv/otx
The simplest OTX that Alice can submit is:
{
inputs: [
{capacity: 151, data: "", type: "", lock: otx Alice}
],
outputs: [
{capacity: 151-51, data: "", type: "", lock: otx Alice}
]
}
In the above OTX, the constraint on payee output is missing. If Alice's signature does not cover payee's lock, once OTX is leaked outside the Z platform, there is still the possibility that Alice's assets will be spent by others.
Payee information needs to be added to constrain OTX. We already know that a payment of less than 61 CKB cannot generate a payee output cell alone. Why avoid using payee's ACP cell is also explained in Another solution in the End-to-end Payment scenario. Then we need to find another way.
What we hope is to be able to specify the lock of the payee and require him or her to play a leading role in the process of aggregating multiple Otxs and building a complete TX. If the lock script of the payer in OTX can not only require verification of OTX itself, but also verify some of its requirements for Full TX, such as requiring payee to fully sign the entire transaction, it may be an idea.
Different from expressing the payee information through the output cell of OTX, We can directly add these requirements in the witness corresponding to the input of the payer.
We can add an additional data structure otx_sighash
in the Lock
part of the OTX witness, otx_sighash
has 3 fields:
Witnesses:
WitnessArgs structure:
Lock:
otx_sighash:
base input index: 0
base output index: 0
hash_array: <input outpoint and output>
otx_pay: 50 CKB
payee_address: <address Z>
signature: <otx signature for pubkey hash Alice>
<...>
The verification logic of Payment OTX Lock is:
otx_sighash
. Bob's payment information is as follows:
order_id:Z0x0002
asset_type: UDT
amount:50 UDT
address:Z lock
tx_type:OTX
tx_hash_call_back: null
otx_fee:1 UDT
otx_url:https://z.tv/otx
Bob's OTX is:
{
inputs: [
{capacity: 144, data: 51, type: xudt z, lock: otx Bob}
],
outputs: [
{capacity: 144, data: 51-51, type: xudt z, lock: otx Bob}
]
}
Similar to Alice's Otx, the witness corresponding to Bob's input is:
Witnesses:
WitnessArgs structure:
Lock:
otx_sighash:
base input index: 0
base output index: 0
hash_array: <input outpoint and output>
otx_pay: 50 UDT
payee_address: <address Z>
signature: <otx signature for pubkey hash Bob>
<...>
Carol's payment information is as follows:
order_id:Z0x0003
asset_type: CKB
amount:1 CKB
address:Z lock
tx_type:OTX
tx_hash_call_back: null
otx_fee:0 CKB
otx_url:https://z.tv/otx
Carol's OTX is:
{
inputs: [
{capacity: 145, data: 9, type: xudt z, lock: Carol}
],
outputs: [
{capacity: 145-1, data: 9+1, type: xudt z, lock: Carol}
]
}
The witness corresponding to Carol's input is:
Witnesses:
WitnessArgs structure:
Lock:
otx_sighash:
base input index: 0
base output index: 0
hash_array: <input outpoint and output>
otx_pay: 1 CKB
payee_address: <address Z>
signature: <otx signature for pubkey hash Carol>
<...>
After Z's service receives the OTX submission, it first associates the order. If the OTX payment is consistent with the order, put the OTX in the transaction pool. Otherwise, an error and reason are returned in the current link.
The aggregator (Builder) starts scanning the OTX transaction pool for aggregation according to a certain strategy, such as a fixed interval. It collects Alice, Bob, and Carol's OTXs and rearranges them, then adds its own inputs and outputs for change. In this way, the unsigned TX is built and enters the signature queue.
Signer fully signs the entire transaction according to the requirements of the OTX lock, and then the signed transaction enters the submission queue.
The Commiter broadcasts the full TX to the CKB node. After confirmation, it will modify the status of the order and perform corresponding shipping actions.
{
inputs: [
{capacity: 151, data: "", type: "", lock: Alice},
{capacity: 144, data: 51, type: xudt z, lock: Bob},
{capacity: 145, data: 9, type: xudt z, lock: Carol},
{capacity: 142, data: 100, type: xudt z, lock: Z}
],
outputs: [
{capacity: 151-51, data: "", type: "", lock: Alice},
{capacity: 144, data: 51-51, type: xudt z, lock: Bob},
{capacity: 145-1, data: 9+1, type: xudt z, lock: Carol},
{capacity: 142+50+1, data: 100+50, type: xudt z, lock: Z} ]
}
Witnesses:
WitnessArgs structure:
Lock:
signature: <valid secp256k1 signature for pubkey hash Z>
<...>
from discussion 3671: What's the Typical Workflow to Use CKB Open Transaction :
- Maker: Who publishes the open transaction template.
- Taker: Who take the template published by Maker and publishes the open transaction to network.
- Builder: The service to collect matched open transactions in the network and create the final transaction or partial signed transaction.
- Signer: The service which watches partial signed transaction and queue the requests to the user. The user approves the request to adds her signature.
- Committer: The service that commits final transaction to CKB. It will re-create the transaction or re-broadcast the open transactions when the commitment fails.
In the definition above:
OTX format, there are two options:
In this article, the fee can be calculated indirectly by putting the otx_pay
field in the witness.
Different from ordinary CKB TX Fee, in the solution of this article, the transaction fee of OTX is given to the aggregator, not the miner.
Alice, Bob and Carol may hope that if the OTX initiated by them is not successfully aggregated after a period of time, they can withdraw the OTX or invalidate the OTX.
A simple solution is that the payer's wallet can spend the input in OTX to recover the assets after a set period. But such an approach is not friendly to payee. The payee may not know until it is prompted by the CKB node for double spending when the transaction is submitted.
The ideal expiration mechanism is:
There is also an opposite way of thinking: the payer is not allowed to withdraw, but this requires the support of the state on the chain, similar to CoTA.
Need to continue to find solutions.
This article proposes that the lock of payer in OTX will require payee to fully sign the transaction.
In this post CKBytes in UDT transfer if the payer double-spends the cell in OTX within the validity period, the merchant aggregation fails , then the payee can pull it into the blacklist.
otx-payment-demo, which is omni lock based:
The demo aggregates 3 OTXs, which are as follows
{
inputs: [
{capacity: 151, data: "", type: "", lock: Alice} ],
outputs: [
{capacity: 151-51, data: "", type: "", lock: Alice} ]
}
{
inputs: [
{capacity: 144, data: 51, type: xudt z, lock: Bob} ],
outputs: [
{capacity: 144, data: 51-51, type: xudt z, lock: Bob} ]
}
{
inputs: [
{capacity: 100, data: "", type: "", lock: Carol},
{capacity: 144, data: 9, type: xudt z, lock: Carol} ],
outputs: [
{capacity: 100-1, data: "", type: "", lock: Carol},
{capacity: 144, data: 9+1, type: xudt z, lock: Carol} ]
}
the final full TX structure is:
{
inputs: [
{capacity: 151, data: "", type: "", lock: Alice},
{capacity: 144, data: 51, type: xudt z, lock: Bob},
{capacity: 100, data: "", type: "", lock: Carol},
{capacity: 144, data: 9, type: xudt z, lock: Carol},
{capacity: 142, data: 100, type: xudt z, lock: Z}
],
outputs: [
{capacity: 151-51, data: "", type: "", lock: Alice},
{capacity: 144, data: 51-51, type: xudt z, lock: Bob},
{capacity: 100-1, data: "", type: "", lock: Carol},
{capacity: 144, data: 9+1, type: xudt z, lock: Carol},
{capacity: 142+50+1, data: 100+50, type: xudt z, lock: Z} ]
}
Requirement: #3680