From 42bb7bb603a291cdfd7bff355bee0ef31a52626e Mon Sep 17 00:00:00 2001 From: nixer89 Date: Fri, 12 Nov 2021 11:40:28 +0100 Subject: [PATCH] add the possibility to create a wallet from Secret Numbers --- package.json | 3 ++- src/Wallet/index.ts | 36 +++++++++++++++++++++++++- test/wallet/index.ts | 61 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 58bf0eea..0b103401 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "ripple-address-codec": "^4.1.1", "ripple-binary-codec": "^1.1.3", "ripple-keypairs": "^1.0.3", - "ws": "^8.2.2" + "ws": "^8.2.2", + "xrpl-secret-numbers": "^0.3.3" }, "resolutions": { "elliptic": "^6.5.4" diff --git a/src/Wallet/index.ts b/src/Wallet/index.ts index 1f7fd8e5..4d8e04ca 100644 --- a/src/Wallet/index.ts +++ b/src/Wallet/index.ts @@ -20,6 +20,10 @@ import { sign, } from 'ripple-keypairs' +import { + Utils +} from 'xrpl-secret-numbers' + import ECDSA from '../ECDSA' import { ValidationError } from '../errors' import { Transaction } from '../models/transactions' @@ -112,7 +116,8 @@ class Wallet { privateKey: string, opts: { masterAddress?: string - seed?: string + seed?: string, + secretNumbers?: string } = {}, ) { this.publicKey = publicKey @@ -197,6 +202,35 @@ class Wallet { }) } + /** + * Derives a wallet from secret numbers. + * + * @param secretNumbers - A string consisting of 8 times 6 numbers (whitespace delimited) used to derive a wallet. + * @param opts - (Optional) Options to derive a Wallet. + * @param opts.masterAddress - Include if a Wallet uses a Regular Key Pair. It must be the master address of the account. + * @returns A Wallet derived from secret numbers. + * @throws ValidationError if unable to derive private key from secret number input. + */ + public static fromSecretNumbers( + secretNumbers: Array | string, + opts: { masterAddress?: string, algorithm?: ECDSA } = {}, + ): Wallet { + let numbersArray:Array = []; + + if (typeof secretNumbers === 'string') { + numbersArray = Utils.parseSecretString(secretNumbers); + } else if (Array.isArray(secretNumbers)) { + numbersArray = secretNumbers; + } + + const entropy = Utils.secretToEntropy(numbersArray) + + return Wallet.fromEntropy(entropy, { + algorithm: opts.algorithm ?? ECDSA.secp256k1, + masterAddress: opts.masterAddress, + }); + } + /** * Derives a wallet from an entropy (array of random numbers). * diff --git a/test/wallet/index.ts b/test/wallet/index.ts index ddfbe162..3c8348d6 100644 --- a/test/wallet/index.ts +++ b/test/wallet/index.ts @@ -227,6 +227,67 @@ describe('Wallet', function () { }) }) + describe('fromSecretNumbers', function () { + const secretNumbersString = '399150 474506 009147 088773 432160 282843 253738 605430' + const secretNumbersArray = ["399150","474506","009147","088773","432160","282843","253738","605430"] + + const publicKey = + '03BFC2F7AE242C3493187FA0B72BE97B2DF71194FB772E507FF9DEA0AD13CA1625' + const privateKey = + '00B6FE8507D977E46E988A8A94DB3B8B35E404B60F8B11AC5213FA8B5ABC8A8D19' + const publicKeyED25519 = + 'ED8079E575450E256C496578480020A33E19B579D58A2DB8FF13FC6B05B9229DE3' + const privateKeyED25519 = + 'EDD2AF6288A903DED9860FC62E778600A985BDF804E40BD8266505553E3222C3DA' + + it('derives a wallet using default algorithm', function () { + const wallet = Wallet.fromSecretNumbers(secretNumbersString) + + assert.equal(wallet.publicKey, publicKey) + assert.equal(wallet.privateKey, privateKey) + }) + + it('derives a wallet from secret numbers as an array using default algorithm', function () { + const wallet = Wallet.fromSecretNumbers(secretNumbersArray) + + assert.equal(wallet.publicKey, publicKey) + assert.equal(wallet.privateKey, privateKey) + }) + + it('derives a wallet using algorithm ecdsa-secp256k1', function () { + const algorithm = ECDSA.secp256k1 + const wallet = Wallet.fromSecretNumbers(secretNumbersString, { algorithm }) + + assert.equal(wallet.publicKey, publicKey) + assert.equal(wallet.privateKey, privateKey) + }) + + it('derives a wallet using algorithm ed25519', function () { + const algorithm = ECDSA.ed25519 + const wallet = Wallet.fromSecretNumbers(secretNumbersString, { algorithm }) + + assert.equal(wallet.publicKey, publicKeyED25519) + assert.equal(wallet.privateKey, privateKeyED25519) + }) + + it('derives a wallet using a Regular Key Pair', function () { + const masterAddress = 'rUAi7pipxGpYfPNg3LtPcf2ApiS8aw9A93' + const regularKeyPair = { + secretNumbers: '399150 474506 009147 088773 432160 282843 253738 605430', + publicKey: + '03BFC2F7AE242C3493187FA0B72BE97B2DF71194FB772E507FF9DEA0AD13CA1625', + privateKey: + '00B6FE8507D977E46E988A8A94DB3B8B35E404B60F8B11AC5213FA8B5ABC8A8D19', + } + + const wallet = Wallet.fromSecretNumbers(regularKeyPair.secretNumbers, { masterAddress }) + + assert.equal(wallet.publicKey, regularKeyPair.publicKey) + assert.equal(wallet.privateKey, regularKeyPair.privateKey) + assert.equal(wallet.classicAddress, masterAddress) + }) + }) + describe('fromEntropy', function () { let entropy const publicKey =