格式化
This commit is contained in:
		
							parent
							
								
									34b4a8b440
								
							
						
					
					
						commit
						8c0fcdc3be
					
				
							
								
								
									
										497
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										497
									
								
								index.js
									
									
									
									
									
								
							@ -1,5 +1,5 @@
 | 
				
			|||||||
// const BigNumber=require('bignumber.js') // 处理整数 https://github.com/MikeMcl/bignumber.js
 | 
					// const BigNumber=require('bignumber.js') // 处理整数 https://github.com/MikeMcl/bignumber.js
 | 
				
			||||||
const BigInt = require("big-integer") // 处理整数 https://github.com/peterolson/BigInteger.js
 | 
					const BigInt = require('big-integer') // 处理整数 https://github.com/peterolson/BigInteger.js
 | 
				
			||||||
const crypto = require('crypto')
 | 
					const crypto = require('crypto')
 | 
				
			||||||
const nacl = require('tweetnacl')
 | 
					const nacl = require('tweetnacl')
 | 
				
			||||||
const bs58check = require('bs58check')
 | 
					const bs58check = require('bs58check')
 | 
				
			||||||
@ -32,24 +32,28 @@ my.COIN_LIST=['TIC','BTC','ETH']
 | 
				
			|||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  isHashable(data, { strict = false } = {}) {
 | 
					  isHashable(data, { strict = false } = {}) {
 | 
				
			||||||
    if (strict) {
 | 
					    if (strict) {
 | 
				
			||||||
      return data && typeof(data)!=='boolean' && data!==Infinity // 允许大多数数据,除了空值、布尔值、无限数
 | 
					      return data && typeof data !== 'boolean' && data !== Infinity // 允许大多数数据,除了空值、布尔值、无限数
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return typeof(data)!=='undefined' // 允许一切数据,除非 undefined
 | 
					    return typeof data !== 'undefined' // 允许一切数据,除非 undefined
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  isHash(hash, { hasher = my.HASHER } = {}) {
 | 
					  isHash(hash, { hasher = my.HASHER } = {}) {
 | 
				
			||||||
    if (my.HASHER_LIST.indexOf(hasher) >= 0) {
 | 
					    if (my.HASHER_LIST.indexOf(hasher) >= 0) {
 | 
				
			||||||
      switch (hasher) {
 | 
					      switch (hasher) {
 | 
				
			||||||
        case 'sha256': return /^[a-fA-F0-9]{64}$/.test(hash)
 | 
					        case 'sha256':
 | 
				
			||||||
        case 'md5': return /^[a-fA-F0-9]{32}$/.test(hash)
 | 
					          return /^[a-fA-F0-9]{64}$/.test(hash)
 | 
				
			||||||
        case 'ripemd160': case 'sha1': return /^[a-fA-F0-9]{40}$/.test(hash)
 | 
					        case 'md5':
 | 
				
			||||||
        case 'sha512': return /^[a-fA-F0-9]{128}$/.test(hash)
 | 
					          return /^[a-fA-F0-9]{32}$/.test(hash)
 | 
				
			||||||
 | 
					        case 'ripemd160':
 | 
				
			||||||
 | 
					        case 'sha1':
 | 
				
			||||||
 | 
					          return /^[a-fA-F0-9]{40}$/.test(hash)
 | 
				
			||||||
 | 
					        case 'sha512':
 | 
				
			||||||
 | 
					          return /^[a-fA-F0-9]{128}$/.test(hash)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return false
 | 
					    return false
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  isSecword(secword, { mode = 'strict' } = {}) {
 | 
				
			||||||
  isSecword(secword, {mode='strict'}={}){ // 注意 not all 12 words combinations are valid for both bitcore and bip39, because there are checksum in mnemonic. 另外,实际上bitcore和bip39对12, 15, 18, ... 长度的合法助记词都返回 true。
 | 
					    // 注意 not all 12 words combinations are valid for both bitcore and bip39, because there are checksum in mnemonic. 另外,实际上bitcore和bip39对12, 15, 18, ... 长度的合法助记词都返回 true。
 | 
				
			||||||
    //// for bitcore-mnemonic. 注意,bitcore-mnemonic 对少于12词的会抛出异常,很蠢。
 | 
					    //// for bitcore-mnemonic. 注意,bitcore-mnemonic 对少于12词的会抛出异常,很蠢。
 | 
				
			||||||
    // if (typeof secword==='string' && 12===secword.split(/ +/).length)
 | 
					    // if (typeof secword==='string' && 12===secword.split(/ +/).length)
 | 
				
			||||||
    //   return BitcoreMnemonic.isValid(secword)
 | 
					    //   return BitcoreMnemonic.isValid(secword)
 | 
				
			||||||
@ -61,58 +65,49 @@ module.exports = {
 | 
				
			|||||||
      if (mode === 'easy') return true // easy模式不检查校验等等严格的合法性了,反正 secword2seed是接受一切字符串的
 | 
					      if (mode === 'easy') return true // easy模式不检查校验等等严格的合法性了,反正 secword2seed是接受一切字符串的
 | 
				
			||||||
      for (let lang of Object.keys(bip39.wordlists)) {
 | 
					      for (let lang of Object.keys(bip39.wordlists)) {
 | 
				
			||||||
        bip39.setDefaultWordlist(lang)
 | 
					        bip39.setDefaultWordlist(lang)
 | 
				
			||||||
        if (bip39.validateMnemonic(secword))
 | 
					        if (bip39.validateMnemonic(secword)) return true
 | 
				
			||||||
          return true
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return false
 | 
					    return false
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  isSeckey(seckey) {
 | 
					  isSeckey(seckey) {
 | 
				
			||||||
    // 比特币、以太坊的私钥:64 hex
 | 
					    // 比特币、以太坊的私钥:64 hex
 | 
				
			||||||
    // nacl.sign 的私钥 128 hex, nacl.box 的私钥 64 hex
 | 
					    // nacl.sign 的私钥 128 hex, nacl.box 的私钥 64 hex
 | 
				
			||||||
    return /^([a-fA-F0-9]{128}|[a-fA-F0-9]{64})$/.test(seckey)
 | 
					    return /^([a-fA-F0-9]{128}|[a-fA-F0-9]{64})$/.test(seckey)
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  isPubkey(pubkey) {
 | 
					  isPubkey(pubkey) {
 | 
				
			||||||
    // 比特币的公钥:压缩型 '02|03' + 64 hex 或 无压缩型 '04' + 128 hex
 | 
					    // 比特币的公钥:压缩型 '02|03' + 64 hex 或 无压缩型 '04' + 128 hex
 | 
				
			||||||
    // 以太坊的公钥:'02|03' + 64 hex
 | 
					    // 以太坊的公钥:'02|03' + 64 hex
 | 
				
			||||||
    // nacl.sign 的公钥:64 hex
 | 
					    // nacl.sign 的公钥:64 hex
 | 
				
			||||||
    return /^((02|03)?[a-fA-F0-9]{64}|04[a-fA-F0-9]{128})$/.test(pubkey) // "d2f186a630f5558ba3ede10a4dd0549da5854eab3ed28ee8534350c2535d38b0"
 | 
					    return /^((02|03)?[a-fA-F0-9]{64}|04[a-fA-F0-9]{128})$/.test(pubkey) // "d2f186a630f5558ba3ede10a4dd0549da5854eab3ed28ee8534350c2535d38b0"
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  isSignature(signature) {
 | 
					  isSignature(signature) {
 | 
				
			||||||
    return /^[a-fA-F0-9]{128,144}$/.test(signature) && (signature.length % 2 === 0) // 128 for nacl, 140/142/144 for crypto and eccrypto in der format.
 | 
					    return /^[a-fA-F0-9]{128,144}$/.test(signature) && signature.length % 2 === 0 // 128 for nacl, 140/142/144 for crypto and eccrypto in der format.
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  hash(data, { hasher = my.HASHER, salt, input = my.INPUT, output = my.OUTPUT } = {}) {
 | 
				
			||||||
  hash(data, {hasher=my.HASHER, salt, input=my.INPUT, output=my.OUTPUT}={}){ // data can be anything, but converts to string or remains be Buffer/TypedArray/DataView
 | 
					    // data can be anything, but converts to string or remains be Buffer/TypedArray/DataView
 | 
				
			||||||
    if (this.isHashable(data)) {
 | 
					    if (this.isHashable(data)) {
 | 
				
			||||||
      if (typeof(data)!=='string' && !(data instanceof Buffer) && !(data instanceof DataView)) 
 | 
					      if (typeof data !== 'string' && !(data instanceof Buffer) && !(data instanceof DataView)) data = JSON.stringify(data)
 | 
				
			||||||
        data=JSON.stringify(data)
 | 
					      if (salt && typeof salt === 'string') data = data + this.hash(salt)
 | 
				
			||||||
      if (salt && typeof(salt)==='string')
 | 
					 | 
				
			||||||
        data=data+this.hash(salt)
 | 
					 | 
				
			||||||
      let inputEncoding = input // my.INPUT_LIST.indexOf(option.input)>=0?option.input:my.INPUT // 'utf8', 'ascii' or 'latin1' for string data, default to utf8 if not specified; ignored for Buffer, TypedArray, or DataView.
 | 
					      let inputEncoding = input // my.INPUT_LIST.indexOf(option.input)>=0?option.input:my.INPUT // 'utf8', 'ascii' or 'latin1' for string data, default to utf8 if not specified; ignored for Buffer, TypedArray, or DataView.
 | 
				
			||||||
      let outputEncoding=(output==='buf')?undefined:output // (my.OUTPUT_LIST.indexOf(output)>=0?output:my.OUTPUT)  // option.output: 留空=》默认输出hex格式;或者手动指定 'buf', hex', 'latin1' or 'base64'
 | 
					      let outputEncoding = output === 'buf' ? undefined : output // (my.OUTPUT_LIST.indexOf(output)>=0?output:my.OUTPUT)  // option.output: 留空=》默认输出hex格式;或者手动指定 'buf', hex', 'latin1' or 'base64'
 | 
				
			||||||
      return crypto.createHash(hasher).update(data, inputEncoding).digest(outputEncoding)
 | 
					      return crypto.createHash(hasher).update(data, inputEncoding).digest(outputEncoding)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  async encrypt(data, { tool, keytype, key, input, output, cipher } = {}) {
 | 
					  async encrypt(data, { tool, keytype, key, input, output, cipher } = {}) {
 | 
				
			||||||
    if (keytype === 'pwd') {
 | 
					    if (keytype === 'pwd') {
 | 
				
			||||||
      if (typeof(key)==='string') {
 | 
					      if (typeof key === 'string') {
 | 
				
			||||||
        let inputEncoding = my.INPUT_LIST.indexOf(input) >= 0 ? input : my.INPUT // 'utf8' by default, 'ascii', 'latin1' for string  or ignored for Buffer/TypedArray/DataView
 | 
					        let inputEncoding = my.INPUT_LIST.indexOf(input) >= 0 ? input : my.INPUT // 'utf8' by default, 'ascii', 'latin1' for string  or ignored for Buffer/TypedArray/DataView
 | 
				
			||||||
        let outputEncoding=(output==='buf')?undefined:(my.OUTPUT_LIST.indexOf(output)>=0?output:my.OUTPUT) // 'latin1', 'base64', 'hex' by default or 'buf' to Buffer explicitly
 | 
					        let outputEncoding = output === 'buf' ? undefined : my.OUTPUT_LIST.indexOf(output) >= 0 ? output : my.OUTPUT // 'latin1', 'base64', 'hex' by default or 'buf' to Buffer explicitly
 | 
				
			||||||
        let ciph=crypto.createCipher(
 | 
					        let ciph = crypto.createCipher(my.CIPHER_LIST.indexOf(cipher) >= 0 ? cipher : my.CIPHER, this.hash(key))
 | 
				
			||||||
          my.CIPHER_LIST.indexOf(cipher)>=0?cipher:my.CIPHER, 
 | 
					        if (typeof data !== 'string' && !(data instanceof Buffer) && !(data instanceof DataView)) data = JSON.stringify(data)
 | 
				
			||||||
          this.hash(key))
 | 
					 | 
				
			||||||
        if (typeof(data)!=='string' && !(data instanceof Buffer) && !(data instanceof DataView)) 
 | 
					 | 
				
			||||||
          data=JSON.stringify(data)
 | 
					 | 
				
			||||||
        let encrypted = ciph.update(data, inputEncoding, outputEncoding)
 | 
					        let encrypted = ciph.update(data, inputEncoding, outputEncoding)
 | 
				
			||||||
        encrypted += ciph.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
 | 
					        encrypted += ciph.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
 | 
				
			||||||
        return encrypted
 | 
					        return encrypted
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }else if (tool==='eccrypto') {  // data 应当是 utf8 的字符串。// 但在浏览器里不能使用 Failed to execute 'encrypt' on 'SubtleCrypto': The provided value is not of type '(ArrayBuffer or ArrayBufferView)'
 | 
					    } else if (tool === 'eccrypto') {
 | 
				
			||||||
 | 
					      // data 应当是 utf8 的字符串。// 但在浏览器里不能使用 Failed to execute 'encrypt' on 'SubtleCrypto': The provided value is not of type '(ArrayBuffer or ArrayBufferView)'
 | 
				
			||||||
      let cipherobject = await eccrypto.encrypt(this.hex2buf(key), data)
 | 
					      let cipherobject = await eccrypto.encrypt(this.hex2buf(key), data)
 | 
				
			||||||
      return cipherobject
 | 
					      return cipherobject
 | 
				
			||||||
    } else if (keytype === 'seckey') {
 | 
					    } else if (keytype === 'seckey') {
 | 
				
			||||||
@ -123,19 +118,18 @@ module.exports = {
 | 
				
			|||||||
      return crypto.publicEncrypt(pubkeyPEM, Buffer.from(data))
 | 
					      return crypto.publicEncrypt(pubkeyPEM, Buffer.from(data))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  async decrypt(data, { keytype, key, input, output, cipher, format } = {}) {
 | 
				
			||||||
  async decrypt(data, {keytype, key, input, output, cipher, format}={}){ // data 应当是 encrypt 输出的数据类型
 | 
					    // data 应当是 encrypt 输出的数据类型
 | 
				
			||||||
    if (keytype === 'pwd') {
 | 
					    if (keytype === 'pwd') {
 | 
				
			||||||
      if (data && (typeof(data)==='string' || data instanceof Buffer) && typeof(key)==='string') {
 | 
					      if (data && (typeof data === 'string' || data instanceof Buffer) && typeof key === 'string') {
 | 
				
			||||||
        let inputEncoding = my.OUTPUT_LIST.indexOf(input) >= 0 ? input : my.OUTPUT // input (=output of encrypt) could be 'latin1', 'base64', 'hex' by default for string or ignored for Buffer
 | 
					        let inputEncoding = my.OUTPUT_LIST.indexOf(input) >= 0 ? input : my.OUTPUT // input (=output of encrypt) could be 'latin1', 'base64', 'hex' by default for string or ignored for Buffer
 | 
				
			||||||
        let outputEncoding=(output==='buf')?undefined:(my.INPUT_LIST.indexOf(output)>=0?output:my.INPUT) // output (=input of encrypt) could be 'latin1', 'ascii', 'utf8' by default or  'buf' to Buffer explicitly
 | 
					        let outputEncoding = output === 'buf' ? undefined : my.INPUT_LIST.indexOf(output) >= 0 ? output : my.INPUT // output (=input of encrypt) could be 'latin1', 'ascii', 'utf8' by default or  'buf' to Buffer explicitly
 | 
				
			||||||
        let decipher=crypto.createDecipher(
 | 
					        let decipher = crypto.createDecipher(my.CIPHER_LIST.indexOf(cipher) >= 0 ? cipher : my.CIPHER, this.hash(key))
 | 
				
			||||||
          my.CIPHER_LIST.indexOf(cipher)>=0?cipher:my.CIPHER, 
 | 
					 | 
				
			||||||
          this.hash(key))
 | 
					 | 
				
			||||||
        let decrypted = decipher.update(data, inputEncoding, outputEncoding)
 | 
					        let decrypted = decipher.update(data, inputEncoding, outputEncoding)
 | 
				
			||||||
        decrypted += decipher.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
 | 
					        decrypted += decipher.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
 | 
				
			||||||
        if (format==='json') { // 如果用户输入错误密码,deciper也能返回结果。为了判断是否正确结果,对应当是 json 格式的原文做解析来验证。
 | 
					        if (format === 'json') {
 | 
				
			||||||
 | 
					          // 如果用户输入错误密码,deciper也能返回结果。为了判断是否正确结果,对应当是 json 格式的原文做解析来验证。
 | 
				
			||||||
          try {
 | 
					          try {
 | 
				
			||||||
            JSON.parse(decrypted)
 | 
					            JSON.parse(decrypted)
 | 
				
			||||||
          } catch (exception) {
 | 
					          } catch (exception) {
 | 
				
			||||||
@ -144,28 +138,33 @@ module.exports = {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        return decrypted
 | 
					        return decrypted
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }else if (keytype==='seckey'){ // cipherobject 需要是 eccrypto 自身encrypt方法返回的对象
 | 
					    } else if (keytype === 'seckey') {
 | 
				
			||||||
 | 
					      // cipherobject 需要是 eccrypto 自身encrypt方法返回的对象
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
        let plaindata = await eccrypto.decrypt(Buffer.from(key, 'hex'), data) // eccrypto 需要调用 Buffer.compare 方法,不能在这里直接用 hex2buf
 | 
					        let plaindata = await eccrypto.decrypt(Buffer.from(key, 'hex'), data) // eccrypto 需要调用 Buffer.compare 方法,不能在这里直接用 hex2buf
 | 
				
			||||||
        return plaindata.toString('utf8')
 | 
					        return plaindata.toString('utf8')
 | 
				
			||||||
      }catch (exception){ // eccrypto 对无法解密的,会抛出异常
 | 
					      } catch (exception) {
 | 
				
			||||||
 | 
					        // eccrypto 对无法解密的,会抛出异常
 | 
				
			||||||
        return null
 | 
					        return null
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  async sign(data, seckey, option = {}) {
 | 
				
			||||||
  async sign(data, seckey, option={}) { // data can be string or buffer or object, results are the same
 | 
					    // data can be string or buffer or object, results are the same
 | 
				
			||||||
    if (this.isHashable(data) && this.isSeckey(seckey)) {
 | 
					    if (this.isHashable(data) && this.isSeckey(seckey)) {
 | 
				
			||||||
      if (option.tool==='nacl' && seckey.length===128) { // 使用nacl的签名算法。注意,nacl.sign需要的seckey是64字节=128字符。
 | 
					      if (option.tool === 'nacl' && seckey.length === 128) {
 | 
				
			||||||
 | 
					        // 使用nacl的签名算法。注意,nacl.sign需要的seckey是64字节=128字符。
 | 
				
			||||||
        option.output = 'buf' // 哈希必须输出为 buffer
 | 
					        option.output = 'buf' // 哈希必须输出为 buffer
 | 
				
			||||||
        let hashBuf = this.hash(data, option)
 | 
					        let hashBuf = this.hash(data, option)
 | 
				
			||||||
        let signature = nacl.sign.detached(hashBuf, Buffer.from(seckey, 'hex'))
 | 
					        let signature = nacl.sign.detached(hashBuf, Buffer.from(seckey, 'hex'))
 | 
				
			||||||
        return Buffer.from(signature).toString('hex') // 签名是64节,128个hex字符
 | 
					        return Buffer.from(signature).toString('hex') // 签名是64节,128个hex字符
 | 
				
			||||||
      }else if (option.tool==='eccrypto' && seckey.length===64) { // eccrypto 对同一组data,seckey生成的签名是固定的,观察到hex长度为140或142,是der格式。
 | 
					      } else if (option.tool === 'eccrypto' && seckey.length === 64) {
 | 
				
			||||||
 | 
					        // eccrypto 对同一组data,seckey生成的签名是固定的,观察到hex长度为140或142,是der格式。
 | 
				
			||||||
        let signature = await eccrypto.sign(Buffer.from(seckey, 'hex'), this.hash(data, { output: 'buf' }))
 | 
					        let signature = await eccrypto.sign(Buffer.from(seckey, 'hex'), this.hash(data, { output: 'buf' }))
 | 
				
			||||||
        return signature.toString('hex')
 | 
					        return signature.toString('hex')
 | 
				
			||||||
      }else if (seckey.length===64){ // 纯 crypto
 | 
					      } else if (seckey.length === 64) {
 | 
				
			||||||
 | 
					        // 纯 crypto
 | 
				
			||||||
        let seckeyPEM = await new keyman.Key('oct', this.hex2buf(seckey), { namedCurve: 'P-256K' }).export('pem') // 私钥导出的der格式为144字节。
 | 
					        let seckeyPEM = await new keyman.Key('oct', this.hex2buf(seckey), { namedCurve: 'P-256K' }).export('pem') // 私钥导出的der格式为144字节。
 | 
				
			||||||
        let hasher = my.HASHER_LIST.indexOf(option.hasher) >= 0 ? option.hasher : my.HASHER
 | 
					        let hasher = my.HASHER_LIST.indexOf(option.hasher) >= 0 ? option.hasher : my.HASHER
 | 
				
			||||||
        let signer = crypto.createSign(hasher)
 | 
					        let signer = crypto.createSign(hasher)
 | 
				
			||||||
@ -175,9 +174,9 @@ module.exports = {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  async verify(data, signature, pubkey, option = {}) {
 | 
				
			||||||
  async verify (data, signature, pubkey, option={}) { // data could be anything, but converts to string or remains be Buffer/TypedArray/DataView
 | 
					    // data could be anything, but converts to string or remains be Buffer/TypedArray/DataView
 | 
				
			||||||
    if (this.isHashable(data) && this.isSignature(signature) && this.isPubkey(pubkey)) {
 | 
					    if (this.isHashable(data) && this.isSignature(signature) && this.isPubkey(pubkey)) {
 | 
				
			||||||
      if ('nacl' === option.tool && signature.length === 128) {
 | 
					      if ('nacl' === option.tool && signature.length === 128) {
 | 
				
			||||||
        option.output = 'buf' // 哈希必须输出为 buffer
 | 
					        option.output = 'buf' // 哈希必须输出为 buffer
 | 
				
			||||||
@ -186,14 +185,17 @@ module.exports = {
 | 
				
			|||||||
        let bufPubkey = Buffer.from(pubkey, 'hex')
 | 
					        let bufPubkey = Buffer.from(pubkey, 'hex')
 | 
				
			||||||
        let verified = nacl.sign.detached.verify(bufHash, bufSignature, bufPubkey)
 | 
					        let verified = nacl.sign.detached.verify(bufHash, bufSignature, bufPubkey)
 | 
				
			||||||
        return verified
 | 
					        return verified
 | 
				
			||||||
      }else if ('eccrypto' === option.tool && signature.length >= 140) { // 默认使用 eccrypto 
 | 
					      } else if ('eccrypto' === option.tool && signature.length >= 140) {
 | 
				
			||||||
 | 
					        // 默认使用 eccrypto
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
          let result = await eccrypto.verify(Buffer.from(pubkey, 'hex'), this.hash(data, { output: 'buf' }), Buffer.from(signature, 'hex')) // 如果给signature添加1位hex,eccrypto 的 verify结果也是true! 估计因为一位hex不被转成字节。
 | 
					          let result = await eccrypto.verify(Buffer.from(pubkey, 'hex'), this.hash(data, { output: 'buf' }), Buffer.from(signature, 'hex')) // 如果给signature添加1位hex,eccrypto 的 verify结果也是true! 估计因为一位hex不被转成字节。
 | 
				
			||||||
          return true
 | 
					          return true
 | 
				
			||||||
        }catch(exception){ // 对能够验证的,eccrypto返回 null;对无法验证的,抛出异常
 | 
					        } catch (exception) {
 | 
				
			||||||
 | 
					          // 对能够验证的,eccrypto返回 null;对无法验证的,抛出异常
 | 
				
			||||||
          return false
 | 
					          return false
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }else if (signature.length >= 140) { // 纯 crypto
 | 
					      } else if (signature.length >= 140) {
 | 
				
			||||||
 | 
					        // 纯 crypto
 | 
				
			||||||
        let pubkeyPEM = await new keyman.Key('oct', this.hex2buf(pubkey), { namedCurve: 'P-256K' }).export('pem') // 公钥导出的der格式为88字节。经测试,同一对压缩和非压缩公钥得出的结果一模一样。
 | 
					        let pubkeyPEM = await new keyman.Key('oct', this.hex2buf(pubkey), { namedCurve: 'P-256K' }).export('pem') // 公钥导出的der格式为88字节。经测试,同一对压缩和非压缩公钥得出的结果一模一样。
 | 
				
			||||||
        let hasher = my.HASHER_LIST.indexOf(option.hasher) >= 0 ? option.hasher : my.HASHER
 | 
					        let hasher = my.HASHER_LIST.indexOf(option.hasher) >= 0 ? option.hasher : my.HASHER
 | 
				
			||||||
        let verifier = crypto.createVerify(hasher)
 | 
					        let verifier = crypto.createVerify(hasher)
 | 
				
			||||||
@ -203,9 +205,9 @@ module.exports = {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return false
 | 
					    return false
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  pass2keypair(pass, option) {
 | 
				
			||||||
  pass2keypair(pass, option){ // 如果使用其他机制,例如密码、随机数,不使用secword,也可生成keypair
 | 
					    // 如果使用其他机制,例如密码、随机数,不使用secword,也可生成keypair
 | 
				
			||||||
    if (this.isHashable(pass)) {
 | 
					    if (this.isHashable(pass)) {
 | 
				
			||||||
      option = option || {}
 | 
					      option = option || {}
 | 
				
			||||||
      option.hasher = my.HASHER_LIST.indexOf(option.hasher) >= 0 ? option.hasher : my.HASHER
 | 
					      option.hasher = my.HASHER_LIST.indexOf(option.hasher) >= 0 ? option.hasher : my.HASHER
 | 
				
			||||||
@ -214,20 +216,19 @@ module.exports = {
 | 
				
			|||||||
      return {
 | 
					      return {
 | 
				
			||||||
        hash: hashBuf.toString('hex'),
 | 
					        hash: hashBuf.toString('hex'),
 | 
				
			||||||
        pubkey: Buffer.from(keypair.publicKey).toString('hex'), // 测试过 不能直接keypair.publicKey.toString('hex'),不是buffer类型
 | 
					        pubkey: Buffer.from(keypair.publicKey).toString('hex'), // 测试过 不能直接keypair.publicKey.toString('hex'),不是buffer类型
 | 
				
			||||||
        seckey: Buffer.from(keypair.secretKey).toString('hex')
 | 
					        seckey: Buffer.from(keypair.secretKey).toString('hex'),
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  entropy2secword(entropy) {
 | 
				
			||||||
  entropy2secword(entropy){ // entropy could be hex string or buffer. Byte length could be of 16, 20, 24, 28, ... which outputs mnemonic of length 12, 15, 18, 21, ...
 | 
					    // entropy could be hex string or buffer. Byte length could be of 16, 20, 24, 28, ... which outputs mnemonic of length 12, 15, 18, 21, ...
 | 
				
			||||||
    return bip39.entropyToMnemonic(entropy) // results are the same for the same entropy.
 | 
					    return bip39.entropyToMnemonic(entropy) // results are the same for the same entropy.
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  secword2entropy(secword) {
 | 
				
			||||||
  secword2entropy(secword){ // secword could be of length 12, 15, 18, ... which outputs hex of length 32, 40, ...
 | 
					    // secword could be of length 12, 15, 18, ... which outputs hex of length 32, 40, ...
 | 
				
			||||||
    return bip39.mnemonicToEntropy(secword) // results are the same for the same secword.
 | 
					    return bip39.mnemonicToEntropy(secword) // results are the same for the same secword.
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  secword2keypair(secword, option) {
 | 
					  secword2keypair(secword, option) {
 | 
				
			||||||
    // option.coin 币种;
 | 
					    // option.coin 币种;
 | 
				
			||||||
    // option.passphase 密码,默认为空;
 | 
					    // option.passphase 密码,默认为空;
 | 
				
			||||||
@ -252,7 +253,7 @@ module.exports = {
 | 
				
			|||||||
        coin: option.coin,
 | 
					        coin: option.coin,
 | 
				
			||||||
        secword: secword,
 | 
					        secword: secword,
 | 
				
			||||||
        pubkey: Buffer.from(keypair.publicKey).toString('hex'), // 测试过 不能直接keypair.publicKey.toString('hex'),不是buffer类型
 | 
					        pubkey: Buffer.from(keypair.publicKey).toString('hex'), // 测试过 不能直接keypair.publicKey.toString('hex'),不是buffer类型
 | 
				
			||||||
        seckey: Buffer.from(keypair.secretKey).toString('hex') // nacl.sign.keyPair.fromSeed 得到的 seckey 是64字节的,不同于比特币/以太坊的32字节密钥。
 | 
					        seckey: Buffer.from(keypair.secretKey).toString('hex'), // nacl.sign.keyPair.fromSeed 得到的 seckey 是64字节的,不同于比特币/以太坊的32字节密钥。
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      // 用 bip39 算法从 secword 到种子,再用 bip32 算法从种子到根私钥。这是比特币、以太坊的标准方式,结果一致。
 | 
					      // 用 bip39 算法从 secword 到种子,再用 bip32 算法从种子到根私钥。这是比特币、以太坊的标准方式,结果一致。
 | 
				
			||||||
@ -263,23 +264,30 @@ module.exports = {
 | 
				
			|||||||
        key = hdmaster
 | 
					        key = hdmaster
 | 
				
			||||||
      } else if (!option.path) {
 | 
					      } else if (!option.path) {
 | 
				
			||||||
        switch (option.coin) {
 | 
					        switch (option.coin) {
 | 
				
			||||||
          case 'BTC': key=hdmaster.derive("m/44'/0'/0'/0/0"); break
 | 
					          case 'BTC':
 | 
				
			||||||
          case 'ETH': key=hdmaster.derive("m/44'/60'/0'/0/0"); break
 | 
					            key = hdmaster.derive("m/44'/0'/0'/0/0")
 | 
				
			||||||
          case 'TIC': default: key=hdmaster.derive("m/44'/60000'/0'/0/0"); break
 | 
					            break
 | 
				
			||||||
 | 
					          case 'ETH':
 | 
				
			||||||
 | 
					            key = hdmaster.derive("m/44'/60'/0'/0/0")
 | 
				
			||||||
 | 
					            break
 | 
				
			||||||
 | 
					          case 'TIC':
 | 
				
			||||||
 | 
					          default:
 | 
				
			||||||
 | 
					            key = hdmaster.derive("m/44'/60000'/0'/0/0")
 | 
				
			||||||
 | 
					            break
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }else { // 指定了路径 option.path,例如 "m/44'/0'/0'/0/6" 或 "m/0/2147483647'/1"
 | 
					      } else {
 | 
				
			||||||
 | 
					        // 指定了路径 option.path,例如 "m/44'/0'/0'/0/6" 或 "m/0/2147483647'/1"
 | 
				
			||||||
        key = hdmaster.derive(option.path)
 | 
					        key = hdmaster.derive(option.path)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return {
 | 
					      return {
 | 
				
			||||||
        coin: option.coin,
 | 
					        coin: option.coin,
 | 
				
			||||||
        secword: secword,
 | 
					        secword: secword,
 | 
				
			||||||
        seckey: key.privateKey.toString('hex'), // 或者 key.toJSON().privateKey。或者 key.privateKey.slice(2) 删除开头的'0x'如果是ethers.HDNode.fromMnemonic(secword)的结果
 | 
					        seckey: key.privateKey.toString('hex'), // 或者 key.toJSON().privateKey。或者 key.privateKey.slice(2) 删除开头的'0x'如果是ethers.HDNode.fromMnemonic(secword)的结果
 | 
				
			||||||
        pubkey: key.publicKey.toString('hex')
 | 
					        pubkey: key.publicKey.toString('hex'),
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  seed2path(seed, { coin = 'TIC' } = { coin: 'TIC' }) {
 | 
					  seed2path(seed, { coin = 'TIC' } = { coin: 'TIC' }) {
 | 
				
			||||||
    // 路径规范 BIP44: m/Purpose'/Coin'/Account'/Change/Index,
 | 
					    // 路径规范 BIP44: m/Purpose'/Coin'/Account'/Change/Index,
 | 
				
			||||||
    // 但实际上 Purpose, Coin 都可任意定;' 可有可无;
 | 
					    // 但实际上 Purpose, Coin 都可任意定;' 可有可无;
 | 
				
			||||||
@ -293,13 +301,17 @@ module.exports = {
 | 
				
			|||||||
    let part4 = parseInt(hash.slice(24, 30), 16)
 | 
					    let part4 = parseInt(hash.slice(24, 30), 16)
 | 
				
			||||||
    let path = `${part0}'/${part1}/${part2}/${part3}/${part4}/${part5}`
 | 
					    let path = `${part0}'/${part1}/${part2}/${part3}/${part4}/${part5}`
 | 
				
			||||||
    switch (coin) {
 | 
					    switch (coin) {
 | 
				
			||||||
      case 'BTC': return `m/44'/0'/${path}`
 | 
					      case 'BTC':
 | 
				
			||||||
      case 'ETH': return `m/44'/60'/${path}`
 | 
					        return `m/44'/0'/${path}`
 | 
				
			||||||
      case 'TIC': default: return `m/44'/60000'/${path}`
 | 
					      case 'ETH':
 | 
				
			||||||
 | 
					        return `m/44'/60'/${path}`
 | 
				
			||||||
 | 
					      case 'TIC':
 | 
				
			||||||
 | 
					      default:
 | 
				
			||||||
 | 
					        return `m/44'/60000'/${path}`
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  secword2account(secword, option) {
 | 
				
			||||||
  secword2account(secword, option){ // account 比 keypair 多了 address 字段。
 | 
					    // account 比 keypair 多了 address 字段。
 | 
				
			||||||
    option = option || {}
 | 
					    option = option || {}
 | 
				
			||||||
    option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN
 | 
					    option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN
 | 
				
			||||||
    let kp = this.secword2keypair(secword, option)
 | 
					    let kp = this.secword2keypair(secword, option)
 | 
				
			||||||
@ -313,8 +325,7 @@ module.exports = {
 | 
				
			|||||||
      return kp
 | 
					      return kp
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  secword2address(secword, option) {
 | 
					  secword2address(secword, option) {
 | 
				
			||||||
    option = option || {}
 | 
					    option = option || {}
 | 
				
			||||||
    option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN
 | 
					    option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN
 | 
				
			||||||
@ -329,11 +340,11 @@ module.exports = {
 | 
				
			|||||||
      return address
 | 
					      return address
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  seckey2pubkey(seckey, option = {}) {
 | 
					  seckey2pubkey(seckey, option = {}) {
 | 
				
			||||||
    option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN
 | 
					    option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN
 | 
				
			||||||
    if (this.isSeckey(seckey) && seckey.length===64){ // 只能用于32字节的私钥(BTC, ETH)。也就是不能用于 TIC 的私钥。
 | 
					    if (this.isSeckey(seckey) && seckey.length === 64) {
 | 
				
			||||||
 | 
					      // 只能用于32字节的私钥(BTC, ETH)。也就是不能用于 TIC 的私钥。
 | 
				
			||||||
      let curve = my.CURVE_LIST.indexOf(option.curve) >= 0 ? option.curve : my.CURVE // 默认为 secp256k1
 | 
					      let curve = my.CURVE_LIST.indexOf(option.curve) >= 0 ? option.curve : my.CURVE // 默认为 secp256k1
 | 
				
			||||||
      // return new crypto.createECDH(curve).setPrivateKey(seckey,'hex').getPublicKey('hex', option.compress===false?'uncompressed':'compressed') // ecdh.getPublicKey(不加参数) 默认为 'compressed'。用 HBuilderX 2.6.4 打包成ios或安卓 app 后 setPrivateKey() 报错:TypeError: null is not an object (evaluating 'this.rand.getBytes')
 | 
					      // return new crypto.createECDH(curve).setPrivateKey(seckey,'hex').getPublicKey('hex', option.compress===false?'uncompressed':'compressed') // ecdh.getPublicKey(不加参数) 默认为 'compressed'。用 HBuilderX 2.6.4 打包成ios或安卓 app 后 setPrivateKey() 报错:TypeError: null is not an object (evaluating 'this.rand.getBytes')
 | 
				
			||||||
      // 从 nodejs 10.0 开始,还有 crypto.ECDH.convertKey 方法,更直接。但可惜,浏览器里不存在 crypto.ECDH。
 | 
					      // 从 nodejs 10.0 开始,还有 crypto.ECDH.convertKey 方法,更直接。但可惜,浏览器里不存在 crypto.ECDH。
 | 
				
			||||||
@ -346,13 +357,13 @@ module.exports = {
 | 
				
			|||||||
      //   return ecc.getPublicCompressed(this.hex2buf(seckey)).toString('hex')
 | 
					      //   return ecc.getPublicCompressed(this.hex2buf(seckey)).toString('hex')
 | 
				
			||||||
      // }
 | 
					      // }
 | 
				
			||||||
      // 注意,Buffer.from(nacl.box.keyPair.fromSecretKey(Buffer.from(seckey,'hex')).publicKey).toString('hex') 得到的公钥与上面的不同
 | 
					      // 注意,Buffer.from(nacl.box.keyPair.fromSecretKey(Buffer.from(seckey,'hex')).publicKey).toString('hex') 得到的公钥与上面的不同
 | 
				
			||||||
    }else if (this.isSeckey(seckey) && seckey.length===128){ // 用于64字节=128 hex的 TIC 私钥
 | 
					    } else if (this.isSeckey(seckey) && seckey.length === 128) {
 | 
				
			||||||
 | 
					      // 用于64字节=128 hex的 TIC 私钥
 | 
				
			||||||
      let keypair = nacl.sign.keyPair.fromSecretKey(Buffer.from(seckey, 'hex'))
 | 
					      let keypair = nacl.sign.keyPair.fromSecretKey(Buffer.from(seckey, 'hex'))
 | 
				
			||||||
      return Buffer.from(keypair.publicKey).toString('hex') // 测试过 不能直接keypair.publicKey.toString('hex'),不是buffer类型
 | 
					      return Buffer.from(keypair.publicKey).toString('hex') // 测试过 不能直接keypair.publicKey.toString('hex'),不是buffer类型
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  seckey2address(seckey, option) {
 | 
					  seckey2address(seckey, option) {
 | 
				
			||||||
    option = option || {}
 | 
					    option = option || {}
 | 
				
			||||||
    option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN
 | 
					    option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN
 | 
				
			||||||
@ -367,9 +378,9 @@ module.exports = {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  pubkey2position(pubkey, { coin } = {}) {
 | 
				
			||||||
  pubkey2position (pubkey, {coin}={}){ // tic, btc, eth 的 position 都是 20节=40字符的。
 | 
					    // tic, btc, eth 的 position 都是 20节=40字符的。
 | 
				
			||||||
    coin = my.COIN_LIST.indexOf(coin) >= 0 ? coin : my.COIN
 | 
					    coin = my.COIN_LIST.indexOf(coin) >= 0 ? coin : my.COIN
 | 
				
			||||||
    if (this.isPubkey(pubkey)) {
 | 
					    if (this.isPubkey(pubkey)) {
 | 
				
			||||||
      if (coin === 'ETH') {
 | 
					      if (coin === 'ETH') {
 | 
				
			||||||
@ -377,7 +388,10 @@ module.exports = {
 | 
				
			|||||||
        if (pubkey.length === 66) {
 | 
					        if (pubkey.length === 66) {
 | 
				
			||||||
          pubkey = this.decompressPubkey(pubkey)
 | 
					          pubkey = this.decompressPubkey(pubkey)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return keccak('keccak256').update(Buffer.from(pubkey.slice(2),'hex')).digest('hex').slice(-40)
 | 
					        return keccak('keccak256')
 | 
				
			||||||
 | 
					          .update(Buffer.from(pubkey.slice(2), 'hex'))
 | 
				
			||||||
 | 
					          .digest('hex')
 | 
				
			||||||
 | 
					          .slice(-40)
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        let h256 = crypto.createHash('sha256').update(Buffer.from(pubkey, 'hex')).digest()
 | 
					        let h256 = crypto.createHash('sha256').update(Buffer.from(pubkey, 'hex')).digest()
 | 
				
			||||||
        let h160 = crypto.createHash('ripemd160').update(h256).digest('hex')
 | 
					        let h160 = crypto.createHash('ripemd160').update(h256).digest('hex')
 | 
				
			||||||
@ -385,13 +399,13 @@ module.exports = {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  position2address(position, { coin, world } = {}) {
 | 
					  position2address(position, { coin, world } = {}) {
 | 
				
			||||||
    if (!/^[\da-fA-F]{40}$/.test(position)) return null // 不论 tic, btc, eth,其 position 都是 40字符的。
 | 
					    if (!/^[\da-fA-F]{40}$/.test(position)) return null // 不论 tic, btc, eth,其 position 都是 40字符的。
 | 
				
			||||||
    coin = my.COIN_LIST.indexOf(coin) >= 0 ? coin : my.COIN
 | 
					    coin = my.COIN_LIST.indexOf(coin) >= 0 ? coin : my.COIN
 | 
				
			||||||
    let address
 | 
					    let address
 | 
				
			||||||
    if (coin==='ETH'){ // 对以太坊,按照 EIP55,把纯位置转换为大小写敏感能自我验证的hex地址。仍然为20节=40符。
 | 
					    if (coin === 'ETH') {
 | 
				
			||||||
 | 
					      // 对以太坊,按照 EIP55,把纯位置转换为大小写敏感能自我验证的hex地址。仍然为20节=40符。
 | 
				
			||||||
      position = position.toLowerCase().replace('0x', '')
 | 
					      position = position.toLowerCase().replace('0x', '')
 | 
				
			||||||
      let hash = keccak('keccak256').update(position).digest('hex')
 | 
					      let hash = keccak('keccak256').update(position).digest('hex')
 | 
				
			||||||
      address = '0x'
 | 
					      address = '0x'
 | 
				
			||||||
@ -403,28 +417,50 @@ module.exports = {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return address
 | 
					      return address
 | 
				
			||||||
    }else if (coin === 'BTC'){ // 对比特币,把纯位置转换为大小写敏感能自我验证的bs58check地址:先加前缀1,再加校验4,共25字节,再转base58。得到26~34个字符,大多数34个。
 | 
					    } else if (coin === 'BTC') {
 | 
				
			||||||
 | 
					      // 对比特币,把纯位置转换为大小写敏感能自我验证的bs58check地址:先加前缀1,再加校验4,共25字节,再转base58。得到26~34个字符,大多数34个。
 | 
				
			||||||
      let prefix
 | 
					      let prefix
 | 
				
			||||||
      switch (world) {
 | 
					      switch (world) {
 | 
				
			||||||
        case 'mainnet': prefix='00'; break; // pubkey hash => 1
 | 
					        case 'mainnet':
 | 
				
			||||||
        case 'mainnetSh': prefix='05'; break; // script hash => 3
 | 
					          prefix = '00'
 | 
				
			||||||
        case 'testnet': prefix='6f'; break; // testnet pubkey hash => m or n
 | 
					          break // pubkey hash => 1
 | 
				
			||||||
        case 'testnetSh': prefix='c4'; break // testnet script hash => 2
 | 
					        case 'mainnetSh':
 | 
				
			||||||
        case 'namecoin': prefix='34'; break; // Namecoin pubkey hash => M or N
 | 
					          prefix = '05'
 | 
				
			||||||
        case 'compact': prefix='15'; break; // compact pubkey (proposed) => 4
 | 
					          break // script hash => 3
 | 
				
			||||||
        default: prefix='00'
 | 
					        case 'testnet':
 | 
				
			||||||
 | 
					          prefix = '6f'
 | 
				
			||||||
 | 
					          break // testnet pubkey hash => m or n
 | 
				
			||||||
 | 
					        case 'testnetSh':
 | 
				
			||||||
 | 
					          prefix = 'c4'
 | 
				
			||||||
 | 
					          break // testnet script hash => 2
 | 
				
			||||||
 | 
					        case 'namecoin':
 | 
				
			||||||
 | 
					          prefix = '34'
 | 
				
			||||||
 | 
					          break // Namecoin pubkey hash => M or N
 | 
				
			||||||
 | 
					        case 'compact':
 | 
				
			||||||
 | 
					          prefix = '15'
 | 
				
			||||||
 | 
					          break // compact pubkey (proposed) => 4
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					          prefix = '00'
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      address = bs58check.encode(Buffer.from(prefix + position, 'hex')) // wallet import format
 | 
					      address = bs58check.encode(Buffer.from(prefix + position, 'hex')) // wallet import format
 | 
				
			||||||
      return address
 | 
					      return address
 | 
				
			||||||
    }else { // 默认为 TIC。把纯位置转换为大小写敏感能自我验证的 b64u(base64 for url) 地址。
 | 
					    } else {
 | 
				
			||||||
 | 
					      // 默认为 TIC。把纯位置转换为大小写敏感能自我验证的 b64u(base64 for url) 地址。
 | 
				
			||||||
      let prefix
 | 
					      let prefix
 | 
				
			||||||
      switch (world) {
 | 
					      switch (world) {
 | 
				
			||||||
        // Base58: https://en.bitcoin.it/wiki/List_of_address_prefixes
 | 
					        // Base58: https://en.bitcoin.it/wiki/List_of_address_prefixes
 | 
				
			||||||
        // Base64: https://baike.baidu.com/item/base64
 | 
					        // Base64: https://baike.baidu.com/item/base64
 | 
				
			||||||
        case 'earth': prefix='4c'; break; // Base58: 0x42=66 => T, Base64: base64 T=0x13=0b00010011 => 0b010011xx = 0x4c~4f
 | 
					        case 'earth':
 | 
				
			||||||
        case 'moon': prefix='b4'; break; // Base58: 0x7f=127,0x80=128 => t, Base64: t=0x2d=0b00101101 => 0b101101xx = 0xB4~B7
 | 
					          prefix = '4c'
 | 
				
			||||||
        case 'w1dev': prefix='74'; break; // Base58: 0x90 => d, Base 64: d=0x1d=0b00011101 => 0b 011101xx = 0x74~77
 | 
					          break // Base58: 0x42=66 => T, Base64: base64 T=0x13=0b00010011 => 0b010011xx = 0x4c~4f
 | 
				
			||||||
        default: prefix='4c'
 | 
					        case 'moon':
 | 
				
			||||||
 | 
					          prefix = 'b4'
 | 
				
			||||||
 | 
					          break // Base58: 0x7f=127,0x80=128 => t, Base64: t=0x2d=0b00101101 => 0b101101xx = 0xB4~B7
 | 
				
			||||||
 | 
					        case 'w1dev':
 | 
				
			||||||
 | 
					          prefix = '74'
 | 
				
			||||||
 | 
					          break // Base58: 0x90 => d, Base 64: d=0x1d=0b00011101 => 0b 011101xx = 0x74~77
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					          prefix = '4c'
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      let checksum = this.hash(this.hash(prefix + position)).slice(0, 6) // 添加 checksum 使得能够检测大小写错误。[todo] 校验码里要不要包含 prefix?
 | 
					      let checksum = this.hash(this.hash(prefix + position)).slice(0, 6) // 添加 checksum 使得能够检测大小写错误。[todo] 校验码里要不要包含 prefix?
 | 
				
			||||||
      //      address = this.hex2eip55(prefix + position + checksum) // 前缀1节,位置20节,校验3节,共24节=48字符(能够完全转化为8个色彩),再转eip55。
 | 
					      //      address = this.hex2eip55(prefix + position + checksum) // 前缀1节,位置20节,校验3节,共24节=48字符(能够完全转化为8个色彩),再转eip55。
 | 
				
			||||||
@ -432,8 +468,7 @@ module.exports = {
 | 
				
			|||||||
      return address
 | 
					      return address
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  address2position() {
 | 
					  address2position() {
 | 
				
			||||||
    if (/^0x[\da-fA-F]{40}$/.test(address)) {
 | 
					    if (/^0x[\da-fA-F]{40}$/.test(address)) {
 | 
				
			||||||
      return address.toLowerCase()
 | 
					      return address.toLowerCase()
 | 
				
			||||||
@ -442,7 +477,8 @@ module.exports = {
 | 
				
			|||||||
      if (hex) {
 | 
					      if (hex) {
 | 
				
			||||||
        return hex.slice(2) // 去除网络前缀
 | 
					        return hex.slice(2) // 去除网络前缀
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }else if (/^[Tt][0-9a-zA-Z\-_]{31}$/.test(address)){ // 格式合法
 | 
					    } else if (/^[Tt][0-9a-zA-Z\-_]{31}$/.test(address)) {
 | 
				
			||||||
 | 
					      // 格式合法
 | 
				
			||||||
      let hex = this.b64u2hex(address)
 | 
					      let hex = this.b64u2hex(address)
 | 
				
			||||||
      let [all, prefix, position, checksum] = hex.match(/^([\da-fA-F]{2})([\da-fA-F]{40})([\da-fA-F]{6})$/)
 | 
					      let [all, prefix, position, checksum] = hex.match(/^([\da-fA-F]{2})([\da-fA-F]{40})([\da-fA-F]{6})$/)
 | 
				
			||||||
      if (this.hash(this.hash(position)).slice(0, 6) === checksum) {
 | 
					      if (this.hash(this.hash(position)).slice(0, 6) === checksum) {
 | 
				
			||||||
@ -450,35 +486,38 @@ module.exports = {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  isAddress(address) {
 | 
					  isAddress(address) {
 | 
				
			||||||
    if (/^(0x)?[\da-fA-F]{40}$/.test(address)) {
 | 
					    if (/^(0x)?[\da-fA-F]{40}$/.test(address)) {
 | 
				
			||||||
      return 'ETH'
 | 
					      return 'ETH'
 | 
				
			||||||
    }else if (/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{26,34}$/.test(address) && address.length!==32){ // 格式合法。常见的是 33或34字符长度
 | 
					    } else if (/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{26,34}$/.test(address) && address.length !== 32) {
 | 
				
			||||||
 | 
					      // 格式合法。常见的是 33或34字符长度
 | 
				
			||||||
      let prefixedPosition = this.b58c2hex(address)
 | 
					      let prefixedPosition = this.b58c2hex(address)
 | 
				
			||||||
      if (prefixedPosition && prefixedPosition.length===42) // 内容合法
 | 
					      if (prefixedPosition && prefixedPosition.length === 42)
 | 
				
			||||||
 | 
					        // 内容合法
 | 
				
			||||||
        return 'BTC'
 | 
					        return 'BTC'
 | 
				
			||||||
    }else if (/^[Ttd][0-9a-zA-Z\-_]{31}$/.test(address)){ // 格式合法
 | 
					    } else if (/^[Ttd][0-9a-zA-Z\-_]{31}$/.test(address)) {
 | 
				
			||||||
 | 
					      // 格式合法
 | 
				
			||||||
      let b64 = address.replace('-', '+').replace('_', '/')
 | 
					      let b64 = address.replace('-', '+').replace('_', '/')
 | 
				
			||||||
      let hex = Buffer.from(b64, 'base64').toString('hex')
 | 
					      let hex = Buffer.from(b64, 'base64').toString('hex')
 | 
				
			||||||
      let [all, prefix, position, checksum] = hex.match(/^([\da-fA-F]{2})([\da-fA-F]{40})([\da-fA-F]{6})$/) // 内容合法
 | 
					      let [all, prefix, position, checksum] = hex.match(/^([\da-fA-F]{2})([\da-fA-F]{40})([\da-fA-F]{6})$/) // 内容合法
 | 
				
			||||||
      if (this.hash(this.hash(prefix+position)).slice(0,6) === checksum) // [todo] 校验码里要不要包含 prefix?
 | 
					      if (this.hash(this.hash(prefix + position)).slice(0, 6) === checksum)
 | 
				
			||||||
 | 
					        // [todo] 校验码里要不要包含 prefix?
 | 
				
			||||||
        return 'TIC'
 | 
					        return 'TIC'
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  pubkey2address(pubkey, option = {}) {
 | 
				
			||||||
  pubkey2address (pubkey, option={}) { // pubkey 应当是string类型
 | 
					    // pubkey 应当是string类型
 | 
				
			||||||
    option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN
 | 
					    option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN
 | 
				
			||||||
    return this.position2address(this.pubkey2position(pubkey, option), option)
 | 
					    return this.position2address(this.pubkey2position(pubkey, option), option)
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  secword2seed(secword, pass) {
 | 
				
			||||||
  secword2seed(secword, pass) { // 遵循bip39的算法。和 ether.HDNode.mnemonic2Seed 结果一样,是64字节的种子。其实
 | 
					    // 遵循bip39的算法。和 ether.HDNode.mnemonic2Seed 结果一样,是64字节的种子。其实
 | 
				
			||||||
    return bip39.mnemonicToSeedSync(secword, pass).toString('hex') // 结果一致于 new BitcoreMnemonic(secword).toSeed(pass).toString('hex') 或 ethers.HDNode.mnemonic2Seed(secword)。其实,bip39.mnemonicToSeedSync 也接受不合法的 secword,只要是个string就行。
 | 
					    return bip39.mnemonicToSeedSync(secword, pass).toString('hex') // 结果一致于 new BitcoreMnemonic(secword).toSeed(pass).toString('hex') 或 ethers.HDNode.mnemonic2Seed(secword)。其实,bip39.mnemonicToSeedSync 也接受不合法的 secword,只要是个string就行。
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  randomSecword(lang = 'english') {
 | 
				
			||||||
  randomSecword(lang='english'){ // accepts case-insensitive lang, such as 'chinese, cn, tw, en'
 | 
					    // accepts case-insensitive lang, such as 'chinese, cn, tw, en'
 | 
				
			||||||
    //// for BitcoreMnemonic
 | 
					    //// for BitcoreMnemonic
 | 
				
			||||||
    // lang=lang.toUpperCase()
 | 
					    // lang=lang.toUpperCase()
 | 
				
			||||||
    // let language = { ZHCN: 'CHINESE', ENUS: 'ENGLISH', FRFR: 'FRENCH', ITIT: 'ITALIAN', JAJP: 'JAPANESE', KOKR: 'KOREAN', ESES: 'SPANISH' }[lang]
 | 
					    // let language = { ZHCN: 'CHINESE', ENUS: 'ENGLISH', FRFR: 'FRENCH', ITIT: 'ITALIAN', JAJP: 'JAPANESE', KOKR: 'KOREAN', ESES: 'SPANISH' }[lang]
 | 
				
			||||||
@ -486,7 +525,16 @@ module.exports = {
 | 
				
			|||||||
    // return new BitcoreMnemonic(BitcoreMnemonic.Words[language]).phrase
 | 
					    // return new BitcoreMnemonic(BitcoreMnemonic.Words[language]).phrase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // for bip39
 | 
					    // for bip39
 | 
				
			||||||
    const langMap = { zhcn: 'chinese_simplified', zhtw: 'chinese_traditional', enus: 'english', frfr: 'french', itit: 'italian', jajp: 'japanese', kokr: 'korean', eses: 'spanish' }
 | 
					    const langMap = {
 | 
				
			||||||
 | 
					      zhcn: 'chinese_simplified',
 | 
				
			||||||
 | 
					      zhtw: 'chinese_traditional',
 | 
				
			||||||
 | 
					      enus: 'english',
 | 
				
			||||||
 | 
					      frfr: 'french',
 | 
				
			||||||
 | 
					      itit: 'italian',
 | 
				
			||||||
 | 
					      jajp: 'japanese',
 | 
				
			||||||
 | 
					      kokr: 'korean',
 | 
				
			||||||
 | 
					      eses: 'spanish',
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    langMap.chinese = langMap.cn = langMap.zh = langMap.china = langMap.zhcn
 | 
					    langMap.chinese = langMap.cn = langMap.zh = langMap.china = langMap.zhcn
 | 
				
			||||||
    langMap.taiwanese = langMap.tw = langMap.zhtw
 | 
					    langMap.taiwanese = langMap.tw = langMap.zhtw
 | 
				
			||||||
    langMap.en = langMap.us = langMap.uk = langMap.enus
 | 
					    langMap.en = langMap.us = langMap.uk = langMap.enus
 | 
				
			||||||
@ -496,15 +544,15 @@ module.exports = {
 | 
				
			|||||||
    langMap.ja = langMap.jp = langMap.japan = langMap.jajp
 | 
					    langMap.ja = langMap.jp = langMap.japan = langMap.jajp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let language = 'english'
 | 
					    let language = 'english'
 | 
				
			||||||
    if (typeof(lang)==='string'){
 | 
					    if (typeof lang === 'string') {
 | 
				
			||||||
      lang = lang.toLowerCase()
 | 
					      lang = lang.toLowerCase()
 | 
				
			||||||
      language = langMap[lang] || (bip39.wordlists[lang] ? lang : 'english')
 | 
					      language = langMap[lang] || (bip39.wordlists[lang] ? lang : 'english')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    bip39.setDefaultWordlist(language)
 | 
					    bip39.setDefaultWordlist(language)
 | 
				
			||||||
    return bip39.generateMnemonic()
 | 
					    return bip39.generateMnemonic()
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  randomSeckey(option) {
 | 
				
			||||||
  randomSeckey(option){ // 跳过 secword 直接产生随机密钥
 | 
					    // 跳过 secword 直接产生随机密钥
 | 
				
			||||||
    option = option || {}
 | 
					    option = option || {}
 | 
				
			||||||
    option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN
 | 
					    option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN
 | 
				
			||||||
    if (option.tool === 'nacl') {
 | 
					    if (option.tool === 'nacl') {
 | 
				
			||||||
@ -512,8 +560,7 @@ module.exports = {
 | 
				
			|||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      return crypto.randomBytes(32).toString('hex') // Buffer.from(nacl.box.keyPair().secretKey).toString('hex') // 32字节
 | 
					      return crypto.randomBytes(32).toString('hex') // Buffer.from(nacl.box.keyPair().secretKey).toString('hex') // 32字节
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  randomKeypair(option = {}) {
 | 
					  randomKeypair(option = {}) {
 | 
				
			||||||
    option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN
 | 
					    option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN
 | 
				
			||||||
    let kp
 | 
					    let kp
 | 
				
			||||||
@ -525,61 +572,59 @@ module.exports = {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      return {
 | 
					      return {
 | 
				
			||||||
        seckey: Buffer.from(kp.secretKey).toString('hex'),
 | 
					        seckey: Buffer.from(kp.secretKey).toString('hex'),
 | 
				
			||||||
        pubkey:Buffer.from(kp.publicKey).toString('hex')
 | 
					        pubkey: Buffer.from(kp.publicKey).toString('hex'),
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      let seckey = this.randomSeckey()
 | 
					      let seckey = this.randomSeckey()
 | 
				
			||||||
      let pubkey = this.seckey2pubkey(seckey)
 | 
					      let pubkey = this.seckey2pubkey(seckey)
 | 
				
			||||||
      return {
 | 
					      return {
 | 
				
			||||||
        seckey,
 | 
					        seckey,
 | 
				
			||||||
        pubkey
 | 
					        pubkey,
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  randomAccount(option = {}) {
 | 
					  randomAccount(option = {}) {
 | 
				
			||||||
    let secword = this.randomSecword(option.lang)
 | 
					    let secword = this.randomSecword(option.lang)
 | 
				
			||||||
    return this.secword2account(secword, option)
 | 
					    return this.secword2account(secword, option)
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  randomString(length = 6, alphabet) {
 | 
				
			||||||
  randomString (length=6, alphabet) { // 长度为 length,字母表为 alphabet 的随机字符串
 | 
					    // 长度为 length,字母表为 alphabet 的随机字符串
 | 
				
			||||||
    alphabet = alphabet||"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$%^&*@"
 | 
					    alphabet = alphabet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$%^&*@'
 | 
				
			||||||
    var text = ''
 | 
					    var text = ''
 | 
				
			||||||
    for (var i = 0; i < length; i++) {
 | 
					    for (var i = 0; i < length; i++) {
 | 
				
			||||||
      text += alphabet.charAt(Math.floor(Math.random() * alphabet.length))
 | 
					      text += alphabet.charAt(Math.floor(Math.random() * alphabet.length))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return text
 | 
					    return text
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  randomNumber({ length, min, max } = {}) {
 | 
				
			||||||
  randomNumber({length, min, max}={}){ // 长度为 length 的随机数字,或者 (min||0) <= num < max
 | 
					    // 长度为 length 的随机数字,或者 (min||0) <= num < max
 | 
				
			||||||
    var num = 0
 | 
					    var num = 0
 | 
				
			||||||
    if (typeof length === 'number' && length > 0) {
 | 
					    if (typeof length === 'number' && length > 0) {
 | 
				
			||||||
      num = parseInt(Math.random() * Math.pow(10, length))
 | 
					      num = parseInt(Math.random() * Math.pow(10, length))
 | 
				
			||||||
      num = this.padStart(num.toString(), length, '0')
 | 
					      num = this.padStart(num.toString(), length, '0')
 | 
				
			||||||
    } else if (typeof max === 'number' && max > 0) {
 | 
					    } else if (typeof max === 'number' && max > 0) {
 | 
				
			||||||
      min = (typeof min === 'number' && min>=0) ? min : 0
 | 
					      min = typeof min === 'number' && min >= 0 ? min : 0
 | 
				
			||||||
      num = parseInt(Math.random() * (max - min)) + min
 | 
					      num = parseInt(Math.random() * (max - min)) + min
 | 
				
			||||||
    }else{ // 如果 option 为空
 | 
					    } else {
 | 
				
			||||||
 | 
					      // 如果 option 为空
 | 
				
			||||||
      num = Math.random()
 | 
					      num = Math.random()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return num
 | 
					    return num
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  padStart(string, targetLength, symbol) {
 | 
				
			||||||
  padStart(string, targetLength, symbol){ // 2020-03: 发现在浏览器里,还不支持 string.padStart(),只好自己写个暂代。
 | 
					    // 2020-03: 发现在浏览器里,还不支持 string.padStart(),只好自己写个暂代。
 | 
				
			||||||
    let padLength = targetLength - string.length
 | 
					    let padLength = targetLength - string.length
 | 
				
			||||||
    for (let index = 1; index <= padLength; index++) {
 | 
					    for (let index = 1; index <= padLength; index++) {
 | 
				
			||||||
      string = symbol + string
 | 
					      string = symbol + string
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return string
 | 
					    return string
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  randomUuid: uuid.v4,
 | 
				
			||||||
  randomUuid:uuid.v4
 | 
					 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  getMerkleHash(hashList, option) {
 | 
					  getMerkleHash(hashList, option) {
 | 
				
			||||||
    // merkle算法略有难度,暂时用最简单的hash代替
 | 
					    // merkle算法略有难度,暂时用最简单的hash代替
 | 
				
			||||||
    if (Array.isArray(hashList)) {
 | 
					    if (Array.isArray(hashList)) {
 | 
				
			||||||
      option = option || {}
 | 
					      option = option || {}
 | 
				
			||||||
      let output=(option.output==='buf')?undefined:(option.output||my.OUTPUT)
 | 
					      let output = option.output === 'buf' ? undefined : option.output || my.OUTPUT
 | 
				
			||||||
      let hasher = crypto.createHash(my.HASHER_LIST.indexOf(option.hasher) >= 0 ? option.hasher : my.HASHER)
 | 
					      let hasher = crypto.createHash(my.HASHER_LIST.indexOf(option.hasher) >= 0 ? option.hasher : my.HASHER)
 | 
				
			||||||
      for (var hash of hashList) {
 | 
					      for (var hash of hashList) {
 | 
				
			||||||
        hasher.update(hash)
 | 
					        hasher.update(hash)
 | 
				
			||||||
@ -587,47 +632,44 @@ module.exports = {
 | 
				
			|||||||
      return hasher.digest(output)
 | 
					      return hasher.digest(output)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  getMerkleRoot(todoHashList, option) {
 | 
					  getMerkleRoot(todoHashList, option) {
 | 
				
			||||||
    //深拷贝传入数组,防止引用对象被改变
 | 
					    //深拷贝传入数组,防止引用对象被改变
 | 
				
			||||||
    let hashList = [...todoHashList]
 | 
					    let hashList = [...todoHashList]
 | 
				
			||||||
    if(!Array.isArray(hashList))
 | 
					    if (!Array.isArray(hashList)) return null
 | 
				
			||||||
      return null
 | 
					    var border = hashList.length
 | 
				
			||||||
    var border = hashList.length;
 | 
					    if (border == 0) return this.hash('')
 | 
				
			||||||
    if(border == 0)
 | 
					    if (border == 1) return this.hash(hashList[0])
 | 
				
			||||||
      return this.hash('')
 | 
					 | 
				
			||||||
    if(border == 1)
 | 
					 | 
				
			||||||
      return this.hash(hashList[0]);
 | 
					 | 
				
			||||||
    while (1) {
 | 
					    while (1) {
 | 
				
			||||||
      let i = 1,j = 0;
 | 
					      let i = 1,
 | 
				
			||||||
 | 
					        j = 0
 | 
				
			||||||
      for (; i < border; i = i + 2) {
 | 
					      for (; i < border; i = i + 2) {
 | 
				
			||||||
        hashList[j] = this.hash(hashList[i - 1] + hashList[i]);
 | 
					        hashList[j] = this.hash(hashList[i - 1] + hashList[i])
 | 
				
			||||||
        if (border == 2) {
 | 
					        if (border == 2) {
 | 
				
			||||||
          return hashList[0];
 | 
					          return hashList[0]
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if(i + 1 == border) break;
 | 
					        if (i + 1 == border) break
 | 
				
			||||||
        j = j + 1;
 | 
					        j = j + 1
 | 
				
			||||||
        if (i + 2 == border) {
 | 
					        if (i + 2 == border) {
 | 
				
			||||||
          i = i + 1;
 | 
					          i = i + 1
 | 
				
			||||||
          hashList[j] = this.hash(hashList[i]);
 | 
					          hashList[j] = this.hash(hashList[i])
 | 
				
			||||||
          break;
 | 
					          break
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      border = j + 1;
 | 
					      border = j + 1
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return hashList
 | 
					    return hashList
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  distanceSig(hash, sig) {
 | 
				
			||||||
  distanceSig(hash, sig){ // hash为64hex字符,sig为128hex字符。返回用hex表达的距离。
 | 
					    // hash为64hex字符,sig为128hex字符。返回用hex表达的距离。
 | 
				
			||||||
    if (this.isSignature(sig) && this.isHash(hash)) {
 | 
					    if (this.isSignature(sig) && this.isHash(hash)) {
 | 
				
			||||||
      var hashSig = this.hash(sig) // 把签名也转成32字节的哈希,同样长度方便比较
 | 
					      var hashSig = this.hash(sig) // 把签名也转成32字节的哈希,同样长度方便比较
 | 
				
			||||||
      return new BigInt(hash, 16).subtract(new BigInt(hashSig, 16)).abs().toString(16)
 | 
					      return new BigInt(hash, 16).subtract(new BigInt(hashSig, 16)).abs().toString(16)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  compareSig(hash, sig1, sig2) {
 | 
				
			||||||
  compareSig(hash, sig1, sig2){ // 返回距离hash更近的sig
 | 
					    // 返回距离hash更近的sig
 | 
				
			||||||
    if (this.isHash(hash)) {
 | 
					    if (this.isHash(hash)) {
 | 
				
			||||||
      if (this.isSignature(sig2) && this.isSignature(sig1)) {
 | 
					      if (this.isSignature(sig2) && this.isSignature(sig1)) {
 | 
				
			||||||
        var dis1 = this.distanceSig(hash, sig1)
 | 
					        var dis1 = this.distanceSig(hash, sig1)
 | 
				
			||||||
@ -636,18 +678,19 @@ module.exports = {
 | 
				
			|||||||
          return sig1
 | 
					          return sig1
 | 
				
			||||||
        } else if (dis1 > dis2) {
 | 
					        } else if (dis1 > dis2) {
 | 
				
			||||||
          return sig2
 | 
					          return sig2
 | 
				
			||||||
        }else if (dis1===dis2) { // 如果极其巧合的距离相等,也可能是一个在左、一个在右,那就按 signature 本身的字符串排序来比较。
 | 
					        } else if (dis1 === dis2) {
 | 
				
			||||||
 | 
					          // 如果极其巧合的距离相等,也可能是一个在左、一个在右,那就按 signature 本身的字符串排序来比较。
 | 
				
			||||||
          return sig1 < sig2 ? sig1 : sig1 === sig2 ? sig1 : sig2
 | 
					          return sig1 < sig2 ? sig1 : sig1 === sig2 ? sig1 : sig2
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }else if (this.isSignature(sig2)){ // 允许其中一个signature是非法的,例如undefined
 | 
					      } else if (this.isSignature(sig2)) {
 | 
				
			||||||
 | 
					        // 允许其中一个signature是非法的,例如undefined
 | 
				
			||||||
        return sig2
 | 
					        return sig2
 | 
				
			||||||
      } else if (this.isSignature(sig1)) {
 | 
					      } else if (this.isSignature(sig1)) {
 | 
				
			||||||
        return sig1
 | 
					        return sig1
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  sortSigList(hash, sigList) {
 | 
					  sortSigList(hash, sigList) {
 | 
				
			||||||
    if (Array.isArray(sigList) && this.isHash(hash)) {
 | 
					    if (Array.isArray(sigList) && this.isHash(hash)) {
 | 
				
			||||||
      sigList.sort(function (sig1, sig2) {
 | 
					      sigList.sort(function (sig1, sig2) {
 | 
				
			||||||
@ -656,15 +699,15 @@ module.exports = {
 | 
				
			|||||||
          if (sig1 === sig2) return 0
 | 
					          if (sig1 === sig2) return 0
 | 
				
			||||||
          else if (winner === sig1) return -1
 | 
					          else if (winner === sig1) return -1
 | 
				
			||||||
          else if (winner === sig2) return 1
 | 
					          else if (winner === sig2) return 1
 | 
				
			||||||
        }else { // 如果 sig1 或 sig2 不是 signature 格式
 | 
					        } else {
 | 
				
			||||||
 | 
					          // 如果 sig1 或 sig2 不是 signature 格式
 | 
				
			||||||
          throw 'Not a signature!'
 | 
					          throw 'Not a signature!'
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
      return sigList
 | 
					      return sigList
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  /** 用于支付宝的接口
 | 
					  /** 用于支付宝的接口
 | 
				
			||||||
   * 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
 | 
					   * 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
 | 
				
			||||||
   * @param $para 需要拼接的数组
 | 
					   * @param $para 需要拼接的数组
 | 
				
			||||||
@ -677,12 +720,13 @@ module.exports = {
 | 
				
			|||||||
      var delimiter = delimiter || ''
 | 
					      var delimiter = delimiter || ''
 | 
				
			||||||
      for (var key of Object.keys(paramSet).sort()) {
 | 
					      for (var key of Object.keys(paramSet).sort()) {
 | 
				
			||||||
        var value = paramSet[key]
 | 
					        var value = paramSet[key]
 | 
				
			||||||
        if (value && typeof value==='object'){ // 万一 bis_content 等对象直接送了进来。
 | 
					        if (value && typeof value === 'object') {
 | 
				
			||||||
 | 
					          // 万一 bis_content 等对象直接送了进来。
 | 
				
			||||||
          value = JSON.stringify(value)
 | 
					          value = JSON.stringify(value)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if ((typeof value === 'string' && value !== '') || typeof value === 'number') {
 | 
					        if ((typeof value === 'string' && value !== '') || typeof value === 'number') {
 | 
				
			||||||
          if (converter === 'urlencode') value = encodeURIComponent(value)
 | 
					          if (converter === 'urlencode') value = encodeURIComponent(value)
 | 
				
			||||||
          string2Sign += (key + '=' + delimiter + value + delimiter + '&')  // 根据产品、版本、请求或响应的不同,有的需要key="vlaue",有的只要key=value。
 | 
					          string2Sign += key + '=' + delimiter + value + delimiter + '&' // 根据产品、版本、请求或响应的不同,有的需要key="vlaue",有的只要key=value。
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      string2Sign = string2Sign.replace(/&$/, '') // 删除末尾的 &
 | 
					      string2Sign = string2Sign.replace(/&$/, '') // 删除末尾的 &
 | 
				
			||||||
@ -692,57 +736,51 @@ module.exports = {
 | 
				
			|||||||
      return string2Sign
 | 
					      return string2Sign
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return ''
 | 
					    return ''
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  rsaSign(string2Sign, prikey, signType) {
 | 
					  rsaSign(string2Sign, prikey, signType) {
 | 
				
			||||||
    signType = signType || 'RSA-SHA1' // could be RSA-SHA256, RSA-SHA1 or more
 | 
					    signType = signType || 'RSA-SHA1' // could be RSA-SHA256, RSA-SHA1 or more
 | 
				
			||||||
    let signer = crypto.createSign(signType)
 | 
					    let signer = crypto.createSign(signType)
 | 
				
			||||||
    return encodeURIComponent(signer.update(string2Sign).sign(prikey, 'base64'))
 | 
					    return encodeURIComponent(signer.update(string2Sign).sign(prikey, 'base64'))
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  rsaVerify(string2Verify, signature, pubkey, signType) {
 | 
					  rsaVerify(string2Verify, signature, pubkey, signType) {
 | 
				
			||||||
    signType = signType || 'RSA-SHA1' // could be RSA-SHA256, RSA-SHA1 or more
 | 
					    signType = signType || 'RSA-SHA1' // could be RSA-SHA256, RSA-SHA1 or more
 | 
				
			||||||
    let verifier = crypto.createVerify(signType)
 | 
					    let verifier = crypto.createVerify(signType)
 | 
				
			||||||
    return verifier.update(string2Verify).verify(pubkey, signature, 'base64')
 | 
					    return verifier.update(string2Verify).verify(pubkey, signature, 'base64')
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					  buf2hex(buffer) {
 | 
				
			||||||
  buf2hex(buffer) { // buffer is an ArrayBuffer
 | 
					    // buffer is an ArrayBuffer
 | 
				
			||||||
    return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
 | 
					    return Array.prototype.map.call(new Uint8Array(buffer), (x) => ('00' + x.toString(16)).slice(-2)).join('')
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  hex2buf(hex) {
 | 
					  hex2buf(hex) {
 | 
				
			||||||
    return new Uint8Array(hex.match(/[\da-f]{2}/gi).map(function (h) {
 | 
					    return new Uint8Array(
 | 
				
			||||||
 | 
					      hex.match(/[\da-f]{2}/gi).map(function (h) {
 | 
				
			||||||
        return parseInt(h, 16)
 | 
					        return parseInt(h, 16)
 | 
				
			||||||
    })) // 注意,arraybuffer没有 toString('hex')功能, Buffer才有。
 | 
					      })
 | 
				
			||||||
  }
 | 
					    ) // 注意,arraybuffer没有 toString('hex')功能, Buffer才有。
 | 
				
			||||||
  ,
 | 
					  },
 | 
				
			||||||
  hex2b58c(hex) {
 | 
					  hex2b58c(hex) {
 | 
				
			||||||
    return bs58check.encode(Buffer.from(hex, 'hex'))
 | 
					    return bs58check.encode(Buffer.from(hex, 'hex'))
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  b58c2hex(box) {
 | 
					  b58c2hex(box) {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      return bs58check.decode(box).toString('hex')
 | 
					      return bs58check.decode(box).toString('hex')
 | 
				
			||||||
    } catch (exception) {
 | 
					    } catch (exception) {
 | 
				
			||||||
      return null
 | 
					      return null
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  hex2b64u(hex) {
 | 
					  hex2b64u(hex) {
 | 
				
			||||||
    if (/^[0-9a-fA-F]+$/.test(hex)) {
 | 
					    if (/^[0-9a-fA-F]+$/.test(hex)) {
 | 
				
			||||||
      return Buffer.from(hex, 'hex').toString('base64').replace(/\+/g, '-').replace(/\//g, '_')
 | 
					      return Buffer.from(hex, 'hex').toString('base64').replace(/\+/g, '-').replace(/\//g, '_')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  b64u2hex(b64u) {
 | 
					  b64u2hex(b64u) {
 | 
				
			||||||
    if (/^[0-9a-zA-Z\-_]+$/.test(b64u)) {
 | 
					    if (/^[0-9a-zA-Z\-_]+$/.test(b64u)) {
 | 
				
			||||||
      let b64 = b64u.replace(/\-/g, '+').replace(/_/g, '/')
 | 
					      let b64 = b64u.replace(/\-/g, '+').replace(/_/g, '/')
 | 
				
			||||||
      return Buffer.from(b64, 'base64').toString('hex')
 | 
					      return Buffer.from(b64, 'base64').toString('hex')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  hex2eip55(hex) {
 | 
					  hex2eip55(hex) {
 | 
				
			||||||
    if (/^(0x)?[\da-fA-F]*$/.test(hex)) {
 | 
					    if (/^(0x)?[\da-fA-F]*$/.test(hex)) {
 | 
				
			||||||
      hex = hex.toLowerCase().replace('0x', '')
 | 
					      hex = hex.toLowerCase().replace('0x', '')
 | 
				
			||||||
@ -758,8 +796,7 @@ module.exports = {
 | 
				
			|||||||
      return result
 | 
					      return result
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  aiid2regcode(aiid) {
 | 
					  aiid2regcode(aiid) {
 | 
				
			||||||
    const alphabet = 'e5fcdg3hqa4b1n0pij2rstuv67mwx89klyz'
 | 
					    const alphabet = 'e5fcdg3hqa4b1n0pij2rstuv67mwx89klyz'
 | 
				
			||||||
    const base = 16367
 | 
					    const base = 16367
 | 
				
			||||||
@ -767,13 +804,12 @@ module.exports = {
 | 
				
			|||||||
    let code = ''
 | 
					    let code = ''
 | 
				
			||||||
    let mod
 | 
					    let mod
 | 
				
			||||||
    while (num > 0) {
 | 
					    while (num > 0) {
 | 
				
			||||||
        mod = num % alphabet.length;
 | 
					      mod = num % alphabet.length
 | 
				
			||||||
      num = (num - mod) / alphabet.length
 | 
					      num = (num - mod) / alphabet.length
 | 
				
			||||||
      code = code + alphabet[mod] // 倒序存放
 | 
					      code = code + alphabet[mod] // 倒序存放
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return code
 | 
					    return code
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  regcode2aiid(code) {
 | 
					  regcode2aiid(code) {
 | 
				
			||||||
    const alphabet = 'e5fcdg3hqa4b1n0pij2rstuv67mwx89klyz'
 | 
					    const alphabet = 'e5fcdg3hqa4b1n0pij2rstuv67mwx89klyz'
 | 
				
			||||||
    const base = 16367
 | 
					    const base = 16367
 | 
				
			||||||
@ -783,11 +819,11 @@ module.exports = {
 | 
				
			|||||||
      num += alphabet.indexOf(code[i]) * Math.pow(alphabet.length, i)
 | 
					      num += alphabet.indexOf(code[i]) * Math.pow(alphabet.length, i)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return num / (base - alphabet.length) - base
 | 
					    return num / (base - alphabet.length) - base
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  // test: https://iancoleman.io/bitcoin-key-compression/
 | 
					  // test: https://iancoleman.io/bitcoin-key-compression/
 | 
				
			||||||
  // compress: https://hacpai.com/article/1550844562914
 | 
					  // compress: https://hacpai.com/article/1550844562914
 | 
				
			||||||
  compressPubkey(uncompressed){ // 把 04xy 的非压缩公钥 转成 02x 或 03x 的压缩公钥
 | 
					  compressPubkey(uncompressed) {
 | 
				
			||||||
 | 
					    // 把 04xy 的非压缩公钥 转成 02x 或 03x 的压缩公钥
 | 
				
			||||||
    let [all, x, y] = uncompressed.toLowerCase().match(/^04(.{64})(.{64})$/)
 | 
					    let [all, x, y] = uncompressed.toLowerCase().match(/^04(.{64})(.{64})$/)
 | 
				
			||||||
    let compressed
 | 
					    let compressed
 | 
				
			||||||
    if (/[1,3,5,7,9,b,d,f]$/.test(y)) {
 | 
					    if (/[1,3,5,7,9,b,d,f]$/.test(y)) {
 | 
				
			||||||
@ -799,20 +835,21 @@ module.exports = {
 | 
				
			|||||||
      return compressed
 | 
					      return compressed
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null // 非压缩公钥有错误。
 | 
					    return null // 非压缩公钥有错误。
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
  ,
 | 
					 | 
				
			||||||
  // uncompress: https://stackoverflow.com/questions/17171542/algorithm-for-elliptic-curve-point-compression/53478265#53478265
 | 
					  // uncompress: https://stackoverflow.com/questions/17171542/algorithm-for-elliptic-curve-point-compression/53478265#53478265
 | 
				
			||||||
  // https://en.bitcoin.it/wiki/Secp256k1
 | 
					  // https://en.bitcoin.it/wiki/Secp256k1
 | 
				
			||||||
  decompressPubkey(compressed){ // 把 02x 或 03x 的压缩公钥 转成 04xy 的非压缩公钥
 | 
					  decompressPubkey(compressed) {
 | 
				
			||||||
 | 
					    // 把 02x 或 03x 的压缩公钥 转成 04xy 的非压缩公钥
 | 
				
			||||||
    // Consts for secp256k1 curve. Adjust accordingly
 | 
					    // Consts for secp256k1 curve. Adjust accordingly
 | 
				
			||||||
    const prime = new BigInt('fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f', 16) // 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1
 | 
					    const prime = new BigInt('fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f', 16) // 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1
 | 
				
			||||||
    const pIdent = new BigInt('3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0c', 16) // prime.add(1).divide(4);
 | 
					    const pIdent = new BigInt('3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0c', 16) // prime.add(1).divide(4);
 | 
				
			||||||
    var signY = new Number(compressed[1]) - 2
 | 
					    var signY = new Number(compressed[1]) - 2
 | 
				
			||||||
    var x = new BigInt(compressed.substring(2), 16)
 | 
					    var x = new BigInt(compressed.substring(2), 16)
 | 
				
			||||||
    var y = x.modPow(3, prime).add(7).mod(prime).modPow(pIdent, prime) // y mod p = +-(x^3 + 7)^((p+1)/4) mod p
 | 
					    var y = x.modPow(3, prime).add(7).mod(prime).modPow(pIdent, prime) // y mod p = +-(x^3 + 7)^((p+1)/4) mod p
 | 
				
			||||||
    if( y.mod(2).toJSNumber() !== signY ) { // If the parity doesn't match it's the *other* root
 | 
					    if (y.mod(2).toJSNumber() !== signY) {
 | 
				
			||||||
 | 
					      // If the parity doesn't match it's the *other* root
 | 
				
			||||||
      y = prime.subtract(y) // y = prime - y
 | 
					      y = prime.subtract(y) // y = prime - y
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return '04' + this.padStart(x.toString(16), 64, '0') + this.padStart(y.toString(16), 64, '0')
 | 
					    return '04' + this.padStart(x.toString(16), 64, '0') + this.padStart(y.toString(16), 64, '0')
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user