-
Notifications
You must be signed in to change notification settings - Fork 94
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
jinguang.huang
committed
Nov 17, 2023
1 parent
744a835
commit 7690e14
Showing
10 changed files
with
348 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { encodePubKeyAddress, decodeAddress } from "./lib/address"; | ||
|
||
export function addressFromPubKey(pubKey: string) { | ||
return encodePubKeyAddress(pubKey, "kaspa"); | ||
} | ||
|
||
export function validateAddress(address: string) { | ||
try { | ||
decodeAddress(address); | ||
} catch (e) { | ||
return false; | ||
} | ||
return true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from "./KaspaWallet"; | ||
export * from "./address"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import { validate } from "./validation"; | ||
import { convert as convertBits } from "./convertBits"; | ||
import * as base32 from "./base32"; | ||
import { base } from "@okxweb3/crypto-lib"; | ||
|
||
export function encodePubKeyAddress(pubKey: string, prefix: string) { | ||
const eight0 = [0,0,0,0,0,0,0,0]; | ||
const prefixData = prefixToArray(prefix).concat([0]); | ||
const versionByte = 0; | ||
|
||
const pubKeyArray = Array.prototype.slice.call(base.fromHex(pubKey), 0); | ||
const payloadData = convertBits(new Uint8Array([versionByte].concat(pubKeyArray)), 8, 5, false); | ||
const checksumData = new Uint8Array(prefixData.length + payloadData.length + eight0.length); | ||
checksumData.set(prefixData); | ||
checksumData.set(payloadData, prefixData.length); | ||
checksumData.set(eight0, prefixData.length + payloadData.length); | ||
const polymodData = checksumToArray(polymod(checksumData)); | ||
|
||
const payload = new Uint8Array(payloadData.length + polymodData.length); | ||
payload.set(payloadData); | ||
payload.set(polymodData, payloadData.length); | ||
|
||
return 'kaspa:' + base32.encode(payload); | ||
} | ||
|
||
export function decodeAddress(address: string) { | ||
validate(hasSingleCase(address), 'Mixed case'); | ||
address = address.toLowerCase(); | ||
|
||
const pieces = address.split(':'); | ||
validate(pieces.length === 2, 'Invalid format: ' + address); | ||
|
||
const prefix = pieces[0]; | ||
validate(prefix === 'kaspa', 'Invalid prefix: ' + address); | ||
const encodedPayload = pieces[1]; | ||
const payload = base32.decode(encodedPayload); | ||
validate(validChecksum(prefix, payload), 'Invalid checksum: ' + address); | ||
|
||
const convertedBits = convertBits(payload.slice(0, -8), 5, 8, true); | ||
const versionByte = convertedBits[0]; | ||
const hashOrPublicKey = convertedBits.slice(1); | ||
|
||
if (versionByte === 1) { | ||
validate(264 === hashOrPublicKey.length * 8, 'Invalid hash size: ' + address); | ||
} else { | ||
validate(256 === hashOrPublicKey.length * 8, 'Invalid hash size: ' + address); | ||
} | ||
|
||
const type = getType(versionByte); | ||
|
||
return { | ||
payload: Buffer.from(hashOrPublicKey), | ||
prefix, | ||
type, | ||
}; | ||
} | ||
|
||
function hasSingleCase(string: string) { | ||
return string === string.toLowerCase() || string === string.toUpperCase(); | ||
} | ||
|
||
function prefixToArray(prefix: string) { | ||
const result = []; | ||
for (let i = 0; i < prefix.length; i++) { | ||
result.push(prefix.charCodeAt(i) & 31); | ||
} | ||
return result; | ||
} | ||
|
||
function checksumToArray(checksum: number) { | ||
const result = []; | ||
for (let i = 0; i < 8; ++i) { | ||
result.push(checksum & 31); | ||
checksum /= 32; | ||
} | ||
return result.reverse(); | ||
} | ||
|
||
function validChecksum(prefix: string, payload: Uint8Array) { | ||
const prefixData = prefixToArray(prefix); | ||
const data = new Uint8Array(prefix.length + 1 + payload.length); | ||
data.set(prefixData); | ||
data.set([0], prefixData.length) | ||
data.set(payload, prefixData.length + 1); | ||
|
||
return polymod(data) === 0; | ||
} | ||
|
||
function getType(versionByte: number) { | ||
switch (versionByte & 120) { | ||
case 0: | ||
return 'pubkey'; | ||
case 8: | ||
return 'scripthash'; | ||
default: | ||
throw new Error('Invalid address type in version byte:' + versionByte); | ||
} | ||
} | ||
|
||
const GENERATOR1 = [0x98, 0x79, 0xf3, 0xae, 0x1e]; | ||
const GENERATOR2 = [0xf2bc8e61, 0xb76d99e2, 0x3e5fb3c4, 0x2eabe2a8, 0x4f43e470]; | ||
|
||
function polymod(data: Uint8Array) { | ||
// Treat c as 8 bits + 32 bits | ||
var c0 = 0, c1 = 1, C = 0; | ||
for (var j = 0; j < data.length; j++) { | ||
// Set C to c shifted by 35 | ||
C = c0 >>> 3; | ||
// 0x[07]ffffffff | ||
c0 &= 0x07; | ||
// Shift as a whole number | ||
c0 <<= 5; | ||
c0 |= c1 >>> 27; | ||
// 0xffffffff >>> 5 | ||
c1 &= 0x07ffffff; | ||
c1 <<= 5; | ||
// xor the last 5 bits | ||
c1 ^= data[j]; | ||
for (var i = 0; i < GENERATOR1.length; ++i) { | ||
if (C & (1 << i)) { | ||
c0 ^= GENERATOR1[i]; | ||
c1 ^= GENERATOR2[i]; | ||
} | ||
} | ||
} | ||
c1 ^= 1; | ||
// Negative numbers -> large positive numbers | ||
if (c1 < 0) { | ||
c1 ^= 1 << 31; | ||
c1 += (1 << 30) * 2; | ||
} | ||
// Unless bitwise operations are used, | ||
// numbers are consisting of 52 bits, except | ||
// the sign bit. The result is max 40 bits, | ||
// so it fits perfectly in one number! | ||
return c0 * (1 << 30) * 4 + c1; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/** | ||
* The following methods are based on `Emilio Almansi`, thanks for their work | ||
* @license | ||
* https://github.com/ealmansi/cashaddrjs | ||
* Copyright (c) 2017-2020 Emilio Almansi | ||
* Distributed under the MIT software license, see the accompanying | ||
* file LICENSE or http://www.opensource.org/licenses/mit-license.php. | ||
*/ | ||
|
||
'use strict'; | ||
|
||
import {validate} from './validation'; | ||
/** | ||
* Base32 encoding and decoding. | ||
* | ||
* @module base32 | ||
*/ | ||
|
||
/** | ||
* Charset containing the 32 symbols used in the base32 encoding. | ||
* @private | ||
*/ | ||
const CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'; | ||
|
||
/** | ||
* Inverted index mapping each symbol into its index within the charset. | ||
* @private | ||
*/ | ||
const CHARSET_INVERSE_INDEX = { | ||
'q': 0, 'p': 1, 'z': 2, 'r': 3, 'y': 4, '9': 5, 'x': 6, '8': 7, | ||
'g': 8, 'f': 9, '2': 10, 't': 11, 'v': 12, 'd': 13, 'w': 14, '0': 15, | ||
's': 16, '3': 17, 'j': 18, 'n': 19, '5': 20, '4': 21, 'k': 22, 'h': 23, | ||
'c': 24, 'e': 25, '6': 26, 'm': 27, 'u': 28, 'a': 29, '7': 30, 'l': 31, | ||
}; | ||
|
||
/** | ||
* Encodes the given array of 5-bit integers as a base32-encoded string. | ||
* | ||
* @static | ||
* @param {Uint8Array} data Array of integers between 0 and 31 inclusive. | ||
* @returns {string} | ||
* @throws {Error} | ||
*/ | ||
export function encode(data: Uint8Array) { | ||
var base32 = ''; | ||
for (var i = 0; i < data.length; ++i) { | ||
var value = data[i]; | ||
validate(0 <= value && value < 32, 'Invalid value: ' + value + '.'); | ||
base32 += CHARSET[value]; | ||
} | ||
return base32; | ||
} | ||
|
||
/** | ||
* Decodes the given base32-encoded string into an array of 5-bit integers. | ||
* | ||
* @static | ||
* @returns {Uint8Array} | ||
* @throws {Error} | ||
* @param str | ||
*/ | ||
export function decode(str: string) { | ||
var data = new Uint8Array(str.length); | ||
for (var i = 0; i < str.length; ++i) { | ||
var value = str[i]; | ||
validate(value in CHARSET_INVERSE_INDEX, 'Invalid value: ' + value + '.'); | ||
// @ts-ignore | ||
data[i] = CHARSET_INVERSE_INDEX[value]; | ||
} | ||
return data; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/** | ||
* The following methods are based on `Emilio Almansi`, thanks for their work | ||
* https://github.com/ealmansi/cashaddrjs | ||
*/ | ||
|
||
// Copyright (c) 2017-2018 Emilio Almansi | ||
// Copyright (c) 2017 Pieter Wuille | ||
// | ||
// Permission is hereby granted, free of charge, to any person obtaining a copy | ||
// of this software and associated documentation files (the "Software"), to deal | ||
// in the Software without restriction, including without limitation the rights | ||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
// copies of the Software, and to permit persons to whom the Software is | ||
// furnished to do so, subject to the following conditions: | ||
// | ||
// The above copyright notice and this permission notice shall be included in | ||
// all copies or substantial portions of the Software. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
// THE SOFTWARE. | ||
|
||
'use strict'; | ||
|
||
import {validate} from './validation'; | ||
|
||
/** | ||
* Converts an array of integers made up of 'from' bits into an | ||
* array of integers made up of 'to' bits. The output array is | ||
* zero-padded if necessary, unless strict mode is true. | ||
* Throws a {@link Error} if input is invalid. | ||
* Original by Pieter Wuille: https://github.com/sipa/bech32. | ||
* | ||
* @param {Uint8Array} data Array of integers made up of 'from' bits. | ||
* @param {number} from Length in bits of elements in the input array. | ||
* @param {number} to Length in bits of elements in the output array. | ||
* @param {bool} strictMode Require the conversion to be completed without padding. | ||
* @returns {Uint8Array} | ||
*/ | ||
export function convert(data: Uint8Array, from: number, to: number, strictMode: boolean) { | ||
var length = strictMode | ||
? Math.floor(data.length * from / to) | ||
: Math.ceil(data.length * from / to); | ||
var mask = (1 << to) - 1; | ||
var result = new Uint8Array(length); | ||
var index = 0; | ||
var accumulator = 0; | ||
var bits = 0; | ||
for (var i = 0; i < data.length; ++i) { | ||
var value = data[i]; | ||
validate(0 <= value && (value >> from) === 0, 'Invalid value: ' + value + '.'); | ||
accumulator = (accumulator << from) | value; | ||
bits += from; | ||
while (bits >= to) { | ||
bits -= to; | ||
result[index] = (accumulator >> bits) & mask; | ||
++index; | ||
} | ||
} | ||
if (!strictMode) { | ||
if (bits > 0) { | ||
result[index] = (accumulator << (to - bits)) & mask; | ||
++index; | ||
} | ||
} else { | ||
validate( | ||
bits < from && ((accumulator << (to - bits)) & mask) === 0, | ||
'Input cannot be converted to ' + to + ' bits without padding, but strict mode was used.' | ||
); | ||
} | ||
return result; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/** | ||
* The following methods are based on `Emilio Almansi`, thanks for their work | ||
* @license | ||
* https://github.com/ealmansi/cashaddrjs | ||
* Copyright (c) 2017-2020 Emilio Almansi | ||
* Distributed under the MIT software license, see the accompanying | ||
* file LICENSE or http://www.opensource.org/licenses/mit-license.php. | ||
*/ | ||
|
||
'use strict'; | ||
|
||
/** | ||
* Validation utility. | ||
* | ||
* @module validation | ||
*/ | ||
|
||
/** | ||
* Validates a given condition, throwing a {@link Error} if | ||
* the given condition does not hold. | ||
* | ||
* @static | ||
* @param {boolean} condition Condition to validate. | ||
* @param {string} message Error message in case the condition does not hold. | ||
*/ | ||
export function validate(condition: any, message: string) { | ||
if (!condition) { | ||
throw new Error(message); | ||
} | ||
} |
Oops, something went wrong.