技术 › 密钥与地址
地址
95 个字符:网络前缀、两把公钥和校验和,采用特殊的 Base58 编码
简而言之
标准门罗币地址 = Base58( prefix ‖ B ‖ A ‖ checksum )。前缀表明网络和地址类型,B 和 A 是花费公钥和查看公钥,校验和是对前面所有内容计算 Keccak-256 后的前 4 个字节。
结构
网络前缀
前缀以 varint 形式写入(较小的数只占一个字节)。由于 Base58 的工作方式,前缀决定了地址的第一个字符:
| 网络 | 标准地址 | 集成地址 | 子地址 |
|---|---|---|---|
| mainnet | 18 → 4… | 19 → 4… | 42 → 8… |
| stagenet | 24 → 5… | 25 → 5… | 36 → 7… |
| testnet | 53 → 9…/A… | 54 → A… | 63 → B… |
校验和
checksum = Keccak256( prefix ‖ B ‖ A )[0..4]
如果地址中有输入错误,校验和就对不上,钱包会拒绝发送。
门罗币的 Base58 不同于比特币的
字母表相同(不含 0 O I l),编码方式不同。比特币把整条消息变成一个大数,再转换为 58 进制。门罗币把数据切成 8 字节的块,每个块单独编码为恰好 11 个字符(最后一个不完整的块按下表编码为更少的字符)。
| 块中字节数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|
| 字符数 | 2 | 3 | 5 | 6 | 7 | 9 | 10 | 11 |
69 字节 = 8 个完整块(88 个字符)+ 5 字节(7 个字符)= 95 个字符。好处是:地址长度始终固定,编码耗时与长度成线性关系。
const ENC_BLOCK = [0, 2, 3, 5, 6, 7, 9, 10, 11];
function encodeBlock(block) {
let n = 0n;
for (const x of block) n = (n << 8n) | BigInt(x); // 块内使用大端序
let s = '';
while (n > 0n) { s = ALPHABET[Number(n % 58n)] + s; n /= 58n; }
return s.padStart(ENC_BLOCK[block.length], '1');
}
集成地址
集成地址(前缀 19)= 标准地址 + 8 字节的支付 ID(共 106 个字符)。支付 ID 在交易中被加密,帮助商店判断是哪个订单付了款。如今更推荐使用子地址:每个订单使用一个新的子地址。
地址中不包含什么
重要的是,地址永远不会被写入区块链。发送方用 A 和 B 构造一个一次性地址,只有它会进入区块链。