Skip to content

Commit dd4a141

Browse files
authored
Merge pull request #428 from XuJiandong/xudt
[0052] Extensible UDT
2 parents 05843e1 + 53b2c5e commit dd4a141

File tree

1 file changed

+383
-0
lines changed

1 file changed

+383
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,383 @@
1+
---
2+
Number: "0052"
3+
Category: Standards Track
4+
Status: Proposal
5+
Author: Xuejie Xiao <xxuejie@gmail.com>, Xu Jiandong <lynndon@gmail.com>
6+
Created: 2024-01-09
7+
---
8+
9+
10+
# Extensible UDT
11+
12+
Extensible UDT(xUDT) is an extension of [Simple
13+
UDT](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0025-simple-udt/0025-simple-udt.md) for
14+
defining more behaviors a UDT might need. While simple UDT provides a minimal
15+
core for issuing UDTs on Nervos CKB, extensible UDT builds on top of simple UDT
16+
for more potential needs, such as regulations.
17+
18+
## **Data Structure**
19+
20+
**xUDT Cell**
21+
22+
An xUDT cell is backward compatible with Simple UDT, all the existing rules
23+
defined in the Simple UDT spec must still hold true for xUDT cells. On top of
24+
sUDT, xUDT extends a cell like the following:
25+
26+
```yaml
27+
data:
28+
<amount: uint128> <xUDT data>
29+
type:
30+
code_hash: xUDT type script
31+
args: <owner lock script hash> <xUDT args>
32+
lock:
33+
<user_defined>
34+
```
35+
The `amount` is a 128-bit unsigned integer in little endian format. The added `xUDT
36+
args` and `xUDT data` parts provide all the new functions needed by xUDT, the
37+
detailed structure is explained below.
38+
39+
### **xUDT Args**
40+
41+
xUDT args has the following structure:
42+
43+
```
44+
<4-byte xUDT flags> <Variable length bytes, extension data>
45+
```
46+
47+
Depending on the content of `flags`, which is represented as a 32-bit unsigned
48+
integer in little-endian format, different extension data might be attached:
49+
50+
• If `flags & 0x1FFFFFFF` is 0, no extension data is required. Note a
51+
backward-compatible way of viewing things, which is that a plain sUDT cell also
52+
has a hidden `flags` field with all zeros.
53+
54+
• If `flags & 0x1FFFFFFF` is 0x1, extension data will contain a
55+
[molecule](https://github.com/nervosnetwork/molecule) serialized `ScriptVec`
56+
structure:
57+
58+
```
59+
table Script {
60+
code_hash: Byte32,
61+
hash_type: byte,
62+
args: Bytes,
63+
}
64+
65+
vector ScriptVec <Script>
66+
```
67+
68+
Each entry included in `ScriptVec` structure is interpreted as
69+
an extension script with additional behaviors. When an xUDT script is
70+
executed, it will run through each included extension script. Only when all
71+
extension scripts pass validation, will xUDT also consider the validation to be
72+
successful.
73+
74+
An extension script can be loaded in any of the following ways:
75+
76+
1. Some extension logics might have a predefined hash, for example, we can
77+
use `0x0000 ... 0001` to represent regulation extension. The actual code for
78+
such scripts can be embedded in xUDT script itself.
79+
2. If an input cell in the current transaction uses a lock script with the same
80+
script hash as the current extension script, we can consider the extension
81+
script to be validated already.
82+
3. If an extension script does not match any of the above criteria, xUDT will
83+
use the code_hash and hash_type included in the extension script to
84+
invoke [ckb_dlopen2](https://github.com/nervosnetwork/ckb-c-stdlib/blob/37eba3102100808ffc6fa2383bcf9e1e2651c8ea/ckb_dlfcn.h#L108-L113) function,
85+
hoping to load a dynamically linked script from cell deps in the current
86+
transaction. If a script can be located successfully, xUDT will then look for
87+
an exported function with the following signature:
88+
89+
```c
90+
int validate(int is_owner_mode, size_t extension_index, const uint8_t* args, size_t args_length);
91+
```
92+
93+
`is_owner_mode` indicates if the current xUDT is unlocked via owner mode(as
94+
described by sUDT), `extension_index` refers to the index of the current
95+
extension in the `ScriptVec` structure. `args` and `args_length` are set to the
96+
script args included in `Script` structure of the current extension script.
97+
98+
If this function returns 0, the current extension script validation is
99+
considered successful.
100+
101+
• If `flags & 0x1FFFFFFF` is 0x2, extension data will contain the blake160 hash
102+
of the `ScriptVec` structure as explained in the previous section. The
103+
actual `extension_scripts` (`ScriptVec`) structure data will be included in a
104+
witness field `input_type` or `output_type` contained in the current
105+
transaction. We will explain this part below. Choosing `input_type` or
106+
`output_type` depends on whether the type script is running on input or output
107+
cells. Under a lot of scenarios, it is `input_type`. But in the following example “Owner
108+
Mode Without Consuming Cell”, we can see it’s possible on `output_type`.
109+
110+
### xUDT Witness
111+
112+
The `input_type` or `output_type` field in witness has the following data
113+
structure in molecule format:
114+
115+
```js
116+
table XudtWitness {
117+
owner_script: ScriptOpt,
118+
owner_signature: BytesOpt,
119+
extension_scripts: ScriptVecOpt,
120+
extension_data: BytesVec,
121+
}
122+
```
123+
124+
The field `owner_script` and `owner_signature` will be used in owner mode. The
125+
field `extension_scripts` is used when `flags & 0x1FFFFFFF` is 0x2 in args.
126+
127+
The length of `extension_data` structure inside must also be the same as
128+
`ScriptVec` in `xUDT args` or `extension_scripts`. An extension script might also
129+
require transaction-specific data for validation. The witness here provides a
130+
place for these data needs.
131+
132+
### Owner Mode Update
133+
134+
As described in RFC sUDT, if an input cell in the current transaction uses an
135+
input lock script with the same script hash as the owner lock script hash, the
136+
`is_owner_mode` will be set to true. In xUDT, this rule is updated by the
137+
following rule:
138+
139+
If an input or output cell in the current transaction uses one or more of the
140+
following:
141+
142+
- input lock script (when `flags & 0x20000000` is **zero** or `flags` is not present)
143+
- output type script (when `flags & 0x40000000` is **non-zero**)
144+
- input type script (when `flags & 0x80000000` is **non-zero**)
145+
146+
With the same script hash as the owner lock script hash, the `is_owner_mode`
147+
will be set to true. The output lock scripts are not included, because they
148+
won’t be run in a transaction.
149+
150+
If the `owner_script` in witness isn’t none and its blake2b hash is the same as
151+
the owner lock script hash in `args`, this script will be run as an extension
152+
script. If the script returns success, `is_owner_mode` is set to true. Note, the
153+
`owner_signature` field can be used by this owner script. When tokens are
154+
minted, the `owner_script` and `owner_signature` can be set to some proper
155+
values. When tokens are transferred, they can be set to none.
156+
157+
### **xUDT Data**
158+
159+
xUDT data is a molecule serialized `XudtData` structure:
160+
161+
```
162+
vector Bytes <byte>
163+
vector BytesVec <Bytes>
164+
165+
table XudtData {
166+
lock: Bytes,
167+
data: BytesVec,
168+
}
169+
```
170+
171+
The `data` field included in `XudtData`, must be of the same length
172+
as `ScriptVec` structure included in xUDT args. Some extensions might require
173+
user-specific data stored in each xUDT cell. xUDT data provides a place for such
174+
data. The `XudtData` can be optional regardless of whether there is any extension
175+
script or not. However, if an extension script requires such data, it must be
176+
present.
177+
178+
The `lock` field included in `XudtData` will not be used by the xUDT script. It
179+
is reserved for lock script specific data for current cells.
180+
181+
An extension script should first locate the index it resides in xUDT
182+
args, then look for the data for the current extension script at the same index
183+
in `data` field of `XudtData` structure.
184+
185+
## **Operations**
186+
187+
xUDT uses the same governance operations as Simple UDT: an owner lock controls
188+
all governance operations, such as minting.
189+
190+
A normal transfer operation of xUDT, however, differs from Simple UDT. Depending
191+
on the flags used, there might be 2 usage patterns:
192+
193+
### **Raw Extension Script**
194+
195+
When `flags & 0x1FFFFFFF` are set to 0x1, raw extension data is included in xUDT args directly.
196+
197+
```yaml
198+
Inputs:
199+
<vec> xUDT_Cell
200+
Data:
201+
<amount: uint128> <xUDT data>
202+
Type:
203+
code_hash: xUDT type script
204+
args: <owner lock script hash> <xUDT args>
205+
Lock:
206+
<user defined>
207+
<...>
208+
Outputs:
209+
<vec> xUDT_Cell
210+
Data:
211+
<amount: uint128> <xUDT data>
212+
Type:
213+
code_hash: xUDT type script
214+
args: <owner lock script hash> <xUDT args>
215+
Lock:
216+
<user defined>
217+
<...>
218+
Witnesses:
219+
WitnessArgs structure:
220+
Lock: <user defined>
221+
Input Type: <XudtWitness>
222+
owner_script: <None>
223+
owner_signature: <None>
224+
extension_scripts: <None>
225+
extension_data:
226+
<vec> BytesVec
227+
<data>
228+
<...>
229+
```
230+
231+
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` or `output_type` field of `WitnessArgs`, is thus treated as `XudtWitness` structure.
232+
233+
Note that each extension script is only executed once in the transaction. When multiple instances of the same extension script are included, each instance will execute independently for each inclusion. The extension script is responsible for checking all xUDT cells of the current type, ensuring each cell data and witness for the current extension script, can be validated against the extension script’s rules.
234+
235+
### **P2SH Style Extension Script**
236+
237+
When `flags & 0x1FFFFFFF` are set to 0x2, only the blake160 hash of extension data is included in xUDT args. The user is required to provide the actual extension data in witness directly:
238+
239+
```yaml
240+
Inputs:
241+
<vec> xUDT_Cell
242+
Data:
243+
<amount: uint128> <xUDT data>
244+
Type:
245+
code_hash: xUDT type script
246+
args: <owner lock script hash> <xUDT args, hash of raw extension data>
247+
Lock:
248+
<user defined>
249+
<...>
250+
Outputs:
251+
<vec> xUDT_Cell
252+
Data:
253+
<amount: uint128> <xUDT data>
254+
Type:
255+
code_hash: xUDT type script
256+
args: <owner lock script hash> <xUDT args, hash of raw extension data>
257+
Lock:
258+
<user defined>
259+
<...>
260+
Witnesses:
261+
WitnessArgs structure:
262+
Lock: <user defined>
263+
Input Type: XudtWitness
264+
owner_script: <None>
265+
owner_signature: <None>
266+
extension_scripts:
267+
<vec> ScriptVec
268+
<script>
269+
<...>
270+
extension_data:
271+
<vec> BytesVec
272+
<data>
273+
<...>
274+
```
275+
276+
The only difference here is that `XudtWitness` in `input_type` or
277+
`output_type` field in the corresponding WitnessArgs structure, contains raw
278+
extension data in `ScriptVec` data structure, xUDT script must first validate
279+
that the hash of raw extension data provide here, is the same as blake160 hash
280+
included in xUDT args. After this, it uses the same logic as the previous
281+
workflow.
282+
283+
### Owner Mode without Consuming Cell
284+
285+
As described above, If an input cell uses an input lock script with same script
286+
hash as the owner lock script hash, the `is_owner_mode` will be set to true. It
287+
isn’t convenient: this requires extra cell to be consumed. With `owner_script`
288+
and `owner_signature` set to proper values, we can use owner mode without extra
289+
cell.
290+
291+
```yaml
292+
Inputs:
293+
<vec>
294+
<Any input cells>
295+
<...>
296+
Outputs:
297+
<vec> xUDT_Cell
298+
Data:
299+
<amount: uint128> <xUDT data>
300+
Type:
301+
code_hash: xUDT type script
302+
args: <owner lock script hash 1> <xUDT args>
303+
Lock:
304+
<user defined>
305+
<...>
306+
Witnesses:
307+
WitnessArgs structure:
308+
Lock: <user defined>
309+
Input Type: <None>
310+
Output Type: XudtWitness
311+
owner_script: <owner script 1>
312+
owner_signature: <signature 1>
313+
extension_scripts:
314+
<vec> ScriptVec
315+
<script>
316+
<...>
317+
extension_data:
318+
<vec> BytesVec
319+
<data>
320+
<...>
321+
```
322+
323+
The example above shows a scenario of owner mode without consuming the owner's
324+
cell. We can implement an extension script as `<owner script 1>` with signature
325+
validation. The `<signature 1>` can be used by `<owner script 1>` to place
326+
signature information.
327+
328+
## Deployment
329+
330+
An [implementation](https://github.com/nervosnetwork/ckb-production-scripts/blob/master/c/xudt_rce.c) of
331+
the spec above has been deployed to Mirana CKB mainnet and Pudge testnet:
332+
333+
- Mirana(mainnet)
334+
335+
| parameter | value |
336+
| --- | --- |
337+
| code_hash | 0x50bd8d6680b8b9cf98b73f3c08faf8b2a21914311954118ad6609be6e78a1b95 |
338+
| hash_type | data1 |
339+
| tx_hash | 0xc07844ce21b38e4b071dd0e1ee3b0e27afd8d7532491327f39b786343f558ab7 |
340+
| index | 0x0 |
341+
| dep_type | code |
342+
343+
This script is not upgradeable due to zero lock (lock args with all zeros).
344+
We have previously deployed scripts with the ability to be upgraded. However, we
345+
consider this upgrading mechanism to be harmful as it compromises the
346+
decentralization of the blockchain. With the ability to upgrade, it means that
347+
the owners (whether it's several people or even just one person) of this cell
348+
can upgrade it at any time. This gives one or several individuals complete
349+
control over the assets associated with the deploy scripts. That's why we have
350+
chosen to use a zero lock when deploying this script.
351+
352+
353+
- Pudge(testnet)
354+
355+
| parameter | value |
356+
| --- | --- |
357+
| code_hash | 0x50bd8d6680b8b9cf98b73f3c08faf8b2a21914311954118ad6609be6e78a1b95 |
358+
| hash_type | data1 |
359+
| tx_hash | 0xbf6fb538763efec2a70a6a3dcb7242787087e1030c4e7d86585bc63a9d337f5f |
360+
| index | 0x0 |
361+
| dep_type | code |
362+
363+
| parameter | value |
364+
| --- | --- |
365+
| code_hash | 0x25c29dc317811a6f6f3985a7a9ebc4838bd388d19d0feeecf0bcd60f6c0975bb |
366+
| hash_type | type |
367+
| tx_hash | 0xbf6fb538763efec2a70a6a3dcb7242787087e1030c4e7d86585bc63a9d337f5f |
368+
| index | 0x0 |
369+
| dep_type | code |
370+
371+
These 2 versions are pointing to the same xUDT deployment.
372+
373+
374+
A reproducible build is supported to verify the deploy script. To build the
375+
deployed the script above, one can use the following steps:
376+
377+
```
378+
$ git clone https://github.com/nervosnetwork/ckb-production-scripts
379+
$ cd ckb-production-scripts
380+
$ git checkout abdcb117b512e35910fa8e30241a7a354e5cacf0
381+
$ git submodule update --init --recursive
382+
$ make all-via-docker
383+
```

0 commit comments

Comments
 (0)