Implemented most features (commit overdue)
* Generate random entropy * BIP39 mnemonic phrase and seed from entropy * Mnemonic phrase importing and validation * BIP32 key derivation from seed * Nano address encoding and validation * Nano unit converter * Modified ed25519 curve for Nano * Block signing for receive and send blocks
This commit is contained in:
parent
4c7abe331f
commit
08e7a140f3
16 changed files with 3862 additions and 0 deletions
117
index.ts
Normal file
117
index.ts
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
import { AddressGenerator } from './lib/address-generator'
|
||||
import { AddressImporter } from './lib/address-importer'
|
||||
import BlockSigner, { SendBlock, ReceiveBlock } from './lib/block-signer'
|
||||
|
||||
const generator = new AddressGenerator()
|
||||
const importer = new AddressImporter()
|
||||
const wallet = {
|
||||
|
||||
/**
|
||||
* Generate a new Nano cryptocurrency wallet
|
||||
*
|
||||
* This function generates a wallet from random entropy. Wallet includes
|
||||
* a BIP39 mnemonic phrase in line with the Nano Ledger implementation and
|
||||
* a seed, the account is derived using BIP32 deterministic hierarchial algorithm
|
||||
* with input parameters 44'/165' and index 0.
|
||||
*
|
||||
* The Nano address is derived from the public key using standard Nano encoding.
|
||||
* The address is prefixed with 'nano_'.
|
||||
*
|
||||
* Generation uses CryptoJS to generate random entropy. You can give your own entropy
|
||||
* as a parameter and it will be used instead.
|
||||
*
|
||||
* An optional seed password can be used to encrypt the mnemonic phrase so the seed
|
||||
* cannot be derived correctly without the password. Recovering the password is not possible.
|
||||
*
|
||||
* @param {string} [entropy] Optional entropy to be used instead of the default
|
||||
* @param {string} [seedPassword] Optional seed password
|
||||
* @returns the generated mnemonic, seed and account
|
||||
*/
|
||||
generate: (entropy?: string, seedPassword?: string) => {
|
||||
return generator.generateWallet(entropy, seedPassword)
|
||||
},
|
||||
|
||||
/**
|
||||
* Import a Nano cryptocurrency wallet from a mnemonic phrase
|
||||
*
|
||||
* This function imports a wallet from a mnemonic phrase. Wallet includes the mnemonic phrase,
|
||||
* a seed derived with BIP39 standard and an account derived using BIP32 deterministic hierarchial
|
||||
* algorithm with input parameters 44'/165' and index 0.
|
||||
*
|
||||
* The Nano address is derived from the public key using standard Nano encoding.
|
||||
* The address is prefixed with 'nano_'.
|
||||
*
|
||||
* @param {string} mnemonic The mnemonic phrase. Words are separated with a space
|
||||
* @param {string} [seedPassword] Optional seed password
|
||||
* @throws Throws an error if the mnemonic phrase doesn't pass validations
|
||||
* @returns the imported mnemonic, seed and account
|
||||
*/
|
||||
fromMnemonic: (mnemonic: string, seedPassword?: string) => {
|
||||
return importer.fromMnemonic(mnemonic, seedPassword)
|
||||
},
|
||||
|
||||
/**
|
||||
* Import a Nano cryptocurrency wallet from a seed
|
||||
*
|
||||
* This function imports a wallet from a seed. Wallet includes the seed and an account derived using
|
||||
* BIP39 standard and an account derived using BIP32 deterministic hierarchial algorithm with input
|
||||
* parameters 44'/165' and index 0.
|
||||
*
|
||||
* The Nano address is derived from the public key using standard Nano encoding.
|
||||
* The address is prefixed with 'nano_'.
|
||||
*
|
||||
* @param {string} seed The seed
|
||||
* @returns the importes seed and account
|
||||
*/
|
||||
fromSeed: (seed: string) => {
|
||||
return importer.fromSeed(seed)
|
||||
},
|
||||
|
||||
/**
|
||||
* Derive accounts for the seed
|
||||
*
|
||||
* This function derives Nano accounts with the BIP32 deterministic hierarchial algorithm
|
||||
* from the given seed with input parameters 44'/165' and indexes based on the from and to
|
||||
* parameters.
|
||||
*
|
||||
* @param {string} seed The seed
|
||||
* @param {number} from The start index
|
||||
* @param {number} to The end index
|
||||
*/
|
||||
accounts: (seed: string, from: number, to: number) => {
|
||||
return importer.fromSeed(seed, from, to).accounts
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
const blockSigner = new BlockSigner()
|
||||
const block = {
|
||||
|
||||
/**
|
||||
* Sign a send block with the input parameters
|
||||
*
|
||||
* @param {SendBlock} data The data for the block
|
||||
* @param {string} privateKey Private key to sign the block
|
||||
*/
|
||||
send: (data: SendBlock, privateKey: string) => {
|
||||
return blockSigner.send(data, privateKey)
|
||||
},
|
||||
|
||||
/**
|
||||
* Sign a receive block with the input parameters
|
||||
*
|
||||
* @param {SendBlock} data The data for the block
|
||||
* @param {string} privateKey Private key to sign the block
|
||||
*/
|
||||
receive: (data: ReceiveBlock, privateKey: string) => {
|
||||
return blockSigner.receive(data, privateKey)
|
||||
},
|
||||
|
||||
// TODO: change representative block
|
||||
|
||||
}
|
||||
|
||||
export default {
|
||||
wallet,
|
||||
block,
|
||||
}
|
||||
38
lib/address-generator.ts
Normal file
38
lib/address-generator.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import Bip32KeyDerivation from './bip32-key-derivation'
|
||||
import Bip39Mnemonic from './bip39-mnemonic'
|
||||
import { Ed25519 } from './ed25519'
|
||||
import { NanoAddress } from './nano-address'
|
||||
|
||||
export class AddressGenerator {
|
||||
|
||||
/**
|
||||
* Generates the wallet
|
||||
*
|
||||
* @param {String} seedPassword Password for the seed
|
||||
*/
|
||||
generateWallet(entropy = '', seedPassword: string = '') {
|
||||
const bip39 = new Bip39Mnemonic(seedPassword)
|
||||
const wallet = bip39.createWallet(entropy)
|
||||
|
||||
const bip44 = new Bip32KeyDerivation(`44'/165'/0'`, wallet.seed)
|
||||
const privateKey = bip44.derivePath().key
|
||||
|
||||
const ed25519 = new Ed25519()
|
||||
const keyPair = ed25519.generateKeys(privateKey)
|
||||
|
||||
const nano = new NanoAddress()
|
||||
const address = nano.deriveAddress(keyPair.publicKey)
|
||||
|
||||
return {
|
||||
mnemonic: wallet.mnemonic,
|
||||
seed: wallet.seed,
|
||||
accounts: [{
|
||||
accountIndex: 0,
|
||||
privateKey: keyPair.privateKey,
|
||||
publicKey: keyPair.publicKey,
|
||||
address,
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
53
lib/address-importer.ts
Normal file
53
lib/address-importer.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import Bip32KeyDerivation from './bip32-key-derivation'
|
||||
import Bip39Mnemonic from './bip39-mnemonic'
|
||||
import { Ed25519 } from './ed25519'
|
||||
import { NanoAddress } from './nano-address'
|
||||
|
||||
export class AddressImporter {
|
||||
|
||||
fromMnemonic(mnemonic: string, seedPassword = '') {
|
||||
const bip39 = new Bip39Mnemonic(seedPassword)
|
||||
if (!bip39.validateMnemonic(mnemonic)) {
|
||||
throw 'Invalid mnemonic phrase'
|
||||
}
|
||||
|
||||
const seed = bip39.mnemonicToSeed(mnemonic)
|
||||
return this.nano(seed, 0, 0, mnemonic)
|
||||
}
|
||||
|
||||
fromSeed(seed: string, from = 0, to = 0) {
|
||||
return this.nano(seed, from, to, undefined)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the wallet
|
||||
* @param {String} seedPassword Password for the seed
|
||||
*/
|
||||
private nano(seed: string, from: number, to: number, mnemonic?: string) {
|
||||
const accounts = []
|
||||
|
||||
for (let i = from; i <= to; i++) {
|
||||
const bip44 = new Bip32KeyDerivation(`44'/165'/${i}'`, seed)
|
||||
const privateKey = bip44.derivePath().key
|
||||
|
||||
const ed25519 = new Ed25519()
|
||||
const keyPair = ed25519.generateKeys(privateKey)
|
||||
|
||||
const nano = new NanoAddress()
|
||||
const address = nano.deriveAddress(keyPair.publicKey)
|
||||
accounts.push({
|
||||
accountIndex: i,
|
||||
privateKey: keyPair.privateKey,
|
||||
publicKey: keyPair.publicKey,
|
||||
address,
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
mnemonic,
|
||||
seed,
|
||||
accounts,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
62
lib/bip32-key-derivation.ts
Normal file
62
lib/bip32-key-derivation.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import { Convert } from './util/convert'
|
||||
|
||||
const CryptoJS = require('crypto-js')
|
||||
|
||||
const ED25519_CURVE = 'ed25519 seed'
|
||||
const HARDENED_OFFSET = 0x80000000
|
||||
|
||||
export default class Bip32KeyDerivation {
|
||||
|
||||
path: string
|
||||
seed: string
|
||||
|
||||
constructor(path: string, seed: string) {
|
||||
this.path = path
|
||||
this.seed = seed
|
||||
}
|
||||
|
||||
derivePath = () => {
|
||||
const { key, chainCode } = this.getKeyFromSeed()
|
||||
const segments = this.path
|
||||
.split('/')
|
||||
.map(v => v.replace('\'', ''))
|
||||
.map(el => parseInt(el, 10))
|
||||
return segments.reduce(
|
||||
(parentKeys, segment) =>
|
||||
this.CKDPriv(parentKeys, segment + HARDENED_OFFSET),
|
||||
{ key, chainCode }
|
||||
)
|
||||
}
|
||||
|
||||
private getKeyFromSeed = () => {
|
||||
return this.derive(
|
||||
CryptoJS.enc.Hex.parse(this.seed),
|
||||
CryptoJS.enc.Utf8.parse(ED25519_CURVE))
|
||||
}
|
||||
|
||||
private CKDPriv = ({ key, chainCode }: { key: string, chainCode: string }, index: number) => {
|
||||
const ib = []
|
||||
ib.push((index >> 24) & 0xff)
|
||||
ib.push((index >> 16) & 0xff)
|
||||
ib.push((index >> 8) & 0xff)
|
||||
ib.push(index & 0xff)
|
||||
const data = '00' + key + Convert.ab2hex(new Uint8Array(ib).buffer)
|
||||
|
||||
return this.derive(
|
||||
CryptoJS.enc.Hex.parse(data),
|
||||
CryptoJS.enc.Hex.parse(chainCode))
|
||||
}
|
||||
|
||||
private derive = (data: string, base: string) => {
|
||||
const hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA512, base)
|
||||
const I = hmac.update(data).finalize().toString()
|
||||
const IL = I.slice(0, I.length / 2)
|
||||
const IR = I.slice(I.length / 2)
|
||||
|
||||
return {
|
||||
key: IL,
|
||||
chainCode: IR,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
100
lib/bip39-mnemonic.ts
Normal file
100
lib/bip39-mnemonic.ts
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import words from './words'
|
||||
import { Util } from './util/Util'
|
||||
import { Convert } from './util/convert'
|
||||
|
||||
const CryptoJS = require('crypto-js')
|
||||
|
||||
export default class Bip39Mnemonic {
|
||||
|
||||
password: string
|
||||
|
||||
constructor(password: string) {
|
||||
this.password = password
|
||||
}
|
||||
|
||||
createWallet = (entropy: string): { mnemonic: string, seed: string } => {
|
||||
if (entropy.length !== 32) {
|
||||
throw 'Invalid entropy length'
|
||||
}
|
||||
|
||||
if (!entropy) {
|
||||
entropy = this.randomHex(64)
|
||||
}
|
||||
|
||||
const entropyBinary = Convert.hexStringToBinary(entropy)
|
||||
const entropySha256Binary = Convert.hexStringToBinary(this.calculateChecksum(entropy))
|
||||
const entropyBinaryWithChecksum = entropyBinary + entropySha256Binary
|
||||
|
||||
const mnemonicWords = []
|
||||
for (let i = 0; i < entropyBinaryWithChecksum.length; i += 11) {
|
||||
mnemonicWords.push(words[parseInt(entropyBinaryWithChecksum.substr(i, 11), 2)])
|
||||
}
|
||||
|
||||
const mnemonicFinal = mnemonicWords.join(' ')
|
||||
const seed = this.mnemonicToSeed(mnemonicFinal)
|
||||
|
||||
return {
|
||||
mnemonic: mnemonicFinal,
|
||||
seed,
|
||||
}
|
||||
}
|
||||
|
||||
validateMnemonic = (mnemonic: string): boolean => {
|
||||
const wordArray = Util.normalizeUTF8(mnemonic).split(' ')
|
||||
if (wordArray.length % 3 !== 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
const bits = wordArray.map((w: string) => {
|
||||
const wordIndex = words.indexOf(w)
|
||||
if (wordIndex === -1) {
|
||||
return false
|
||||
}
|
||||
return (wordIndex.toString(2)).padStart(11, '0')
|
||||
}).join('')
|
||||
|
||||
const dividerIndex = Math.floor(bits.length / 33) * 32
|
||||
const entropyBits = bits.slice(0, dividerIndex)
|
||||
const checksumBits = bits.slice(dividerIndex)
|
||||
const entropyBytes = entropyBits.match(/(.{1,8})/g).map((bin: string) => parseInt(bin, 2))
|
||||
|
||||
if (entropyBytes.length < 16) return false
|
||||
if (entropyBytes.length > 32) return false
|
||||
if (entropyBytes.length % 4 !== 0) return false
|
||||
|
||||
const entropyHex = Convert.bytesToHexString(entropyBytes)
|
||||
const newChecksum = this.calculateChecksum(entropyHex)
|
||||
const inputChecksum = Convert.binaryToHexString(checksumBits)
|
||||
|
||||
if (newChecksum != inputChecksum) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
mnemonicToSeed = (mnemonic: string): string => {
|
||||
const normalizedMnemonic = Util.normalizeUTF8(mnemonic)
|
||||
const normalizedPassword = 'mnemonic' + Util.normalizeUTF8(this.password)
|
||||
|
||||
return CryptoJS.PBKDF2(
|
||||
normalizedMnemonic,
|
||||
normalizedPassword,
|
||||
{
|
||||
keySize: 512 / 32,
|
||||
iterations: 2048,
|
||||
hasher: CryptoJS.algo.SHA512,
|
||||
})
|
||||
.toString(CryptoJS.enc.Hex)
|
||||
}
|
||||
|
||||
private randomHex = (length: number): string => {
|
||||
return CryptoJS.lib.WordArray.random(length / 2).toString()
|
||||
}
|
||||
|
||||
private calculateChecksum = (entropyHex: string): string => {
|
||||
const entropySha256 = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(entropyHex)).toString()
|
||||
return entropySha256.substr(0, entropySha256.length / 32)
|
||||
}
|
||||
|
||||
}
|
||||
118
lib/block-signer.ts
Normal file
118
lib/block-signer.ts
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
import BigNumber from 'bignumber.js'
|
||||
import * as blake from 'blakejs'
|
||||
import { Ed25519 } from './ed25519'
|
||||
import { NanoAddress } from './nano-address'
|
||||
import NanoConverter from './nano-converter'
|
||||
import { Convert } from './util/convert'
|
||||
|
||||
export default class BlockSigner {
|
||||
|
||||
nanoAddress = new NanoAddress()
|
||||
ed25519 = new Ed25519()
|
||||
|
||||
preamble = 0x6.toString().padStart(64, '0')
|
||||
|
||||
send(data: SendBlock, privateKey: string) {
|
||||
const balance = NanoConverter.convert(data.walletBalanceRaw, 'RAW', 'NANO')
|
||||
const newBalance = new BigNumber(balance).minus(new BigNumber(data.amount))
|
||||
const rawBalance = NanoConverter.convert(newBalance, 'NANO', 'RAW')
|
||||
const hexBalance = Convert.dec2hex(rawBalance, 16).toUpperCase()
|
||||
const account = this.nanoAddressToHexString(data.fromAddress)
|
||||
const link = this.nanoAddressToHexString(data.toAddress)
|
||||
const representative = this.nanoAddressToHexString(data.representativeAddress)
|
||||
|
||||
const signatureBytes = this.ed25519.sign(
|
||||
this.generateHash(this.preamble, account, data.frontier, representative, hexBalance, link),
|
||||
Convert.hex2ab(privateKey))
|
||||
|
||||
return {
|
||||
type: 'state',
|
||||
account: data.fromAddress,
|
||||
previous: data.frontier,
|
||||
representative: data.representativeAddress,
|
||||
balance: rawBalance,
|
||||
link,
|
||||
signature: Convert.ab2hex(signatureBytes),
|
||||
work: data.work,
|
||||
}
|
||||
}
|
||||
|
||||
receive(data: ReceiveBlock, privateKey: string) {
|
||||
let balance = '0'
|
||||
if (data.walletBalanceRaw != '0') {
|
||||
balance = NanoConverter.convert(data.walletBalanceRaw, 'RAW', 'NANO')
|
||||
}
|
||||
|
||||
const amountNano = NanoConverter.convert(data.amount, 'RAW', 'NANO')
|
||||
const newBalance = new BigNumber(balance).plus(new BigNumber(amountNano))
|
||||
const rawBalance = NanoConverter.convert(newBalance, 'NANO', 'RAW')
|
||||
const hexBalance = Convert.dec2hex(rawBalance, 16).toUpperCase()
|
||||
const account = this.nanoAddressToHexString(data.walletAddress)
|
||||
const representative = this.nanoAddressToHexString(data.representativeAddress)
|
||||
const link = data.hash
|
||||
|
||||
const signatureBytes = this.ed25519.sign(
|
||||
this.generateHash(this.preamble, account, data.frontier, representative, hexBalance, link),
|
||||
Convert.hex2ab(privateKey))
|
||||
|
||||
return {
|
||||
type: 'state',
|
||||
account: data.walletAddress,
|
||||
previous: data.frontier,
|
||||
representative: data.representativeAddress,
|
||||
balance: rawBalance,
|
||||
link,
|
||||
signature: Convert.ab2hex(signatureBytes),
|
||||
work: data.work,
|
||||
}
|
||||
}
|
||||
|
||||
private generateHash(preamble: string, account: string, previous: string, representative: string, balance: string, link: string) {
|
||||
const ctx = blake.blake2bInit(32, undefined)
|
||||
blake.blake2bUpdate(ctx, Convert.hex2ab(preamble))
|
||||
blake.blake2bUpdate(ctx, Convert.hex2ab(account))
|
||||
blake.blake2bUpdate(ctx, Convert.hex2ab(previous))
|
||||
blake.blake2bUpdate(ctx, Convert.hex2ab(representative))
|
||||
blake.blake2bUpdate(ctx, Convert.hex2ab(balance))
|
||||
blake.blake2bUpdate(ctx, Convert.hex2ab(link))
|
||||
return blake.blake2bFinal(ctx)
|
||||
}
|
||||
|
||||
private nanoAddressToHexString(addr: string): string {
|
||||
addr = addr.slice(-60)
|
||||
const isValid = /^[13456789abcdefghijkmnopqrstuwxyz]+$/.test(addr)
|
||||
if (isValid) {
|
||||
const keyBytes = this.nanoAddress.decodeNanoBase32(addr.substring(0, 52))
|
||||
const hashBytes = this.nanoAddress.decodeNanoBase32(addr.substring(52, 60))
|
||||
const blakeHash = blake.blake2b(keyBytes, undefined, 5).reverse()
|
||||
if (Convert.ab2hex(hashBytes) == Convert.ab2hex(blakeHash)) {
|
||||
const key = Convert.ab2hex(keyBytes).toUpperCase()
|
||||
return key
|
||||
}
|
||||
throw 'Checksum mismatch'
|
||||
} else {
|
||||
throw 'Illegal characters'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export interface SendBlock {
|
||||
walletBalanceRaw: string
|
||||
fromAddress: string
|
||||
toAddress: string
|
||||
representativeAddress: string
|
||||
frontier: string
|
||||
amount: string
|
||||
work: string
|
||||
}
|
||||
|
||||
export interface ReceiveBlock {
|
||||
walletBalanceRaw: string
|
||||
walletAddress: string
|
||||
representativeAddress: string
|
||||
frontier: string
|
||||
hash: string
|
||||
amount: string
|
||||
work: string
|
||||
}
|
||||
250
lib/ed25519.ts
Normal file
250
lib/ed25519.ts
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
import * as blake from 'blakejs'
|
||||
import { Convert } from './util/convert'
|
||||
import { Curve25519 } from './util/curve25519'
|
||||
|
||||
export class Ed25519 {
|
||||
|
||||
curve: Curve25519
|
||||
X: Int32Array
|
||||
Y: Int32Array
|
||||
L: Uint8Array
|
||||
|
||||
constructor() {
|
||||
this.curve = new Curve25519()
|
||||
this.X = this.curve.gf([0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169])
|
||||
this.Y = this.curve.gf([0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666])
|
||||
this.L = new Uint8Array([0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10])
|
||||
}
|
||||
|
||||
pack(r: Uint8Array, p: Int32Array[]): void {
|
||||
const CURVE = this.curve
|
||||
const tx = CURVE.gf(),
|
||||
ty = CURVE.gf(),
|
||||
zi = CURVE.gf()
|
||||
CURVE.inv25519(zi, p[2])
|
||||
CURVE.M(tx, p[0], zi)
|
||||
CURVE.M(ty, p[1], zi)
|
||||
CURVE.pack25519(r, ty)
|
||||
r[31] ^= CURVE.par25519(tx) << 7
|
||||
}
|
||||
|
||||
modL(r: Uint8Array, x: Uint32Array | Float64Array): void {
|
||||
let carry, i, j, k
|
||||
for (i = 63; i >= 32; --i) {
|
||||
carry = 0
|
||||
for (j = i - 32, k = i - 12; j < k; ++j) {
|
||||
x[j] += carry - 16 * x[i] * this.L[j - (i - 32)]
|
||||
carry = (x[j] + 128) >> 8
|
||||
x[j] -= carry * 256
|
||||
}
|
||||
x[j] += carry
|
||||
x[i] = 0
|
||||
}
|
||||
|
||||
carry = 0
|
||||
for (j = 0; j < 32; j++) {
|
||||
x[j] += carry - (x[31] >> 4) * this.L[j]
|
||||
carry = x[j] >> 8
|
||||
x[j] &= 255
|
||||
}
|
||||
|
||||
for (j = 0; j < 32; j++) {
|
||||
x[j] -= carry * this.L[j]
|
||||
}
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
x[i + 1] += x[i] >>> 8
|
||||
r[i] = x[i] & 0xff
|
||||
}
|
||||
}
|
||||
|
||||
reduce(r: Uint8Array): void {
|
||||
const x = new Uint32Array(64)
|
||||
for (let i = 0; i < 64; i++) {
|
||||
x[i] = r[i]
|
||||
}
|
||||
|
||||
this.modL(r, x)
|
||||
}
|
||||
|
||||
scalarmult(p: Int32Array[], q: Int32Array[], s: Uint8Array): void {
|
||||
const CURVE = this.curve
|
||||
CURVE.set25519(p[0], CURVE.gf0)
|
||||
CURVE.set25519(p[1], CURVE.gf1)
|
||||
CURVE.set25519(p[2], CURVE.gf1)
|
||||
CURVE.set25519(p[3], CURVE.gf0)
|
||||
for (let i = 255; i >= 0; --i) {
|
||||
const b = (s[(i / 8) | 0] >>> (i & 7)) & 1
|
||||
CURVE.cswap(p, q, b)
|
||||
CURVE.add(q, p)
|
||||
CURVE.add(p, p)
|
||||
CURVE.cswap(p, q, b)
|
||||
}
|
||||
}
|
||||
|
||||
scalarbase(p: Int32Array[], s: Uint8Array): void {
|
||||
const CURVE = this.curve
|
||||
const q = [CURVE.gf(), CURVE.gf(), CURVE.gf(), CURVE.gf()]
|
||||
CURVE.set25519(q[0], this.X)
|
||||
CURVE.set25519(q[1], this.Y)
|
||||
CURVE.set25519(q[2], CURVE.gf1)
|
||||
CURVE.M(q[3], this.X, this.Y)
|
||||
this.scalarmult(p, q, s)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an ed25519 keypair
|
||||
* @param {String} seed A 32 byte cryptographic secure random hexadecimal string. This is basically the secret key
|
||||
* @param {Object} Returns sk (Secret key) and pk (Public key) as 32 byte hexadecimal strings
|
||||
*/
|
||||
generateKeys(seed: string): { privateKey: string, publicKey: string } {
|
||||
const pk = new Uint8Array(32)
|
||||
const p = [this.curve.gf(), this.curve.gf(), this.curve.gf(), this.curve.gf()]
|
||||
const h = blake
|
||||
.blake2b(Convert.hex2ab(seed), undefined, 64)
|
||||
.slice(0, 32)
|
||||
|
||||
h[0] &= 0xf8
|
||||
h[31] &= 0x7f
|
||||
h[31] |= 0x40
|
||||
|
||||
this.scalarbase(p, h)
|
||||
this.pack(pk, p)
|
||||
|
||||
return {
|
||||
privateKey: seed,
|
||||
publicKey: Convert.ab2hex(pk),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a message signature
|
||||
* @param {Uint8Array} msg Message to be signed as byte array
|
||||
* @param {Uint8Array} secretKey Secret key as byte array
|
||||
* @param {Uint8Array} Returns the signature as 64 byte typed array
|
||||
*/
|
||||
sign(msg: Uint8Array, secretKey: Uint8Array): Uint8Array {
|
||||
const signedMsg = this.naclSign(msg, secretKey)
|
||||
const sig = new Uint8Array(64)
|
||||
|
||||
for (let i = 0; i < sig.length; i++) {
|
||||
sig[i] = signedMsg[i]
|
||||
}
|
||||
|
||||
return sig
|
||||
}
|
||||
|
||||
private naclSign(msg: Uint8Array, secretKey: Uint8Array): Uint8Array {
|
||||
if (secretKey.length !== 32) {
|
||||
throw new Error('bad secret key size')
|
||||
}
|
||||
|
||||
const signedMsg = new Uint8Array(64 + msg.length)
|
||||
this.cryptoSign(signedMsg, msg, msg.length, secretKey)
|
||||
|
||||
return signedMsg
|
||||
}
|
||||
|
||||
private cryptoSign(sm: Uint8Array, m: Uint8Array, n: number, sk: Uint8Array): number {
|
||||
const CURVE = this.curve
|
||||
const d = new Uint8Array(64)
|
||||
const h = new Uint8Array(64)
|
||||
const r = new Uint8Array(64)
|
||||
const x = new Float64Array(64)
|
||||
const p = [CURVE.gf(), CURVE.gf(), CURVE.gf(), CURVE.gf()]
|
||||
|
||||
let i
|
||||
let j
|
||||
|
||||
const pk = Convert.hex2ab(this.generateKeys(Convert.ab2hex(sk)).publicKey)
|
||||
|
||||
this.cryptoHash(d, sk, 32)
|
||||
d[0] &= 248
|
||||
d[31] &= 127
|
||||
d[31] |= 64
|
||||
|
||||
const smlen = n + 64
|
||||
for (i = 0; i < n; i++) {
|
||||
sm[64 + i] = m[i]
|
||||
}
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
sm[32 + i] = d[32 + i]
|
||||
}
|
||||
|
||||
this.cryptoHash(r, sm.subarray(32), n + 32)
|
||||
this.reduce(r)
|
||||
this.scalarbase(p, r)
|
||||
this.pack(sm, p)
|
||||
|
||||
for (i = 32; i < 64; i++) {
|
||||
sm[i] = pk[i - 32]
|
||||
}
|
||||
|
||||
this.cryptoHash(h, sm, n + 64)
|
||||
this.reduce(h)
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
x[i] = 0
|
||||
}
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
x[i] = r[i]
|
||||
}
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
for (j = 0; j < 32; j++) {
|
||||
x[i + j] += h[i] * d[j]
|
||||
}
|
||||
}
|
||||
|
||||
this.modL(sm.subarray(32), x)
|
||||
|
||||
return smlen
|
||||
}
|
||||
|
||||
private cryptoHash(out: Uint8Array, m: Uint8Array, n: number): number {
|
||||
const input = new Uint8Array(n)
|
||||
for (let i = 0; i < n; ++i) {
|
||||
input[i] = m[i]
|
||||
}
|
||||
|
||||
const hash = blake.blake2b(input)
|
||||
for (let i = 0; i < 64; ++i) {
|
||||
out[i] = hash[i]
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Replace sha512 with blakejs
|
||||
* Verify a message signature
|
||||
* @param {Uint8Array} msg Message to be signed as byte array
|
||||
* @param {Uint8Array} pk Public key as 32 byte array
|
||||
* @param {Uint8Array} sig Signature as 64 byte array
|
||||
* @param {Boolean} Returns true if signature is valid
|
||||
*/
|
||||
// verify(msg, pk, sig) {
|
||||
// let CURVE = this.curve
|
||||
// let p = [CURVE.gf(), CURVE.gf(), CURVE.gf(), CURVE.gf()],
|
||||
// q = [CURVE.gf(), CURVE.gf(), CURVE.gf(), CURVE.gf()]
|
||||
|
||||
// if (sig.length !== 64) return false
|
||||
// if (pk.length !== 32) return false
|
||||
// if (CURVE.unpackNeg(q, pk)) return false
|
||||
|
||||
// // compute k = SHA512(R || A || M)
|
||||
// let k = this.sha512.init().update(sig.subarray(0, 32)).update(pk).digest(msg)
|
||||
// this.reduce(k)
|
||||
// this.scalarmult(p, q, k)
|
||||
|
||||
// let t = new Uint8Array(32)
|
||||
// this.scalarbase(q, sig.subarray(32))
|
||||
// CURVE.add(p, q)
|
||||
// this.pack(t, p)
|
||||
|
||||
// return Util.compare(sig.subarray(0, 32), t)
|
||||
// }
|
||||
|
||||
}
|
||||
88
lib/nano-address.ts
Normal file
88
lib/nano-address.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import * as blake from 'blakejs'
|
||||
import { Convert } from './util/convert'
|
||||
|
||||
export class NanoAddress {
|
||||
|
||||
readonly alphabet = '13456789abcdefghijkmnopqrstuwxyz'
|
||||
readonly prefix = 'nano_'
|
||||
|
||||
deriveAddress = (publicKey: string): string => {
|
||||
const publicKeyBytes = Convert.hex2ab(publicKey)
|
||||
const checksum = blake
|
||||
.blake2b(publicKeyBytes, undefined, 5)
|
||||
.reverse()
|
||||
|
||||
const encoded = this.encodeNanoBase32(publicKeyBytes)
|
||||
const encodedChecksum = this.encodeNanoBase32(checksum)
|
||||
|
||||
return this.prefix + encoded + encodedChecksum
|
||||
}
|
||||
|
||||
encodeNanoBase32 = (publicKey: Uint8Array): string => {
|
||||
const length = publicKey.length
|
||||
const leftover = (length * 8) % 5
|
||||
const offset = leftover === 0 ? 0 : 5 - leftover
|
||||
|
||||
let value = 0
|
||||
let output = ''
|
||||
let bits = 0
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
value = (value << 8) | publicKey[i]
|
||||
bits += 8
|
||||
|
||||
while (bits >= 5) {
|
||||
output += this.alphabet[(value >>> (bits + offset - 5)) & 31]
|
||||
bits -= 5
|
||||
}
|
||||
}
|
||||
|
||||
if (bits > 0) {
|
||||
output += this.alphabet[(value << (5 - (bits + offset))) & 31]
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
decodeNanoBase32 = (input: string): Uint8Array => {
|
||||
const length = input.length
|
||||
const leftover = (length * 5) % 8
|
||||
const offset = leftover === 0 ? 0 : 8 - leftover
|
||||
|
||||
let bits = 0
|
||||
let value = 0
|
||||
let index = 0
|
||||
let output = new Uint8Array(Math.ceil((length * 5) / 8))
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
value = (value << 5) | this.readChar(input[i])
|
||||
bits += 5
|
||||
|
||||
if (bits >= 8) {
|
||||
output[index++] = (value >>> (bits + offset - 8)) & 255
|
||||
bits -= 8
|
||||
}
|
||||
}
|
||||
|
||||
if (bits > 0) {
|
||||
output[index++] = (value << (bits + offset - 8)) & 255
|
||||
}
|
||||
|
||||
if (leftover !== 0) {
|
||||
output = output.slice(1)
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
readChar(char: string): number {
|
||||
const idx = this.alphabet.indexOf(char)
|
||||
|
||||
if (idx === -1) {
|
||||
throw `Invalid character found: ${char}`
|
||||
}
|
||||
|
||||
return idx
|
||||
}
|
||||
|
||||
}
|
||||
48
lib/nano-converter.ts
Normal file
48
lib/nano-converter.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import BigNumber from 'bignumber.js'
|
||||
|
||||
export default class NanoConverter {
|
||||
|
||||
/**
|
||||
* Converts the input value to the wanted unit
|
||||
*
|
||||
* @param input {BigNumber} value
|
||||
* @param inputUnit {Unit} the unit to convert from
|
||||
* @param outputUnit {Unit} the unit to convert to
|
||||
*/
|
||||
static convert(input: BigNumber, inputUnit: string, outputUnit: string): string {
|
||||
let value = new BigNumber(input.toString())
|
||||
|
||||
switch (inputUnit) {
|
||||
case 'RAW':
|
||||
value = value
|
||||
break
|
||||
case 'NANO':
|
||||
case 'MRAI':
|
||||
value = value.shiftedBy(30)
|
||||
break
|
||||
case 'KRAI':
|
||||
value = value.shiftedBy(27)
|
||||
break
|
||||
case 'RAI':
|
||||
value = value.shiftedBy(24)
|
||||
break
|
||||
default:
|
||||
throw `Unkown input unit ${inputUnit}, expected one of the following: RAW, NANO, MRAI, KRAI, RAI`
|
||||
}
|
||||
|
||||
switch (outputUnit) {
|
||||
case 'RAW':
|
||||
return value.toFixed(0)
|
||||
case 'NANO':
|
||||
case 'MRAI':
|
||||
return value.shiftedBy(-30).toFixed(15, 1)
|
||||
case 'KRAI':
|
||||
return value.shiftedBy(-27).toFixed(12, 1)
|
||||
case 'RAI':
|
||||
return value.shiftedBy(-24).toFixed(9, 1)
|
||||
default:
|
||||
throw `Unknown output unit ${outputUnit}, expected one of the following: RAW, NANO, MRAI, KRAI, RAI`
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
118
lib/util/convert.ts
Normal file
118
lib/util/convert.ts
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
export class Convert {
|
||||
|
||||
/**
|
||||
* Convert a string (UTF-8 encoded) to a byte array
|
||||
*
|
||||
* @param {String} str UTF-8 encoded string
|
||||
* @return {Uint8Array} Byte array
|
||||
*/
|
||||
static str2bin(str: string): Uint8Array {
|
||||
str = str.replace(/\r\n/g, '\n')
|
||||
const bin = new Uint8Array(str.length * 3)
|
||||
let p = 0
|
||||
for (let i = 0, len = str.length; i < len; i++) {
|
||||
const c = str.charCodeAt(i)
|
||||
if (c < 128) {
|
||||
bin[p++] = c
|
||||
} else if (c < 2048) {
|
||||
bin[p++] = (c >>> 6) | 192
|
||||
bin[p++] = (c & 63) | 128
|
||||
} else {
|
||||
bin[p++] = (c >>> 12) | 224
|
||||
bin[p++] = ((c >>> 6) & 63) | 128
|
||||
bin[p++] = (c & 63) | 128
|
||||
}
|
||||
}
|
||||
return bin.subarray(0, p)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Array of 8 bytes (int64) to hex string
|
||||
*
|
||||
* @param {Uint8Array} bin Array of bytes
|
||||
* @return {String} Hex encoded string
|
||||
*/
|
||||
static ab2hex = (buf: ArrayBuffer): string => {
|
||||
return Array.prototype.map.call(new Uint8Array(buf), x => ('00' + x.toString(16)).slice(-2)).join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert hex string to array of 8 bytes (int64)
|
||||
*
|
||||
* @param {String} bin Array of bytes
|
||||
* @return {Uint8Array} Array of 8 bytes (int64)
|
||||
*/
|
||||
static hex2ab = (hex: string): Uint8Array => {
|
||||
const ab = []
|
||||
for (let i = 0; i < hex.length; i += 2) {
|
||||
ab.push(parseInt(hex.substr(i, 2), 16))
|
||||
}
|
||||
return new Uint8Array(ab)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a decimal number to hex string
|
||||
*
|
||||
* @param {String} str Decimal to be converted
|
||||
* @param {Number} bytes Length of the output to be padded
|
||||
* @returns Hexadecimal representation of the inputed decimal
|
||||
*/
|
||||
static dec2hex = (str: number | string, bytes: number): string => {
|
||||
const decimals = str.toString().split('')
|
||||
const sum = []
|
||||
let hex = []
|
||||
let i: number
|
||||
let s: number
|
||||
|
||||
while (decimals.length) {
|
||||
const dec = decimals.shift()
|
||||
if (!dec) {
|
||||
throw 'Invalid decimal'
|
||||
}
|
||||
|
||||
s = 1 * +dec
|
||||
for (i = 0; s || i < sum.length; i++) {
|
||||
s += (sum[i] || 0) * 10
|
||||
sum[i] = s % 16
|
||||
s = (s - sum[i]) / 16
|
||||
}
|
||||
}
|
||||
|
||||
while (sum.length) {
|
||||
const dec = sum.pop()
|
||||
if (!dec) {
|
||||
throw 'Invalid decimal'
|
||||
}
|
||||
|
||||
hex.push(dec.toString(16))
|
||||
}
|
||||
|
||||
let joined = hex.join('')
|
||||
|
||||
if (joined.length % 2 != 0) {
|
||||
joined = '0' + joined
|
||||
}
|
||||
|
||||
if (bytes > joined.length / 2) {
|
||||
const diff = bytes - joined.length / 2
|
||||
for (let i = 0; i < diff; i++) {
|
||||
joined = '00' + joined
|
||||
}
|
||||
}
|
||||
|
||||
return joined
|
||||
}
|
||||
|
||||
static bytesToHexString = (bytes: number[]): string => {
|
||||
return [...bytes].map(b => b.toString(16).padStart(2, '0')).join('')
|
||||
}
|
||||
|
||||
static hexStringToBinary = (hex: string): string => {
|
||||
return [...hex].map(c => (parseInt(c, 16).toString(2)).padStart(4, '0')).join('')
|
||||
}
|
||||
|
||||
static binaryToHexString = (bin: string): string => {
|
||||
return parseInt(bin, 2).toString(16)
|
||||
}
|
||||
|
||||
}
|
||||
711
lib/util/curve25519.ts
Normal file
711
lib/util/curve25519.ts
Normal file
|
|
@ -0,0 +1,711 @@
|
|||
import { Util } from './util'
|
||||
|
||||
export class Curve25519 {
|
||||
|
||||
gf0: Int32Array
|
||||
gf1: Int32Array
|
||||
D: Int32Array
|
||||
D2: Int32Array
|
||||
I: Int32Array
|
||||
_9: Uint8Array
|
||||
_121665: Int32Array
|
||||
|
||||
constructor() {
|
||||
this.gf0 = this.gf()
|
||||
this.gf1 = this.gf([1])
|
||||
this._9 = new Uint8Array(32)
|
||||
this._9[0] = 9
|
||||
this._121665 = this.gf([0xdb41, 1])
|
||||
this.D = this.gf([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203])
|
||||
this.D2 = this.gf([0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406])
|
||||
this.I = this.gf([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83])
|
||||
}
|
||||
|
||||
gf(init?: number[]): Int32Array {
|
||||
const r = new Int32Array(16)
|
||||
if (init) {
|
||||
for (let i = 0; i < init.length; i++) {
|
||||
r[i] = init[i]
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
A(o: Int32Array, a: Int32Array, b: Int32Array): void {
|
||||
for (let i = 0; i < 16; i++) {
|
||||
o[i] = a[i] + b[i]
|
||||
}
|
||||
}
|
||||
|
||||
Z(o: Int32Array, a: Int32Array, b: Int32Array): void {
|
||||
for (let i = 0; i < 16; i++) {
|
||||
o[i] = a[i] - b[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Avoid loops for better performance
|
||||
M(o: Int32Array, a: Int32Array, b: Int32Array): void {
|
||||
let v, c,
|
||||
t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0,
|
||||
t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0,
|
||||
t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0,
|
||||
t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0
|
||||
const b0 = b[0],
|
||||
b1 = b[1],
|
||||
b2 = b[2],
|
||||
b3 = b[3],
|
||||
b4 = b[4],
|
||||
b5 = b[5],
|
||||
b6 = b[6],
|
||||
b7 = b[7],
|
||||
b8 = b[8],
|
||||
b9 = b[9],
|
||||
b10 = b[10],
|
||||
b11 = b[11],
|
||||
b12 = b[12],
|
||||
b13 = b[13],
|
||||
b14 = b[14],
|
||||
b15 = b[15]
|
||||
|
||||
v = a[0]
|
||||
t0 += v * b0
|
||||
t1 += v * b1
|
||||
t2 += v * b2
|
||||
t3 += v * b3
|
||||
t4 += v * b4
|
||||
t5 += v * b5
|
||||
t6 += v * b6
|
||||
t7 += v * b7
|
||||
t8 += v * b8
|
||||
t9 += v * b9
|
||||
t10 += v * b10
|
||||
t11 += v * b11
|
||||
t12 += v * b12
|
||||
t13 += v * b13
|
||||
t14 += v * b14
|
||||
t15 += v * b15
|
||||
v = a[1]
|
||||
t1 += v * b0
|
||||
t2 += v * b1
|
||||
t3 += v * b2
|
||||
t4 += v * b3
|
||||
t5 += v * b4
|
||||
t6 += v * b5
|
||||
t7 += v * b6
|
||||
t8 += v * b7
|
||||
t9 += v * b8
|
||||
t10 += v * b9
|
||||
t11 += v * b10
|
||||
t12 += v * b11
|
||||
t13 += v * b12
|
||||
t14 += v * b13
|
||||
t15 += v * b14
|
||||
t16 += v * b15
|
||||
v = a[2]
|
||||
t2 += v * b0
|
||||
t3 += v * b1
|
||||
t4 += v * b2
|
||||
t5 += v * b3
|
||||
t6 += v * b4
|
||||
t7 += v * b5
|
||||
t8 += v * b6
|
||||
t9 += v * b7
|
||||
t10 += v * b8
|
||||
t11 += v * b9
|
||||
t12 += v * b10
|
||||
t13 += v * b11
|
||||
t14 += v * b12
|
||||
t15 += v * b13
|
||||
t16 += v * b14
|
||||
t17 += v * b15
|
||||
v = a[3]
|
||||
t3 += v * b0
|
||||
t4 += v * b1
|
||||
t5 += v * b2
|
||||
t6 += v * b3
|
||||
t7 += v * b4
|
||||
t8 += v * b5
|
||||
t9 += v * b6
|
||||
t10 += v * b7
|
||||
t11 += v * b8
|
||||
t12 += v * b9
|
||||
t13 += v * b10
|
||||
t14 += v * b11
|
||||
t15 += v * b12
|
||||
t16 += v * b13
|
||||
t17 += v * b14
|
||||
t18 += v * b15
|
||||
v = a[4]
|
||||
t4 += v * b0
|
||||
t5 += v * b1
|
||||
t6 += v * b2
|
||||
t7 += v * b3
|
||||
t8 += v * b4
|
||||
t9 += v * b5
|
||||
t10 += v * b6
|
||||
t11 += v * b7
|
||||
t12 += v * b8
|
||||
t13 += v * b9
|
||||
t14 += v * b10
|
||||
t15 += v * b11
|
||||
t16 += v * b12
|
||||
t17 += v * b13
|
||||
t18 += v * b14
|
||||
t19 += v * b15
|
||||
v = a[5]
|
||||
t5 += v * b0
|
||||
t6 += v * b1
|
||||
t7 += v * b2
|
||||
t8 += v * b3
|
||||
t9 += v * b4
|
||||
t10 += v * b5
|
||||
t11 += v * b6
|
||||
t12 += v * b7
|
||||
t13 += v * b8
|
||||
t14 += v * b9
|
||||
t15 += v * b10
|
||||
t16 += v * b11
|
||||
t17 += v * b12
|
||||
t18 += v * b13
|
||||
t19 += v * b14
|
||||
t20 += v * b15
|
||||
v = a[6]
|
||||
t6 += v * b0
|
||||
t7 += v * b1
|
||||
t8 += v * b2
|
||||
t9 += v * b3
|
||||
t10 += v * b4
|
||||
t11 += v * b5
|
||||
t12 += v * b6
|
||||
t13 += v * b7
|
||||
t14 += v * b8
|
||||
t15 += v * b9
|
||||
t16 += v * b10
|
||||
t17 += v * b11
|
||||
t18 += v * b12
|
||||
t19 += v * b13
|
||||
t20 += v * b14
|
||||
t21 += v * b15
|
||||
v = a[7]
|
||||
t7 += v * b0
|
||||
t8 += v * b1
|
||||
t9 += v * b2
|
||||
t10 += v * b3
|
||||
t11 += v * b4
|
||||
t12 += v * b5
|
||||
t13 += v * b6
|
||||
t14 += v * b7
|
||||
t15 += v * b8
|
||||
t16 += v * b9
|
||||
t17 += v * b10
|
||||
t18 += v * b11
|
||||
t19 += v * b12
|
||||
t20 += v * b13
|
||||
t21 += v * b14
|
||||
t22 += v * b15
|
||||
v = a[8]
|
||||
t8 += v * b0
|
||||
t9 += v * b1
|
||||
t10 += v * b2
|
||||
t11 += v * b3
|
||||
t12 += v * b4
|
||||
t13 += v * b5
|
||||
t14 += v * b6
|
||||
t15 += v * b7
|
||||
t16 += v * b8
|
||||
t17 += v * b9
|
||||
t18 += v * b10
|
||||
t19 += v * b11
|
||||
t20 += v * b12
|
||||
t21 += v * b13
|
||||
t22 += v * b14
|
||||
t23 += v * b15
|
||||
v = a[9]
|
||||
t9 += v * b0
|
||||
t10 += v * b1
|
||||
t11 += v * b2
|
||||
t12 += v * b3
|
||||
t13 += v * b4
|
||||
t14 += v * b5
|
||||
t15 += v * b6
|
||||
t16 += v * b7
|
||||
t17 += v * b8
|
||||
t18 += v * b9
|
||||
t19 += v * b10
|
||||
t20 += v * b11
|
||||
t21 += v * b12
|
||||
t22 += v * b13
|
||||
t23 += v * b14
|
||||
t24 += v * b15
|
||||
v = a[10]
|
||||
t10 += v * b0
|
||||
t11 += v * b1
|
||||
t12 += v * b2
|
||||
t13 += v * b3
|
||||
t14 += v * b4
|
||||
t15 += v * b5
|
||||
t16 += v * b6
|
||||
t17 += v * b7
|
||||
t18 += v * b8
|
||||
t19 += v * b9
|
||||
t20 += v * b10
|
||||
t21 += v * b11
|
||||
t22 += v * b12
|
||||
t23 += v * b13
|
||||
t24 += v * b14
|
||||
t25 += v * b15
|
||||
v = a[11]
|
||||
t11 += v * b0
|
||||
t12 += v * b1
|
||||
t13 += v * b2
|
||||
t14 += v * b3
|
||||
t15 += v * b4
|
||||
t16 += v * b5
|
||||
t17 += v * b6
|
||||
t18 += v * b7
|
||||
t19 += v * b8
|
||||
t20 += v * b9
|
||||
t21 += v * b10
|
||||
t22 += v * b11
|
||||
t23 += v * b12
|
||||
t24 += v * b13
|
||||
t25 += v * b14
|
||||
t26 += v * b15
|
||||
v = a[12]
|
||||
t12 += v * b0
|
||||
t13 += v * b1
|
||||
t14 += v * b2
|
||||
t15 += v * b3
|
||||
t16 += v * b4
|
||||
t17 += v * b5
|
||||
t18 += v * b6
|
||||
t19 += v * b7
|
||||
t20 += v * b8
|
||||
t21 += v * b9
|
||||
t22 += v * b10
|
||||
t23 += v * b11
|
||||
t24 += v * b12
|
||||
t25 += v * b13
|
||||
t26 += v * b14
|
||||
t27 += v * b15
|
||||
v = a[13]
|
||||
t13 += v * b0
|
||||
t14 += v * b1
|
||||
t15 += v * b2
|
||||
t16 += v * b3
|
||||
t17 += v * b4
|
||||
t18 += v * b5
|
||||
t19 += v * b6
|
||||
t20 += v * b7
|
||||
t21 += v * b8
|
||||
t22 += v * b9
|
||||
t23 += v * b10
|
||||
t24 += v * b11
|
||||
t25 += v * b12
|
||||
t26 += v * b13
|
||||
t27 += v * b14
|
||||
t28 += v * b15
|
||||
v = a[14]
|
||||
t14 += v * b0
|
||||
t15 += v * b1
|
||||
t16 += v * b2
|
||||
t17 += v * b3
|
||||
t18 += v * b4
|
||||
t19 += v * b5
|
||||
t20 += v * b6
|
||||
t21 += v * b7
|
||||
t22 += v * b8
|
||||
t23 += v * b9
|
||||
t24 += v * b10
|
||||
t25 += v * b11
|
||||
t26 += v * b12
|
||||
t27 += v * b13
|
||||
t28 += v * b14
|
||||
t29 += v * b15
|
||||
v = a[15]
|
||||
t15 += v * b0
|
||||
t16 += v * b1
|
||||
t17 += v * b2
|
||||
t18 += v * b3
|
||||
t19 += v * b4
|
||||
t20 += v * b5
|
||||
t21 += v * b6
|
||||
t22 += v * b7
|
||||
t23 += v * b8
|
||||
t24 += v * b9
|
||||
t25 += v * b10
|
||||
t26 += v * b11
|
||||
t27 += v * b12
|
||||
t28 += v * b13
|
||||
t29 += v * b14
|
||||
t30 += v * b15
|
||||
|
||||
t0 += 38 * t16
|
||||
t1 += 38 * t17
|
||||
t2 += 38 * t18
|
||||
t3 += 38 * t19
|
||||
t4 += 38 * t20
|
||||
t5 += 38 * t21
|
||||
t6 += 38 * t22
|
||||
t7 += 38 * t23
|
||||
t8 += 38 * t24
|
||||
t9 += 38 * t25
|
||||
t10 += 38 * t26
|
||||
t11 += 38 * t27
|
||||
t12 += 38 * t28
|
||||
t13 += 38 * t29
|
||||
t14 += 38 * t30
|
||||
|
||||
c = 1
|
||||
v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536
|
||||
v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536
|
||||
v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536
|
||||
v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536
|
||||
v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536
|
||||
v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536
|
||||
v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536
|
||||
v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536
|
||||
v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536
|
||||
v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536
|
||||
v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536
|
||||
v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536
|
||||
v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536
|
||||
v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536
|
||||
v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536
|
||||
v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536
|
||||
t0 += c - 1 + 37 * (c - 1)
|
||||
|
||||
c = 1
|
||||
v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536
|
||||
v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536
|
||||
v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536
|
||||
v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536
|
||||
v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536
|
||||
v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536
|
||||
v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536
|
||||
v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536
|
||||
v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536
|
||||
v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536
|
||||
v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536
|
||||
v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536
|
||||
v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536
|
||||
v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536
|
||||
v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536
|
||||
v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536
|
||||
t0 += c - 1 + 37 * (c - 1)
|
||||
|
||||
o[0] = t0
|
||||
o[1] = t1
|
||||
o[2] = t2
|
||||
o[3] = t3
|
||||
o[4] = t4
|
||||
o[5] = t5
|
||||
o[6] = t6
|
||||
o[7] = t7
|
||||
o[8] = t8
|
||||
o[9] = t9
|
||||
o[10] = t10
|
||||
o[11] = t11
|
||||
o[12] = t12
|
||||
o[13] = t13
|
||||
o[14] = t14
|
||||
o[15] = t15
|
||||
}
|
||||
|
||||
S(o: Int32Array, a: Int32Array): void {
|
||||
this.M(o, a, a)
|
||||
}
|
||||
|
||||
add(p: Int32Array[], q: Int32Array[]): void {
|
||||
const a = this.gf(), b = this.gf(), c = this.gf(),
|
||||
d = this.gf(), e = this.gf(), f = this.gf(),
|
||||
g = this.gf(), h = this.gf(), t = this.gf()
|
||||
|
||||
this.Z(a, p[1], p[0])
|
||||
this.Z(t, q[1], q[0])
|
||||
this.M(a, a, t)
|
||||
this.A(b, p[0], p[1])
|
||||
this.A(t, q[0], q[1])
|
||||
this.M(b, b, t)
|
||||
this.M(c, p[3], q[3])
|
||||
this.M(c, c, this.D2)
|
||||
this.M(d, p[2], q[2])
|
||||
this.A(d, d, d)
|
||||
this.Z(e, b, a)
|
||||
this.Z(f, d, c)
|
||||
this.A(g, d, c)
|
||||
this.A(h, b, a)
|
||||
this.M(p[0], e, f)
|
||||
this.M(p[1], h, g)
|
||||
this.M(p[2], g, f)
|
||||
this.M(p[3], e, h)
|
||||
}
|
||||
|
||||
set25519(r: Int32Array, a: Int32Array): void {
|
||||
for (let i = 0; i < 16; i++) {
|
||||
r[i] = a[i]
|
||||
}
|
||||
}
|
||||
|
||||
car25519(o: Int32Array): void {
|
||||
let i, v, c = 1
|
||||
for (i = 0; i < 16; i++) {
|
||||
v = o[i] + c + 65535
|
||||
c = Math.floor(v / 65536)
|
||||
o[i] = v - c * 65536
|
||||
}
|
||||
|
||||
o[0] += c - 1 + 37 * (c - 1)
|
||||
}
|
||||
|
||||
// b is 0 or 1
|
||||
sel25519(p: Int32Array, q: Int32Array, b: number): void {
|
||||
let i, t
|
||||
const c = ~(b - 1)
|
||||
for (i = 0; i < 16; i++) {
|
||||
t = c & (p[i] ^ q[i])
|
||||
p[i] ^= t
|
||||
q[i] ^= t
|
||||
}
|
||||
}
|
||||
|
||||
inv25519(o: Int32Array, i: Int32Array): void {
|
||||
let a
|
||||
const c = this.gf()
|
||||
for (a = 0; a < 16; a++) {
|
||||
c[a] = i[a]
|
||||
}
|
||||
|
||||
for (a = 253; a >= 0; a--) {
|
||||
this.S(c, c)
|
||||
if (a !== 2 && a !== 4) {
|
||||
this.M(c, c, i)
|
||||
}
|
||||
}
|
||||
|
||||
for (a = 0; a < 16; a++) {
|
||||
o[a] = c[a]
|
||||
}
|
||||
}
|
||||
|
||||
neq25519(a: Int32Array, b: Int32Array): boolean {
|
||||
const c = new Uint8Array(32), d = new Uint8Array(32)
|
||||
this.pack25519(c, a)
|
||||
this.pack25519(d, b)
|
||||
return !Util.compare(c, d)
|
||||
}
|
||||
|
||||
par25519(a: Int32Array): number {
|
||||
const d = new Uint8Array(32)
|
||||
this.pack25519(d, a)
|
||||
return d[0] & 1
|
||||
}
|
||||
|
||||
pow2523(o: Int32Array, i: Int32Array): void {
|
||||
let a
|
||||
const c = this.gf()
|
||||
for (a = 0; a < 16; a++) {
|
||||
c[a] = i[a]
|
||||
}
|
||||
|
||||
for (a = 250; a >= 0; a--) {
|
||||
this.S(c, c)
|
||||
if (a !== 1) this.M(c, c, i)
|
||||
}
|
||||
|
||||
for (a = 0; a < 16; a++) {
|
||||
o[a] = c[a]
|
||||
}
|
||||
}
|
||||
|
||||
cswap(p: Int32Array[], q: Int32Array[], b: number): void {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
this.sel25519(p[i], q[i], b)
|
||||
}
|
||||
}
|
||||
|
||||
pack25519(o: Uint8Array, n: Int32Array): void {
|
||||
let i
|
||||
const m = this.gf()
|
||||
const t = this.gf()
|
||||
for (i = 0; i < 16; i++) {
|
||||
t[i] = n[i]
|
||||
}
|
||||
|
||||
this.car25519(t)
|
||||
this.car25519(t)
|
||||
this.car25519(t)
|
||||
for (let j = 0; j < 2; j++) {
|
||||
m[0] = t[0] - 0xffed
|
||||
for (i = 1; i < 15; i++) {
|
||||
m[i] = t[i] - 0xffff - ((m[i - 1] >>> 16) & 1)
|
||||
m[i - 1] &= 0xffff
|
||||
}
|
||||
|
||||
m[15] = t[15] - 0x7fff - ((m[14] >>> 16) & 1)
|
||||
const b = (m[15] >>> 16) & 1
|
||||
m[14] &= 0xffff
|
||||
this.sel25519(t, m, 1 - b)
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
o[2 * i] = t[i] & 0xff
|
||||
o[2 * i + 1] = t[i] >>> 8
|
||||
}
|
||||
}
|
||||
|
||||
unpack25519(o: Int32Array, n: Int32Array): void {
|
||||
for (let i = 0; i < 16; i++) {
|
||||
o[i] = n[2 * i] + (n[2 * i + 1] << 8)
|
||||
}
|
||||
|
||||
o[15] &= 0x7fff
|
||||
}
|
||||
|
||||
unpackNeg(r: Int32Array[], p: Int32Array): number {
|
||||
const t = this.gf(),
|
||||
chk = this.gf(),
|
||||
num = this.gf(),
|
||||
den = this.gf(),
|
||||
den2 = this.gf(),
|
||||
den4 = this.gf(),
|
||||
den6 = this.gf()
|
||||
|
||||
this.set25519(r[2], this.gf1)
|
||||
this.unpack25519(r[1], p)
|
||||
this.S(num, r[1])
|
||||
this.M(den, num, this.D)
|
||||
this.Z(num, num, r[2])
|
||||
this.A(den, r[2], den)
|
||||
|
||||
this.S(den2, den)
|
||||
this.S(den4, den2)
|
||||
this.M(den6, den4, den2)
|
||||
this.M(t, den6, num)
|
||||
this.M(t, t, den)
|
||||
|
||||
this.pow2523(t, t)
|
||||
this.M(t, t, num)
|
||||
this.M(t, t, den)
|
||||
this.M(t, t, den)
|
||||
this.M(r[0], t, den)
|
||||
|
||||
this.S(chk, r[0])
|
||||
this.M(chk, chk, den)
|
||||
if (this.neq25519(chk, num)) {
|
||||
this.M(r[0], r[0], this.I)
|
||||
}
|
||||
|
||||
this.S(chk, r[0])
|
||||
this.M(chk, chk, den)
|
||||
if (this.neq25519(chk, num)) {
|
||||
return -1
|
||||
}
|
||||
|
||||
if (this.par25519(r[0]) === (p[31] >>> 7)) {
|
||||
this.Z(r[0], this.gf0, r[0])
|
||||
}
|
||||
|
||||
this.M(r[3], r[0], r[1])
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal scalar mult function
|
||||
* @param {Uint8Array} q Result
|
||||
* @param {Uint8Array} s Secret key
|
||||
* @param {Uint8Array} p Public key
|
||||
*/
|
||||
cryptoScalarmult(q: Uint8Array, s: Uint8Array, p: Uint8Array): void {
|
||||
const x = new Int32Array(80)
|
||||
let r, i
|
||||
const a = this.gf(), b = this.gf(), c = this.gf(),
|
||||
d = this.gf(), e = this.gf(), f = this.gf()
|
||||
|
||||
this.unpack25519(x, p)
|
||||
for (i = 0; i < 16; i++) {
|
||||
b[i] = x[i]
|
||||
d[i] = a[i] = c[i] = 0
|
||||
}
|
||||
|
||||
a[0] = d[0] = 1
|
||||
for (i = 254; i >= 0; --i) {
|
||||
r = (s[i >>> 3] >>> (i & 7)) & 1
|
||||
this.sel25519(a, b, r)
|
||||
this.sel25519(c, d, r)
|
||||
this.A(e, a, c)
|
||||
this.Z(a, a, c)
|
||||
this.A(c, b, d)
|
||||
this.Z(b, b, d)
|
||||
this.S(d, e)
|
||||
this.S(f, a)
|
||||
this.M(a, c, a)
|
||||
this.M(c, b, e)
|
||||
this.A(e, a, c)
|
||||
this.Z(a, a, c)
|
||||
this.S(b, a)
|
||||
this.Z(c, d, f)
|
||||
this.M(a, c, this._121665)
|
||||
this.A(a, a, d)
|
||||
this.M(c, c, a)
|
||||
this.M(a, d, f)
|
||||
this.M(d, b, x)
|
||||
this.S(b, e)
|
||||
this.sel25519(a, b, r)
|
||||
this.sel25519(c, d, r)
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
x[i + 16] = a[i]
|
||||
x[i + 32] = c[i]
|
||||
x[i + 48] = b[i]
|
||||
x[i + 64] = d[i]
|
||||
}
|
||||
|
||||
const x32 = x.subarray(32)
|
||||
const x16 = x.subarray(16)
|
||||
this.inv25519(x32, x32)
|
||||
this.M(x16, x16, x32)
|
||||
this.pack25519(q, x16)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the common key as the produkt of sk1 * pk2
|
||||
* @param {Uint8Array} sk A 32 byte secret key of pair 1
|
||||
* @param {Uint8Array} pk A 32 byte public key of pair 2
|
||||
* @return {Uint8Array} sk * pk
|
||||
*/
|
||||
scalarMult(sk: Uint8Array, pk: Uint8Array) {
|
||||
const q = new Uint8Array(32)
|
||||
this.cryptoScalarmult(q, sk, pk)
|
||||
|
||||
return q
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a curve 25519 keypair
|
||||
* @param {Uint8Array} seed A 32 byte cryptographic secure random array. This is basically the secret key
|
||||
* @param {Object} Returns sk (Secret key) and pk (Public key) as 32 byte typed arrays
|
||||
*/
|
||||
generateKeys(seed: Uint8Array): { sk: Uint8Array, pk: Uint8Array } {
|
||||
const sk = seed.slice()
|
||||
const pk = new Uint8Array(32)
|
||||
if (sk.length !== 32) {
|
||||
throw 'Invalid secret key size, expected 32 bytes'
|
||||
}
|
||||
|
||||
sk[0] &= 0xf8
|
||||
sk[31] &= 0x7f
|
||||
sk[31] |= 0x40
|
||||
|
||||
this.cryptoScalarmult(pk, sk, this._9)
|
||||
|
||||
return {
|
||||
sk,
|
||||
pk,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
30
lib/util/util.ts
Normal file
30
lib/util/util.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
export class Util {
|
||||
|
||||
/**
|
||||
* Time constant comparison of two arrays
|
||||
*
|
||||
* @param {Uint8Array} lh First array of bytes
|
||||
* @param {Uint8Array} rh Second array of bytes
|
||||
* @return {Boolean} True if the arrays are equal (length and content), false otherwise
|
||||
*/
|
||||
static compare(lh: Uint8Array, rh: Uint8Array): boolean {
|
||||
if (lh.length !== rh.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
let i
|
||||
let d = 0
|
||||
const len = lh.length
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
d |= lh[i] ^ rh[i]
|
||||
}
|
||||
|
||||
return d === 0
|
||||
}
|
||||
|
||||
static normalizeUTF8 = (str: string): string => {
|
||||
return str ? str.normalize('NFKD') : ''
|
||||
}
|
||||
|
||||
}
|
||||
2054
lib/words.ts
Normal file
2054
lib/words.ts
Normal file
File diff suppressed because it is too large
Load diff
30
package-lock.json
generated
Normal file
30
package-lock.json
generated
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"name": "nanocurrency-web",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "12.7.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.12.tgz",
|
||||
"integrity": "sha512-KPYGmfD0/b1eXurQ59fXD1GBzhSQfz6/lKBxkaHX9dKTzjXbK68Zt7yGUxUsCS1jeTy/8aL+d9JEr+S54mpkWQ==",
|
||||
"dev": true
|
||||
},
|
||||
"bignumber": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bignumber/-/bignumber-1.1.0.tgz",
|
||||
"integrity": "sha1-5qsKdD2l8+oBjlwXWX0SH3howVk="
|
||||
},
|
||||
"blakejs": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz",
|
||||
"integrity": "sha1-ad+S75U6qIylGjLfarHFShVfx6U="
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.6.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz",
|
||||
"integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
27
package.json
Normal file
27
package.json
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "nanocurrency-web",
|
||||
"version": "1.0.0",
|
||||
"description": "Toolset for Nano cryptocurrency client side offline integrations",
|
||||
"author": "Miro Metsänheimo <miro@metsanheimo.fi>",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/numsu/nanocurrency-web-js#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/numsu/nanocurrency-web-js.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/numsu/nanocurrency-web-js/issues"
|
||||
},
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"bignumber": "^1.1.0",
|
||||
"blakejs": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.7.12",
|
||||
"typescript": "3.6.3"
|
||||
}
|
||||
}
|
||||
18
tsconfig.json
Normal file
18
tsconfig.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"outDir": "./dist",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"downlevelIteration": true,
|
||||
"types": [
|
||||
"node",
|
||||
"index.d.ts"
|
||||
],
|
||||
"lib": [
|
||||
"es2017"
|
||||
]
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue