Extensible UDT
Extensible UDT(xUDT) is an extension on Simple UDT for defining more behaviors a UDT might need. While simple UDT provides a minimal core for issuing UDTs on Nervos CKB, extensible UDT builds on top of simple UDT for more potential needs, such as regulations.
Data Structure
xUDT Cell
An xUDT cell is backwards compatible with Simple UDT, all the existing rules defined in the Simple UDT spec must still hold true for xUDT cells. On top of sUDT, xUDT extends a cell like following:
data:
<amount: uint128> <xudt data>
type:
code_hash: extensible_udt type script
args: <owner lock script hash> <xudt args>
lock:
<user_defined>
The added xudt args and xudt data parts provide all the new functions needed by xUDT, we will explain the detailed structure below:
xUDT Args
xUDT args has the following structure:
| 4 byte xUDT flags | Variable length extension data |
|---|
Depending on the content of flags, different extension data might be attached:
- If
flagsis all 0, we won’t need any extension data. Note a backward-compatible way of viewing things, is that a plain sUDT cell also has a hiddenflagsfield with all zeros. - If
flagsis 0x1, extension data will contain a molecule serializedScriptVecstructure:
table Script {
code_hash: Byte32,
hash_type: byte,
args: Bytes,
}
vector ScriptVec <Script>;
Each entry included in ScriptVec structure is interpreted as a CKB script hash for an extension script with additional behaviors. When an xUDT script is executed, it will run through each included extension script here. Only when all extension scripts pass validation, will xUDT also consider the validation to be successful.
An extension script can be loaded in any of the following ways:
- Some extension logics might have predefined hash, for example, we can use
0x0000 ... 0001to represent regulation extension. The actual code for such scripts can be embedded in xUDT script itself. - Owner lock can be used: if an input cell in current transaction uses a lock script with the same script hash as current extension script, we can consider the extension script to be validated already.
- If an extension script does not match any of the above criteria, xUDT will use the code_hash and hash_type included in the extension script to invoke ckb_dlopen2 function, hoping to load a dynamically linked script from cell deps in current transaction. If a script can be located successfully, xUDT will then look for an exported function with the following signature:
int validate(int is_owner_mode, size_t extension_index, const uint8_t* args, size_t args_length);
is_owner_mode indicates if current xUDT is unlocked via owner mode(as described by sUDT), extension_index refers to the index of current extension in the ScriptVec structure. args and args_length are set to the script args included in Script structure of current extension script.
If this function returns 0, the validation for current extension script is consider successful.
- If
flagsis 0x2, extension data will contain the blake160 hash of theScriptVecstructure as explained in previous section. The actualScriptVecstructure data will be included in a witness contained in the current transaction. We will explain this part below.
xUDT Data
xUDT data is a molecule serialized XUDTData structure:
vector Bytes <byte>;
vector BytesVec <Bytes>;
table XUDTData {
lock: Bytes;
data: BytesVec;
}
The data field included in XUDTData, must be of the same length as ScriptVec structure included in xUDT args. Some extensions might require user specific data stored in each xUDT cell. xUDT data provides a place for such data.
The lock field included in XUDTData will not be used by xUDT script. It is reserved for lock script specific data for current cells.;
An extension script will first need to first locate the index it resides in xUDT args, then look for the data for current extension script at the same index in data field of XUDTData structure.
Operations
xUDT uses the same governance operations as Simple UDT: an owner lock controls all governance operations, such as minting.
A normal transfer operation of xUDT, however, differs from Simple UDT. Depending on the flags used, there might be 2 usage pattern:
Raw Extension Script
When flags is set to 0x1, raw extension data is included in xUDT args directly.
Inputs:
<vec> xUDT_Cell
Data:
<amount: uint128> <xudt data>
Type:
code_hash: extensible_udt type script
args: <owner lock script hash> <xudt args>
Lock:
<user defined>
<...>
Outputs:
<vec> xUDT_Cell
Data:
<amount: uint128> <xudt data>
Type:
code_hash: extensible_udt type script
args: <owner lock script hash> <xudt args>
Lock:
<user defined>
<...>
Witnesses:
WitnessArgs structure:
Lock: <user defined>
Input Type: <BytesVec structure>
The witness of the same index as the first input xUDT cell is located by xUDT script. It is parsed first as WitnessArgs structure, the input_type field of WitnessArgs, is thus treated as BytesVec structure. This structure must also be of the same length as xUDT args and xUDT data part. An extension script might also require transaction specific data so as to validate. Witness here provides a place for this data needs.
Notice each extension script is only executed once in the transaction. The extension script is responsible for checking all xUDT cells of the current type, ensuring each cell data and witness for current extension script, can be validated per extension script’s rules.
P2SH Style Extension Script
When flags is set to 0x2, only the blake160 hash of extension data is included in xUDT args. User is required to provide the actual extension data in witness directly:
Inputs:
<vec> xUDT_Cell
Data:
<amount: uint128> <xudt data>
Type:
code_hash: extensible_udt type script
args: <owner lock script hash> <xudt args>
Lock:
<user defined>
<...>
Outputs:
<vec> xUDT_Cell
Data:
<amount: uint128> <xudt data>
Type:
code_hash: extensible_udt type script
args: <owner lock script hash> <xudt args>
Lock:
<user defined>
<...>
Witnesses:
WitnessArgs structure:
Lock: <user defined>
Input Type: <Raw Extension Data> <BytesVec structure>
The only difference here, is that input_type field in the corresponding WitnessArgs structure, contains raw extension data in ScriptVec data structure, xUDT script must first validate that the hash of raw extension data provide here, is the same as blake160 hash included in xUDT args. After this, it uses the same logic as the previous workflow.