OP_RETURN操作
- OP_RETURN 是比特币脚本语言中的一个操作码,它可以将文本、图像、哈希或其他数据形式的信息插入到交易中,而无需将比特币发送给另一方。它主要用于在比特币区块链上存储小量信息,而不会影响比特币的转账功能。OP_RETURN使得比特币不仅仅作为一种加密货币使用,还能被用作数据存储和验证的工具,同时也为其他资产和应用程序的开发提供了基础。
OP_RETURN 的优缺点
-
空间有限: OP_RETURN不适合存储大规模数据。最初,OP_RETURN 只能允许嵌入 40 字节 的数据,限制了用户可以存储的信息量。后来,随着 Bitcoin Core v0.11 的发布,OP_RETURN 的数据限制从 40 字节增加到了 80 字节,使得可以存储更多的元数据。
-
无法花费的输出:该脚本将交易输出存储在未花费交易输出 (UTXO) 集中。每次使用 OP_RETURN 操作码都会导致相应的比特币无法被花费,这在大量使用时可能会浪费比特币。
OP_RETURN 的常见用途
-
记录数据:通过 OP_RETURN,可以将任何信息存储到区块链上,比如证明、所有权声明、身份认证、用户名注册等。
-
时间戳服务:通过将特定数据(如文件的哈希值)嵌入到区块链交易中,用户可以证明该数据在某一时间点之前存在。这通常被称为区块链时间戳服务。
-
资产发行:许多基于比特币的项目使用 OP_RETURN 来实现代币或其他资产的发行,比如 Omni Layer 和 Counterparty。通过这种方式,用户可以通过比特币网络追踪和转移其他类型的资产。
-
智能合约:尽管比特币的脚本语言并不具备以太坊那样复杂的智能合约功能,但简单的合约逻辑仍然可以通过 OP_RETURN 实现。OP_RETURN 还可以用来标记和触发外部应用程序的事件。
Blockname 项目
- Blockname 项目是使用区块链注册域名(
node bin/register.js "somename.tld" 12.34.56.78
)。项目使用raw-op-return (“git+https://github.com/quartzjer/raw-op-return.git”),专门用于比特币中的OP_RETURN操作码。它允许在比特币交易中嵌入任意数据,常用于创建不可变的链上记录。依赖是通过GitHub的链接直接引用的。
// 引入必要的库
var bitcoin = require('bitcoinjs-lib'); // 用于创建和处理比特币交易的库
var opret = require('raw-op-return'); // 处理比特币的OP_RETURN输出,允许嵌入任意数据
var ip = require('ip'); // 处理IP地址的库
var level = require('level-party'); // 允许多个进程共享同一LevelDB数据库的库
// 配置命令行参数解析
var yargs = require('yargs')
.describe('db', '提示存储数据库的目录') // 说明'db'参数用于指定数据库目录
.describe('key', 'WIF格式的私钥,用于注册交易') // 说明'key'参数用于提供私钥
.boolean('test') // 定义'test'参数为布尔值
.describe('test', '使用测试网水龙头为注册提供资金') // 说明'test'参数用于测试网
.default('test', true) // 设置默认值为'true',使用测试网
.usage('Usage: $0 "domain" 1.2.3.4:5678 [satoshis] [refundto]') // 提供命令行工具的用法说明
.demand(2); // 要求至少提供2个参数
var argv = yargs.argv; // 解析命令行参数
// 获取并解析命令行输入的域名和IP地址
var domain = argv._[0]; // 获取第一个位置参数,域名
var ipp = argv._[1].split(':'); // 获取第二个位置参数,IP:端口对
try {
var server = ip.toBuffer(ipp[0]); } catch(E) {
} // 将IP地址转换为Buffer对象,便于比特币交易处理
if(!server || server.length != 4) return console.error('错误的IP地址',argv._[1]); // 验证IP地址是否有效
// 检查是否指定了端口,注册NS提示
if(ipp.length > 1) {
var port = parseInt(ipp[1]) || 53; // 获取端口号,默认为53(DNS端口)
var ns = new Buffer(2); // 创建2字节的缓冲区存储端口号
ns.writeUInt16BE(port,0); // 将端口号写入缓冲区
var hint = '*.' + domain + server.toString('hex') + ns.toString('hex'); // 构建带有端口号的NS提示
} else {
var hint = '*!' + domain + server.toString('hex'); // 构建不带端口号的提示
}
// 如果提示太大,输出错误信息
if(hint.length > 40) return console.error('提示太长,名称必须小于32个字符:', hint);
// 设置比特币网络:根据test参数选择测试网或主网
var network = argv.test ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
var key = argv.key ? bitcoin.ECKey.fromWIF(argv.key) : bitcoin.ECKey.makeRandom(); // 使用提供的私钥,或者生成一个新的私钥
var address = key.pub.getAddress(network).toString(); // 从私钥生成比特币地址
var dbdir = argv.db || (__dirname + '/db'); // 设置数据库目录
var db = level(dbdir, {
encoding: 'json' }); // 打开数据库,使用JSON编码
var helloblock = require('helloblock-js')({
network:argv.test?'testnet':'mainnet'}); // 设置helloblock-js,指定使用测试网或主网
// 可选的花费用于注册
var value = parseInt(argv._[2]) || 1000; // 默认注册花费为1000聪
var refund = argv._[3] || address; // 如果没有指定退款地址,默认使用生成的地址
// 输出一些调试信息
console.log('使用私钥 (WIF)', key.toWIF(), argv.test);
console.log('公钥地址 (需要资金)', address);
console.log('注册提示 `%s` 到 %s (OP_RETURN `%s`)', domain, ip.toString(server), hint);
console.log('花费 %d 并将余额返还到 %s', value, refund);
// 如果是测试模式,使用测试网水龙头获取资金
if(argv.test) {
helloblock.faucet.withdraw(address, 10000, function(err, res, ret) {
if(err) return console.error('faucet withdrawl failed', err);
unspent(); // 成功获取资金后,继续寻找未花费输出
});
} else {
// 实时模式,开始查找未花费的输出
unspent();
}
// 不断查找地址的未花费输出,直到找到足够的资金
function unspent() {
helloblock.addresses.getUnspents(address, function(err, res, unspents) {
if(err) return console.error('fetching unspent failed', err);
var total = 0;
unspents.forEach(function(utx) {
total += utx.value; // 累加所有未花费输出的金额
});
if(total < value) {
console.log('not enough funds found yet, waiting...', total, value);
return setTimeout(unspent, 10*1000); // 如果资金不足,等待10秒后重试
}
register(key, refund, unspents, hint); // 如果资金足够,执行注册
});
}
// 执行注册交易
function register(from, to, sources, hint) {
console.log('执行注册');
// 签署交易
var signTransaction = function(tx, callback) {
tx.sign(0, from); // 使用提供的密钥签署交易的第一个输入
callback(false, tx);
};
// 传播交易到比特币网络
var propagateTransaction = function(tx, callback) {
helloblock.transactions.propagate(tx, function(err, res, body) {
callback(err, res); // 传播完成后调用回调
});
};
// 使用OP_RETURN模块
opret.post({
stringData: hint, // 注册的提示数据
address: to, // 目标地址
fee: network.estimateFee, // 估计手续费
unspentOutputs: sources, // 使用的未花费输出
propagateTransaction: propagateTransaction, // 传播交易的回调函数
signTransaction: signTransaction // 签署交易的回调函数
}, function(error, postedTx) {
if(error) return console.error('注册错误', error);
console.log('已注册提示', postedTx.txHash); // 输出交易哈希
db.put(domain, {
ip:ip.toString(server),v:postedTx.totalInputsValue}, function() {
process.exit(); // 将注册信息保存到数据库后退出程序
});
});
}