比特币铭文

铭文是在单个聪上铭刻任意内容,创建独特的比特币原生数字收藏的协议。

铭文的优势

  • 铭文不依赖侧链或单独的代币。
  • 铭文可以保存在比特币钱包中,并使用比特币交易进行传输。
  • 铭文与比特币本身一样持久、不可变、安全和去中心化。

铭文的劣势

  • 为了发送特定铭文,必须根据序数理论严格控制输入输出的顺序和大小。
  • 铭文滥用了比特币链作为货币交易的功能,把比特币网络看成了去中心化的存储设施,存储了和交易不相干的信息。
  • 铭文基于共识,这个共识并不在比特币代码中,后续可能被其它共识取代。

铭文的内容模型

铭文的内容模型就是web媒体类型,包含MIME type和内容本身,存储在taproot的花费脚本中。taproot技术是锁定utxo的几种方式之一,它允许根据私钥或者隐含的脚本来花费utxo。隐含的脚本对于其内部内容的限制很少,而且上链费用较低,铭文正是存储在该脚本中。

铭文过程包括两步,首先是使用taproot script-path创造一个utxo,这个script path包含了铭文内容脚本的哈希;然后是使用铭文script来花费这个utxo,实现铭文内容的上链。这两步的交易分别称为提交交易和显露交易。

比特币脚本有其特定的编程语言,铭文利用了其中的条件语句,通过OP_PUSH把铭文放入了一个永远不会执行的代码块内,实现了内容的上链。这个代码块被称为信封。下面是放入了”Hello World!”这个字符串的信封:

1
2
3
4
5
6
7
8
OP_FALSE
OP_IF
OP_PUSH "ord"
OP_PUSH 1
OP_PUSH "text/plain;charset=utf-8"
OP_PUSH 0
OP_PUSH "Hello World!"
OP_ENDIF

在这个信封里,首先推入的是协议名称:ord
OP_PUSH 1 表明下次推入的是MINE type;OP_PUSH 0 则表示下次推入的是内容本身。
如果铭文内容很长,可以使用多个OP_PUSH,因为一个OP_PUSH推入的数据不能超过520 bytes。
这个铭文内容包含在显露交易中,并锚定在该交易输入的第一个聪中。之后,这个被铭刻的聪可以根据序数理论进行转移、交易等等。

字段

信封内可以包含多个字段,每个字段包含两次数据推入,分别是标签和值。目前定义了6种字段:

  • content-type MIME类型,标签为0。
  • pointer 标签为2,指定铭文在输入utxo的哪个聪上,默认为第一个聪。
  • parent 标签为3,指定铭文的父铭文id。
  • metadata 标签为5,指定元信息,使用CBOR编码元信息。
  • metaprotocol 标签为7,元协议。
  • content_encoding 标签为9,铭文内容字符编码。
  • delegate 标签为11,代理另一个铭文的内容,指定代理铭文的id。

铭文内容和字段之间是一个空的Data Push。

不识别标签根据是奇数还是偶数有着不同的处理策略。偶数标签会影响铭文的创建和转移,因此,不识别的偶数标签必须被展示为unbunded,即不存在于任何聪之上。奇数标签不会影响铭文的创建和转移,比如元信息,因此可以忽略。

铭文ID

铭文包含在显露交易的输入之中。为了唯一确定铭文,使用下面的形式给铭文赋予ID:

521f8eccffa4c41a3a7728dd012ea5a4a02feed81f41159231251ecf1e5c79dai0

在字符i之前是交易ID,i之后是这个交易内的铭文的编号,一个交易可能会生成多个铭文,分布在不同的input之上,下面是有5个inputs的交易的铭文分布的例子,总共生成了7个铭文:

Input Inscription Count Indices
0 2 i0,i1
1 1 i2
2 3 i3,i4,i5
3 0
4 1 i6

铭文序号

铭文可以根据显露交易出现的顺序从0开始进行编号。这里的一个例外是,如果铭文创建后,在同一个区块内被当成交易费使用,其编号会排在区块内铭文的最后。

被诅咒的铭文的编号是从-1开始,不断递减。从区块824544开始,被诅咒的铭文被认为是正常的铭文而以正数进行编号。

源码阅读

  • 铭文定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pub struct Inscription {
pub body: Option<Vec<u8>>, // 铭文内容
pub content_encoding: Option<Vec<u8>>, // 字符编码
pub content_type: Option<Vec<u8>>, // 内容类型
pub delegate: Option<Vec<u8>>, // 代理
pub duplicate_field: bool, // 是否有重复字段
pub incomplete_field: bool, // 是否有不完全的字段
pub metadata: Option<Vec<u8>>, // 元信息
pub metaprotocol: Option<Vec<u8>>, // 元协议
pub parents: Vec<Vec<u8>>, // 祖先
pub pointer: Option<Vec<u8>>, // 偏移指针
pub rune: Option<Vec<u8>>, // 符文
pub unrecognized_even_field: bool, // 是否有不认识的偶数标签字段
}
  • 铭文写入taproot script-path脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
pub fn append_reveal_script_to_builder(&self, mut builder: script::Builder) -> script::Builder {
builder = builder
.push_opcode(opcodes::OP_FALSE)
.push_opcode(opcodes::all::OP_IF)
.push_slice(envelope::PROTOCOL_ID);

Tag::ContentType.append(&mut builder, &self.content_type);
Tag::ContentEncoding.append(&mut builder, &self.content_encoding);
Tag::Metaprotocol.append(&mut builder, &self.metaprotocol);
Tag::Parent.append_array(&mut builder, &self.parents);
Tag::Delegate.append(&mut builder, &self.delegate);
Tag::Pointer.append(&mut builder, &self.pointer);
Tag::Metadata.append(&mut builder, &self.metadata);
Tag::Rune.append(&mut builder, &self.rune);

if let Some(body) = &self.body {
builder = builder.push_slice(envelope::BODY_TAG);
for chunk in body.chunks(MAX_SCRIPT_ELEMENT_SIZE) {
builder = builder.push_slice::<&script::PushBytes>(chunk.try_into().unwrap());
}
}

builder.push_opcode(opcodes::all::OP_ENDIF)
}
  • 铭文ID
1
2
3
4
pub struct InscriptionId {
pub txid: Txid,
pub index: u32,
}
  • 信封
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
// 二进制信封
type RawEnvelope = Envelope<Vec<Vec<u8>>>;
// 解析成铭文的信封
pub(crate) type ParsedEnvelope = Envelope<Inscription>;

pub struct Envelope<T> {
pub input: u32, // 信封位于交易的第几个输入
pub offset: u32, // 交易里的第几个信封
pub payload: T, // 信封类型
pub pushnum: bool, // 信封内是否有用push_num来表示tag
pub stutter: bool, // 是否有空白行
}

// 解析二进制信封
impl From<RawEnvelope> for ParsedEnvelope {
fn from(envelope: RawEnvelope) -> Self {
// body的tag为0,二进制表示为空字节数组
let body = envelope
.payload
.iter()
.enumerate()
.position(|(i, push)| i % 2 == 0 && push.is_empty());

let mut fields: BTreeMap<&[u8], Vec<&[u8]>> = BTreeMap::new();

let mut incomplete_field = false;

// 遍历每一对字段,放入fields map种,寻找不完全字段
for item in envelope.payload[..body.unwrap_or(envelope.payload.len())].chunks(2) {
match item {
[key, value] => fields.entry(key).or_default().push(value),
_ => incomplete_field = true,
}
}
// 寻找重复字段
let duplicate_field = fields.iter().any(|(_key, values)| values.len() > 1);

// 提取字段
let content_encoding = Tag::ContentEncoding.take(&mut fields);
let content_type = Tag::ContentType.take(&mut fields);
let delegate = Tag::Delegate.take(&mut fields);
let metadata = Tag::Metadata.take(&mut fields);
let metaprotocol = Tag::Metaprotocol.take(&mut fields);
let parents = Tag::Parent.take_array(&mut fields);
let pointer = Tag::Pointer.take(&mut fields);
let rune = Tag::Rune.take(&mut fields);

// 剩下未提取的字段是否有偶数字段
let unrecognized_even_field = fields
.keys()
.any(|tag| tag.first().map(|lsb| lsb % 2 == 0).unwrap_or_default());

Self {
payload: Inscription {
// body可能存在多次datapush,合并成一个字节数组
body: body.map(|i| {
envelope.payload[i + 1..]
.iter()
.flatten()
.cloned()
.collect()
}),
content_encoding,
content_type,
delegate,
duplicate_field,
incomplete_field,
metadata,
metaprotocol,
parents,
pointer,
rune,
unrecognized_even_field,
},
input: envelope.input,
offset: envelope.offset,
pushnum: envelope.pushnum,
stutter: envelope.stutter,
}
}
}

impl RawEnvelope {
// taproot script -> RawEnvelope
// 一个script内可能有多个信封
fn from_tapscript(tapscript: &Script, input: usize) -> Result<Vec<Self>> {
let mut envelopes = Vec::new();

let mut instructions = tapscript.instructions().peekable();

let mut stuttered = false;
while let Some(instruction) = instructions.next().transpose()? {
if instruction == PushBytes((&[]).into()) { // 信封开头:OP_FALSE
let (stutter, envelope) =
Self::from_instructions(&mut instructions, input, envelopes.len(), stuttered)?;
if let Some(envelope) = envelope {
envelopes.push(envelope);
} else {
stuttered = stutter;
}
}
}
Ok(envelopes)
}

// 获取指令栈顶端指令,并移除
fn accept(instructions: &mut Peekable<Instructions>, instruction: Instruction) -> Result<bool> {
if instructions.peek() == Some(&Ok(instruction)) {
instructions.next().transpose()?;
Ok(true)
} else {
Ok(false)
}
}

// OP指令 -> envelpe
fn from_instructions(
instructions: &mut Peekable<Instructions>,
input: usize,
offset: usize,
stutter: bool,
) -> Result<(bool, Option<Self>)> {
if !Self::accept(instructions, Op(opcodes::all::OP_IF))? {
let stutter = instructions.peek() == Some(&Ok(PushBytes((&[]).into())));
return Ok((stutter, None));
}

if !Self::accept(instructions, PushBytes((&PROTOCOL_ID).into()))? {
let stutter = instructions.peek() == Some(&Ok(PushBytes((&[]).into())));
return Ok((stutter, None));
}

let mut pushnum = false;

let mut payload = Vec::new();

loop {
match instructions.next().transpose()? {
None => return Ok((false, None)),
Some(Op(opcodes::all::OP_ENDIF)) => {
return Ok((
false,
Some(Envelope {
input: input.try_into().unwrap(),
offset: offset.try_into().unwrap(),
payload,
pushnum,
stutter,
}),
));
}
Some(Op(opcodes::all::OP_PUSHNUM_NEG1)) => {
pushnum = true;
payload.push(vec![0x81]);
}
Some(Op(opcodes::all::OP_PUSHNUM_1)) => {
pushnum = true;
payload.push(vec![1]);
}
Some(Op(opcodes::all::OP_PUSHNUM_2)) => {
pushnum = true;
payload.push(vec![2]);
}
Some(Op(opcodes::all::OP_PUSHNUM_3)) => {
pushnum = true;
payload.push(vec![3]);
}
Some(Op(opcodes::all::OP_PUSHNUM_4)) => {
pushnum = true;
payload.push(vec![4]);
}
Some(Op(opcodes::all::OP_PUSHNUM_5)) => {
pushnum = true;
payload.push(vec![5]);
}
Some(Op(opcodes::all::OP_PUSHNUM_6)) => {
pushnum = true;
payload.push(vec![6]);
}
Some(Op(opcodes::all::OP_PUSHNUM_7)) => {
pushnum = true;
payload.push(vec![7]);
}
Some(Op(opcodes::all::OP_PUSHNUM_8)) => {
pushnum = true;
payload.push(vec![8]);
}
Some(Op(opcodes::all::OP_PUSHNUM_9)) => {
pushnum = true;
payload.push(vec![9]);
}
Some(Op(opcodes::all::OP_PUSHNUM_10)) => {
pushnum = true;
payload.push(vec![10]);
}
Some(Op(opcodes::all::OP_PUSHNUM_11)) => {
pushnum = true;
payload.push(vec![11]);
}
Some(Op(opcodes::all::OP_PUSHNUM_12)) => {
pushnum = true;
payload.push(vec![12]);
}
Some(Op(opcodes::all::OP_PUSHNUM_13)) => {
pushnum = true;
payload.push(vec![13]);
}
Some(Op(opcodes::all::OP_PUSHNUM_14)) => {
pushnum = true;
payload.push(vec![14]);
}
Some(Op(opcodes::all::OP_PUSHNUM_15)) => {
pushnum = true;
payload.push(vec![15]);
}
Some(Op(opcodes::all::OP_PUSHNUM_16)) => {
pushnum = true;
payload.push(vec![16]);
}
Some(PushBytes(push)) => {
// body的tag和body都在这里
payload.push(push.as_bytes().to_vec());
}
Some(_) => return Ok((false, None)),
}
}
}
}
  • 标签的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
pub(crate) enum Tag {
Pointer = 2,
#[allow(unused)]
Unbound = 66,

ContentType = 1,
Parent = 3,
Metadata = 5,
Metaprotocol = 7,
ContentEncoding = 9,
Delegate = 11,
Rune = 13,
#[allow(unused)]
Note = 15,
#[allow(unused)]
Nop = 255,
}

impl Tag {
// 是否分多次push,只有元信息需要
fn chunked(self) -> bool {
matches!(self, Self::Metadata)
}

// 标签的u8表示
pub(crate) fn bytes(self) -> [u8; 1] {
[self as u8]
}

// 序列化标签到tapscript脚本
pub(crate) fn append(self, builder: &mut script::Builder, value: &Option<Vec<u8>>) {
if let Some(value) = value {
let mut tmp = script::Builder::new();
mem::swap(&mut tmp, builder);

if self.chunked() {
for chunk in value.chunks(MAX_SCRIPT_ELEMENT_SIZE) {
tmp = tmp
.push_slice::<&script::PushBytes>(self.bytes().as_slice().try_into().unwrap()) // key
.push_slice::<&script::PushBytes>(chunk.try_into().unwrap()); // value
}
} else {
tmp = tmp
.push_slice::<&script::PushBytes>(self.bytes().as_slice().try_into().unwrap()) // key
.push_slice::<&script::PushBytes>(value.as_slice().try_into().unwrap()); // value
}

mem::swap(&mut tmp, builder);
}
}

// 从fields中提取标签
pub(crate) fn take(self, fields: &mut BTreeMap<&[u8], Vec<&[u8]>>) -> Option<Vec<u8>> {
if self.chunked() {
let value = fields.remove(self.bytes().as_slice())?;

if value.is_empty() {
None
} else {
// 多次push的数据需要flatten到一维
Some(value.into_iter().flatten().cloned().collect())
}
} else {
let values = fields.get_mut(self.bytes().as_slice())?;

if values.is_empty() {
None
} else {
// 这里考虑到了重复的标签
let value = values.remove(0).to_vec();

if values.is_empty() {
fields.remove(self.bytes().as_slice());
}

Some(value)
}
}
}
}

为比特币网络建立铭文索引

铭文附着在聪上,上文已经介绍了聪和UTXO的关联,这里基本上就是建立铭文和聪的关联。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
// 待分配的浮动铭文
pub(super) struct Flotsam {
inscription_id: InscriptionId,
offset: u64,
origin: Origin,
}

// 铭文去向
enum Origin {
New { // 新铭文创建
cursed: bool,
fee: u64,
hidden: bool,
parents: Vec<InscriptionId>,
reinscription: bool,
unbound: bool,
vindicated: bool,
},
Old { // 旧铭文转移
sequence_number: u32,
old_satpoint: SatPoint,
},
}

// 铭文索引都在这个结构体内完成
pub(super) struct InscriptionUpdater<'a, 'tx> {
pub(super) blessed_inscription_count: u64,
pub(super) cursed_inscription_count: u64,
pub(super) flotsam: Vec<Flotsam>,
pub(super) height: u32, // 当前索引区块高度
pub(super) home_inscription_count: u64,
pub(super) home_inscriptions: &'a mut Table<'tx, u32, InscriptionIdValue>,
pub(super) id_to_sequence_number: &'a mut Table<'tx, InscriptionIdValue, u32>,
pub(super) inscription_number_to_sequence_number: &'a mut Table<'tx, i32, u32>,
pub(super) lost_sats: u64,
pub(super) next_sequence_number: u32,
pub(super) reward: u64, // 当前索引区块奖励
pub(super) transaction_buffer: Vec<u8>,
pub(super) transaction_id_to_transaction: &'a mut Table<'tx, &'static TxidValue, &'static [u8]>,
pub(super) sat_to_sequence_number: &'a mut MultimapTable<'tx, u64, u32>,
pub(super) sequence_number_to_children: &'a mut MultimapTable<'tx, u32, u32>,
pub(super) sequence_number_to_entry: &'a mut Table<'tx, u32, InscriptionEntryValue>,
pub(super) timestamp: u32,
pub(super) unbound_inscriptions: u64,
}

impl<'a, 'tx> InscriptionUpdater<'a, 'tx> {
// 索引指定的交易
pub(super) fn index_inscriptions(
&mut self,
tx: &Transaction,
txid: Txid,
input_utxo_entries: &[ParsedUtxoEntry],
output_utxo_entries: &mut [UtxoEntryBuf],
utxo_cache: &mut HashMap<OutPoint, UtxoEntryBuf>,
index: &Index,
input_sat_ranges: Option<&Vec<&[u8]>>,
) -> Result {
// 浮动铭文,待分配到新的聪
let mut floating_inscriptions = Vec::new();
let mut id_counter = 0;
// 在同一个offset铭刻的铭文
let mut inscribed_offsets = BTreeMap::new();
let jubilant = self.height >= index.settings.chain().jubilee_height();
// 输入大小
let mut total_input_value = 0;
// 输出大小
let total_output_value = tx
.output
.iter()
.map(|txout| txout.value.to_sat())
.sum::<u64>();

// 解析出该交易包含的所有铭文
let envelopes = ParsedEnvelope::from_transaction(tx);
let has_new_inscriptions = !envelopes.is_empty();
let mut envelopes = envelopes.into_iter().peekable();

// 处理每个输入,找到已存在和新创建的铭文
for (input_index, txin) in tx.input.iter().enumerate() {
// 跳过区块奖励输入,这个输入里不会有铭文存在
if txin.previous_output.is_null() {
total_input_value += Height(self.height).subsidy();
continue;
}

// 获取输入中已经存在的铭文
let mut transferred_inscriptions = input_utxo_entries[input_index].parse_inscriptions();
transferred_inscriptions.sort_by_key(|(sequence_number, _)| *sequence_number);

// 加入 floating_inscriptions 和 inscribed_offsets 中
for (sequence_number, old_satpoint_offset) in transferred_inscriptions {
let old_satpoint = SatPoint {
outpoint: txin.previous_output,
offset: old_satpoint_offset,
};

let inscription_id = InscriptionEntry::load(
self
.sequence_number_to_entry
.get(sequence_number)?
.unwrap()
.value(),
)
.id;

// 铭文在该交易中的offset
let offset = total_input_value + old_satpoint_offset;
floating_inscriptions.push(Flotsam {
offset,
inscription_id,
origin: Origin::Old {
sequence_number,
old_satpoint,
},
});

// offset -> 铭文 的映射
inscribed_offsets
.entry(offset)
.or_insert((inscription_id, 0))
.1 += 1;
}

// 更新 total_input_value
let offset = total_input_value;
let input_value = input_utxo_entries[input_index].total_value();
total_input_value += input_value;

// 把这个输入的新的铭文也加入进到floating_inscriptions和inscribed_offsets 中
while let Some(inscription) = envelopes.peek() {
if inscription.input != u32::try_from(input_index).unwrap() {
break;
}

let inscription_id = InscriptionId {
txid,
index: id_counter,
};

// 确定铭文类型
let curse = if inscription.payload.unrecognized_even_field {
Some(Curse::UnrecognizedEvenField)
} else if inscription.payload.duplicate_field {
Some(Curse::DuplicateField)
} else if inscription.payload.incomplete_field {
Some(Curse::IncompleteField)
} else if inscription.input != 0 {
Some(Curse::NotInFirstInput)
} else if inscription.offset != 0 {
Some(Curse::NotAtOffsetZero)
} else if inscription.payload.pointer.is_some() {
Some(Curse::Pointer)
} else if inscription.pushnum {
Some(Curse::Pushnum)
} else if inscription.stutter {
Some(Curse::Stutter)
} else if let Some((id, count)) = inscribed_offsets.get(&offset) {
if *count > 1 {
Some(Curse::Reinscription)
} else {
// 如果已经存在的铭文是cursed,那么不算是Reinscription
let initial_inscription_sequence_number =
self.id_to_sequence_number.get(id.store())?.unwrap().value();

let entry = InscriptionEntry::load(
self
.sequence_number_to_entry
.get(initial_inscription_sequence_number)?
.unwrap()
.value(),
);

let initial_inscription_was_cursed_or_vindicated =
entry.inscription_number < 0 || Charm::Vindicated.is_set(entry.charms);

if initial_inscription_was_cursed_or_vindicated {
None
} else {
Some(Curse::Reinscription)
}
}
} else {
None
};

// 铭文的偏移(相对于整个交易输出)
let offset = inscription
.payload
.pointer()
.filter(|&pointer| pointer < total_output_value)
.unwrap_or(offset);

floating_inscriptions.push(Flotsam {
inscription_id,
offset,
origin: Origin::New {
cursed: curse.is_some() && !jubilant,
fee: 0,
hidden: inscription.payload.hidden(),
parents: inscription.payload.parents(),
reinscription: inscribed_offsets.contains_key(&offset),
unbound: input_value == 0
|| curse == Some(Curse::UnrecognizedEvenField)
|| inscription.payload.unrecognized_even_field,
vindicated: curse.is_some() && jubilant,
},
});

inscribed_offsets
.entry(offset)
.or_insert((inscription_id, 0))
.1 += 1;

envelopes.next();
id_counter += 1;
}
}

// 索引:txid -> tx buffer
if index.index_transactions && has_new_inscriptions {
tx.consensus_encode(&mut self.transaction_buffer)
.expect("in-memory writers don't error");

self
.transaction_id_to_transaction
.insert(&txid.store(), self.transaction_buffer.as_slice())?;

self.transaction_buffer.clear();
}

let potential_parents = floating_inscriptions
.iter()
.map(|flotsam| flotsam.inscription_id)
.collect::<HashSet<InscriptionId>>();

for flotsam in &mut floating_inscriptions {
if let Flotsam {
origin: Origin::New {
parents: purported_parents,
..
},
..
} = flotsam
{
let mut seen = HashSet::new();
// 根据祖先铭文的定义,父铭文必须出现在交易中,这里过滤掉了其它不在交易中的父铭文
purported_parents
.retain(|parent| seen.insert(*parent) && potential_parents.contains(parent));
}
}

// 计算了每个浮动铭文的平均交易费
for flotsam in &mut floating_inscriptions {
if let Flotsam {
origin: Origin::New { ref mut fee, .. },
..
} = flotsam
{
*fee = (total_input_value - total_output_value) / u64::from(id_counter);
}
}

// 是否包含coinbase输入
let is_coinbase = tx
.input
.first()
.map(|tx_in| tx_in.previous_output.is_null())
.unwrap_or_default();

if is_coinbase {
// 当成交易费燃烧掉的铭文需要加到coinbase交易中来
floating_inscriptions.append(&mut self.flotsam);
}

floating_inscriptions.sort_by_key(|flotsam| flotsam.offset);
let mut inscriptions = floating_inscriptions.into_iter().peekable();

// 计算铭文新的位置(OutPoint + offset)
let mut new_locations = Vec::new();
let mut output_value = 0;
for (vout, txout) in tx.output.iter().enumerate() {
let end = output_value + txout.value.to_sat();

while let Some(flotsam) = inscriptions.peek() {
if flotsam.offset >= end {
break;
}

let new_satpoint = SatPoint {
outpoint: OutPoint {
txid,
vout: vout.try_into().unwrap(),
},
offset: flotsam.offset - output_value,
};

new_locations.push((
new_satpoint,
inscriptions.next().unwrap(),
txout.script_pubkey.is_op_return(),
));
}

output_value = end;
}

// 更新铭文位置
for (new_satpoint, flotsam, op_return) in new_locations.into_iter() {
let output_utxo_entry =
&mut output_utxo_entries[usize::try_from(new_satpoint.outpoint.vout).unwrap()];

self.update_inscription_location(
input_sat_ranges,
flotsam,
new_satpoint,
op_return,
Some(output_utxo_entry),
utxo_cache,
index,
)?;
}

if is_coinbase {
for flotsam in inscriptions {
let new_satpoint = SatPoint {
outpoint: OutPoint::null(),
offset: self.lost_sats + flotsam.offset - output_value,
};
self.update_inscription_location(
input_sat_ranges,
flotsam,
new_satpoint,
false,
None,
utxo_cache,
index,
)?;
}
self.lost_sats += self.reward - output_value;
Ok(())
} else {
// 剩下的铭文被归集到coinbase交易中
self.flotsam.extend(inscriptions.map(|flotsam| Flotsam {
offset: self.reward + flotsam.offset - output_value,
..flotsam
}));
// 加入该比交易的手续费
self.reward += total_input_value - output_value;
Ok(())
}
}

// 计算偏移量为input_offset的铭文位于哪个聪
fn calculate_sat(input_sat_ranges: Option<&Vec<&[u8]>>, input_offset: u64) -> Option<Sat> {
let input_sat_ranges = input_sat_ranges?;

let mut offset = 0;
for chunk in input_sat_ranges
.iter()
.flat_map(|slice| slice.chunks_exact(11))
{
let (start, end) = SatRange::load(chunk.try_into().unwrap());
let size = end - start;
if offset + size > input_offset {
let n = start + input_offset - offset;
return Some(Sat(n));
}
offset += size;
}

unreachable!()
}

// 更新铭文位置
fn update_inscription_location(
&mut self,
input_sat_ranges: Option<&Vec<&[u8]>>,
flotsam: Flotsam,
new_satpoint: SatPoint,
op_return: bool,
mut normal_output_utxo_entry: Option<&mut UtxoEntryBuf>,
utxo_cache: &mut HashMap<OutPoint, UtxoEntryBuf>,
index: &Index,
) -> Result {
let inscription_id = flotsam.inscription_id;
let (unbound, sequence_number) = match flotsam.origin {
Origin::Old {
sequence_number,
old_satpoint,
} => {
if op_return { // 旧铭文燃烧
let entry = InscriptionEntry::load(
self
.sequence_number_to_entry
.get(&sequence_number)?
.unwrap()
.value(),
);

let mut charms = entry.charms;
Charm::Burned.set(&mut charms);

self.sequence_number_to_entry.insert(
sequence_number,
&InscriptionEntry { charms, ..entry }.store(),
)?;
}

if let Some(ref sender) = index.event_sender {
// 转移事件
sender.blocking_send(Event::InscriptionTransferred {
block_height: self.height,
inscription_id,
new_location: new_satpoint,
old_location: old_satpoint,
sequence_number,
})?;
}

(false, sequence_number)
}
Origin::New { // 新铭文创建
cursed,
fee,
hidden,
parents,
reinscription,
unbound,
vindicated,
} => {
let inscription_number = if cursed {
let number: i32 = self.cursed_inscription_count.try_into().unwrap();
self.cursed_inscription_count += 1;
-(number + 1)
} else {
let number: i32 = self.blessed_inscription_count.try_into().unwrap();
self.blessed_inscription_count += 1;
number
};

let sequence_number = self.next_sequence_number;
self.next_sequence_number += 1;

self
.inscription_number_to_sequence_number
.insert(inscription_number, sequence_number)?;

// 新铭文位于哪个聪
let sat = if unbound {
None
} else {
Self::calculate_sat(input_sat_ranges, flotsam.offset)
};

let mut charms = 0;

if cursed {
Charm::Cursed.set(&mut charms);
}

if reinscription {
Charm::Reinscription.set(&mut charms);
}

if let Some(sat) = sat {
charms |= sat.charms();
}

if op_return {
Charm::Burned.set(&mut charms);
}

if new_satpoint.outpoint == OutPoint::null() {
Charm::Lost.set(&mut charms);
}

if unbound {
Charm::Unbound.set(&mut charms);
}

if vindicated {
Charm::Vindicated.set(&mut charms);
}

// 建立聪->铭文序列号索引
if let Some(Sat(n)) = sat {
self.sat_to_sequence_number.insert(&n, &sequence_number)?;
}

// 父铭文 -> 子铭文索引
let parent_sequence_numbers = parents
.iter()
.map(|parent| {
let parent_sequence_number = self
.id_to_sequence_number
.get(&parent.store())?
.unwrap()
.value();

self
.sequence_number_to_children
.insert(parent_sequence_number, sequence_number)?;

Ok(parent_sequence_number)
})
.collect::<Result<Vec<u32>>>()?;

// 铭文创建事件
if let Some(ref sender) = index.event_sender {
sender.blocking_send(Event::InscriptionCreated {
block_height: self.height,
charms,
inscription_id,
location: (!unbound).then_some(new_satpoint),
parent_inscription_ids: parents,
sequence_number,
})?;
}

// 铭文序列号 -> 铭文内容索引
self.sequence_number_to_entry.insert(
sequence_number,
&InscriptionEntry {
charms,
fee,
height: self.height,
id: inscription_id,
inscription_number,
parents: parent_sequence_numbers,
sat,
sequence_number,
timestamp: self.timestamp,
}
.store(),
)?;

// 铭文id -> 铭文序列号 索引
self
.id_to_sequence_number
.insert(&inscription_id.store(), sequence_number)?;

if !hidden {
self
.home_inscriptions
.insert(&sequence_number, inscription_id.store())?;

if self.home_inscription_count == 100 {
self.home_inscriptions.pop_first()?;
} else {
self.home_inscription_count += 1;
}
}

(unbound, sequence_number)
}
};

let satpoint = if unbound {
let new_unbound_satpoint = SatPoint {
outpoint: unbound_outpoint(),
offset: self.unbound_inscriptions,
};
self.unbound_inscriptions += 1;
normal_output_utxo_entry = None;
new_unbound_satpoint
} else {
new_satpoint
};

// The special outpoints, i.e., the null outpoint and the unbound outpoint,
// don't follow the normal rulesr. Unlike real outputs they get written to
// more than once. So we create a new UTXO entry here and commit() will
// merge it with any existing entry.
let output_utxo_entry = normal_output_utxo_entry.unwrap_or_else(|| {
assert!(Index::is_special_outpoint(satpoint.outpoint));
utxo_cache
.entry(satpoint.outpoint)
.or_insert(UtxoEntryBuf::empty(index))
});

// 更新output utxo,加入铭文信息
output_utxo_entry.push_inscription(sequence_number, satpoint.offset, index);

Ok(())
}
}