使用 Node.js 创建一个比特币地址

前言

在之前的文章有写过比特币地址的生成原理,感兴趣的小伙伴可以点击 详解比特币地址的生成原理 这一篇文章进行查看。

我是不相信那些在线方式创建的比特币地址,所以决定自己写代码创建一个,这样全程都是我操作的,所以比较放心。

根据比特币地址生成原理那篇文章提到的算法,我把每一步的值列出来

每一步算法的结果

下面这些结果用来做算法校验。

// 第1步
let ECDSA_PrivateKey = '18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725';

// 第2步
let ECDSA_PublicKey = '0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352';

// 第3步
let ECDSA_PublicKey_SHA256 = '0b7c28c9b7290c98d7438e70b3d3f7c848fbd7d1dc194ff83f4f7cc9b1378e98';

// 第4步
let ECDSA_PublicKey_SHA256_RIPEMD160 = 'f54a5851e9372b87810a8e60cdd2e7cfd80b6e31';

// 第5步: 添加 Version
let MAIN_NETWORK_VERSION = '00';
let ECDSA_PublicKey_SHA256_RIPEMD160_MainNetwork = MAIN_NETWORK_VERSION + ECDSA_PublicKey_SHA256_RIPEMD160;

// 第6步: 将 ECDSA_PublicKey_SHA256_RIPEMD160 进行第1次 SHA256 计算
let ECDSA_PublicKey_SHA256_RIPEMD160_SHA256 = 'ad3c854da227c7e99c4abfad4ea41d71311160df2e415e713318c70d67c6b41c';

// 第7步: 将第6步的结果进行第2次 SHA256 计算
let ECDSA_PublicKey_SHA256_RIPEMD160_SHA256_SHA256 = 'c7f18fe8fcbed6396741e58ad259b5cb16b7fd7f041904147ba1dcffabf747fd';

// 第8步: 取第二次 SHA256Hash 结果的前 4 个字节(这是地址的校验和)
let ECDSA_PublicKey_SHA256_RIPEMD160_Checksum = 'c7f18fe8'

// 第9步: 计算
let ECDSA_PublicKey_SHA256_RIPEMD160_Checksum_Key = MAIN_NETWORK_VERSION + ECDSA_PublicKey_SHA256_RIPEMD160 + ECDSA_PublicKey_SHA256_RIPEMD160_Checksum;

// 第10步: base58
let bitcoinAddress = '1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs';

写代码进行校验

const crypto = require('crypto');
const bs58 = require(`bs58`);

// 第1步
let ECDSA_PrivateKey = Buffer.from('18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725', 'hex');
let privateKey = ECDSA_PrivateKey || crypto.randomBytes(32);//使用做例子测试
console.log(`第1步:私钥:${privateKey.toString("hex")}`,);

// 第2步
let ecdh = crypto.createECDH('secp256k1').setPrivateKey(privateKey);
let cpublicKey = Buffer.from(ecdh.getPublicKey('hex', 'compressed'), 'hex');
let ECDSA_PublicKey = '0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352';
console.log(`
第2步:椭圆曲线加密算法生成公钥:${cpublicKey.toString(`hex`).toLowerCase()}
校验==>${cpublicKey.toString(`hex`).toLowerCase() === ECDSA_PublicKey}
`);

// 第3步
let sha1 = crypto.createHash('sha256').update(Buffer.from(ECDSA_PublicKey, 'hex')).digest();
let ECDSA_PublicKey_SHA256 = '0b7c28c9b7290c98d7438e70b3d3f7c848fbd7d1dc194ff83f4f7cc9b1378e98';
console.log(`
第3步:SHA256处理:${sha1.toString('hex')}
校验==>${sha1.toString('hex') === ECDSA_PublicKey_SHA256}
`);


// 第4步
let pubkeyHash = crypto.createHash('ripemd160').update(sha1).digest();
let ECDSA_PublicKey_SHA256_RIPEMD160 = 'f54a5851e9372b87810a8e60cdd2e7cfd80b6e31';
console.log(`
第4步:RIPEMD160处理:${pubkeyHash.toString('hex')}
校验==>${pubkeyHash.toString('hex') === ECDSA_PublicKey_SHA256_RIPEMD160}
`);

// 第5步: 添加 Version
const version = Buffer.from([0x00]);
let extendedPriKey = Buffer.alloc(version.length + pubkeyHash.length);
extendedPriKey = Buffer.concat([version, pubkeyHash], extendedPriKey.length);

let MAIN_NETWORK_VERSION = '00';
let ECDSA_PublicKey_SHA256_RIPEMD160_MainNetwork = MAIN_NETWORK_VERSION + ECDSA_PublicKey_SHA256_RIPEMD160;
console.log(`
第5步:添加 Version:${extendedPriKey.toString(`hex`)}
校验==>${extendedPriKey.toString('hex') === ECDSA_PublicKey_SHA256_RIPEMD160_MainNetwork}
`);

// 第6步: 将 ECDSA_PublicKey_SHA256_RIPEMD160 进行第1次 SHA256 计算
let sha2 = crypto.createHash(`sha256`).update(Buffer.from(extendedPriKey, 'hex')).digest();
let ECDSA_PublicKey_SHA256_RIPEMD160_SHA256 = 'ad3c854da227c7e99c4abfad4ea41d71311160df2e415e713318c70d67c6b41c';
console.log(`
第6步:SHA256:${sha2.toString(`hex`)}
校验==>${sha2.toString('hex') === ECDSA_PublicKey_SHA256_RIPEMD160_SHA256}
`);

// 第7步: 将第6步的结果进行第2次 SHA256 计算
let sha3 = crypto.createHash('sha256').update(sha2).digest();
let ECDSA_PublicKey_SHA256_RIPEMD160_SHA256_SHA256 = 'c7f18fe8fcbed6396741e58ad259b5cb16b7fd7f041904147ba1dcffabf747fd';
console.log(`
第7步:SHA256:${sha3.toString(`hex`)}
校验==>${sha3.toString('hex') === ECDSA_PublicKey_SHA256_RIPEMD160_SHA256_SHA256}
`);

// 第8步: 取第二次 SHA256Hash 结果的前 4 个字节(这是地址的校验和)
let checksum = Buffer.alloc(4);
sha3.copy(checksum, 0, 0, checksum.length);
let ECDSA_PublicKey_SHA256_RIPEMD160_Checksum = 'c7f18fe8'
console.log(`
第8步:校验和:${checksum.toString(`hex`)}
校验==>${checksum.toString('hex') === ECDSA_PublicKey_SHA256_RIPEMD160_Checksum}
`);

// 第9步: 计算
let btcAddress = Buffer.alloc(extendedPriKey.length + checksum.length);
btcAddress = Buffer.concat([extendedPriKey, checksum], btcAddress.length);
let ECDSA_PublicKey_SHA256_RIPEMD160_Checksum_Key = MAIN_NETWORK_VERSION + ECDSA_PublicKey_SHA256_RIPEMD160 + ECDSA_PublicKey_SHA256_RIPEMD160_Checksum;
console.log(`
第9步:计算二进制地址:${btcAddress.toString(`hex`)}
校验==>${btcAddress.toString('hex') === ECDSA_PublicKey_SHA256_RIPEMD160_Checksum_Key}
`);


// 第10步: base58
let address = bs58.encode(btcAddress);
let bitcoinAddress = '1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs';
console.log(`
第10步:base58:${address.toString(`hex`)}
校验==>${address.toString('hex') === bitcoinAddress}
`);

校验结果如下

全部通过校验

最终代码

简单的封装一下

"use strict";
let utility = {
    // 生成比特币地址
    createBitcoinAddress() {
        const crypto = require('crypto');
        const bs58 = require(`bs58`);

        // 第1步
        let privateKey = crypto.randomBytes(32);
        // Test
        // let privateKey = Buffer.from('18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725', 'hex');

        // 第2步
        let ecdh = crypto.createECDH('secp256k1').setPrivateKey(privateKey);
        let cpublicKey = Buffer.from(ecdh.getPublicKey('hex', 'compressed'), 'hex');

        // 第3步
        let sha1 = crypto.createHash('sha256').update(Buffer.from(cpublicKey, 'hex')).digest();

        // 第4步
        let pubkeyHash = crypto.createHash('ripemd160').update(sha1).digest();

        // 第5步: 添加 Version
        const version = Buffer.from([0x00]);
        let extendedPriKey = Buffer.alloc(version.length + pubkeyHash.length);
        extendedPriKey = Buffer.concat([version, pubkeyHash], extendedPriKey.length);

        // 第6步: 将 ECDSA_PublicKey_SHA256_RIPEMD160 进行第1次 SHA256 计算
        let sha2 = crypto.createHash(`sha256`).update(Buffer.from(extendedPriKey, 'hex')).digest();


        // 第7步: 将第6步的结果进行第2次 SHA256 计算
        let sha3 = crypto.createHash('sha256').update(sha2).digest();

        // 第8步: 取第二次 SHA256Hash 结果的前 4 个字节(这是地址的校验和)
        let checksum = Buffer.alloc(4);
        sha3.copy(checksum, 0, 0, checksum.length);

        // 第9步: 计算
        let btcAddress = Buffer.alloc(extendedPriKey.length + checksum.length);
        btcAddress = Buffer.concat([extendedPriKey, checksum], btcAddress.length);

        // 第10步: base58
        let address = bs58.encode(btcAddress);

        return {
            address,
            privateKey: privateKey.toString(`hex`)
        };
    }
}
console.log(utility.createBitcoinAddress());

总结

如果您也打算自己创建一个比特币地址,又恰好您是一名会写 JavaScript 代码的开发者。

那么选择自己创建不仅是一件很酷的事情,它可以让你创建出来的地址绝对的安全。