区块链

比特币地址生成与验证 Nodejs 版

2018-12-27  本文已影响6人  金牛茶馆

地址生成

地址生成流程:

0 - 获取一个随机的32字节ECDSA密钥

9a9a6539856be209b8ea2adbd155c0919646d108515b60b7b13d6a79f1ae5174

1 - 使用椭圆曲线加密算法(ECDSA-secp256k1)计算上述私钥所对应的非压缩公钥

0340A609475AFA1F9A784CAD0DB5D5BA7DBAAB2147A5D7B9BBDE4D1334A0E40A5E

2 - 对(压缩)公钥进行SHA-256哈希计算

2b0995c0703c96d694f03a8987f89d387459fc359694737547a75764989c5e16

3 - 对步骤2的哈希值进行RIPEMD-160哈希计算

154de7cabbb5822075e92c57a27ca3ef3e8be50c

4 - 在步骤3的哈希值前添加地址版本号(主网为0x00,测试网为0xef)

00154de7cabbb5822075e92c57a27ca3ef3e8be50c

5 - 对步骤4的扩展RIPEMD-160哈希值进行SHA256哈希计算

ab7d579d497d75ab7e337212345635a4c071c249c6e8ec07532d2ea4d82290e6

6 - 对步骤5的哈希值再次进行SHA256哈希计算

fc897c2001ef5e99b2e37853e84dd041bebe6f831f462729de2af27e4ab9ea7e

7 - 取步骤6结果值的前4个字节作为校验码

fc897c20

8 - 将校验码添加到步骤4的扩展RIPEMD-160哈希值末尾

00154de7cabbb5822075e92c57a27ca3ef3e8be50cfc897c20

9 - 使用Base58Check编码将结果从字节字符串转换为base58字符串。

12weWzbq5jT7c3MHbHD2WP2uLXEUtaGLXZ

代码:

async createBitcoinAddress(){
      /** Create Bitcoin Address */
      const crypto = require(`crypto`);
      const ecdh = crypto.createECDH('secp256k1');
      const bs58 = require(`bs58`);
      // 0 - Having a private ECDSA key
      var privateKey = crypto.randomBytes(32);
      console.log(`Private key:[${privateKey.toString(`hex`)}]`);
      // 1 - Take the corresponding public key generated with it (33 bytes, 1 byte 0x02 (y-coord is even), 
      // and 32 bytes corresponding to X coordinate)
      ecdh.setPrivateKey(privateKey);
      var cpublicKey = Buffer.from(ecdh.getPublicKey('hex', 'compressed'), 'hex');
      console.log(`Public key:[${cpublicKey.toString(`hex`).toUpperCase()}]`);
      // 2 - Perform SHA-256 hashing on the public key
      var sha1 = crypto.createHash(`sha256`).update(cpublicKey).digest();
      console.log(`SHA-256:[${sha1.toString(`hex`)}]`);
      // 3 - Perform RIPEMD-160 hashing on the result of SHA-256
      var ripemd160 = crypto.createHash(`rmd160`).update(sha1).digest();
      console.log(`RIPEMD-160:[${ripemd160.toString(`hex`)}]`);
      // 4 - Add version byte in front of RIPEMD-160 hash (0x00 for Main Network, 0x6f for Testnet)
      const version = Buffer.from([0x00]);
      var extendedPriKey = Buffer.alloc(ripemd160.length + version.length);
      extendedPriKey = Buffer.concat([version, ripemd160], extendedPriKey.length);
      console.log(`Extended RIPEMD-160:[${extendedPriKey.toString(`hex`)}]`);
      // 5 - Perform SHA-256 hash on the extended RIPEMD-160 result
      var sha2 = crypto.createHash(`sha256`).update(extendedPriKey).digest();
      console.log(`SHA-256:[${sha2.toString(`hex`)}]`);
      // 6 - Perform SHA-256 hash on the result of the previous SHA-256 hash
      var sha3 = crypto.createHash(`sha256`).update(sha2).digest();
      console.log(`SHA-256:[${sha3.toString(`hex`)}]`);
      // 7 - Take the first 4 bytes of the second SHA-256 hash. This is the address checksum
      var checksum = Buffer.alloc(4);
      sha3.copy(checksum, 0, 0, checksum.length);
      console.log(`Checksum:[${checksum.toString(`hex`)}]`);
      // 8 - Add the 4 checksum bytes from stage 7 at the end of extended RIPEMD-160 hash from stage 4. 
      // This is the 25-byte binary Bitcoin Address.
      var btcAddress = Buffer.alloc(extendedPriKey.length + checksum.length);
      btcAddress = Buffer.concat([extendedPriKey, checksum], btcAddress.length);
      console.log(`25-byte binary bitcoin address:[${btcAddress.toString(`hex`)}]`);
      // 9 - Convert the result from a byte string into a base58 string using Base58Check encoding. 
      // This is the most commonly used Bitcoin Address format
      var address = bs58.encode(btcAddress);
      console.log(`Address:[${address}]`);
      this.success(address);

    }

校验流程

根据生成流程来实现校验方式。

1.把地址base58解码成字节数组
2.把数组分成两个字节数组,字节数组(一)是后4字节数组,字节数组(二)是减去后4字节的数组
3.把字节数组(二)两次Sha256 Hash
4.取字节数组(二)hash后的前4位,跟字节数组(一)比较。如果相同校验通过。
5.校验通过的解码字节数组取第一个字节(0xff),得到版本号
6.检验版本号的合法性(根据主网参数校验)

代码:

async isAddress(address) {
      try{
        // 1.把地址base58解码成字节数组
        const arr = bs58.decode(address);
        const buf = new Buffer(arr);

        // 2.把数组分成两个字节数组,字节数组(一)是后4字节数组,字节数组(二)是减去后4字节的数组
        const checksum = buf.slice(-4);
        const bytes = buf.slice(0, buf.length - 4);

        // 3.把字节数组(二)两次Sha256 Hash
        const shax1 = createHash('sha256').update(bytes).digest();
        const shax2 = createHash('sha256').update(shax1).digest();
        // 4.取字节数组(二)hash后的前4位,跟字节数组(一)比较。如果相同校验通过。
        const newChecksum = shax2.slice(0, 4);
        if (checksum.toString('hex') !== newChecksum.toString('hex')) {
          throw new Error('Invalid checksum');
        }
        // 5.校验通过的解码字节数组取第一个字节(0xff),得到版本号
        const version = buf.toString('hex').slice(0,2);
        // 6.检验版本号的合法性(根据主网参数校验)00 为普通地址,05为脚本地址,注意大小写。
        if(version !== '00' && version !== '05'){
          throw new Error('Invalid version');
        }
        
      }catch(e){
        return false;
      }
      return true;
    }
  }

参考文档:https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses
附网络判断码:

Crypto Coin Public Address Private Wallet Import Format Script Hash
BTC 0x00 0x80 0x05
BTC-TEST 0x6F 0xEF 0xC4
DOGE 0x1E 0x9E 0x16
DOGE-TEST 0x71 0xF1 0xC4
LTC 0x30 0xB0 0x05
LTC-TEST 0x6F 0xEF 0xC4
NMC 0x34 0xB4 0x05
PPC 0x37 0xB7 0x75
URO 0x44 0xC4 0x05
上一篇下一篇

猜你喜欢

热点阅读