美文网首页区块链
比特币地址生成与验证 Nodejs 版

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

作者: 金牛茶馆 | 来源:发表于2018-12-27 17:29 被阅读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

    相关文章

      网友评论

        本文标题:比特币地址生成与验证 Nodejs 版

        本文链接:https://www.haomeiwen.com/subject/ppmrlqtx.html