mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-04 21:15:47 +00:00
[Feature] Generate Wallet from secret numbers (#1799)
* add the possibility to create a wallet from Secret Numbers * Move it off the Wallet class
This commit is contained in:
36
package-lock.json
generated
36
package-lock.json
generated
@@ -23,7 +23,7 @@
|
||||
"@types/node": "^14.18.35",
|
||||
"@types/ws": "^8.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.28.0",
|
||||
"@typescript-eslint/parser": "^5.28 .0",
|
||||
"@typescript-eslint/parser": "^5.28.0",
|
||||
"@xrplf/eslint-config": "^1.9.1",
|
||||
"@xrplf/prettier-config": "^1.9.1",
|
||||
"assert": "^2.0.0",
|
||||
@@ -2797,7 +2797,6 @@
|
||||
"version": "1.0.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/brorand/-/brorand-1.0.30.tgz",
|
||||
"integrity": "sha512-moU/Mp0MA5vFNGj1/A7Z5TpNC1uyS82I6KZp0Oxk9OKC2XD0S6aQGLVv9ryBYAs259Cq7h9iM1jN9zbuCrUI9w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@@ -2934,8 +2933,7 @@
|
||||
"node_modules/@types/node": {
|
||||
"version": "14.18.36",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.36.tgz",
|
||||
"integrity": "sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ=="
|
||||
},
|
||||
"node_modules/@types/normalize-package-data": {
|
||||
"version": "2.4.1",
|
||||
@@ -16184,6 +16182,16 @@
|
||||
"resolved": "packages/xrpl",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/xrpl-secret-numbers": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/xrpl-secret-numbers/-/xrpl-secret-numbers-0.3.4.tgz",
|
||||
"integrity": "sha512-B3m0OLRsmNLQpN/BUR15+LC4yejM/pdneoWgijfBYbgjVVnpyCF5+Ur7zbAs4nCAlBUZYXnxp+o/rSNZkke9jQ==",
|
||||
"dependencies": {
|
||||
"@types/brorand": "^1.0.30",
|
||||
"brorand": "^1.1.0",
|
||||
"ripple-keypairs": "^1.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
@@ -16317,7 +16325,8 @@
|
||||
"ripple-address-codec": "^4.3.0",
|
||||
"ripple-binary-codec": "^1.8.0",
|
||||
"ripple-keypairs": "^1.3.0",
|
||||
"ws": "^8.2.2"
|
||||
"ws": "^8.2.2",
|
||||
"xrpl-secret-numbers": "^0.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@geut/browser-node-core": "^2.0.13",
|
||||
@@ -18553,7 +18562,6 @@
|
||||
"version": "1.0.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/brorand/-/brorand-1.0.30.tgz",
|
||||
"integrity": "sha512-moU/Mp0MA5vFNGj1/A7Z5TpNC1uyS82I6KZp0Oxk9OKC2XD0S6aQGLVv9ryBYAs259Cq7h9iM1jN9zbuCrUI9w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@@ -18690,8 +18698,7 @@
|
||||
"@types/node": {
|
||||
"version": "14.18.36",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.36.tgz",
|
||||
"integrity": "sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ=="
|
||||
},
|
||||
"@types/normalize-package-data": {
|
||||
"version": "2.4.1",
|
||||
@@ -29040,7 +29047,18 @@
|
||||
"ripple-binary-codec": "^1.8.0",
|
||||
"ripple-keypairs": "^1.3.0",
|
||||
"typedoc": "^0.24.6",
|
||||
"ws": "^8.2.2"
|
||||
"ws": "^8.2.2",
|
||||
"xrpl-secret-numbers": "^0.3.3"
|
||||
}
|
||||
},
|
||||
"xrpl-secret-numbers": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/xrpl-secret-numbers/-/xrpl-secret-numbers-0.3.4.tgz",
|
||||
"integrity": "sha512-B3m0OLRsmNLQpN/BUR15+LC4yejM/pdneoWgijfBYbgjVVnpyCF5+Ur7zbAs4nCAlBUZYXnxp+o/rSNZkke9jQ==",
|
||||
"requires": {
|
||||
"@types/brorand": "^1.0.30",
|
||||
"brorand": "^1.1.0",
|
||||
"ripple-keypairs": "^1.1.5"
|
||||
}
|
||||
},
|
||||
"xtend": {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"@types/node": "^14.18.35",
|
||||
"@types/ws": "^8.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.28.0",
|
||||
"@typescript-eslint/parser": "^5.28 .0",
|
||||
"@typescript-eslint/parser": "^5.28.0",
|
||||
"@xrplf/eslint-config": "^1.9.1",
|
||||
"@xrplf/prettier-config": "^1.9.1",
|
||||
"assert": "^2.0.0",
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xrpl-announce) for release announcements. We recommend that xrpl.js (ripple-lib) users stay up-to-date with the latest stable release.
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
* Add `walletFromSecretNumbers` to derive a wallet from [XLS-12](https://github.com/XRPLF/XRPL-Standards/issues/15). Currently only works with `secp256k1` keys, but will work with `ED25519` keys as part of 3.0 via [#2376](https://github.com/XRPLF/xrpl.js/pull/2376).
|
||||
|
||||
## 2.10.0 (2023-08-07)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
"ripple-address-codec": "^4.3.0",
|
||||
"ripple-binary-codec": "^1.8.0",
|
||||
"ripple-keypairs": "^1.3.0",
|
||||
"ws": "^8.2.2"
|
||||
"ws": "^8.2.2",
|
||||
"xrpl-secret-numbers": "^0.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@geut/browser-node-core": "^2.0.13",
|
||||
|
||||
37
packages/xrpl/src/Wallet/walletFromSecretNumbers.ts
Normal file
37
packages/xrpl/src/Wallet/walletFromSecretNumbers.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Account } from 'xrpl-secret-numbers'
|
||||
|
||||
import ECDSA from '../ECDSA'
|
||||
|
||||
import { Wallet } from '.'
|
||||
|
||||
/**
|
||||
* Derives a wallet from secret numbers.
|
||||
* NOTE: This uses a default encoding algorithm of secp256k1 to match the popular wallet
|
||||
* [Xumm (aka Xaman)](https://xumm.app/)'s behavior.
|
||||
* This may be different from the DEFAULT_ALGORITHM for other ways to generate a Wallet.
|
||||
*
|
||||
* @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.
|
||||
* @param opts.algorithm - The digital signature algorithm to generate an address for.
|
||||
* @returns A Wallet derived from secret numbers.
|
||||
* @throws ValidationError if unable to derive private key from secret number input.
|
||||
*/
|
||||
export function walletFromSecretNumbers(
|
||||
secretNumbers: string[] | string,
|
||||
opts?: { masterAddress?: string; algorithm?: ECDSA },
|
||||
): Wallet {
|
||||
const secret = new Account(secretNumbers).getFamilySeed()
|
||||
const updatedOpts: { masterAddress?: string; algorithm?: ECDSA } = {
|
||||
masterAddress: undefined,
|
||||
algorithm: undefined,
|
||||
}
|
||||
// Use secp256k1 since that's the algorithm used by popular wallets like Xumm when generating secret number accounts
|
||||
if (opts === undefined) {
|
||||
updatedOpts.algorithm = ECDSA.secp256k1
|
||||
} else {
|
||||
updatedOpts.masterAddress = opts.masterAddress
|
||||
updatedOpts.algorithm = opts.algorithm ?? ECDSA.secp256k1
|
||||
}
|
||||
return Wallet.fromSecret(secret, updatedOpts)
|
||||
}
|
||||
@@ -13,6 +13,8 @@ export * from './errors'
|
||||
|
||||
export { Wallet } from './Wallet'
|
||||
|
||||
export { walletFromSecretNumbers } from './Wallet/walletFromSecretNumbers'
|
||||
|
||||
export { keyToRFC1751Mnemonic, rfc1751MnemonicToKey } from './Wallet/rfc1751'
|
||||
|
||||
export * from './Wallet/signer'
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { assert } from 'chai'
|
||||
import { decode } from 'ripple-binary-codec'
|
||||
|
||||
import { NFTokenMint, Payment, Transaction } from '../../src'
|
||||
import {
|
||||
NFTokenMint,
|
||||
Payment,
|
||||
Transaction,
|
||||
walletFromSecretNumbers,
|
||||
} from '../../src'
|
||||
import ECDSA from '../../src/ECDSA'
|
||||
import { Wallet } from '../../src/Wallet'
|
||||
import requests from '../fixtures/requests'
|
||||
@@ -297,6 +302,86 @@ 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'
|
||||
// TODO: Uncomment when the `deriveKeypair` fix is merged so `deriveSecretNumbers` with ed25519 works.
|
||||
// const publicKeyED25519 =
|
||||
// 'ED8079E575450E256C496578480020A33E19B579D58A2DB8FF13FC6B05B9229DE3'
|
||||
// const privateKeyED25519 =
|
||||
// 'EDD2AF6288A903DED9860FC62E778600A985BDF804E40BD8266505553E3222C3DA'
|
||||
|
||||
it('derives a wallet using default algorithm', function () {
|
||||
const wallet = walletFromSecretNumbers(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 = walletFromSecretNumbers(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 = walletFromSecretNumbers(secretNumbersString, {
|
||||
algorithm,
|
||||
})
|
||||
|
||||
assert.equal(wallet.publicKey, publicKey)
|
||||
assert.equal(wallet.privateKey, privateKey)
|
||||
})
|
||||
|
||||
// TODO: Uncomment this test when the `deriveKeypair` fix is merged
|
||||
// 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 = walletFromSecretNumbers(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: number[]
|
||||
const publicKey =
|
||||
|
||||
Reference in New Issue
Block a user