比特币符文

比特币符文是比特币链上新资产发行的协议,它允许使用比特币交易来发行、铸造和转移比特币原生的数字货币。

符石 (Runestone)

符文协议的信息,称为符石(runestone),存储在比特币交易的输出UTXO中。

  • 符石输出的脚本(Script PublicKey)以OP_RETURN开头,后面跟OP_13,然后是0个或者多个数据推入。这些推入的数据是经过编码的128位的整数。
  • 符石包含的信息,可以声明发行一个新的符文,或者铸造已经存在的符文,以及从交易的输入UTXO转移符文到输出UTXO。
  • 一个交易的输出UTXO可以持有多个符文。
  • 符文通过ID来唯一辨识,ID由符文发行的交易索引和区块高度组成,用文本BLOCK:TX来表示。比如,在第500个区块里的第20个交易发行的符文的id是500:20
// 符石声明
pub struct Runestone {
pub etching: Option<Etching>, // 发行
pub edicts: Vec<Edict>, // 转移
pub mint: Option<RuneId>, // 铸币
pub pointer: Option<u32>, // 未分配符文归属哪个输出
}

// 符文ID声明
pub struct RuneId {
pub block: u64,
pub tx: u32,
}

符石解译

从交易中解译符石包含如下步骤:

  1. 找到交易中锁定脚本(script pubkey)以OP_RETURN OP_13开头的第一个输出。
  2. 把接下来的数据推入合并成一个字节数组。
  3. 把字节数据按照小端编码解码128位的整数数组。
  4. 解析整数数组得到Message信息。
  5. 解析Mesage信息得到符石。

解译过程中有可能会得到非法的符石,即纪念碑。

// 无类型信息
pub(super) struct Message {
pub(super) flaw: Option<Flaw>, // 解析错误
pub(super) edicts: Vec<Edict>, // 法令:存储转移信息
pub(super) fields: HashMap<u128, VecDeque<u128>>, // 字段:存储发行和铸造信息
}

// 符石标签
enum Tag {
// Body 标签标记符石的结尾,Body之后的所有数据被解译为法令
Body = 0,
// Flags 是发行标记位,标记的位置是 1 << Flag_Value
// Flags 的值可为:
// enum Flag {
// Etching = 0,
// Terms = 1,
// Turbo = 2,
// Cenotaph = 127,
// }
Flags = 2,
// 符文名称
Rune = 4,
// 预铸造
Premine = 6,
// 允许的公开铸造次数
Cap = 8,
// 每次铸造的数量
Amount = 10,
HeightStart = 12,
HeightEnd = 14,
OffsetStart = 16,
OffsetEnd = 18,
// 铸造
Mint = 20,
Pointer = 22,
Cenotaph = 126,

Divisibility = 1,
Spacers = 3,
Symbol = 5,
Nop = 127,
}

impl Message {
// 从整数数组中解译Message
pub(super) fn from_integers(tx: &Transaction, payload: &[u128]) -> Self {
let mut edicts = Vec::new();
let mut fields: HashMap<u128, VecDeque<u128>> = HashMap::<u128, VecDeque<u128>>::new();
let mut flaw = None;

// 整数数组以key-value的方式存储
for i in (0..payload.len()).step_by(2) {
let tag = payload[i];

if Tag::Body == tag {
// Body 后的数据全部都是转移法令
let mut id = RuneId::default();
// 转移法令包含4个数:符文区块高度、符文交易索引、转移数量、输出索引
for chunk in payload[i + 1..].chunks(4) {
if chunk.len() != 4 {
flaw.get_or_insert(Flaw::TrailingIntegers);
break;
}

// 增量编码ID
let Some(next) = id.next(chunk[0], chunk[1]) else {
flaw.get_or_insert(Flaw::EdictRuneId);
break;
};

// 构造转移法令
let Some(edict) = Edict::from_integers(tx, next, chunk[2], chunk[3]) else {
flaw.get_or_insert(Flaw::EdictOutput);
break;
};

id = next;
edicts.push(edict);
}
break;
}

// 其它key-value对都只有一个
let Some(&value) = payload.get(i + 1) else {
flaw.get_or_insert(Flaw::TruncatedField);
break;
};

fields.entry(tag).or_default().push_back(value);
}

Self {
flaw,
edicts,
fields,
}
}
}

// 二进制符石
enum Payload {
Valid(Vec<u8>),
Invalid(Flaw),
}

// 符石实现
impl Runestone {
pub const MAGIC_NUMBER: opcodes::Opcode = opcodes::all::OP_PUSHNUM_13;
pub const COMMIT_CONFIRMATIONS: u16 = 6;

// 从交易中提取符石原始数据块
fn payload(transaction: &Transaction) -> Option<Payload> {
// 搜索每个输出
for output in &transaction.output {
// 锁定脚本
let mut instructions = output.script_pubkey.instructions();

// 锁定脚本需要以 OP_RETURN 开头
if instructions.next() != Some(Ok(Instruction::Op(opcodes::all::OP_RETURN))) {
continue;
}

// 后面紧接着 OP_PUSHNUM_13
if instructions.next() != Some(Ok(Instruction::Op(Runestone::MAGIC_NUMBER))) {
continue;
}

// 后续数据push串联到一起
let mut payload = Vec::new();

for result in instructions {
match result {
Ok(Instruction::PushBytes(push)) => {
payload.extend_from_slice(push.as_bytes());
}
Ok(Instruction::Op(_)) => {
return Some(Payload::Invalid(Flaw::Opcode));
}
Err(_) => {
return Some(Payload::Invalid(Flaw::InvalidScript));
}
}
}

return Some(Payload::Valid(payload));
}

None
}

// 原始数据块 => 整数数组
fn integers(payload: &[u8]) -> Result<Vec<u128>, varint::Error> {
let mut integers = Vec::new();
let mut i = 0;

while i < payload.len() {
let (integer, length) = varint::decode(&payload[i..])?;
integers.push(integer);
i += length;
}

Ok(integers)
}

// 符石解析
pub fn decipher(transaction: &Transaction) -> Option<Artifact> {
// 获取原始数据块
let payload = match Runestone::payload(transaction) {
Some(Payload::Valid(payload)) => payload,
Some(Payload::Invalid(flaw)) => {
return Some(Artifact::Cenotaph(Cenotaph {
flaw: Some(flaw),
..default()
}));
}
None => return None,
};

// 解析数组
let Ok(integers) = Runestone::integers(&payload) else {
return Some(Artifact::Cenotaph(Cenotaph {
flaw: Some(Flaw::Varint),
..default()
}));
};

// 从数据解译裸Message
let Message {
mut flaw,
edicts,
mut fields,
} = Message::from_integers(transaction, &integers);

// 获取发行标记位
let mut flags = Tag::Flags
.take(&mut fields, |[flags]| Some(flags))
.unwrap_or_default();
// 发行信息
let etching = Flag::Etching.take(&mut flags).then(|| Etching {
divisibility: Tag::Divisibility.take(&mut fields, |[divisibility]| {
let divisibility = u8::try_from(divisibility).ok()?;
(divisibility <= Etching::MAX_DIVISIBILITY).then_some(divisibility)
}),
premine: Tag::Premine.take(&mut fields, |[premine]| Some(premine)),
rune: Tag::Rune.take(&mut fields, |[rune]| Some(Rune(rune))),
spacers: Tag::Spacers.take(&mut fields, |[spacers]| {
let spacers = u32::try_from(spacers).ok()?;
(spacers <= Etching::MAX_SPACERS).then_some(spacers)
}),
symbol: Tag::Symbol.take(&mut fields, |[symbol]| {
char::from_u32(u32::try_from(symbol).ok()?)
}),
terms: Flag::Terms.take(&mut flags).then(|| Terms {
cap: Tag::Cap.take(&mut fields, |[cap]| Some(cap)),
height: (
Tag::HeightStart.take(&mut fields, |[start_height]| {
u64::try_from(start_height).ok()
}),
Tag::HeightEnd.take(&mut fields, |[start_height]| {
u64::try_from(start_height).ok()
}),
),
amount: Tag::Amount.take(&mut fields, |[amount]| Some(amount)),
offset: (
Tag::OffsetStart.take(&mut fields, |[start_offset]| {
u64::try_from(start_offset).ok()
}),
Tag::OffsetEnd.take(&mut fields, |[end_offset]| u64::try_from(end_offset).ok()),
),
}),
turbo: Flag::Turbo.take(&mut flags),
});

// 铸币信息
let mint = Tag::Mint.take(&mut fields, |[block, tx]| {
RuneId::new(block.try_into().ok()?, tx.try_into().ok()?)
});

let pointer = Tag::Pointer.take(&mut fields, |[pointer]| {
let pointer = u32::try_from(pointer).ok()?;
(u64::from(pointer) < u64::try_from(transaction.output.len()).unwrap()).then_some(pointer)
});

if etching
.map(|etching| etching.supply().is_none())
.unwrap_or_default()
{
flaw.get_or_insert(Flaw::SupplyOverflow);
}

if flags != 0 {
flaw.get_or_insert(Flaw::UnrecognizedFlag);
}

if fields.keys().any(|tag| tag % 2 == 0) {
flaw.get_or_insert(Flaw::UnrecognizedEvenTag);
}

if let Some(flaw) = flaw {
return Some(Artifact::Cenotaph(Cenotaph {
flaw: Some(flaw),
mint,
etching: etching.and_then(|etching| etching.rune),
}));
}
// 构造符石
Some(Artifact::Runestone(Self {
edicts,
etching,
mint,
pointer,
}))
}

// 符石编码进入锁定脚本
pub fn encipher(&self) -> ScriptBuf {
let mut payload = Vec::new();

// 发行信息编码
if let Some(etching) = self.etching {
let mut flags = 0;
Flag::Etching.set(&mut flags);

if etching.terms.is_some() {
Flag::Terms.set(&mut flags);
}

if etching.turbo {
Flag::Turbo.set(&mut flags);
}
// 发行位编码,key-value写入payload
Tag::Flags.encode([flags], &mut payload);

// rune 名称编码
Tag::Rune.encode_option(etching.rune.map(|rune| rune.0), &mut payload);
Tag::Divisibility.encode_option(etching.divisibility, &mut payload);
Tag::Spacers.encode_option(etching.spacers, &mut payload);
Tag::Symbol.encode_option(etching.symbol, &mut payload);
Tag::Premine.encode_option(etching.premine, &mut payload);

// 公开铸造条件编码
if let Some(terms) = etching.terms {
Tag::Amount.encode_option(terms.amount, &mut payload);
Tag::Cap.encode_option(terms.cap, &mut payload);
Tag::HeightStart.encode_option(terms.height.0, &mut payload);
Tag::HeightEnd.encode_option(terms.height.1, &mut payload);
Tag::OffsetStart.encode_option(terms.offset.0, &mut payload);
Tag::OffsetEnd.encode_option(terms.offset.1, &mut payload);
}
}

// 铸造编码
if let Some(RuneId { block, tx }) = self.mint {
Tag::Mint.encode([block.into(), tx.into()], &mut payload);
}

// 指针编码
Tag::Pointer.encode_option(self.pointer, &mut payload);

// 转移指令编码
if !self.edicts.is_empty() {
// 以Tag::Body开头
varint::encode_to_vec(Tag::Body.into(), &mut payload);

let mut edicts = self.edicts.clone();
// 先根据id排序
edicts.sort_by_key(|edict| edict.id);

let mut previous = RuneId::default();
for edict in edicts {
// 增量编码
let (block, tx) = previous.delta(edict.id).unwrap();
// 编码4个数
varint::encode_to_vec(block, &mut payload);
varint::encode_to_vec(tx, &mut payload);
varint::encode_to_vec(edict.amount, &mut payload);
varint::encode_to_vec(edict.output.into(), &mut payload);
previous = edict.id;
}
}

// payload -> script
let mut builder = script::Builder::new()
.push_opcode(opcodes::all::OP_RETURN)
.push_opcode(Runestone::MAGIC_NUMBER);

for chunk in payload.chunks(u32::MAX.try_into().unwrap()) {
let push: &script::PushBytes = chunk.try_into().unwrap();
builder = builder.push_slice(push);
}

builder.into_script()
}
}

发行 (Etching)

通过发行可以创造符文。在发行的时候可以设置符文的属性,一旦设置好了,任何人,包括发行人都不可以改变这些属性。

pub struct Etching {
pub divisibility: Option<u8>, // 可分性
pub premine: Option<u128>, // 预挖掘
pub rune: Option<Rune>, // 符文名称
pub spacers: Option<u32>, // 分隔符
pub symbol: Option<char>, // 符号
pub terms: Option<Terms>, // 公开铸造条款
pub turbo: bool, // 是否可升级
}

impl Etching {
pub const MAX_DIVISIBILITY: u8 = 38;
pub const MAX_SPACERS: u32 = 0b00000111_11111111_11111111_11111111;

// 供应量 = 预铸造 + 每次铸造数量 x 最大铸造次数
pub fn supply(&self) -> Option<u128> {
let premine = self.premine.unwrap_or_default();
let cap = self.terms.and_then(|terms| terms.cap).unwrap_or_default();
let amount = self
.terms
.and_then(|terms| terms.amount)
.unwrap_or_default();
premine.checked_add(cap.checked_mul(amount)?)
}
}

名称

符文名称由A-Z的字母组成,在1-16个字符长度之间。名字可以包含分隔符来增强可读性。注意,分隔符和名字唯一性不相干,只是增强可读性而已,相同字母顺序的符文,即使有不同的分隔符,都被看成是相同的。分隔符不算在符文名字的长度里。

符石的名称可以通过26进制的整数来编码:

Name Encoding
A 0
B 1
Y 24
Z 25
AA 26
AB 27
AY 50
AZ 51
BA 52

依次类推。

如果发行的时候忽略符文名称,会按照下述方式生成默认的名称:

fn reserve(block: u64, tx: u32) -> Rune {
Rune(6402364363415443603228541259936211926
+ (u128::from(block) << 32 | u128::from(tx))
}

6402364363415443603228541259936211926对应的是符文名称AAAAAAAAAAAAAAAAAAAAAAAAAA。最开始的时候,小于AAAAAAAAAAAAAAAAAAAAAAAAAAA及其的符文名称被锁定。新的符文名称必须在其被锁定的区块到来后才能使用。在最开始,所有的符文名称都不小于13个字符,新的符文名称在840,000区块开始解锁,这个区块也是符文协议生效的区块。在这之后,每17,500个区块周期,下一个最短的符文名称陆续被解锁。所以,在区块840,000和区块857,500之间,12字符的符文名字被解锁;在区块857,500和区块 875,000 之间,11字符的符文名字被解锁,依此类推,直到在区块1,032,500和区块1,050,000之间,1个字符的符文名字被解锁。

为了防止提前发布发行交易,如果一个非预留名称的符文被发行,发行的交易中必须包含一个已经提交的符文名称证明。这个证明需要在输入的见证中包含一个以小端整数编码的符文名称,而这个输入花费的输出必须要有6个以上的区块链确认。如果不存在这样的一个证明,那么发行会被认为无效。

可分性

可分性定义了符文的原子大小。可分性表示为符文数量中小数点后允许的位数。可分性为0的符文不可以被细分;可分性为1的符文可以被细分为10个原子单位;可分性为1的符文可以被细分为100个原子单位,一次类推。

符号

符文的货币符号是一个单独的Unicode编码,比如$,或者🧿,在符文数量的后面展示。

一个可分性为2的符号为🧿的符文的101个原子货币表示为 1.01🧿
如果一个符文没定义符号,默认的符号是通用货币符号¤,这个符号也称为圣甲虫。

预铸造

发行符文的时候可以把供应量的部分预留给发行人,这个称为预铸造。

条款(Terms)

pub struct Terms {
pub amount: Option<u128>,
pub cap: Option<u128>,
pub height: (Option<u64>, Option<u64>),
pub offset: (Option<u64>, Option<u64>),
}

符文可以公开铸造,允许任何人去获取一部分供应。公开铸造涉及到条款,条款在发行的时候来指定。

只有在所有条款都满足的条件下才可以铸造,任何一个条款不满足的情况下铸造终止。比如,一个铸造可以要求在某个起始区块和某个终止区块内进行,并且设定了铸造次数的上限。那么,只有在这两个区块之间,并且铸造次数没达到设定的上限时,铸造才可以进行。

最大铸造次数 (Cap)

一个符文可以铸造的最大次数,达到最大铸造次数后,铸造关闭。

单次铸造量 (Amount)

每个铸造创造固定量的货币。

开始区块 (Start Height)

铸造从该区块开始

结束区块 (End Height)

在该区块及之后的区块,铸造都不能进行

开始区块偏移 (Start Offset)

指定符文可以开始铸造的相对区块,相对于符文被发行的区块而言。

结束区块偏移 (End Offset)

指定服务不可以铸造的相对区块,相对于符文被发行的区块而言

下面是发行的代码实现。

// 发行 rune
fn etched(
&mut self,
tx_index: u32,
tx: &Transaction,
artifact: &Artifact,
) -> Result<Option<(RuneId, Rune)>> {
let rune = match artifact {
Artifact::Runestone(runestone) => match runestone.etching {
Some(etching) => etching.rune,
None => return Ok(None),
},
// 纪念碑里的发行也是有效的
Artifact::Cenotaph(cenotaph) => match cenotaph.etching {
Some(rune) => Some(rune),
None => return Ok(None),
},
};

let rune = if let Some(rune) = rune {
if rune < self.minimum // 最小长度要求
|| rune.is_reserved() // 符文名称是否是保留的
|| self.rune_to_id.get(rune.0)?.is_some() // 符文是否已经存在
|| !self.tx_commits_to_rune(tx, rune)? // 是否抢跑
{
return Ok(None);
}
rune
} else {
let reserved_runes = self
.statistic_to_count
.get(&Statistic::ReservedRunes.into())?
.map(|entry| entry.value())
.unwrap_or_default();

self
.statistic_to_count
.insert(&Statistic::ReservedRunes.into(), reserved_runes + 1)?;

Rune::reserved(self.height.into(), tx_index)
};

Ok(Some((
RuneId {
block: self.height.into(),
tx: tx_index,
},
rune,
)))
}

// 检查符文名称证明
fn tx_commits_to_rune(&self, tx: &Transaction, rune: Rune) -> Result<bool> {
// 符文名称编码
// 该编码需要提前commit
let commitment = rune.commitment();

for input in &tx.input {
// extracting a tapscript does not indicate that the input being spent
// was actually a taproot output. this is checked below, when we load the
// output's entry from the database
let Some(tapscript) = input.witness.tapscript() else {
continue;
};

for instruction in tapscript.instructions() {
// ignore errors, since the extracted script may not be valid
let Ok(instruction) = instruction else {
break;
};

let Some(pushbytes) = instruction.push_bytes() else {
continue;
};

// 找到commit名称的交易输出
if pushbytes.as_bytes() != commitment {
continue;
}

let Some(tx_info) = self
.client
.get_raw_transaction_info(&input.previous_output.txid, None)
.into_option()?
else {
panic!(
"can't get input transaction: {}",
input.previous_output.txid
);
};

let taproot = tx_info.vout[input.previous_output.vout.into_usize()]
.script_pub_key
.script()?
.is_p2tr();

if !taproot {
continue;
}

let commit_tx_height = self
.client
.get_block_header_info(&tx_info.blockhash.unwrap())
.into_option()?
.unwrap()
.height;

let confirmations = self
.height
.checked_sub(commit_tx_height.try_into().unwrap())
.unwrap()
+ 1;

if confirmations >= Runestone::COMMIT_CONFIRMATIONS.into() {
return Ok(true);
}
}
}

Ok(false)
}

铸造 (Minting)

当一个符文可以公开铸造时,任何人都可以通过铸造交易创造固定量的该符文的新货币。

  • 符石通过在Mint字段指定符文ID来铸造符文。
  • 如果铸造是公开的,铸造的符文会加到交易输入的未分配符文中。
  • 这些符文可以使用edicts来进行转移,否则的话,会被转移到第一个非OP_RETURN输出,或者使用Pointer字段来指定输出。
  • 铸造可以在发行后在任意的交易里进行,包括在发行的区块。

下面是铸造的代码实现。

// 铸造
fn mint(&mut self, id: RuneId) -> Result<Option<Lot>> {
// id_to_entry是rune id -> rune的存储表
let Some(entry) = self.id_to_entry.get(&id.store())? else {
return Ok(None);
};

// 加载符文
let mut rune_entry = RuneEntry::load(entry.value());

// 判断是否处于公开铸造
let Ok(amount) = rune_entry.mintable(self.height.into()) else {
return Ok(None);
};

drop(entry);

// 更新铸造信息
rune_entry.mints += 1;

self.id_to_entry.insert(&id.store(), rune_entry.store())?;

Ok(Some(Lot(amount)))
}

转移 (Transfering)

当交易的输入包含符文,或者新的符文被预铸造或者公开铸造时,这些符文被转移到了交易的输出。符文石中的法令Edicts负责定义输入如何转移到输出。

法令 (Edicts)

一个符文石可以包含任意数量的法令。法令由符文ID,符文数量以及一个输出数量组成。法令按顺序进行处理,将未分配的符文分配到交易输出。

pub struct Edict {
pub id: RuneId,
pub amount: u128,
pub output: u32,
}

符文ID中的区块高度和交易索引都采用增量编码。法令中的第一个符文ID的区块高度是绝对高度,接下来的ID编码分别是和上个ID的差值。如果差值为0,交易ID的索引也为差值,否则就是绝对索引。

下面是一个包含4个法令的例子:

block TX amount output
10 5 5 1
50 1 25 4
10 7 1 8
10 5 10 3

首先对法令按照区块高度和交易索引进行排序:

block TX amount output
10 5 5 1
10 5 10 3
10 7 1 8
50 1 25 4

接着进行增量编码:

block TX amount output
10 5 5 1
0 0 10 3
0 2 1 8
50 1 25 4

下面是符文转移的一些规则:

  • 如果一个法令分配了大于未分配状态的符文,amount 会被缩减为可分配的最大数量。
  • 在发行符文的时候还不知道符文的ID,因此可以用0:0来表示当前交易里发行的符文。
  • 指令中如果amount指定为0,那么会分配所有的未分配符文。
  • 指令中output如果等于输出utxo数量,会按次序给每个非OP_RETURN输出分配amount数量的符文。
  • 指令中如果amount指定为0,output等于utxo数量,会把该符文平均分配给每一个非OP_RETURN的输出。如果符文不可整除的话,前面R个输出会多1个符文,R是未分配符文除以非OP_RETURN输出的取余。
  • 如果任何指令的符文IDblock为0,且tx大于0,或者output大于交易输出utxo个数,这个符石被称为纪念碑。
  • 注意到,纪念碑中的指令不会被处理,交易中的所有输入符文都将被燃烧。

指针 (Pointer)

处理完所有法令后,剩余的未分配符文将传输到交易的第一个非 OP_RETURN输出。符石可以选择包含一个指针替代默认输出的指针。

燃烧 (Burning)

可以通过使用法令或指针将符文传输到OP_RETURN 输出来燃烧符文。

纪念碑

符石可能因多种原因而出现格式错误,包括符石OP_RETURN中的非推送数据操作码、无效的变体或无法识别的符石字段等,格式错误的符文石称为纪念碑.

在包含纪念碑的交易中的符文会被烧毁。在包含纪念碑的交易中蚀刻的符文被设置为不可铸造。在包含纪念碑的交易中的铸币计入铸币上限,但铸造的符文会被烧毁。

纪念碑是一种升级机制,允许符文被赋予新的语义,从而改变符文的创建和传输方式,同时不会误导未升级的客户端这些符文的位置,因为未升级的客户端会看到这些符文已被烧毁。

索引符文

通过索引符文,可以快速查询某个UTXO拥有哪些符文,以及对应的数量。

// rune 索引更新结构体
pub(super) struct RuneUpdater<'a, 'tx, 'client> {
pub(super) block_time: u32,
pub(super) burned: HashMap<RuneId, Lot>,
pub(super) client: &'client Client,
pub(super) event_sender: Option<&'a mpsc::Sender<Event>>,
pub(super) height: u32,
pub(super) id_to_entry: &'a mut Table<'tx, RuneIdValue, RuneEntryValue>,
pub(super) inscription_id_to_sequence_number: &'a Table<'tx, InscriptionIdValue, u32>,
pub(super) minimum: Rune,
pub(super) outpoint_to_balances: &'a mut Table<'tx, &'static OutPointValue, &'static [u8]>,
pub(super) rune_to_id: &'a mut Table<'tx, u128, RuneIdValue>,
pub(super) runes: u64,
pub(super) sequence_number_to_rune_id: &'a mut Table<'tx, u32, RuneIdValue>,
pub(super) statistic_to_count: &'a mut Table<'tx, u64, u64>,
pub(super) transaction_id_to_rune: &'a mut Table<'tx, &'static TxidValue, u128>,
}

impl<'a, 'tx, 'client> RuneUpdater<'a, 'tx, 'client> {
// 索引指定交易
pub(super) fn index_runes(&mut self, tx_index: u32, tx: &Transaction, txid: Txid) -> Result<()> {
// 解码该交易内的符石,一个交易只会有一个符石,按照交易输出的顺序进行查找
let artifact = Runestone::decipher(tx);

// 获取输入UTXO里面所有未分配的符文数量
let mut unallocated: HashMap<RuneId, Lot> = self.unallocated(tx)?;

// 先构造输出UTXO需要分配的符文数据结构
let mut allocated: Vec<HashMap<RuneId, Lot>> = vec![HashMap::new(); tx.output.len()];

if let Some(artifact) = &artifact {
if let Some(id) = artifact.mint() {
if let Some(amount) = self.mint(id)? {
// 对于包含铸造事件的符石,在未分配的符文总量中加入铸造的符文
*unallocated.entry(id).or_default() += amount;

if let Some(sender) = self.event_sender {
sender.blocking_send(Event::RuneMinted {
block_height: self.height,
txid,
rune_id: id,
amount: amount.n(),
})?;
}
}
}

// 发行符文
let etched = self.etched(tx_index, tx, artifact)?;

if let Artifact::Runestone(runestone) = artifact {
if let Some((id, ..)) = etched {
// 未分配符文中加入预铸造的符文
*unallocated.entry(id).or_default() +=
runestone.etching.unwrap().premine.unwrap_or_default();
}

// 转移符文
for Edict { id, amount, output } in runestone.edicts.iter().copied() {
let amount = Lot(amount);

// edicts with output values greater than the number of outputs
// should never be produced by the edict parser
let output = usize::try_from(output).unwrap();
assert!(output <= tx.output.len());

let id = if id == RuneId::default() {
// id == 0 并且符石有发行动作
let Some((id, ..)) = etched else {
continue;
};

id
} else {
id
};

// 未分配的符文需要存在
let Some(balance) = unallocated.get_mut(&id) else {
continue;
};

let mut allocate = |balance: &mut Lot, amount: Lot, output: usize| {
if amount > 0 {
*balance -= amount;
*allocated[output].entry(id).or_default() += amount;
}
};

// 如果output指定为输出的个数,会触发特殊效果
if output == tx.output.len() {
// find non-OP_RETURN outputs
let destinations = tx
.output
.iter()
.enumerate()
.filter_map(|(output, tx_out)| {
// 对于所有不是OP_RETURN的输出
(!tx_out.script_pubkey.is_op_return()).then_some(output)
})
.collect::<Vec<usize>>();

if !destinations.is_empty() {
if amount == 0 {
// if amount is zero, divide balance between eligible outputs
let amount = *balance / destinations.len() as u128;
let remainder = usize::try_from(*balance % destinations.len() as u128).unwrap();

for (i, output) in destinations.iter().enumerate() {
allocate(
balance,
if i < remainder { amount + 1 } else { amount },
*output,
);
}
} else {
// if amount is non-zero, distribute amount to eligible outputs
for output in destinations {
allocate(balance, amount.min(*balance), output);
}
}
}
} else {
// Get the allocatable amount
// 如果 amount为0,则分配所有的未分配符文
let amount = if amount == 0 {
*balance
} else {
// 否则指定的amount,如果超过最大可分配量,会截断
amount.min(*balance)
};

allocate(balance, amount, output);
}
}
}

if let Some((id, rune)) = etched {
// 索引新的符文
self.create_rune_entry(txid, artifact, id, rune)?;
}
}

// 燃烧吧,符文
let mut burned: HashMap<RuneId, Lot> = HashMap::new();

if let Some(Artifact::Cenotaph(_)) = artifact {
// 符石如果解译成了纪念碑,燃烧所有未分配符文
for (id, balance) in unallocated {
*burned.entry(id).or_default() += balance;
}
} else {
let pointer = artifact
.map(|artifact| match artifact {
Artifact::Runestone(runestone) => runestone.pointer,
Artifact::Cenotaph(_) => unreachable!(),
})
.unwrap_or_default();

// assign all un-allocated runes to the default output, or the first non
// OP_RETURN output if there is no default
if let Some(vout) = pointer
.map(|pointer| pointer.into_usize())
.inspect(|&pointer| assert!(pointer < allocated.len()))
.or_else(|| {
tx.output
.iter()
.enumerate()
.find(|(_vout, tx_out)| !tx_out.script_pubkey.is_op_return())
.map(|(vout, _tx_out)| vout)
})
{
for (id, balance) in unallocated {
if balance > 0 {
*allocated[vout].entry(id).or_default() += balance;
}
}
} else {
for (id, balance) in unallocated {
if balance > 0 {
*burned.entry(id).or_default() += balance;
}
}
}
}

// update outpoint balances
let mut buffer: Vec<u8> = Vec::new();
for (vout, balances) in allocated.into_iter().enumerate() {
if balances.is_empty() {
continue;
}

// increment burned balances
if tx.output[vout].script_pubkey.is_op_return() {
// 通过pointer可以指定输出balance到OP_RETURN的输出
for (id, balance) in &balances {
*burned.entry(*id).or_default() += *balance;
}
continue;
}

buffer.clear();

let mut balances = balances.into_iter().collect::<Vec<(RuneId, Lot)>>();

// Sort balances by id so tests can assert balances in a fixed order
balances.sort();

let outpoint = OutPoint {
txid,
vout: vout.try_into().unwrap(),
};

for (id, balance) in balances {
Index::encode_rune_balance(id, balance.n(), &mut buffer);

if let Some(sender) = self.event_sender {
sender.blocking_send(Event::RuneTransferred {
outpoint,
block_height: self.height,
txid,
rune_id: id,
amount: balance.0,
})?;
}
}

self
.outpoint_to_balances
.insert(&outpoint.store(), buffer.as_slice())?;
}

// increment entries with burned runes
for (id, amount) in burned {
*self.burned.entry(id).or_default() += amount;

if let Some(sender) = self.event_sender {
sender.blocking_send(Event::RuneBurned {
block_height: self.height,
txid,
rune_id: id,
amount: amount.n(),
})?;
}
}

Ok(())
}
}