TIC 地址采用 b64u 编码
This commit is contained in:
parent
2a0692e9be
commit
bdec8f6acb
132
index.js
132
index.js
@ -83,10 +83,6 @@ module.exports = {
|
||||
return /^((02|03)?[a-fA-F0-9]{64}|04[a-fA-F0-9]{128})$/.test(pubkey) // "d2f186a630f5558ba3ede10a4dd0549da5854eab3ed28ee8534350c2535d38b0"
|
||||
}
|
||||
,
|
||||
isAddress (address) {
|
||||
return /^[m|t|d|T][123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{33}$/.test(address) // && address.length>25 && bs58check.decode(address.slice(1)) && ['A'].indexOf(address[0]>=0)) {
|
||||
}
|
||||
,
|
||||
isSignature(signature){
|
||||
return /^[a-fA-F0-9]{128,144}$/.test(signature)
|
||||
}
|
||||
@ -304,7 +300,7 @@ module.exports = {
|
||||
return null
|
||||
}
|
||||
,
|
||||
pubkey2position (pubkey, {coin}={}){
|
||||
pubkey2position (pubkey, {coin}={}){ // tic, btc, eth 的 position 都是 20节=40字符的。
|
||||
coin = my.COIN_LIST.indexOf(coin)>=0?coin:my.COIN
|
||||
if(this.isPubkey(pubkey)){
|
||||
if (coin==='ETH'){
|
||||
@ -322,11 +318,11 @@ module.exports = {
|
||||
return null
|
||||
}
|
||||
,
|
||||
position2address(position, {coin, netType}={}){
|
||||
if (!position) return null
|
||||
position2address(position, {coin, net, format}={}){
|
||||
if (!/^[\da-fA-F]{40}$/.test(position)) return null // 不论 tic, btc, eth,其 position 都是 40字符的。
|
||||
coin = my.COIN_LIST.indexOf(coin)>=0?coin:my.COIN
|
||||
let address
|
||||
if (coin==='ETH'){ // 对以太坊,按照 EIP55,把纯位置转换为大小写敏感能自我验证的hex地址
|
||||
if (coin==='ETH'){ // 对以太坊,按照 EIP55,把纯位置转换为大小写敏感能自我验证的hex地址。仍然为20节=40符。
|
||||
position = position.toLowerCase().replace('0x', '')
|
||||
let hash = keccak('keccak256').update(position).digest('hex')
|
||||
address = '0x'
|
||||
@ -338,9 +334,9 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
return address
|
||||
}else if (coin === 'BTC'){ // 对比特币,把纯位置转换为大小写敏感能自我验证的bs58check地址
|
||||
}else if (coin === 'BTC'){ // 对比特币,把纯位置转换为大小写敏感能自我验证的bs58check地址:先加前缀,再加校验,共25字节,再转base58。得到26~34个字符,大多数34个。
|
||||
let prefix
|
||||
switch (netType) {
|
||||
switch (net) {
|
||||
case 'mainnet': prefix='00'; break; // pubkey hash => 1
|
||||
case 'mainnetSh': prefix='05'; break; // script hash => 3
|
||||
case 'testnet': prefix='6f'; break; // testnet pubkey hash => m or n
|
||||
@ -351,52 +347,60 @@ module.exports = {
|
||||
}
|
||||
address=bs58check.encode(Buffer.from(prefix+position, 'hex')) // wallet import format
|
||||
return address
|
||||
}else {
|
||||
}else { // 默认为 TIC。把纯位置转换为大小写敏感能自我验证的 b64u(base64 for url) 地址。
|
||||
let prefix
|
||||
switch (netType){
|
||||
case 'mainnet': prefix='42'; break; // '42'=>T, '6E'=>m
|
||||
case 'testnet': prefix='7F'; break; // '7F'=>t
|
||||
case 'devnet': prefix='5A'; break; // '5A'=>d
|
||||
default: prefix='42' // 默认暂且为 42,为了兼容已经运行的链。
|
||||
switch (net){
|
||||
// Base58: https://en.bitcoin.it/wiki/List_of_address_prefixes
|
||||
// Base64: https://baike.baidu.com/item/base64
|
||||
case 'mainnet': prefix='4c'; break; // Base58: 0x42=66 => T, Base64: base64 T=0x13=0b00010011 => 0b010011xx = 0x4c~4f
|
||||
case 'testnet': prefix='b4'; break; // Base58: 0x7f=127,0x80=128 => t, Base64: t=0x2d=0b00101101 => 0b101101xx = 0xB4~B7
|
||||
case 'devnet': prefix='74'; break; // Base58: 0x90 => d, Base 64: d=0x1d=0b00011101 => 0b 011101xx = 0x74~77
|
||||
default: prefix='4c'
|
||||
}
|
||||
address=bs58check.encode(Buffer.from(prefix+position, 'hex')) // wallet import format
|
||||
let checksum = this.hash(this.hash(position)).slice(0,6) // 添加 checksum 使得能够检测大小写错误。注意,不包含 prefix,这样更纯粹、专注。
|
||||
// address = this.hex2eip55(prefix + position + checksum) // 前缀1节,位置20节,校验3节,共24节=48字符(能够完全转化为8个色彩),再转eip55。
|
||||
address = this.hex2b64u(prefix + position + checksum) // 实际采用 b64u (named by luk.lu as base 64 for url), 共 32字符。
|
||||
return address
|
||||
}
|
||||
return null
|
||||
}
|
||||
,
|
||||
address2position(){
|
||||
if (/^0x[\da-fA-F]{40}$/.test(address)){
|
||||
return address.toLowerCase()
|
||||
}else if (/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{26,34}$/.test(address)){
|
||||
let hex = this.b58c2hex(address)
|
||||
if (hex) {
|
||||
return hex.slice(2) // 去除网络前缀
|
||||
}
|
||||
}else if (/^[Tt][0-9a-zA-Z\-_]{31}$/.test(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})$/)
|
||||
if (this.hash(this.hash(position)).slice(0,6) === checksum) {
|
||||
return position
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
,
|
||||
isAddress(address){
|
||||
if (/^0x[\da-fA-F]{40}$/.test(address)){
|
||||
return 'ETH'
|
||||
}else if (/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{26,34}$/.test(address) // 格式合法
|
||||
&& this.b58c2hex(address)){ // 内容合法
|
||||
return 'BTC'
|
||||
}else if (/^[Ttd][0-9a-zA-Z\-_]{31}$/.test(address)){ // 格式合法
|
||||
let b64 = address.replace('-', '+').replace('_', '/')
|
||||
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})$/)
|
||||
return this.hash(this.hash(position)).slice(0,6) === checksum
|
||||
}
|
||||
return null
|
||||
}
|
||||
,
|
||||
pubkey2address (pubkey, option={}) { // pubkey 应当是string类型
|
||||
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
|
||||
if (this.isPubkey(pubkey)) {
|
||||
let h256 = crypto.createHash('sha256').update(Buffer.from(pubkey, 'hex')).digest()
|
||||
let h160 = crypto.createHash('ripemd160').update(h256).digest('hex')
|
||||
let prefix
|
||||
if (option.coin==='TIC'){
|
||||
switch (option.netType){
|
||||
case 'mainnet': prefix='42'; break; // '42'=>T, '6E'=>m
|
||||
case 'testnet': prefix='7F'; break; // '7F'=>t
|
||||
case 'devnet': prefix='5A'; break; // '5A'=>d
|
||||
default: prefix='42' // 默认暂且为 42,为了兼容已经运行的链。
|
||||
}
|
||||
}else if (option.coin==='BTC'){
|
||||
switch (option.netType) {
|
||||
case 'mainnet': prefix='00'; break; // 1
|
||||
case 'testnet': prefix='6f'; break; // m or n
|
||||
case 'p2sh': prefix='05'; break; // 3
|
||||
default: prefix='00'
|
||||
}
|
||||
}else if (option.coin==='ETH'){
|
||||
// 注意,必须要用非压缩的64字节的公钥的buffer,并去掉开头的 04。
|
||||
return '0x' + keccak('keccak256').update(Buffer.from(pubkey.slice(2),'hex')).digest('hex').slice(-40)
|
||||
// 或 const { keccak256 } = require('ethereumjs-util'); keccak256(Buffer.from(pubkey.slice(2),'hex)).toString('hex').slice(40)
|
||||
// 或 const { Keccak } = require('sha3'); new Keccak('').update(Bufer.from(pubkey.slice(2),'hex')).digest('hex').slice(-40)
|
||||
}else {
|
||||
return null
|
||||
}
|
||||
var wifAddress=bs58check.encode(Buffer.from(prefix+h160,'hex')) // wallet import format
|
||||
return wifAddress
|
||||
}
|
||||
return null
|
||||
return this.position2address(this.pubkey2position(pubkey, option), option)
|
||||
}
|
||||
,
|
||||
secword2seed(secword, pass) { // 遵循bip39的算法。和 ether.HDNode.mnemonic2Seed 结果一样,是64字节的种子。
|
||||
@ -624,11 +628,26 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
,
|
||||
hex2b64u(hex){
|
||||
if (/^[0-9a-fA-F]+$/.test(hex)) {
|
||||
return Buffer.from(hex,'hex').toString('base64').replace(/\+/g,'-').replace(/\//g, '_')
|
||||
}
|
||||
return null
|
||||
}
|
||||
,
|
||||
b64u2hex(b64u){
|
||||
if (/^[0-9a-zA-Z\-_]+$/.test(b64u)){
|
||||
let b64 = b64u.replace(/\-/g, '+').replace(/_/g, '/')
|
||||
return Buffer.from(b64, 'base64').toString('hex')
|
||||
}
|
||||
return null
|
||||
}
|
||||
,
|
||||
hex2eip55(hex){
|
||||
if (typeof(hex)==='string' && hex.length===64) {
|
||||
if (/^(0x)?[\da-fA-F]*$/.test(hex)) {
|
||||
hex = hex.toLowerCase().replace('0x', '')
|
||||
let hash = keccak('keccak256').update(hex).digest('hex')
|
||||
result = '0x'
|
||||
let result = ''
|
||||
for (var i = 0; i < hex.length; i++) {
|
||||
if (parseInt(hash[i], 16) >= 8) {
|
||||
result += hex[i].toUpperCase()
|
||||
@ -643,18 +662,23 @@ module.exports = {
|
||||
,
|
||||
// test: https://iancoleman.io/bitcoin-key-compression/
|
||||
// compress: https://hacpai.com/article/1550844562914
|
||||
compressPubkey(uncompressed){
|
||||
compressPubkey(uncompressed){ // 把 04xy 的非压缩公钥 转成 02x 或 03x 的压缩公钥
|
||||
let [all, x, y]=uncompressed.toLowerCase().match(/^04(.{64})(.{64})$/)
|
||||
let compressed
|
||||
if (/[1,3,5,7,9,b,d,f]$/.test(y)){
|
||||
return '03'+x // y为奇数=>前缀03
|
||||
compressed = '03'+x // y为奇数=>前缀03
|
||||
}else{
|
||||
return '02'+x // y为偶数=>前缀02
|
||||
compressed = '02'+x // y为偶数=>前缀02
|
||||
}
|
||||
if (this.decompressPubkey(compressed)===uncompressed) {
|
||||
return compressed
|
||||
}
|
||||
return null // 非压缩公钥有错误。
|
||||
}
|
||||
,
|
||||
// uncompress: https://stackoverflow.com/questions/17171542/algorithm-for-elliptic-curve-point-compression/53478265#53478265
|
||||
// https://en.bitcoin.it/wiki/Secp256k1
|
||||
decompressPubkey(compressed){
|
||||
decompressPubkey(compressed){ // 把 02x 或 03x 的压缩公钥 转成 04xy 的非压缩公钥
|
||||
// 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 pIdent = new BigInt('3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0c', 16) // prime.add(1).divide(4);
|
||||
@ -662,7 +686,7 @@ module.exports = {
|
||||
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
|
||||
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' + x.toString(16).padStart(64, '0') + y.toString(16).padStart(64, '0')
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
const bigInt = require("big-integer");
|
||||
|
||||
// Consts for P256 curve. Adjust accordingly
|
||||
const two = new bigInt(2),
|
||||
// 115792089210356248762697446949407573530086143415290314195533631308867097853951
|
||||
prime = two.pow(256).subtract( two.pow(224) ).add( two.pow(192) ).add( two.pow(96) ).subtract(1),
|
||||
b = new bigInt( '41058363725152142129326129780047268409114441015993725554835256314039467401291' ),
|
||||
// Pre-computed value, or literal
|
||||
// 28948022302589062190674361737351893382521535853822578548883407827216774463488
|
||||
pIdent = prime.add(1).divide(4);
|
||||
|
||||
function pad_with_zeroes(number, length) {
|
||||
var retval = '' + number;
|
||||
while (retval.length < length) {
|
||||
retval = '0' + retval;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Point decompress NIST curve
|
||||
* @param {string} Compressed representation in hex string
|
||||
* @return {string} Uncompressed representation in hex string
|
||||
*/
|
||||
function ECPointDecompress( comp ) {
|
||||
var signY = new Number(comp[1]) - 2;
|
||||
var x = new bigInt(comp.substring(2), 16);
|
||||
// y^2 = x^3 - 3x + b
|
||||
var y = x.pow(3).subtract( x.multiply(3) ).add( b ).modPow( pIdent, prime );
|
||||
// If the parity doesn't match it's the *other* root
|
||||
if( y.mod(2).toJSNumber() !== signY ) {
|
||||
// y = prime - y
|
||||
y = prime.subtract( y );
|
||||
}
|
||||
return '04' + pad_with_zeroes(x.toString(16), 64) + pad_with_zeroes(y.toString(16), 64);
|
||||
}
|
||||
|
||||
console.log(ECPointDecompress('035d77c1e3eac37f685aeea2ae872c4e7e4d159756e57601db3bcccbc549f360b2'))
|
Loading…
Reference in New Issue
Block a user