This scheme builds upon the original Cobuild design, modifying and supplementing it as needed. To understand this plan, please first review the foundational Cobuild scheme.
The main focus here is on simplifying the implementation of dynamic OpenTransactions (OTx) to address intent expressions and stateful contract call simulations in the UTXO model.
DEMO(inprogress) : ckb-cobuild-poc (github.com)
First, regarding the data structure of OTx, the following modifications have been made:
// A complete CKB Transaction must contain only one OtxStart structure, which denotes the start
// of consecutive OTx within the CKB Transaction. OtxStart is marked in input/output cells, as well as in
// cell/header deps, indicating the position of the first component belonging to an OTx.
// It does not need to mark the start of witness, as OtxStart might already denote the start of the
// first OTx-related component within the witness.
//
// Following the OtxStart structure, the witness data will contain several OTx structures, each
// corresponding to an OTx included in the current CKB Transaction.
//
// Depending on the specific position of OtxStart within the witnesses array, the OtxStart structure itself
// might not be covered by any signature in the CKB Transaction.
table OtxStart {
start_input_cell: Uint32,
start_output_cell: Uint32,
start_cell_deps: Uint32,
start_header_deps: Uint32,
}
table SealPair {
script_hash: Byte32,
seal: ByteVec,
}
vector Seals <SealPair>;
// Otx represents a specific OTx structure, and for one OTx, it exclusively has one witness presented
// as an Otx structure. The position of the witness represented as Otx in the CKB Transaction does not have
// a fixed relationship with the positions of the input/output cells contained in that OTx.
table Otx {
// An Otx, for example, might have 3 input cells, and these input cells are not necessarily under the same lock.
// They could be under different lock scripts, each signed with a different signing algorithm.
// As there is only one witness for an OTx, we need to include multiple signatures from different lock scripts
// in this single witness. Hence, we use seals here, where the use of SealPair is similar to Action,
// matching first by script hash, and then extracting the Bytes type lock field from the SealPair.
seals: Seals,
// The flag will determine whether the following items are allowed to have dynamic parts
// When flag & 0b00000001 == 1, the number of input_cells can vary
// When flag & 0b00000010 == 1, the number of output_cells can vary
// When flag & 0b00000100 == 1, the number of cell_deps can vary
// When flag & 0b00001000 == 1, the number of header_deps can vary
// flag & 0b11110000 must equal 0, corresponding to four reserved bits
flag: byte,
// fixed_input_cells indicate the number of fixed input cells in the current Otx.
// output_cells / cell_deps / header_deps are similar
fixed_input_cells: Uint32,
fixed_output_cells: Uint32,
fixed_cell_deps: Uint32,
fixed_header_deps: Uint32,
message: Message,
// If the flag allows dynamic properties, additional quantities can be dynamically appended during transaction assembly,
// and both quantity and dynamically appended values are not covered by signatures.
dynamic_input_cells: Uint32,
dynamic_output_cells: Uint32,
dynamic_cell_deps: Uint32,
dynamic_header_deps: Uint32,
}
If the flag indicates that a feature is not allowed to be dynamic, then the dynamic count for that feature must be zero.
Among these, flag, fixed_input_cells, fixed_output_cells, fixed_cell_deps, fixed_header_deps, message
are covered by the signing_hash of the lock within the range of fixed_input_cells; the seals of the lock within dynamic_input_cells covers, in addition to the fixed parts, the dynamic inputs.
When searching for the seal corresponding to a lock in SealPair, for locks in fixed_input_cells, search from the front; for locks in dynamic_input_cells, search from the back. If a lock appears in both areas, it will correspond to two seals, with the seal for fixed_input appearing first and the seal for dynamic_input appearing last.
Another change targets the Cobuild’s Action, where an additional item, script_location
, is added to the original Action structure to specify the script’s transaction location—input_lock, input_type, output_type—allowing for more refined control.
table Action {
script_info_hash: Byte32, // script info
script_location: byte, // script_location 0-input_lock, 1-input_type, 2-output_type
script_hash: Byte32, // script hash
data: Bytes, // action data
}
When OTx’s corresponding properties allow dynamic addition, data can be dynamically appended during transaction assembly.
A simple example:
inputs:
Capacity Cell
outputs:
<empty>
Witness:
message:
dob_mint_script_hash
mint_dynamic_dob
outputs can be dynamically added
In this example, the user wants to use the dob_mint_script to perform DOB Mint, where the corresponding outputs are empty.
Once the Aggregator receives the corresponding OTx, it can dynamically append outputs and mint according to the following rules: when processing an OTx, if the minting serial number is a multiple of 17, then mint a DOB of rarity 1; if the minting serial number is a multiple of 29, then mint a DOB of rarity 2; otherwise, mint a normal DOB of rarity 0.
During minting, a certain amount of CKB is deducted as a fee.
The Aggregator completes the corresponding DOB output and change Cell according to the rules, and adjusts the number of dynamic_output_cells to match the actual situation.
The corresponding mint script should check:
- The correctness of the mint logic, i.e., the Aggregator produced the corresponding outputs according to the rules.
- Consistency of the Capacity change, i.e., the Capacity belonging to the user after processing should be returned to the user in the outputs.