添加 cid 转换函数

This commit is contained in:
陆柯 2022-05-28 09:08:55 +08:00
parent 447a3eb442
commit 74694866bc

125
index.js
View File

@ -31,6 +31,17 @@ my.INPUT = 'utf8' // 默认的加密方法的明文格式。utf8 能够兼容 la
my.INPUT_LIST = ['utf8', 'ascii', 'latin1'] // ignored for Buffer/TypedArray/DataView my.INPUT_LIST = ['utf8', 'ascii', 'latin1'] // ignored for Buffer/TypedArray/DataView
my.COIN = 'TIC' // 默认的币种 my.COIN = 'TIC' // 默认的币种
my.COIN_LIST = ['TIC', 'EXT', 'BTC', 'ETH'] my.COIN_LIST = ['TIC', 'EXT', 'BTC', 'ETH']
my.REGEXP_ALPHABET = {
hex: /^[0-9a-fA-F]+$/,
b32: /^[A-Za-z2-7=]+$/,
b32h: /^[0-9A-Va-v=]+$/,
b36: /^[0-9A-Z-a-z]+$/,
b58: /^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$/,
b62: /^[A-Za-z0-9]+$/,
b64: /^[A-Za-z0-9\+\/=]+$/,
b64u: /^[A-Za-z0-9\-_]+$/,
b64t: /^[A-Za-z0-9\._]+$/,
}
/** /**
* *
@ -164,7 +175,10 @@ class TICrypto {
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(input)>=0?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(input)>=0?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) // output: 留空=》默认输出hex格式或者手动指定 'buf', hex', 'latin1' or 'base64' let outputEncoding = output === 'buf' ? undefined : output // (my.OUTPUT_LIST.indexOf(output)>=0?output:my.OUTPUT) // 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
} }
@ -349,7 +363,10 @@ class TICrypto {
// 如果使用其他机制例如密码、随机数不使用secword也可生成keypair // 如果使用其他机制例如密码、随机数不使用secword也可生成keypair
if (this.isHashable(pass)) { if (this.isHashable(pass)) {
hasher = my.HASHER_LIST.indexOf(hasher) >= 0 ? hasher : my.HASHER hasher = my.HASHER_LIST.indexOf(hasher) >= 0 ? hasher : my.HASHER
var hashBuf = crypto.createHash(hasher).update(pass).digest() var hashBuf = crypto
.createHash(hasher)
.update(pass)
.digest()
var keypair = nacl.sign.keyPair.fromSeed(hashBuf) // nacl的seed要求是32字节 var keypair = nacl.sign.keyPair.fromSeed(hashBuf) // nacl的seed要求是32字节
return { return {
hash: hashBuf.toString('hex'), hash: hashBuf.toString('hex'),
@ -412,7 +429,10 @@ class TICrypto {
if (tool === 'nacl') { if (tool === 'nacl') {
// 采用自己的算法bip39算法从secword到种子hash后用 nacl.sign.keyPair.fromSeed()方法。 // 采用自己的算法bip39算法从secword到种子hash后用 nacl.sign.keyPair.fromSeed()方法。
hasher = my.HASHER_LIST.indexOf(hasher) >= 0 ? hasher : my.HASHER hasher = my.HASHER_LIST.indexOf(hasher) >= 0 ? hasher : my.HASHER
let hashBuf = crypto.createHash(hasher).update(this.secword2seed(secword, pass)).digest() let hashBuf = crypto
.createHash(hasher)
.update(this.secword2seed(secword, pass))
.digest()
let keypair = nacl.sign.keyPair.fromSeed(hashBuf) // nacl.sign.keyPair.fromSeed 要求32字节的种子而 this.secword2seed生成的是64字节种子所以要先做一次sha256 let keypair = nacl.sign.keyPair.fromSeed(hashBuf) // nacl.sign.keyPair.fromSeed 要求32字节的种子而 this.secword2seed生成的是64字节种子所以要先做一次sha256
return { return {
coin: coin, coin: coin,
@ -622,8 +642,14 @@ class TICrypto {
.digest('hex') .digest('hex')
.slice(-40) .slice(-40)
} else { } else {
let h256 = crypto.createHash('sha256').update(Buffer.from(pubkey, 'hex')).digest() let h256 = crypto
let h160 = crypto.createHash('ripemd160').update(h256).digest('hex') .createHash('sha256')
.update(Buffer.from(pubkey, 'hex'))
.digest()
let h160 = crypto
.createHash('ripemd160')
.update(h256)
.digest('hex')
return h160 return h160
} }
} }
@ -647,7 +673,9 @@ class TICrypto {
if (coin === 'ETH') { if (coin === 'ETH') {
// 对以太坊,按照 EIP55把纯位置转换为大小写敏感能自我验证的hex地址。仍然为20节=40符。 // 对以太坊,按照 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'
for (var i = 0; i < position.length; i++) { for (var i = 0; i < position.length; i++) {
if (parseInt(hash[i], 16) >= 8) { if (parseInt(hash[i], 16) >= 8) {
@ -720,15 +748,18 @@ class TICrypto {
* 地址和PubKeyHash(即position)之间能互相转化 * 地址和PubKeyHash(即position)之间能互相转化
*/ */
static address2position () { static address2position () {
if (/^0x[\da-fA-F]{40}$/.test(address)) { // ETH if (/^0x[\da-fA-F]{40}$/.test(address)) {
// ETH
// todo: 如果是大小写敏感的,进行有效性验证 // todo: 如果是大小写敏感的,进行有效性验证
return address.toLowerCase() return address.toLowerCase()
} else if (/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{26,34}$/.test(address)) { // BTC } else if (/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{26,34}$/.test(address)) {
// BTC
let hex = this.b58c_to_hex(address) let hex = this.b58c_to_hex(address)
if (hex) { if (hex) {
return hex.slice(2) // 去除网络前缀 return hex.slice(2) // 去除网络前缀
} }
} else if (/^[Tt][0-9a-zA-Z\._]{31}$/.test(address)) { // TIC } else if (/^[Tt][0-9a-zA-Z\._]{31}$/.test(address)) {
// TIC
// 格式合法 // 格式合法
let hex = this.b64t_to_hex(address) let hex = this.b64t_to_hex(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})$/)
@ -1045,7 +1076,10 @@ class TICrypto {
// 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
} }
@ -1264,7 +1298,10 @@ class TICrypto {
* @returns * @returns
*/ */
static b64_to_b64t (b64 = '') { static b64_to_b64t (b64 = '') {
return b64.replace(/\+/g, '.').replace(/\//g, '_').replace(/=/g, '') return b64
.replace(/\+/g, '.')
.replace(/\//g, '_')
.replace(/=/g, '')
} }
static b64t_to_b64 (b64t = '') { static b64t_to_b64 (b64t = '') {
@ -1338,7 +1375,9 @@ class TICrypto {
static hex_to_eip55 (hex) { static hex_to_eip55 (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', '')
let hash = keccak('keccak256').update(hex).digest('hex') let hash = keccak('keccak256')
.update(hex)
.digest('hex')
let result = '' let result = ''
for (var i = 0; i < hex.length; i++) { for (var i = 0; i < hex.length; i++) {
if (parseInt(hash[i], 16) >= 8) { if (parseInt(hash[i], 16) >= 8) {
@ -1394,13 +1433,73 @@ class TICrypto {
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.substr(2), 16) var x = new BigInt(compressed.substr(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 (y.mod(2).toJSNumber() !== signY) {
// If the parity doesn't match it's the *other* root // 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')
} }
static cid_to_cosh ({ cid }) {
if (/^[Q|1]/.test(cid)) {
return this.b58_to_hex(cid).slice(4)
} else if (/^b/.test(cid)) {
return this.b32_to_hex(cid.substr(1)).slice(8)
} else if (/^z/.test(cid)) {
return this.b58_to_hex(cid.substr(1)).slice(8)
}
}
static cosh_to_cid ({ cosh, cidBase = 'b32', cidVersion = 1, cidCodec = 'raw', cidAlgo = 'sha256' }) {
const multibase = {
identity: 0x00,
b2: '0',
b8: '7',
b10: '9',
b16: 'f',
B16: 'F',
b32: 'b',
B32: 'B',
b32h: 'v',
B32h: 'V',
b36: 'k',
b64: 'm',
b64p: 'M',
b64u: 'u',
b64up: 'U',
b58: 'z',
}
const multicodec = {
dagpb: '70',
p2pkey: '72',
raw: '55'
}
const multialgo = {
identify: '00',
sha256: '12'
}
if (cidVersion === 0) {
return this.hex_to_b58(`${multialgo[cidAlgo]}${Number(cosh.length/2).toString(16)}${cosh}`)
}
if (cidVersion === 1) {
let fullHex = `01${multicodec[cidCodec]}${multialgo[cidAlgo]}${Number(cosh.length/2).toString(16)}${cosh}`
if (cidBase==='b32') {
return multibase[cidBase] + this.hex_to_b32(fullHex).toLowerCase().replace(/=/g,'')
}else if (cidBase==='b58') {
return multibase[cidBase] + this.hex_to_b58(fullHex)
}
}
}
static string_to_raw_cid (str) {
return this.cosh_to_cid({ cosh: this.hash(str), cidVersion:1, cidCodec:'raw' })
}
} }
// 必须单独写 module.exports不要和类定义写在一起否则会导致 jsdoc 解析不到类内文档。 // 必须单独写 module.exports不要和类定义写在一起否则会导致 jsdoc 解析不到类内文档。