最近符文Runes协议是比特币生态最火的项目,于是我利用晚上的时间,把Runes协议使用Go语言实现了一遍,项目地址:https://github.com/bxelab/runestone,另外也基于这个Runestone库编写对应的一个命令行客户端在这里,基于对Runes协议的深入理解,发现网上很多项目对符文的用法是不对的,于是我这里再写一篇技术文章,详细介绍一下。大家如果要进行符文的开发,可以参考。
Etching(蚀刻)是Rune铭文协议中用于创建新符文的结构。它包含以下可选字段:
divisibility
:表示符文的可分割性。相当于ERC20中的decimal字段,小数位数。premine
:预挖矿的数量。不设就表示不预挖rune
:符文的名称,以修改后的基数-26整数编码。spacers
:表示在符文名称字符之间显示的间隔符。symbol
:符文的货币符号。一个UTF8字符terms
:包含铸造条款,如数量、上限、开始和结束的区块高度。
这你需要进一步说明一下terms,其定义如下:type Terms struct {
Amount *uint128.Uint128 //Mint一次能够铸造的数量
Cap *uint128.Uint128 //能够Mint多少次
Height [2]*uint64 //允许Mint的开始高度和结束高度(绝对值)
Offset [2]*uint64 //允许Mint的开始高度和结束高度(相当于发行符文的高度而言的相对值)
}
因为允许预挖,而允许后续Mint,所以这个符文的发型总量就是:
预挖+Amount*Cap
符文的数采用uint128,并不是以太坊的uint256也不是比特币的uint64,所以这个符文数量也可以是很大很大的。
RuneId是标识符文的唯一标识符,由区块高度和交易索引编码而成,并以BLOCK:TX
的文本形式表示。
Edict(法令)是用于转移符文的结构,包含以下字段:
id
:涉及的符文ID。amount
:转移的符文数量。output
:指定的输出索引。Rune协议的消息称为Runestones,它们包含以下字段:
edicts
:一个Edict(法令)的集合,用于转移Rune。etching
:一个可选的Etching(蚀刻),用于创建Rune。mint
:一个可选的RuneId,表示要铸造的Rune的ID。pointer
:一个可选的u32,指向未被Edict分配的Rune应转移至的输出。Cenotaph(墓碑)是当Runestones(铭文石)不符合协议规则时产生的结构。它代表无效的铭文操作,可能导致输入的符文被销毁。也就是说如果我们定义了一个符文,但是这个符文又不满足协议规范,那么这个符文就会被标记为墓碑。
通过这些规则,Rune铭文协议确保了Rune名称的唯一性和预测性,同时也提供了一种机制来防止名称被提前抢注或滥用。这种命名规则不仅为Rune铭文协议提供了灵活性,还保证了系统的安全性和稳定性。
我们先定义了一个Etching对象,然后基于该对象构建Runestone,并使用Runestone可对其进行编码,从而得到蚀刻需要的二进制数据。示例代码如下:
runeName := "STUDYZY.GMAIL.COM"
symbol := '曾'
myRune, err := runestone.SpacedRuneFromString(runeName)
if err != nil {
fmt.Println(err)
return
}
amt := uint128.From64(666666)
ca := uint128.From64(21000000)
etching := &runestone.Etching{ //定义Etching
Rune: &myRune.Rune,
Spacers: &myRune.Spacers,
Symbol: &symbol,
Terms: &runestone.Terms{
Amount: &amt,
Cap: &ca,
},
}
r := runestone.Runestone{Etching: etching} // 构建Runestone
data, err := r.Encipher() //编码为二进制
在Rune铭文协议中,使用比特币脚本的OP_RETURN
操作码是实现Etching蚀刻内容上链的关键步骤。OP_RETURN
允许我们将特定的数据,即上面编码成二进制的符文的蚀刻信息,嵌入到比特币区块链的交易中。这些数据被永久记录在区块链上,不可篡改,为每个符文提供了一个独一无二的“印记”。
在交易的输出中使用OP_RETURN
操作码,后跟要MAGIC_NUMBER:OP_13,然后再跟蚀刻的符文信息。这些信息通常包括符文的名称、属性和其他相关数据。这笔输出不要求必须是第0号输出。
以下是构造OP_RETURN脚本的代码:
//build op_return script
builder := txscript.NewScriptBuilder()
// Push OP_RETURN
builder.AddOp(txscript.OP_RETURN)
// Push MAGIC_NUMBER
builder.AddOp(MAGIC_NUMBER)
for len(payload) > 0 {
chunkSize := txscript.MaxScriptElementSize
if len(payload) < chunkSize {
chunkSize = len(payload)
}
chunk := payload[:chunkSize]
builder.AddData(chunk)
payload = payload[chunkSize:]
}
return builder.Script()
在Rune铭文协议中,为了确保蚀刻的合法性和防止抢跑攻击(Front-running),引入了TapScript和Commitment承诺的概念。TapScript是比特币的Taproot结构的一部分,提供了一种更高效和更隐私的交易格式。
func (r Rune) Commitment() []byte {
bytes := r.Value.Big().Bytes()
// Reverse bytes to get little-endian representation
for i, j := 0, len(bytes)-1; i < j; i, j = i+1, j-1 {
bytes[i], bytes[j] = bytes[j], bytes[i]
}
end := len(bytes)
for end > 0 && bytes[end-1] == 0 {
end--
}
return bytes[:end]
}
接下来我们就可以将TapScript构建出来:
func CreateCommitmentScript(pk *btcec.PublicKey, commitment []byte) ([]byte, error) {
builder := txscript.NewScriptBuilder()
//push pubkey
pk32 := schnorr.SerializePubKey(pk)
builder.AddData(pk32)
builder.AddOp(txscript.OP_CHECKSIG)
//Commitment script
builder.AddOp(txscript.OP_FALSE)
builder.AddOp(txscript.OP_IF)
builder.AddData(commitment)
builder.AddOp(txscript.OP_ENDIF)
return builder.Script()
}
P2TR(Pay-to-Taproot)交易是一种比特币交易格式,它利用了Taproot结构来提高交易的效率和隐私性。无论是之前的Ordinals协议,BRC20或者是现在的Rune铭文,都是基于P2TR交易。在前面3.1.和3.2步骤中,我们已经构造好了我们要蚀刻铭文的TapScript了,那么接下来就需要给这个脚本对应的地址转移一定数量的BTC(这个交易就叫Commit Transaction提交交易),然后等提交交易上链成功后,等确认数>=6,则我们可以发起Reverse Transaction揭示交易,在这笔交易中才包含了3.2的TapScript和3.1的OP_RETURN。
通过这种分阶段的交易过程,Rune铭文协议实现了一种安全且透明的方式来引入新的符文到区块链中。这种机制提高了整个系统的可靠性,并且为未来的扩展和协议更新提供了灵活性。
如果我们蚀刻的符文允许Mint,满足Mint的要求(没有被Mint完,Mint高度满足要求等),那么我们可以通过构造一笔Mint交易来铸造已蚀刻定义的符文。
符文的Etching蚀刻在3.3揭示交易中上链到比特币网络,上链的区块高度和该揭示交易所在区块的交易索引值共同构成了符文的唯一标识:RuneId。我们使用 {区块高度}:{揭示交易索引值} 来标识将要铸造的符文,确保其唯一性。我们可以使用Runestone对符文ID进行编码,代码为:
runeIdStr := "2609649:946" //你要Mint的符文ID
runeId, _ := runestone.RuneIdFromString(runeIdStr)
r := runestone.Runestone{Mint: runeId}
data, err := r.Encipher()
与蚀刻类似,使用OP_RETURN
将铸造(Mint)操作记录在区块链上。不同之处在于,我们Mint的时候不再需要P2TR交易,也就是说,我们只需要一笔普通转账交易即可,而不是构造两笔交易。
Runes协议的发明人在发布符文时也在代码中硬编码预定义了一个符文:UNCOMMON•GOODS,这个符文大家都可以挖,其符文ID是:1:0,每个交易挖出一个。
在Rune铭文协议中,符文的转移是通过所谓的“法令”(Edict)来实现的,这是一种特殊的结构,用于指定如何将符文从一个所有者转移到另一个所有者。以下是符文转移过程的详细步骤和规则:
每个Edict包含三个主要字段:
id
:指定要转移的符文的ID。amount
:要转移的符文数量。output
:指定接收转移符文的输出索引。Edict的设计允许在一个Runestones中包含多个法令,从而在一个比特币交易中实现多种符文的同时转移。
runeId1, _ := runestone.RuneIdFromString("2755031:186")
runeId2, _ := runestone.RuneIdFromString("2609649:946")
r := runestone.Runestone{Edicts: []runestone.Edict{
{
ID: *runeId1,
Amount: uint128.From64(1000),
Output: 1,
},
{
ID: *runeId2,
Amount: uint128.From64(100),
Output: 1,
},
}}
data, err := r.Encipher() //Delta编码
与蚀刻和铸造过程类似,转移符文的法令也通过比特币脚本的OP_RETURN
操作码上链。这确保了转移操作的透明性和不可逆性,为所有网络参与者提供了验证转移正确性的能力。
符文的转移遵循比特币的未花费交易输出(UTXO)模型。在比特币网络中,每个交易的输出(UTXO)都代表了一定数量的比特币,可以作为下一个交易的输入。在Rune协议中,UTXO的概念被用来表示和转移特定的符文。 也就是说,我在一个Mint交易中Mint了一个符文A,并占据了1000聪的UTXO。接下来我构造一笔普通转账交易,没有OP_RETURN,把这个UTXO花费掉,并转账给用户B,那么用户B将会收到这个符文A。
在Runestones被执行时,其中的法令会按照它们出现的顺序被处理。这意味着,如果一个Runestones中有多个法令引用了相同的符文ID,它们将按照在Runestones中出现的顺序依次被处理。
在处理法令之前,所有输入的符文(包括新铸造的或预挖的符文)都是未分配的。每个法令会减少相应符文ID的未分配余额,并增加分配给交易输出的余额。 如果一个法令试图分配的符文数量超过了当前未分配的符文数量,该法令的分配数量将被减少到当前未分配的符文数量。这意味着,所有的未分配符文都将被完全分配。
amount
字段为零,则表示将分配该符文ID的所有剩余单位。output
字段等于交易输出的数量,则表示将等量的符文分配到每个非OP_RETURN
的输出。如果Runestones中的任何法令引用了无效的符文ID(例如,区块高度为零且交易索引非零),或者output
字段的值大于交易输出的数量,那么整个Runestones将被视为无效,即成为“墓碑”(Cenotaph),并且所有输入的符文都将被销毁。
当交易中的Runestones不符合协议规则时,如包含无法识别的标签或标志,输入的符文将被销毁,这通过Cenotaph(墓碑)结构来表示。
销毁符文的机制,即所谓的“墓碑”(Cenotaph),会在以下情况下被触发:
当触发销毁机制时,以下步骤会被执行:
Rune铭文协议是一个创新的区块链技术,它利用比特币网络的安全性和去中心化特性,为数字资产的创建、转移和交易提供了一个独特和高效的框架。相对BRC-20来说,具有手续费更低,扩展性更好的优点。其中也有多个特殊的名词,这在之前的区块链生态并不常用,我们需要记住:
通过TapScript+OP_RETURN:用两笔交易将蚀刻内容上链,确保其不可篡改。而且一定记住两笔交易之间的区块高度差至少要达到6。 而在Mint、转账等行为时,通过OP_RETURN实现了符文交易的上链。