可扩展的 UDT(Extensible UDT,本文统一称为 xUDT)是基于 Simple UDT 的扩展,可用于定义更多 UDT 可能须要的行为。sUDT 为在 Nervos CKB 上发行 UDT 提供了一个最基本的核心,xUDT 则能够创建在 sUDT 的基础上,知足更多的潜在需求,例如监管。git
xUDT cell 向后兼容于 sUDT,全部 sUDT 规范中定义的既存规则仍然适用于 xUDT cell。在 sUDT 的基础上,xUDT 扩展的 cell 以下:github
data: <amount: uint128> <xudt data> type: code_hash: extensible_udt type script args: <owner lock script hash> <xudt args> lock: <user_defined>
这个被加上去的 xudt args 和 xudt data 部分提供了全部 xUDT 所需的新功能,咱们将会在下文阐述这些细节的架构。数据结构
xUDT args 的架构以下:架构
依赖于 flags 的内容,可能会附加不一样的扩展数据:函数
table Script { code_hash: Byte32, hash_type: byte, args: Bytes, } vector ScriptVec <Script>;
ScriptVec 结构中包含的每一个条目,都被解释为具备附加行为的扩展脚本的 CKB 脚本哈希。当一个 xUDT 脚本被执行时,它将运行这里所包含的每一个扩展脚本。只有当全部扩展脚本都经过验证时,xUDT 才会认为该验证是成功的。区块链
一个扩展的脚本能够被下列的任一种方式加载:ui
int validate(int is_owner_mode, size_t extension_index, const uint8_t* args, size_t args_length);
is_owner_mode 表示当前 xUDT 是否经过全部者模式解锁(如 sUDT 所述),extension_index 指的是当前的扩展在 ScriptVec 结构中的索引。args 和 args_length 被设置为当前扩展脚本的 Script 结构中所包含的 script args。spa
若是该函数返回 0,则认为对当前扩展脚本的验证是成功的。code
xUDT 数据是一个以 XUDTData 结构来进行序列化的模组:索引
vector Bytes <byte>; vector BytesVec <Bytes>; table XUDTData { lock: Bytes; data: BytesVec; }
包含在 XUDTData 中的 data 字段,必须与包含在 xUDT args 中的 ScriptVec 结构的长度相同。一些扩展可能须要在每一个 xUDT cell 中存储特定于用户的数据。xUDT 数据为此类数据提供了一个放置的位子。
XUDTData 中包含的 lock 字段不会被 xUDT 脚本使用,它被保留用于当前 cell 的锁定脚本特定数据。
扩展脚本首先须要找到它定位在 xUDT args 中的索引,而后在 XUDTData 结构的 data 字段的同一个索引处,查找当前扩展脚本的数据。
xUDT 采用与 sUDT 相同的治理操做:全部者锁控制全部治理行为,如代币铸造等。
然而,xUDT 的正常调用操做不一样于 sUDT。根据所用的 flags,可能会有两种使用模式:
当 flags 设置为 0x1 时,原始扩展数据将直接包含在 xUDT args 中。
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>
与第一个输入 xUDT cell 相同的索引的 witness 是由 xUDT 脚本定位的。它首先被解析为 WitnessArgs 结构,所以 WitnessArgs 的 input_type 字段被视为 BytesVec 结构。这个结构也必须与 xUDT args 和 xUDT data 部分的长度相同。扩展脚本可能还须要特定交易的数据,以便进行验证。Witness 在此为这种数据需求提供了一个放置的位置。
注意,每一个扩展脚本在交易中只执行一次。扩展脚本负责检查当前类型的全部 xUDT cell,确保当前扩展脚本的每一个 cell 数据和 witness 均可以根据扩展脚本的规则进行验证。
当 flags 设置为 0x2 时,xUDT args 只包含扩展数据的 blake160 哈希。用户会被要求直接提供在 Witness 中的扩展数据:
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>
这里惟一的区别是,在相应的 WitnessArgs 结构中的 input_type 字段包含在 ScriptVec 数据结构中的原始扩展数据,xUDT 脚本必须首先验证这里提供的原始扩展数据的哈希,是否与 xUDT args 中包含的 blake160 哈希相同。在此以后,它将使用与前面工做流中相同的逻辑。