比特币铭文

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

铭文的优势

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

铭文的劣势

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

铭文的内容模型

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

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

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

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开始,被诅咒的铭文被认为是正常的铭文而以正数进行编号。

源码阅读

  • 铭文定义
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脚本
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
pub struct InscriptionId {
pub txid: Txid,
pub index: u32,
}
  • 信封
// 二进制信封
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)),
}
}
}
}
  • 标签的实现
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的关联,这里基本上就是建立铭文和聪的关联。

// 待分配的浮动铭文
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(())
}
}