简介门罗币(Monero,缩写:XMR)是一个创建于2014年4月开源加密货币,它着重于隐私、分权和可扩展性。与自比特币衍生的许多加密货币不同,Monero基于CryptoNote协议,并在区块链模糊化方面有显著的算法差异。第一步先安装门罗钱包并创建个人账户https://www.getmo
门罗币(Monero,缩写:XMR)是一个创建于2014年4月开源加密货币,它着重于隐私、分权和可扩展性。与自比特币衍生的许多加密货币不同,Monero基于CryptoNote协议,并在区块链模糊化方面有显著的算法差异。
https://www.getmonero.org/zh-cn/downloads/#gui
按照指引创建自己的助记词,个人秘钥对
Monero 不使用 BIP-39 标准来生成助记词。相反,Monero 使用自己的助记词系统,其生成助记词的算法与 BIP-39 不同。Monero 助记词通常基于一个随机的 256-bit 的种子,使用了专门的词汇表生成的助记词,并且其格式与 BIP-39 助记词不兼容。 助记词长度为 25个词语 和 13个词语
export function createMeneroMnemonic(len:any){
const buffer = crypto.randomBytes(32);
const hexStr = buffer.toString('hex');
const out = [];
for(let i=0; i< hexStr.length/8;i++){
const word = endianSwap(hexStr.slice(8 * i, 8 * i +8));
const x = parseInt(word, 16);
const w1 = x % n;
const w2 = (Math.floor(x / n) + w1) % n;
const w3 = (Math.floor(x / (n * n)) + w2) % n;
out.push(wordList[w1], wordList[w2], wordList[w3]);
}
// 计算校验和
const checksum = getChecksum(out.join(' '));
out.push(checksum);
return out.join(' ');
}
Monero 的助记词的推导方式 和 Bip39的最大区别是在 生成校验和时机
一 生成32字节(64位)随机熵,将其转成16进制格式 二 对这个随机熵按照每8位进行切割。进行端序调整后得到16进制字符串word 三 将word转换成为10进制整数x。 四 根据不同的算法,对x获取索引,然后根据索引到助记词库表查找对应的单词,添加到List里面 五 循环结束后,根据list里面的单词,按照一定的规则,计算校验和(取所有单词的前3个字节,拼接成一个字符串,进行crc32 hash 运算,得到非负整数,并取模,得到的结果作为索引再去助记词表中查单词,加到List中)
一 生成128~256位随机熵 二 根据这个随机熵,计算4/8位的校验和 三 将随机熵 + 校验和 拼接成一个新的16位的字符串 四 将字符串按照11位每段进行切割,每一段依次作为索引去助记词库找单词作为助记词 五 最后得到12 ~24 个的助记词列表
● 作用:secret_spend_key 是你的私密消费密钥,类似于比特币的私钥,用于签署交易,以证明你对某个 Monero 地址的所有权。 ● 安全性:它是钱包中最重要的密钥,如果泄露,任何人都可以花费你钱包中的 Monero,因此需要妥善保管。 ● 用法:当用户发起交易时,secret_spend_key 会生成相应的签名,允许花费这些 Monero。
● 作用:secret_view_key 用于查看账户中的交易情况。它允许持有者解密和查看所有交易信息,而不会影响钱包的安全性。 ● 安全性:泄露此密钥不会影响 Monero 的花费权限。很多 Monero 观察钱包(view-only wallet)使用 secret_view_key 以查看余额和交易记录。 ● 用法:商家和第三方钱包可以使用 secret_view_key 追踪收款情况,但不会暴露对资金的控制权。
● 作用:public_spend_key 是与 secret_spend_key 对应的公钥,用来生成 Monero 地址的一个部分。 ● 安全性:这是一个公开的信息,不需要加密保护。它是 Monero 地址的一部分,不会透露任何交易信息或钱包余额。 ● 用法:在生成 Monero 地址时,将 public_spend_key 和 public_view_key 结合在一起,形成完整的 Monero 地址。
● 作用:public_view_key 是与 secret_view_key 对应的公钥,用来生成 Monero 地址的另一部分。 ● 安全性:public_view_key 是公开信息,用于生成地址,而不会透露任何其他信息。 ● 用法:和 public_spend_key 结合后生成一个唯一的 Monero 地址。
在monero-ts这个SDK中,它其实已经帮我们封装好了指定的生成逻辑,因此,我们只需要调用它的SDK即可
与传统的BIP44 协议不一样的地方是,这边的Seed,其实传的就是助记词。不需要进行转换(这个坑钻研了很久)
const config = new MoneroWalletConfig();
// 只需要助记词 + 网络类型 即可
config.setSeed(mnemonic);
config.setNetworkType('mainnet')
const wallet = await MoneroWalletFull.createWallet(config);
const address = await wallet.getPrimaryAddress()
const publicSpendKey = await wallet.getPublicSpendKey();
const privateSpendKey = await wallet.getPrivateSpendKey();
const publicViewKey = await wallet.getPublicViewKey();
const privateViewKey = await wallet.getPrivateViewKey()
// 只需要网络类型 + 助记词语言类型
let walletKeys = await moneroTs.createWalletKeys({networkType: moneroTs.MoneroNetworkType.MAINNET, language: "English"});
const mnemonic = await walletKeys.getSeed();
const address = await walletKeys.getPrimaryAddress();
const spendKey = await walletKeys.getPublicSpendKey();
const privateKey = await walletKeys.getPrivateSpendKey();
console.log("Seed phrase: " + mnemonic);
console.log("Address: " + address); // get address of account 0, subaddress 0
console.log("Spend key: " + spendKey);
console.log("Private key: " + privateKey);
值得注意的是,在Monero 链上,RPC 节点分为两种 Daemon RPC Node 这个可以通过https://monero.fail/?chain=monero&network=mainnet 来获取 Wallet RPC Node 一般找不到,需要的话要自己搭建。因此通过Postman请求balance,签名,发送交易几乎无解(在没有自建节点的情况下),但是,我们可以变相的通过SDK来对其进行模拟,也能支持交易的发送
针对发送交易,monero提供了两种的发送方式
relayTx(tx) 常见与热钱包进行交易处理的情况 核心处理逻辑 一 获取 钱包的助记词,path,网络类型,密码参数 二 设置钱包的连接管理器,多设置几个PRC节点 三 开启钱包 四 设置Daemon节点 五 调用wallet.createTx创建一笔交易,该交易是已经签完名了的 六 直接调用wallet.relayTx(tx)广播交易
export async function createAndSendMeneroTransaction(){
// 根据密码打开当前的钱包
const config = new MoneroWalletConfig();
// config.setSeed(mnemonic);
config.setPath("/Users/zhulida/Monero/wallets/Leo/Leo")
// 需要被加密
config.setPassword(process.env.MENERO_PASSWORD)
config.setNetworkType('mainnet')
let connectionManager = new MoneroConnectionManager();
await connectionManager.addConnection({uri: "http://137.220.120.19:18089",priority: 1});
await connectionManager.addConnection({uri: "http://node.community.rino.io:18081"});
await connectionManager.addConnection({uri: "xmr.monopolymoney.eu:18089"});
config.setConnectionManager(connectionManager)
const wallet = await MoneroWalletFull.openWallet(config);
// const walletProxy =await wallet.getWalletProxy();
// console.log(walletProxy)
wallet.setDaemonConnection({uri: "http://137.220.120.19:18089"})
const balance = await wallet.getBalance();
const pvk = await wallet.getPublicViewKey();
console.log(balance,pvk)
// await wallet.setConnectionManager(connectionManager)
// 广播交易
let tx = await wallet.createTx({
accountIndex: 0,
address: "89PmbkZCu2s4xpkuYYHsUTGiUnpsNsaxFCqEF2J322NacY183Najv1MPxaGa7oc4XMf6dJ2Eo7fKNjZPp2SgV5sGDxxn68z",
amount: 1000000000n,
priority: MoneroTxPriority.NORMAL,
})
console.log(tx);
return await wallet.relayTx(tx);
}
submitTx(tx)常见于离线签名的场景 其核心处理逻辑如下 一 根据 助记词 和 网络类型 创建离线钱包 (不用设置节点) 二 根据 网络类型,PRIVATE_VIEW_KEY,主账户地址,和块高度,节点地址 生成 ViewOnly钱包 三 设置Daemon节点连接信息 四 对钱包进行同步处理 五 同步结束后,使用ViewOnly钱包创建一笔交易(因为没有PRIVIEW_SPEND_KEY,所以该交易还未被签名) 六 采用 离线钱包对 交易进行签名 七 签名后,再使用ViewOnly钱包发送消息submitTx
export async function signTransaction(params:any) {
const start = Date.now();
// 生成一个离线钱包(用来离线签)
const { mnemonic } = params;
const config = new MoneroWalletConfig();
config.setSeed(mnemonic);
// config.setPath('/Users/zhulida/Monero/wallets/Leo/temp/Offline');
config.setNetworkType(MoneroNetworkType.MAINNET)
// const offlineWallet = await MoneroWalletFull.openWallet(config);
let offlineWallet = await moneroTs.createWalletFull(config);
const initOfflineCost = Date.now();
console.log("离线钱包创建完成,耗时:",initOfflineCost-start);
// 生成一个viewOnly钱包(用来创建交易)
// create and sync view-only wallet without spend key
const viewOnlyConfig = new MoneroWalletConfig();
// viewOnlyConfig.setPath('/Users/zhulida/Monero/wallets/Leo/temp/ViewOnly');
viewOnlyConfig.setNetworkType(MoneroNetworkType.MAINNET);
viewOnlyConfig.setPrimaryAddress("46YkJDmikFfBAv61FH9pQsbSPm8K2nbT2eQ4gsgSCSi2jN2aE2YhjjQ39RVhxpAqzo4xoHQcGMbWzVqNnepGd3aoSevHHkU");
viewOnlyConfig.setPrivateViewKey(process.env.PRIVATE_VIEW_KEY);
viewOnlyConfig.setRestoreHeight(3260750);
viewOnlyConfig.setServer("http://137.220.120.19:18089")
let viewOnlyWallet =await moneroTs.createWalletFull(viewOnlyConfig);
// let viewOnlyWallet = await MoneroWalletFull.openWallet(viewOnlyConfig);
const initViewOnly = Date.now();
console.log("viewOnly钱包创建完成,耗时",initViewOnly-initOfflineCost);
await viewOnlyWallet.setDaemonConnection("http://137.220.120.19:18089")
await viewOnlyWallet.sync();
const sync = Date.now();
console.log("钱包同步完成,耗时",sync-initViewOnly)
const balance = await viewOnlyWallet.getBalance();
const pvk = await viewOnlyWallet.getPublicViewKey();
console.log(balance,pvk)
const tx = await viewOnlyWallet.createTx({
accountIndex: 0,
address: "89PmbkZCu2s4xpkuYYHsUTGiUnpsNsaxFCqEF2J322NacY183Najv1MPxaGa7oc4XMf6dJ2Eo7fKNjZPp2SgV5sGDxxn68z",
amount: 2000000000n,
})
const createTxConst = Date.now();
console.log("创建交易完成,耗时",createTxConst-sync)
// console.log(tx)
let signedTxSet = await offlineWallet.signTxs(tx.txSet.unsignedTxHex);
console.log("离线签名完成,耗时",Date.now()-createTxConst)
console.log(signedTxSet)
const txHash = await viewOnlyWallet.submitTxs(signedTxSet.getSignedTxHex())
console.log("交易总耗时",Date.now()-start)
return txHash
}
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!