From 4e16dcdd9b92c6f7e5278c6440c1f9f663eec664 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Thu, 13 Jan 2022 10:25:10 +0100 Subject: [PATCH 01/27] feat: KeyChain and KeyChainStore --- .../src/types/Account/methods/encrypt.spec.js | 2 +- .../types/Account/methods/generateAddress.js | 72 +++++++--- .../src/types/Account/methods/getAddress.js | 46 +++--- .../src/types/Identities/Identities.js | 2 +- .../methods/getIdentityHDKeyById.js | 5 +- .../methods/getIdentityHDKeyById.spec.js | 8 +- .../Identities/methods/getIdentityIds.spec.js | 2 - .../wallet-lib/src/types/KeyChain/KeyChain.js | 131 +++++++++++++----- .../src/types/KeyChain/KeyChain.spec.js | 121 +++++++++++----- .../KeyChain/methods/generateKeyForChild.js | 19 --- .../KeyChain/methods/generateKeyForPath.js | 19 --- .../KeyChain/methods/getDIP15ExtendedKey.js | 2 +- .../KeyChain/methods/getFirstUnusedAddress.js | 14 ++ .../types/KeyChain/methods/getForAddress.js | 12 ++ .../src/types/KeyChain/methods/getForPath.js | 42 ++++++ .../KeyChain/methods/getHardenedBIP44HDKey.js | 5 +- .../methods/getHardenedDIP9FeatureHDKey.js | 5 +- .../types/KeyChain/methods/getIssuedPaths.js | 5 + .../types/KeyChain/methods/getKeyForChild.js | 10 -- .../types/KeyChain/methods/getKeyForPath.js | 29 ---- .../types/KeyChain/methods/getPrivateKey.js | 18 --- .../src/types/KeyChain/methods/getRootKey.js | 5 + .../KeyChain/methods/getWatchedAddresses.js | 7 + .../KeyChain/methods/markAddressAsUsed.js | 18 +++ .../types/KeyChain/methods/maybeLookAhead.js | 84 +++++++++++ .../src/types/KeyChainStore/KeyChainStore.js | 16 +++ .../types/KeyChainStore/KeyChainStore.spec.js | 47 +++++++ .../KeyChainStore/methods/addKeyChain.js | 15 ++ .../KeyChainStore/methods/getKeyChain.js | 5 + .../KeyChainStore/methods/getKeyChains.js | 5 + .../methods/getMasterKeyChain.js | 6 + .../methods/makeChildKeyChainStore.js | 21 +++ .../src/types/Wallet/Wallet.spec.js | 17 ++- .../src/types/Wallet/methods/fromAddress.js | 8 +- .../types/Wallet/methods/fromAddress.spec.js | 14 +- .../types/Wallet/methods/fromHDPrivateKey.js | 6 +- .../Wallet/methods/fromHDPrivateKey.spec.js | 7 +- .../types/Wallet/methods/fromHDPublicKey.js | 6 +- .../Wallet/methods/fromHDPublicKey.spec.js | 49 ++++--- .../src/types/Wallet/methods/fromMnemonic.js | 14 +- .../types/Wallet/methods/fromMnemonic.spec.js | 47 +++---- .../types/Wallet/methods/fromPrivateKey.js | 8 +- .../Wallet/methods/fromPrivateKey.spec.js | 12 +- .../src/types/Wallet/methods/fromPublicKey.js | 8 +- .../Wallet/methods/fromPublicKey.spec.js | 12 +- .../src/types/Wallet/methods/fromSeed.js | 4 +- .../src/types/Wallet/methods/fromSeed.spec.js | 14 +- .../utils/bip44/ensureAddressesToGapLimit.js | 10 +- 48 files changed, 705 insertions(+), 329 deletions(-) delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/generateKeyForChild.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/generateKeyForPath.js create mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getFirstUnusedAddress.js create mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getForAddress.js create mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getForPath.js create mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getIssuedPaths.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getKeyForChild.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getKeyForPath.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getPrivateKey.js create mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getRootKey.js create mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getWatchedAddresses.js create mode 100644 packages/wallet-lib/src/types/KeyChain/methods/markAddressAsUsed.js create mode 100644 packages/wallet-lib/src/types/KeyChain/methods/maybeLookAhead.js create mode 100644 packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.js create mode 100644 packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.spec.js create mode 100644 packages/wallet-lib/src/types/KeyChainStore/methods/addKeyChain.js create mode 100644 packages/wallet-lib/src/types/KeyChainStore/methods/getKeyChain.js create mode 100644 packages/wallet-lib/src/types/KeyChainStore/methods/getKeyChains.js create mode 100644 packages/wallet-lib/src/types/KeyChainStore/methods/getMasterKeyChain.js create mode 100644 packages/wallet-lib/src/types/KeyChainStore/methods/makeChildKeyChainStore.js diff --git a/packages/wallet-lib/src/types/Account/methods/encrypt.spec.js b/packages/wallet-lib/src/types/Account/methods/encrypt.spec.js index 89653f84bdc..facc317ea96 100644 --- a/packages/wallet-lib/src/types/Account/methods/encrypt.spec.js +++ b/packages/wallet-lib/src/types/Account/methods/encrypt.spec.js @@ -29,7 +29,7 @@ describe('Account - encrypt', function suite() { const secret = 'secret'; it('should encrypt extPubKey with aes', () => { - const extPubKey = account.keyChain.getKeyForPath(derivationPath, 'HDPublicKey').toString(); + const extPubKey = account.keyChainStore.getMasterKeyChain().getForPath(derivationPath).key.toString(); const encryptedExtPubKey = account.encrypt('aes', extPubKey, secret).toString(); const bytes = CryptoJS.AES.decrypt(encryptedExtPubKey, secret); const decrypted = bytes.toString(CryptoJS.enc.Utf8); diff --git a/packages/wallet-lib/src/types/Account/methods/generateAddress.js b/packages/wallet-lib/src/types/Account/methods/generateAddress.js index 450f582fe73..6d19109c108 100644 --- a/packages/wallet-lib/src/types/Account/methods/generateAddress.js +++ b/packages/wallet-lib/src/types/Account/methods/generateAddress.js @@ -1,58 +1,94 @@ -const { PublicKey } = require('@dashevo/dashcore-lib'); const EVENTS = require('../../../EVENTS'); const { WALLET_TYPES } = require('../../../CONSTANTS'); const { is } = require('../../../utils'); + /** * Generate an address from a path and import it to the store * @param {string} path - * @return {AddressObj} Address information + * @param {boolean} [isWatchedAddress=true] - if the address will be watched + * @return {AddressInfo} Address information * */ -function generateAddress(path) { +function generateAddress(path, isWatchedAddress = true) { if (is.undefOrNull(path)) throw new Error('Expected path to generate an address'); let index = 0; - let privateKey; let address; + let keyPathData; const { network } = this; switch (this.walletType) { case WALLET_TYPES.ADDRESS: - address = this.keyChain.address; + address = this.keyChainStore.getMasterKeyChain().rootKey; + if (isWatchedAddress) { + this.keyChainStore.issuedPaths.set(0, { + path: 0, + address, + isUsed: false, + isWatched: true, + }); + } break; case WALLET_TYPES.PUBLICKEY: - address = new PublicKey(this.keyChain.publicKey.toString()).toAddress(network).toString(); + // eslint-disable-next-line no-case-declarations + const { rootKey } = this.keyChainStore.getMasterKeyChain(); + address = rootKey.toAddress(network).toString(); + if (isWatchedAddress) { + this.keyChainStore.issuedPaths.set(0, { + key: rootKey, + path: 0, + address, + isUsed: false, + isWatched: true, + }); + } break; + case WALLET_TYPES.HDPRIVATE: case WALLET_TYPES.HDWALLET: // eslint-disable-next-line prefer-destructuring - index = parseInt(path.toString().split('/')[5], 10); - privateKey = this.keyChain.getKeyForPath(path); - address = privateKey.publicKey.toAddress(network).toString(); + index = parseInt(path.toString().split('/')[2], 10); + keyPathData = this.keyChainStore + .getMasterKeyChain() + .getForPath(path, { isWatched: isWatchedAddress }); + address = keyPathData.address.toString(); break; case WALLET_TYPES.HDPUBLIC: index = parseInt(path.toString().split('/')[5], 10); - privateKey = this.keyChain.getKeyForChild(index); - address = privateKey.publicKey.toAddress(network).toString(); + // eslint-disable-next-line no-case-declarations + keyPathData = this.keyChainStore + .getMasterKeyChain() + .getForPath(path, { isWatched: isWatchedAddress }); + address = keyPathData.address.toString(); break; + // TODO: DEPRECATE USAGE OF SINGLE_ADDRESS in favor or PRIVATEKEY + case WALLET_TYPES.PRIVATEKEY: case WALLET_TYPES.SINGLE_ADDRESS: default: - privateKey = this.keyChain.getKeyForPath(path.toString()); - address = privateKey.publicKey.toAddress(network).toString(); + keyPathData = this.keyChainStore + .getMasterKeyChain() + .getForPath(path, { isWatched: isWatchedAddress }); + address = keyPathData.address.toString(); + break; } const addressData = { path: path.toString(), index, address, - // privateKey, transactions: [], + utxos: {}, balanceSat: 0, unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false, }; - this.storage.importAddresses(addressData, this.walletId); + const accountStore = this.storage + .getWalletStore(this.walletId) + .getPathState(this.accountPath); + + const chainStore = this.storage.getChainStore(this.network); + + accountStore.addresses[addressData.path] = addressData.address.toString(); + chainStore.importAddress(addressData.address.toString()); this.emit(EVENTS.GENERATED_ADDRESS, { type: EVENTS.GENERATED_ADDRESS, payload: addressData }); return addressData; } + module.exports = generateAddress; diff --git a/packages/wallet-lib/src/types/Account/methods/getAddress.js b/packages/wallet-lib/src/types/Account/methods/getAddress.js index d4771dc7738..86efad07386 100644 --- a/packages/wallet-lib/src/types/Account/methods/getAddress.js +++ b/packages/wallet-lib/src/types/Account/methods/getAddress.js @@ -1,40 +1,26 @@ const { WALLET_TYPES } = require('../../../CONSTANTS'); -const getTypePathFromWalletType = (walletType, addressType = 'external', index, BIP44PATH) => { - let type; - let path; - - const addressTypeIndex = (addressType === 'external') ? 0 : 1; - switch (walletType) { - case WALLET_TYPES.HDWALLET: - type = addressType; - path = `${BIP44PATH}/${addressTypeIndex}/${index}`; - break; - case WALLET_TYPES.HDPUBLIC: - type = 'external'; - path = `${BIP44PATH}/${addressTypeIndex}/${index}`; - break; - case WALLET_TYPES.PUBLICKEY: - case WALLET_TYPES.ADDRESS: - case WALLET_TYPES.PRIVATEKEY: - case WALLET_TYPES.SINGLE_ADDRESS: - default: - type = 'misc'; - path = '0'; - } - return { type, path }; -}; /** * Get a specific addresss based on the index and type of address. * @param {number} index - The index on the type - * @param {AddressType} [_type="external"] - Type of the address (external, internal, misc) + * @param {AddressType} [addressType="external"] - Type of the address (external, internal, misc) * @return */ -function getAddress(index = 0, _type = 'external') { - const { type, path } = getTypePathFromWalletType(this.walletType, _type, index, this.BIP44PATH); +function getAddress(addressIndex = 0, addressType = 'external') { + const addressTypeIndex = (addressType === 'external') ? 0 : 1; + + const { addresses } = this.storage.getWalletStore(this.walletId).getPathState(this.accountPath); + const addressPath = ([WALLET_TYPES.HDPUBLIC, WALLET_TYPES.HDWALLET].includes(this.walletType)) + ? `m/${addressTypeIndex}/${addressIndex}` : '0'; + + const address = addresses[addressPath]; + if (!address) return this.generateAddress(addressPath); - const { wallets } = this.storage.getStore(); - const matchingTypeAddresses = wallets[this.walletId].addresses[type]; - return (matchingTypeAddresses[path]) ? matchingTypeAddresses[path] : this.generateAddress(path); + const chainStore = this.storage.getChainStore(this.network); + return { + index: addressIndex, + path: addressPath, + ...chainStore.getAddress(address), + }; } module.exports = getAddress; diff --git a/packages/wallet-lib/src/types/Identities/Identities.js b/packages/wallet-lib/src/types/Identities/Identities.js index f721356dcce..f457fe9fc99 100644 --- a/packages/wallet-lib/src/types/Identities/Identities.js +++ b/packages/wallet-lib/src/types/Identities/Identities.js @@ -10,7 +10,7 @@ class Identities { this.storage = wallet.storage; - this.keyChain = wallet.keyChain; + this.keyChain = wallet.keyChainStore.getMasterKeyChain(); } } diff --git a/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.js b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.js index 374925a14a3..79cd6eedc77 100644 --- a/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.js +++ b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.js @@ -5,7 +5,10 @@ * @return {HDPrivateKey} */ function getIdentityHDKeyById(identityId, keyIndex) { - const identityIndex = this.storage.getIndexedIdentityIds(this.walletId).indexOf(identityId); + const identityIndex = this.storage + .getWalletStore(this.walletId) + .getIndexedIdentityIds() + .indexOf(identityId); if (identityIndex === -1) { throw new Error(`Identity with ID ${identityId} is not associated with wallet, or it's not synced`); diff --git a/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.spec.js b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.spec.js index e13dc087569..2d8f3a2b829 100644 --- a/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.spec.js +++ b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.spec.js @@ -1,7 +1,6 @@ const { expect } = require('chai'); const mockedStore = require('../../../../fixtures/sirentonight-fullstore-snapshot-1562711703'); const getIdentityHDKeyById = require('./getIdentityHDKeyById'); -const searchTransaction = require('../../Storage/methods/searchTransaction'); let walletMock; let fetchTransactionInfoCalledNb = 0; @@ -10,12 +9,15 @@ describe('Wallet#getIdentityHDKeyById', function suite() { this.timeout(10000); before(() => { expectedKeyMock = "123"; + const walletStoreMock = { + getIndexedIdentityIds: () => mockedStore.wallets[Object.keys(mockedStore.wallets)].identityIds + } + const storageMock = { store: mockedStore, getStore: () => mockedStore, mappedAddress: {}, - searchTransaction, - getIndexedIdentityIds: () => mockedStore.wallets[Object.keys(mockedStore.wallets)].identityIds, + getWalletStore: () => walletStoreMock, }; const walletId = Object.keys(mockedStore.wallets)[0]; walletMock = { diff --git a/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.spec.js b/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.spec.js index df90868e4c2..df2e887176b 100644 --- a/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.spec.js +++ b/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.spec.js @@ -1,7 +1,6 @@ const { expect } = require('chai'); const mockedStore = require('../../../../fixtures/sirentonight-fullstore-snapshot-1562711703'); const getIdentityIds = require('./getIdentityIds'); -const searchTransaction = require('../../Storage/methods/searchTransaction'); let mockedWallet; let fetchTransactionInfoCalledNb = 0; @@ -12,7 +11,6 @@ describe('Wallet#getIdentityIds', function suite() { store: mockedStore, getStore: () => mockedStore, mappedAddress: {}, - searchTransaction, getIndexedIdentityIds: () => mockedStore.wallets[Object.keys(mockedStore.wallets)].identityIds, }; const walletId = Object.keys(mockedStore.wallets)[0]; diff --git a/packages/wallet-lib/src/types/KeyChain/KeyChain.js b/packages/wallet-lib/src/types/KeyChain/KeyChain.js index 643dcc62737..74931778764 100644 --- a/packages/wallet-lib/src/types/KeyChain/KeyChain.js +++ b/packages/wallet-lib/src/types/KeyChain/KeyChain.js @@ -1,52 +1,107 @@ const { Networks, HDPrivateKey, HDPublicKey } = require('@dashevo/dashcore-lib'); -const { has } = require('lodash'); +const { PrivateKey, PublicKey } = require('@dashevo/dashcore-lib'); +const { doubleSha256 } = require('../../utils/crypto'); +const { mnemonicToHDPrivateKey } = require('../../utils/mnemonic'); -// eslint-disable-next-line no-underscore-dangle -const _defaultOpts = { - network: Networks.testnet.toString(), - keys: {}, -}; +function generateKeyChainId(key) { + const keyChainIdSuffix = doubleSha256(key.toString()).toString('hex').slice(0, 10); + return `kc${keyChainIdSuffix}`; +} -class KeyChain { - constructor(opts = JSON.parse(JSON.stringify(_defaultOpts))) { - const defaultOpts = JSON.parse(JSON.stringify(_defaultOpts)); - this.network = defaultOpts.network; - this.keys = { ...defaultOpts.keys }; - - if (has(opts, 'HDPrivateKey')) { - this.type = 'HDPrivateKey'; - this.HDPrivateKey = (typeof opts.HDPrivateKey === 'string') ? HDPrivateKey(opts.HDPrivateKey) : opts.HDPrivateKey; - this.network = this.HDPrivateKey.network; - } else if (has(opts, 'HDPublicKey')) { - this.type = 'HDPublicKey'; - this.HDPublicKey = (typeof opts.HDPublicKey === 'string') ? HDPublicKey(opts.HDPublicKey) : opts.HDPublicKey; - this.network = this.HDPublicKey.network; - } else if (has(opts, 'privateKey')) { - this.type = 'privateKey'; - this.privateKey = opts.privateKey; - } else if (has(opts, 'publicKey')) { - this.type = 'publicKey'; - this.publicKey = opts.publicKey; - } else if (has(opts, 'address')) { - this.type = 'address'; - this.address = opts.address.toString(); - } else { - throw new Error('Expect privateKey, publicKey, HDPublicKey, HDPrivateKey or Address'); +function fromOptions(opts) { + let rootKey; + let rootKeyType; + let network = Networks.testnet.toString(); + let passphrase = ''; + + if (opts) { + if (opts.passphrase) { + passphrase = opts.passphrase; + } + if (opts.mnemonic) { + rootKeyType = 'HDPrivateKey'; + rootKey = (typeof opts.mnemonic === 'string') ? HDPrivateKey(opts.HDPrivateKey) : opts.HDPrivateKey; + } + if (opts.network) { + network = opts.network; + } + if (opts.HDPrivateKey) { + rootKeyType = 'HDPrivateKey'; + rootKey = (typeof opts.HDPrivateKey === 'string') ? HDPrivateKey(opts.HDPrivateKey) : opts.HDPrivateKey; + network = rootKey.network.toString(); + } else if (opts.HDPublicKey) { + rootKeyType = 'HDPublicKey'; + rootKey = (typeof opts.HDPublicKey === 'string') ? HDPublicKey(opts.HDPublicKey) : opts.HDPublicKey; + network = rootKey.network.toString(); + } else if (opts.privateKey) { + rootKeyType = 'privateKey'; + rootKey = (typeof opts.privateKey === 'string') ? new PrivateKey(opts.privateKey, opts.network) : opts.privateKey; + network = rootKey.network.toString(); + } else if (opts.publicKey) { + rootKeyType = 'publicKey'; + rootKey = (typeof opts.publicKey === 'string') ? new PublicKey(opts.publicKey, opts.network) : opts.publicKey; + network = rootKey.network.toString(); + } else if (opts.address) { + rootKeyType = 'address'; + rootKey = opts.address.toString(); + } else if (opts.mnemonic) { + return fromOptions({ + ...opts, + HDPrivateKey: mnemonicToHDPrivateKey(opts.mnemonic, network, passphrase), + }); } - if (opts.network) this.network = opts.network; - if (opts.keys) this.keys = { ...opts.keys }; } + + const lookAheadOpts = { + isWatched: true, + paths: {}, + ...opts.lookAheadOpts, + }; + + return { + rootKeyType, + rootKey, + network, + passphrase, + lookAheadOpts, + }; } -KeyChain.prototype.generateKeyForChild = require('./methods/generateKeyForChild'); -KeyChain.prototype.generateKeyForPath = require('./methods/generateKeyForPath'); +class KeyChain { + constructor(opts = {}) { + const { + rootKey, + rootKeyType, + network, + lookAheadOpts, + } = fromOptions(opts); + if (!rootKeyType || !rootKey) { + throw new Error('Expect one of [mnemonic, HDPrivateKey, HDPublicKey, privateKey, publicKey, address] to be provided.'); + } + this.keyChainId = generateKeyChainId(rootKey); + + this.rootKey = rootKey; + this.network = network; + this.rootKeyType = rootKeyType; + this.lookAheadOpts = { isWatched: true, ...lookAheadOpts }; + + this.issuedPaths = new Map(); + + this.maybeLookAhead(); + } +} +KeyChain.prototype.getForPath = require('./methods/getForPath'); +KeyChain.prototype.getForAddress = require('./methods/getForAddress'); KeyChain.prototype.getDIP15ExtendedKey = require('./methods/getDIP15ExtendedKey'); +KeyChain.prototype.getFirstUnusedAddress = require('./methods/getFirstUnusedAddress'); KeyChain.prototype.getHardenedBIP44HDKey = require('./methods/getHardenedBIP44HDKey'); KeyChain.prototype.getHardenedDIP9FeatureHDKey = require('./methods/getHardenedDIP9FeatureHDKey'); KeyChain.prototype.getHardenedDIP15AccountKey = require('./methods/getHardenedDIP15AccountKey'); -KeyChain.prototype.getKeyForChild = require('./methods/getKeyForChild'); -KeyChain.prototype.getKeyForPath = require('./methods/getKeyForPath'); -KeyChain.prototype.getPrivateKey = require('./methods/getPrivateKey'); +KeyChain.prototype.getRootKey = require('./methods/getRootKey'); +KeyChain.prototype.getWatchedAddresses = require('./methods/getWatchedAddresses'); +KeyChain.prototype.getIssuedPaths = require('./methods/getIssuedPaths'); +KeyChain.prototype.maybeLookAhead = require('./methods/maybeLookAhead'); +KeyChain.prototype.markAddressAsUsed = require('./methods/markAddressAsUsed'); KeyChain.prototype.sign = require('./methods/sign'); module.exports = KeyChain; diff --git a/packages/wallet-lib/src/types/KeyChain/KeyChain.spec.js b/packages/wallet-lib/src/types/KeyChain/KeyChain.spec.js index 3cf9bb0b5a1..f392769cdda 100644 --- a/packages/wallet-lib/src/types/KeyChain/KeyChain.spec.js +++ b/packages/wallet-lib/src/types/KeyChain/KeyChain.spec.js @@ -8,36 +8,32 @@ let keychain2; const mnemonic = 'during develop before curtain hazard rare job language become verb message travel'; const mnemonic2 = 'birth kingdom trash renew flavor utility donkey gasp regular alert pave layer'; const pk = '4226d5e2fe8cbfe6f5beb7adf5a5b08b310f6c4a67fc27826779073be6f5699e'; - +const hdPublicKey = 'xpub661MyMwAqRbcFGB6XSWBsD725rJDUbFUpy4zWe2u22nJ2BxpoHFxtVDfKnTnvVQHohnY7AsVpRTHDv6PyPQTYu1KxFPKw29MAVXPEpz1G7V'; const expectedRootDIP15AccountKey_0 = 'tprv8hRzmheQujhJN5XP2dj955nAFCKeEoSifJRWuutdbwWRtusdDQ426jbp75EqErUSuTxmPyxYmP1TpcF5qdxGhXLNXRLMGsRLG6NFCv1WnaQ'; const expectedRootDIP15AccountKey_1 = 'tprv8hRzmheQujhJQyCtFTuUFHxB3Ag5VLB994zhH4CfxbA41cq73HT2mpYq5M33V54oJyn6g514saxxVJB886G55eYX56J6D6x87UNNT6iQHkR'; const expectedKeyForChild_0 = 'tprv8d4podc2Tg459CH2bwLHXj3vdJFBT2rdsk5Nr1djH7hzHdt5LRdvN6QyFwMiDy7ffRdik7fEVRKKgsHB4F18sh8xF6jFXpKq4sUgGBoSbKw'; describe('Keychain', function suite() { - this.timeout(10000); + this.timeout(1000); it('should create a keychain', () => { - const expectedException1 = 'Expect privateKey, publicKey, HDPublicKey, HDPrivateKey or Address'; + const expectedException1 = 'Expect one of [mnemonic, HDPrivateKey, HDPublicKey, privateKey, publicKey, address] to be provided.'; expect(() => new KeyChain()).to.throw(expectedException1); - expect(() => new KeyChain(mnemonic)).to.throw(expectedException1); - keychain = new KeyChain({ HDPrivateKey: mnemonicToHDPrivateKey(mnemonic, 'testnet') }); - expect(keychain.type).to.equal('HDPrivateKey'); + keychain = new KeyChain({ mnemonic: mnemonic, network: 'testnet' }); + expect(keychain.rootKeyType).to.equal('HDPrivateKey'); expect(keychain.network.toString()).to.equal('testnet'); - expect(keychain.keys).to.deep.equal({}); + expect(keychain.rootKey.network.toString()).to.equal('testnet'); - keychain2 = new KeyChain({ HDPrivateKey: mnemonicToHDPrivateKey(mnemonic2, 'mainnet') }); - }); - it('should get private key', () => { - expect(keychain.getPrivateKey().toString()).to.equal(pk); + keychain2 = new KeyChain({ mnemonic: mnemonic2, network: 'livenet' }); }); it('should generate key for full path', () => { const path = 'm/44\'/1\'/0\'/0/0'; - const pk2 = keychain.getKeyForPath(path); + const pk2 = keychain.getForPath(path).key; const address = new Dashcore.Address(pk2.publicKey.toAddress()).toString(); expect(address).to.equal('yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT'); }); it('should get hardened feature path', () => { const hardenedPk = keychain.getHardenedBIP44HDKey(); - const pk2 = keychain.getKeyForPath('m/44\'/1\''); + const pk2 = keychain.getForPath('m/44\'/1\'').key; expect(pk2.toString()).to.equal(hardenedPk.toString()); }); it('should get DIP15 account key', function () { @@ -46,6 +42,7 @@ describe('Keychain', function suite() { const rootDIP15AccountKey_1 = keychain.getHardenedDIP15AccountKey(1); expect(rootDIP15AccountKey_1.toString()).to.deep.equal(expectedRootDIP15AccountKey_1); }); + it('should get DIP15 extended key', function () { const userUniqueId = '0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'; const contactUniqueId = '0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'; @@ -65,11 +62,10 @@ describe('Keychain', function suite() { const DIP15ExtKey_1 = keychain.getDIP15ExtendedKey(userAhash, userBhash, 0, 0); expect(DIP15ExtKey_1.privateKey.toString()).to.equal('60581b6dca8244d3fb3cfe619b5a22277e5423b01e5285f356981f247e0f4a60') expect(DIP15ExtKey_1.publicKey.toString()).to.equal('03deaac00f721151307fbc7bf80d7b8afab98c1f026d67e5f56b21e2013f551ce6') - }); it('should derive from hardened feature path', () => { const hardenedHDKey = keychain.getHardenedBIP44HDKey(); - const pk2 = keychain.getKeyForPath(`m/44'/1'`); + const pk2 = keychain.getForPath(`m/44'/1'`).key; expect(pk2.toString()).to.equal(hardenedHDKey.toString()); expect(hardenedHDKey.toString()).to.deep.equal('tprv8dtrJNytYHRiZY585hmHGbguS6VjGpK49puSB7oXZjLHcQfrAzQkF4ZCxM2DkEbyY85J4EYcZ8EjT5ZCU8ozB727TDdodbfXet5GkGau2RQ'); const derivedPk = hardenedHDKey.deriveChild(0, true).deriveChild(0).deriveChild(0); @@ -78,29 +74,88 @@ describe('Keychain', function suite() { }); it('should get hardened DIP9FeatureHDKey', function () { const hardenedHDKey = keychain.getHardenedDIP9FeatureHDKey(); - const pk2 = keychain.getKeyForPath(`m/9'/1'`); + const pk2 = keychain.getForPath(`m/9'/1'`).key; expect(pk2.toString()).to.equal(hardenedHDKey.toString()); expect(hardenedHDKey.toString()).to.deep.equal('tprv8fBJjWoGgCpGRCbyzE9RUA59rmoN1RUijhLnXGL4VHnLxvSe523yVg4GrGzbR6TyXtdynAEh5z8UX55EXt2Cb3xjvrsx2PgTY9BHxzFVkWn'); }); - it('should generate key for child', () => { + it('should get key for path', () => { const keychain2 = new KeyChain({ HDPrivateKey: mnemonicToHDPrivateKey(mnemonic, 'testnet') }); - const keyForChild = keychain2.generateKeyForChild(0); + const keyForChild = keychain2.getForPath('m/0').key; expect(keyForChild.toString()).to.equal(expectedKeyForChild_0); }); + it('should mark address watched and get watched addresses', function () { + const key0 = keychain.getForPath('m/0'); + keychain.getForPath('m/0').isWatched = true + key0.isWatched = true; + const key1 = keychain.getForPath('m/1', { isWatched: true }); + const key2 = keychain.getForPath('m/2', { isWatched: true }); - it('should sign', () => { + const watchedAddresses = keychain.getWatchedAddresses(); + let expectedWatchedAddresses = [ + keychain.getForPath('m/0').address.toString(), + keychain.getForPath('m/1').address.toString(), + keychain.getForPath('m/2').address.toString() + ]; + expect(watchedAddresses).to.deep.equal(expectedWatchedAddresses); + }); + it('should get watched addresses', function () { + const watchedAddresses = keychain.getWatchedAddresses(); + const expectedWatchedAddresses = [ + 'ybQDfNwiDjk8ZH5UUmHQzAMEmjbrbK5dAj', + 'yhFX5rseJPitV45HUCaa9haeGHtLuooBaq', + 'yhqxsmYk6jfoGWf1hJKq7d4U2cGHCgzpFU' + ] + expect(watchedAddresses).to.deep.equal(expectedWatchedAddresses); + }); + // it('should get watched public keys', function () { + // const watchedPubKeys = keychain.getWatchedPublicKeys(); + // const expectedWatchedPubKeys = [ + // '03e6ab8177a7ca2699da4f83ca3c27768fb88b70ae9d6bde1cba8de88355ccf199', + // '0246a870b65153b98e453ff08f7198d06bce2a790286f44d90929aceafafa0673f', + // '025ad16f78f67801e52abe5b512d66e3896d4c2fa3ca3150349437a5dd13519967' + // ] + // expect(watchedPubKeys).to.deep.equal(expectedWatchedPubKeys) + // }); + it('should remove an address from watched addresses', function () { + const data0 = keychain.getForPath('m/0', { isWatched: false }); + const data1 = keychain.getForPath('m/1'); + const data2 = keychain.getForPath('m/2'); + data2.isWatched = false; + expect(keychain.getWatchedAddresses().length).to.equal(1); }); -}); -describe('Keychain - clone', function suite() { - this.timeout(10000); - it('should clone', () => { - const keychain2 = new KeyChain(keychain); - expect(keychain2).to.deep.equal(keychain); - expect(keychain2.keys).to.deep.equal(keychain.keys); + it('should get address for path', function (){ + const address0_1 = keychain.getForPath('m/1').address; + expect(address0_1.toString()).to.equal('yhFX5rseJPitV45HUCaa9haeGHtLuooBaq') + }) + it('should mark address as used', function () { + const address0_0 = keychain.getForPath('m/0').address; + keychain.markAddressAsUsed(address0_0); + expect(keychain.issuedPaths.get('m/0').isUsed).to.equal(true) }); }); +describe('Keychain - HDPublicKey', function suite(){ + let hdpubKeyChain; + it('should initiate from a HDPublicKey', function () { + hdpubKeyChain = new KeyChain({ + HDPublicKey: new Dashcore.HDPublicKey(hdPublicKey), + network: 'testnet' + }); + // As the HDPublicKey starts with xpub, it's livenet and should take priority over our network being set. + expect(hdpubKeyChain.network.toString()).to.equal('livenet'); + expect(hdpubKeyChain.keyChainId).to.equal('kc5059442d66'); + expect(hdpubKeyChain.getRootKey().toString()).to.equal(hdPublicKey); + }); + it('should derivate', function () { + const key0_1 = hdpubKeyChain.getForPath('m/1').key; + expect(key0_1.publicKey.toAddress(hdpubKeyChain.network).toString()).to.equal('XoL5LcBiDWcj6L7fFwytsFoX5Vz7BVXw9w') + }); + it('should get address for path', function (){ + const address0_1 = hdpubKeyChain.getForPath('m/2').address; + expect(address0_1.toString()).to.equal('XwAzpxQKbgebaLiadq1c6rDeFJ4FKPUufy') + }) +}) describe('Keychain - single privateKey', function suite() { this.timeout(10000); it('should correctly errors out when not a HDPublicKey (privateKey)', () => { @@ -108,18 +163,16 @@ describe('Keychain - single privateKey', function suite() { const network = 'livenet'; const pkKeyChain = new KeyChain({ privateKey, network }); expect(pkKeyChain.network).to.equal(network); - expect(pkKeyChain.keys).to.deep.equal({}); - expect(pkKeyChain.type).to.equal('privateKey'); - expect(pkKeyChain.privateKey).to.equal(privateKey); + expect(pkKeyChain.rootKeyType).to.equal('privateKey'); + expect(pkKeyChain.rootKey.toString()).to.equal(privateKey); - const expectedException1 = 'Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate keys'; - const expectedException2 = 'Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate child'; - expect(() => pkKeyChain.generateKeyForPath()).to.throw(expectedException1); - expect(() => pkKeyChain.generateKeyForChild()).to.throw(expectedException2); + const expectedException1 = 'Wallet is not loaded from a mnemonic or a HDPrivateKey, impossible to derivate keys for path m/0'; + expect(() => pkKeyChain.getForPath('m/0')).to.throw(expectedException1); }); it('should get private key', () => { const privateKey = Dashcore.PrivateKey().toString(); const pkKeyChain = new KeyChain({ privateKey, network: 'livenet' }); - expect(pkKeyChain.getPrivateKey().toString()).to.equal(privateKey); + expect(pkKeyChain.getRootKey().toString()).to.equal(privateKey); + expect(pkKeyChain.rootKey.toString()).to.equal(privateKey); }); }); diff --git a/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForChild.js b/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForChild.js deleted file mode 100644 index 5d6dcaeba0f..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForChild.js +++ /dev/null @@ -1,19 +0,0 @@ -const { - HDPublicKey, -} = require('@dashevo/dashcore-lib'); -/** - * Derive from HDPrivateKey to a child - * @param {number} index - Child index to derivee to - * @param {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys - * @return {HDPrivateKey|HDPublicKey} - */ -function generateKeyForChild(index, type = 'HDPrivateKey') { - if (!['HDPrivateKey', 'HDPublicKey'].includes(this.type)) { - throw new Error('Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate child'); - } - const HDKey = this[this.type]; - const hdPublicKey = HDKey.deriveChild(index); - if (type === 'HDPublicKey') return HDPublicKey(hdPublicKey); - return hdPublicKey; -} -module.exports = generateKeyForChild; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForPath.js b/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForPath.js deleted file mode 100644 index e266c96d757..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForPath.js +++ /dev/null @@ -1,19 +0,0 @@ -const { - HDPublicKey, -} = require('@dashevo/dashcore-lib'); -/** - * Derive from HDPrivateKey to a specific path - * @param {string} path - * @param {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys - * @return {HDPrivateKey|HDPublicKey} - */ -function generateKeyForPath(path, type = 'HDPrivateKey') { - if (!['HDPrivateKey', 'HDPublicKey'].includes(this.type)) { - throw new Error('Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate keys'); - } - const HDKey = this[this.type]; - const hdPrivateKey = HDKey.derive(path); - if (type === 'HDPublicKey') return HDPublicKey(hdPrivateKey); - return hdPrivateKey; -} -module.exports = generateKeyForPath; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getDIP15ExtendedKey.js b/packages/wallet-lib/src/types/KeyChain/methods/getDIP15ExtendedKey.js index a7eed1547dc..b65dc10d7da 100644 --- a/packages/wallet-lib/src/types/KeyChain/methods/getDIP15ExtendedKey.js +++ b/packages/wallet-lib/src/types/KeyChain/methods/getDIP15ExtendedKey.js @@ -8,7 +8,7 @@ * @return {HDPrivateKey|HDPublicKey} */ function getDIP15ExtendedKey(userUniqueId, contactUniqueId, index = 0, accountIndex = 0, type = 'HDPrivateKey') { - if (!['HDPrivateKey', 'HDPublicKey'].includes(this.type)) { + if (!['HDPrivateKey', 'HDPublicKey'].includes(this.rootKeyType)) { throw new Error('Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate keys'); } if (!userUniqueId || !contactUniqueId) throw new Error('Required userUniqueId and contactUniqueId to be defined'); diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getFirstUnusedAddress.js b/packages/wallet-lib/src/types/KeyChain/methods/getFirstUnusedAddress.js new file mode 100644 index 00000000000..cb72d60c493 --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChain/methods/getFirstUnusedAddress.js @@ -0,0 +1,14 @@ +function getFirstUnusedAddress() { + const allUnused = this.getIssuedPaths() + .filter((path)=>{ + return path.isUsed === false + }); + + const firstUnused = allUnused.slice(0,1)[0] + + return { + path: firstUnused.path, + address: firstUnused.address.toString() + } +} +module.exports = getFirstUnusedAddress; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getForAddress.js b/packages/wallet-lib/src/types/KeyChain/methods/getForAddress.js new file mode 100644 index 00000000000..4b8a01ddf04 --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChain/methods/getForAddress.js @@ -0,0 +1,12 @@ +function getForAddress(address) { + const searchResult = [...this.issuedPaths.entries()] + .find(([, el]) => el.address.toString() === address.toString()); + + if (!searchResult) { + return null; + } + const [path] = searchResult; + return this.getForPath(path); +} + +module.exports = getForAddress; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getForPath.js b/packages/wallet-lib/src/types/KeyChain/methods/getForPath.js new file mode 100644 index 00000000000..1388abdf05f --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChain/methods/getForPath.js @@ -0,0 +1,42 @@ +const logger = require('../../../logger'); + +function getForPath(path, opts = {}) { + if (path === undefined) throw new Error('Expect a valid path to derivate'); + const stringifiedPath = path.toString(); + logger.silly(`KeyChain.getForPath(${stringifiedPath})`); + const isUsed = (opts && opts.isUsed !== undefined) ? opts.isUsed : false; + const isWatched = (opts && opts.isWatched !== undefined) ? opts.isWatched : false; + const isDerivable = ['HDPrivateKey', 'HDPublicKey'].includes(this.rootKeyType); + if (!isDerivable && stringifiedPath !== '0') { + throw new Error(`Wallet is not loaded from a mnemonic or a HDPrivateKey, impossible to derivate keys for path ${stringifiedPath}`); + } + + let data; + if (this.issuedPaths.has(stringifiedPath)) { + data = this.issuedPaths.get(stringifiedPath); + if (opts && opts.isWatched !== undefined && data.isWatched !== opts.isWatched) { + data.isWatched = opts.isWatched; + } + if (opts && opts.isUsed !== undefined && data.isUsed !== opts.isUsed) { + data.isUsed = opts.isUsed; + } + return data; + } + + const key = (isDerivable) ? this.rootKey.derive(stringifiedPath) : this.getRootKey(); + + data = { + path: stringifiedPath, + key, + isUsed, + isWatched, + address: key.publicKey.toAddress(this.network), + issuedTime: +new Date(), + }; + + this.issuedPaths.set(stringifiedPath, data); + + return data; +} + +module.exports = getForPath; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedBIP44HDKey.js b/packages/wallet-lib/src/types/KeyChain/methods/getHardenedBIP44HDKey.js index 75a6628475f..48da61c1616 100644 --- a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedBIP44HDKey.js +++ b/packages/wallet-lib/src/types/KeyChain/methods/getHardenedBIP44HDKey.js @@ -2,11 +2,10 @@ const { BIP44_TESTNET_ROOT_PATH, BIP44_LIVENET_ROOT_PATH } = require('../../../C /** * Return a safier root keys to derivate from - * @param {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys * @return {HDPrivateKey|HDPublicKey} */ -function getHardenedBIP44HDKey(type = 'HDPrivateKey') { +function getHardenedBIP44HDKey() { const pathRoot = (this.network.toString() === 'testnet') ? BIP44_TESTNET_ROOT_PATH : BIP44_LIVENET_ROOT_PATH; - return this.generateKeyForPath(pathRoot, type); + return this.getForPath(pathRoot).key; } module.exports = getHardenedBIP44HDKey; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP9FeatureHDKey.js b/packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP9FeatureHDKey.js index 06a4733afb0..422c9fab667 100644 --- a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP9FeatureHDKey.js +++ b/packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP9FeatureHDKey.js @@ -2,11 +2,10 @@ const { DIP9_LIVENET_ROOT_PATH, DIP9_TESTNET_ROOT_PATH } = require('../../../CON /** * Return a safier root path to derivate from - * @param {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys * @return {HDPrivateKey|HDPublicKey} */ -function getHardenedDIP9FeatureHDKey(type = 'HDPrivateKey') { +function getHardenedDIP9FeatureHDKey() { const pathRoot = (this.network.toString() === 'testnet') ? DIP9_TESTNET_ROOT_PATH : DIP9_LIVENET_ROOT_PATH; - return this.generateKeyForPath(pathRoot, type); + return this.getForPath(pathRoot).key; } module.exports = getHardenedDIP9FeatureHDKey; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getIssuedPaths.js b/packages/wallet-lib/src/types/KeyChain/methods/getIssuedPaths.js new file mode 100644 index 00000000000..324750853ba --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChain/methods/getIssuedPaths.js @@ -0,0 +1,5 @@ +function getWatchedAddresses() { + return [...this.issuedPaths.values()]; +} + +module.exports = getWatchedAddresses; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getKeyForChild.js b/packages/wallet-lib/src/types/KeyChain/methods/getKeyForChild.js deleted file mode 100644 index 768f1740d55..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/getKeyForChild.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Generate a key by deriving it's direct child - * @param index - {Number} - * @return {HDPrivateKey | HDPublicKey} - */ -function getKeyForChild(index = 0, type = 'HDPrivateKey') { - return this.generateKeyForChild(index, type); -} - -module.exports = getKeyForChild; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getKeyForPath.js b/packages/wallet-lib/src/types/KeyChain/methods/getKeyForPath.js deleted file mode 100644 index 4f7bb180d1d..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/getKeyForPath.js +++ /dev/null @@ -1,29 +0,0 @@ -const { HDPrivateKey, PrivateKey } = require('@dashevo/dashcore-lib'); -/** - * Get a key from the cache or generate if none - * @param path - * @param type - def : HDPrivateKey - Expected return datatype of the keys - * @return {HDPrivateKey | HDPublicKey} - */ -function getKeyForPath(path, type = 'HDPrivateKey') { - if (type === 'HDPublicKey') { - // In this case, we do not generate or keep in cache. - return this.generateKeyForPath(path, type); - } - - if (this.type === 'HDPrivateKey') { - if (!this.keys[path]) { - this.keys[path] = this.generateKeyForPath(path, type).toString(); - } - return new HDPrivateKey(this.keys[path]); - } - if (this.type === 'privateKey') { - if (!this.keys[path]) { - this.keys[path] = this.getPrivateKey(path).toString(); - return new PrivateKey(this.keys[path]); - } - return new PrivateKey(this.keys[path]); - } - return new HDPrivateKey(this.keys[path]); -} -module.exports = getKeyForPath; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getPrivateKey.js b/packages/wallet-lib/src/types/KeyChain/methods/getPrivateKey.js deleted file mode 100644 index 602c287daf7..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/getPrivateKey.js +++ /dev/null @@ -1,18 +0,0 @@ -const { - PrivateKey, -} = require('@dashevo/dashcore-lib'); - -/** - * @return {PrivateKey} - */ -function getPrivateKey() { - let pk; - if (this.type === 'HDPrivateKey') { - pk = PrivateKey(this.HDPrivateKey.privateKey); - } - if (this.type === 'privateKey') { - pk = PrivateKey(this.privateKey); - } - return pk; -} -module.exports = getPrivateKey; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getRootKey.js b/packages/wallet-lib/src/types/KeyChain/methods/getRootKey.js new file mode 100644 index 00000000000..f2b54723a48 --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChain/methods/getRootKey.js @@ -0,0 +1,5 @@ +function getRootKey() { + return this.rootKey; +} + +module.exports = getRootKey; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getWatchedAddresses.js b/packages/wallet-lib/src/types/KeyChain/methods/getWatchedAddresses.js new file mode 100644 index 00000000000..8154329119a --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChain/methods/getWatchedAddresses.js @@ -0,0 +1,7 @@ +function getWatchedAddresses() { + return [...this.issuedPaths.entries()] + .filter(([, el]) => el.isWatched === true) + .map(([, el]) => el.address.toString()); +} + +module.exports = getWatchedAddresses; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/markAddressAsUsed.js b/packages/wallet-lib/src/types/KeyChain/methods/markAddressAsUsed.js new file mode 100644 index 00000000000..9df149d7dd4 --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChain/methods/markAddressAsUsed.js @@ -0,0 +1,18 @@ +const logger = require('../../../logger'); + +function markAddressAsUsed(address) { + + const searchResult = [...this.issuedPaths.entries()] + .find(([, el]) => el.address.toString() === address.toString()); + + if (searchResult) { + + const [, addressData] = searchResult; + logger.silly(`KeyChain - Marking ${address} ${addressData.path} as used`); + addressData.isUsed = true; + + return this.maybeLookAhead(); + } + +} +module.exports = markAddressAsUsed; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/maybeLookAhead.js b/packages/wallet-lib/src/types/KeyChain/methods/maybeLookAhead.js new file mode 100644 index 00000000000..e84db9fd842 --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChain/methods/maybeLookAhead.js @@ -0,0 +1,84 @@ +function maybeLookAhead() { + const { lookAheadOpts } = this; + const generatedPaths = []; + + if (Object.keys(lookAheadOpts.paths).length === 0) { + return generatedPaths; + } + + const usedPaths = [...this.issuedPaths.entries()] + .filter(([, el]) => el.isUsed === true) + .map(([path]) => path); + + const sortedUsedPathByBase = {}; + + usedPaths + .forEach((usedPath) => { + const splitted = usedPath.split('/'); + // Removes the index to sort which and how many base path has been generated + const basePath = splitted.splice(0, splitted.length - 1).join('/'); + if (!sortedUsedPathByBase[basePath]) sortedUsedPathByBase[basePath] = []; + sortedUsedPathByBase[basePath].push(usedPath); + }); + + const lastUsedIndexes = {}; + const lastGeneratedIndexes = {}; + + Object + .entries(lookAheadOpts.paths) + .forEach(([basePath]) => { + lastUsedIndexes[basePath] = -1; + lastGeneratedIndexes[basePath] = -1; + }); + + Object + .entries(sortedUsedPathByBase) + .forEach(([basePath, basePaths]) => { + // Sorting by index is also needed as the user might have manually issue a key + // and set it up to watched or used outside of lookAhead bounds + const sortedBasePaths = basePaths.sort((a, b) => a.split('/').splice(-1) - b.split('/').splice(-1)); + + let prevIndex; + sortedBasePaths.forEach((path) => { + const addressData = this.issuedPaths.get(path); + + const currentIndex = parseInt(path.split('/').splice(-1), 10); + + if (addressData.isUsed) { + lastUsedIndexes[basePath] = currentIndex; + } + + lastGeneratedIndexes[basePath] = currentIndex; + prevIndex = currentIndex; + }); + }); + + const isWatched = lookAheadOpts.isWatched || false; + + Object + .entries(lastGeneratedIndexes) + .forEach(([basePath]) => { + const lastUsedAndLastGenGap = lastGeneratedIndexes[basePath] - lastUsedIndexes[basePath]; + const pathAmountToGenerate = lookAheadOpts.paths[basePath] - lastUsedAndLastGenGap; + + if (pathAmountToGenerate > 0) { + const lastIndex = lastGeneratedIndexes[basePath]; + const lastIndexToGenerate = lastIndex + pathAmountToGenerate; + + if (lastIndexToGenerate > lastIndex) { + for ( + let index = lastIndex + 1; + index <= lastIndexToGenerate; + index += 1) { + const timeNow = +new Date(); + const pathData = this.getForPath(`${basePath}/${index}`, { isWatched }); + if (pathData.issuedTime >= timeNow) { + generatedPaths.push(pathData); + } + } + } + } + }); + return generatedPaths; +} +module.exports = maybeLookAhead; diff --git a/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.js b/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.js new file mode 100644 index 00000000000..fd5c24dfacb --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.js @@ -0,0 +1,16 @@ +class KeyChainStore { + constructor() { + this.keyChains = new Map(); + this.walletKeyChainId = null; + this.masterKeyChainId = null; + this.accountKeyChains = new Map(); + } +} + +KeyChainStore.prototype.addKeyChain = require('./methods/addKeyChain'); +KeyChainStore.prototype.getKeyChain = require('./methods/getKeyChain'); +KeyChainStore.prototype.getKeyChains = require('./methods/getKeyChains'); +KeyChainStore.prototype.makeChildKeyChainStore = require('./methods/makeChildKeyChainStore'); +KeyChainStore.prototype.getMasterKeyChain = require('./methods/getMasterKeyChain'); + +module.exports = KeyChainStore; diff --git a/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.spec.js b/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.spec.js new file mode 100644 index 00000000000..b32737bb7a2 --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.spec.js @@ -0,0 +1,47 @@ +const {HDPrivateKey} = require("@dashevo/dashcore-lib"); +const KeyChainsStore = require('./KeyChainStore'); +const KeyChain = require("../KeyChain/KeyChain"); +const { expect } = require('chai'); + +describe('KeyChainStore', function suite() { + let keyChainsStore; + let hdPrivateKey = new HDPrivateKey() + let hdPublicKey = new HDPrivateKey().hdPublicKey + let keyChain = new KeyChain({HDPrivateKey: hdPrivateKey}) + let keyChainPublic = new KeyChain({HDPublicKey: hdPublicKey}) + let walletKeyChain = new KeyChain({HDPrivateKey:new HDPrivateKey()}); + it('should create a KeyChainStore', () => { + keyChainsStore = new KeyChainsStore(); + expect(keyChainsStore).to.exist; + expect(keyChainsStore.keyChains).to.be.a('Map') + }); + it('should be able to add a keyChain', function () { + keyChainsStore.addKeyChain(keyChain) + expect(keyChainsStore.keyChains.has(keyChain.keyChainId)).to.equal(true); + keyChainsStore.addKeyChain(keyChainPublic) + expect(keyChainsStore.keyChains.has(keyChainPublic.keyChainId)).to.equal(true); + }); + it('should allow to specify a specific master keychain', function () { + keyChainsStore.addKeyChain(walletKeyChain, { isMasterKeyChain: true }); + expect(keyChainsStore.keyChains.has(walletKeyChain.keyChainId)).to.equal(true); + }); + it('should get all keyChains', function () { + const keyChains = keyChainsStore.getKeyChains() + expect(keyChains).to.deep.equal([keyChain, keyChainPublic, walletKeyChain]); + }); + it('should get a keychain by its ID', () => { + const requestedKeychain = keyChainsStore.getKeyChain(keyChainPublic.keyChainId); + expect(requestedKeychain).to.equal(keyChainPublic); + }) + it('should get a master keychain', function () { + const requestedWalletKeyChain = keyChainsStore.getMasterKeyChain(); + expect(requestedWalletKeyChain).to.equal(walletKeyChain); + }); + it('should make a child key chain store', function () { + const childKeyChainStore = keyChainsStore.makeChildKeyChainStore('m/0') + expect(childKeyChainStore).to.exist; + expect(childKeyChainStore.keyChains).to.be.a('Map') + expect(childKeyChainStore.getMasterKeyChain().rootKeyType).to.be.equal(HDPrivateKey.name) + }); +}); + diff --git a/packages/wallet-lib/src/types/KeyChainStore/methods/addKeyChain.js b/packages/wallet-lib/src/types/KeyChainStore/methods/addKeyChain.js new file mode 100644 index 00000000000..04e60a50739 --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChainStore/methods/addKeyChain.js @@ -0,0 +1,15 @@ +function addKeyChain(keychain, opts = {}) { + if (this.keyChains.has(keychain.keyChainId)) { + throw new Error(`Trying to add already existing keyChain ${keychain.keyChainId}`); + } + + this.keyChains.set(keychain.keyChainId, keychain); + + if (opts) { + if (opts.isMasterKeyChain && !this.masterKeyChainId) { + this.masterKeyChainId = keychain.keyChainId; + } + } +} + +module.exports = addKeyChain; diff --git a/packages/wallet-lib/src/types/KeyChainStore/methods/getKeyChain.js b/packages/wallet-lib/src/types/KeyChainStore/methods/getKeyChain.js new file mode 100644 index 00000000000..d964ba395ee --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChainStore/methods/getKeyChain.js @@ -0,0 +1,5 @@ +function getKeyChain(keyChainId) { + return this.keyChains.get(keyChainId); +} + +module.exports = getKeyChain; diff --git a/packages/wallet-lib/src/types/KeyChainStore/methods/getKeyChains.js b/packages/wallet-lib/src/types/KeyChainStore/methods/getKeyChains.js new file mode 100644 index 00000000000..4e0e16160cd --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChainStore/methods/getKeyChains.js @@ -0,0 +1,5 @@ +function getKeyChains() { + return Array.from(this.keyChains.values()); +} + +module.exports = getKeyChains; diff --git a/packages/wallet-lib/src/types/KeyChainStore/methods/getMasterKeyChain.js b/packages/wallet-lib/src/types/KeyChainStore/methods/getMasterKeyChain.js new file mode 100644 index 00000000000..512d6a28133 --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChainStore/methods/getMasterKeyChain.js @@ -0,0 +1,6 @@ +function getMasterKeyChain() { + const keyChainId = this.masterKeyChainId; + return this.keyChains.get(keyChainId); +} + +module.exports = getMasterKeyChain; diff --git a/packages/wallet-lib/src/types/KeyChainStore/methods/makeChildKeyChainStore.js b/packages/wallet-lib/src/types/KeyChainStore/methods/makeChildKeyChainStore.js new file mode 100644 index 00000000000..b341184f90d --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChainStore/methods/makeChildKeyChainStore.js @@ -0,0 +1,21 @@ +const { Networks } = require('@dashevo/dashcore-lib'); +const { BIP44_LIVENET_ROOT_PATH, BIP44_TESTNET_ROOT_PATH } = require('../../../CONSTANTS'); +const KeyChain = require('../../KeyChain/KeyChain'); +const logger = require('../../../logger'); + +function makeChildKeyChainStore(path, opts) { + logger.debug(`KeyChainStore - make a child keychainstore for ${path}`); + const masterKeyChain = this.getMasterKeyChain(); + if (!masterKeyChain) throw new Error('Requires a master keychain to be added first.'); + + const childKeyChainStore = new this.constructor(); + const keyChainOpts = { network: masterKeyChain.network, ...opts }; + + // Accessing the type from getKeyForPath would behave on browser differently due to mangling. + keyChainOpts[masterKeyChain.rootKeyType] = masterKeyChain.getForPath(path).key; + const childKeyChain = new KeyChain(keyChainOpts); + childKeyChainStore.addKeyChain(childKeyChain, { isMasterKeyChain: true }); + return childKeyChainStore; +} + +module.exports = makeChildKeyChainStore; diff --git a/packages/wallet-lib/src/types/Wallet/Wallet.spec.js b/packages/wallet-lib/src/types/Wallet/Wallet.spec.js index 5f4a4368e20..a6d416bfbee 100644 --- a/packages/wallet-lib/src/types/Wallet/Wallet.spec.js +++ b/packages/wallet-lib/src/types/Wallet/Wallet.spec.js @@ -23,7 +23,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); - expect(wallet1.keyChain.type).to.be.deep.equal('HDPrivateKey'); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('HDPrivateKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -50,7 +50,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet1.keyChain.type).to.be.deep.equal('HDPrivateKey'); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('HDPrivateKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -77,7 +77,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet1.keyChain.type).to.be.deep.equal('HDPrivateKey'); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('HDPrivateKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -95,7 +95,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet1.keyChain.type).to.be.deep.equal('HDPublicKey'); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('HDPublicKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -112,7 +112,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet1.keyChain.type).to.be.deep.equal('privateKey'); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('privateKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -132,7 +132,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet1.keyChain.type).to.be.deep.equal('publicKey'); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('publicKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -149,7 +149,7 @@ describe('Wallet - class', function suite() { expect(wallet2.plugins).to.be.deep.equal({}); expect(wallet2.accounts).to.be.deep.equal([]); expect(wallet2.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet2.keyChain.type).to.be.deep.equal('publicKey'); + expect(wallet2.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('publicKey'); expect(wallet2.passphrase).to.be.deep.equal(null); expect(wallet2.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet2.injectDefaultPlugins).to.be.deep.equal(true); @@ -170,7 +170,7 @@ describe('Wallet - class', function suite() { }); }); describe('Wallet - Get/Create Account', function suite() { - this.timeout(15000); + this.timeout(10000); const wallet1 = new Wallet({ mnemonic: fluidMnemonic.mnemonic, ...mocks }); it('should be able to create/get a wallet', async () => { @@ -246,6 +246,5 @@ describe('Wallet - Get/Create Account', function suite() { it('should not leak', () => { const mockOpts1 = { }; fromHDPublicKey.call(mockOpts1, gatherSail.testnet.external.hdpubkey); - expect(mockOpts1.keyChain.keys).to.deep.equal({}); }); }); diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromAddress.js b/packages/wallet-lib/src/types/Wallet/methods/fromAddress.js index c69c7749b9d..2397a91aab9 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromAddress.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromAddress.js @@ -1,14 +1,18 @@ const { is } = require('../../../utils'); const KeyChain = require('../../KeyChain/KeyChain'); const { WALLET_TYPES } = require('../../../CONSTANTS'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); /** * @param address */ -module.exports = function fromAddress(address) { +module.exports = function fromAddress(address, network) { if (!is.address(address)) throw new Error('Expected a valid address (typeof Address or String)'); this.walletType = WALLET_TYPES.ADDRESS; this.mnemonic = null; this.address = address.toString(); - this.keyChain = new KeyChain({ address }); + + const keyChain = new KeyChain({ address, network }); + this.keyChainStore = new KeyChainStore(); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromAddress.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromAddress.spec.js index 44bd996a4d2..f0839308fc9 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromAddress.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromAddress.spec.js @@ -18,18 +18,20 @@ describe('Wallet - fromAddress', function suite() { expect(self1.walletType).to.equal(WALLET_TYPES.ADDRESS); expect(self1.mnemonic).to.equal(null); expect(self1.address).to.equal(cR4t6ePublicKey.toAddress().toString()); - expect(self1.keyChain.type).to.equal('address'); - expect(self1.keyChain.address).to.equal(cR4t6ePublicKey.toAddress().toString()); - expect(self1.keyChain.keys).to.deep.equal({}); + + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('address'); + expect(keyChain.rootKey).to.equal(cR4t6ePublicKey.toAddress().toString()); const self2 = {}; fromAddress.call(self2, cR4t6ePublicKey.toAddress().toString()); expect(self2.walletType).to.equal(WALLET_TYPES.ADDRESS); expect(self2.mnemonic).to.equal(null); expect(self2.address).to.equal(cR4t6ePublicKey.toAddress().toString()); - expect(self2.keyChain.type).to.equal('address'); - expect(self2.keyChain.address).to.equal(cR4t6ePublicKey.toAddress().toString()); - expect(self2.keyChain.keys).to.deep.equal({}); + + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('address'); + expect(keyChain.rootKey).to.equal(cR4t6ePublicKey.toAddress().toString()); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.js b/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.js index da2aba6e047..90b6f1a9061 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.js @@ -3,6 +3,7 @@ const { is, } = require('../../../utils'); const KeyChain = require('../../KeyChain/KeyChain'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); const { WALLET_TYPES } = require('../../../CONSTANTS'); /** @@ -14,5 +15,8 @@ module.exports = function fromHDPrivateKey(hdPrivateKey) { this.walletType = WALLET_TYPES.HDWALLET; this.mnemonic = null; this.HDPrivateKey = HDPrivateKey(hdPrivateKey); - this.keyChain = new KeyChain({ HDPrivateKey: hdPrivateKey }); + + const keyChain = new KeyChain({ HDPrivateKey: this.HDPrivateKey }); + this.keyChainStore = new KeyChainStore(); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.spec.js index 5c277483190..c947a076b4c 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.spec.js @@ -16,9 +16,10 @@ describe('Wallet - fromHDPrivateKey', function suite() { expect(self1.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self1.mnemonic).to.equal(null); expect(self1.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); - expect(self1.keyChain.type).to.equal('HDPrivateKey'); - expect(self1.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); - expect(self1.keyChain.keys).to.deep.equal({}); + + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.js b/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.js index 0631c051151..aa6a84078a1 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.js @@ -2,6 +2,7 @@ const Dashcore = require('@dashevo/dashcore-lib'); const { is } = require('../../../utils'); const KeyChain = require('../../KeyChain/KeyChain'); const { WALLET_TYPES } = require('../../../CONSTANTS'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); const normalizeHDPubKey = (key) => (is.string(key) ? Dashcore.HDPublicKey(key) : key); /** @@ -13,5 +14,8 @@ module.exports = function fromHDPublicKey(_hdPublicKey) { this.walletType = WALLET_TYPES.HDPUBLIC; this.mnemonic = null; this.HDPublicKey = normalizeHDPubKey(_hdPublicKey); - this.keyChain = new KeyChain({ HDPublicKey: this.HDPublicKey }); + + const keyChain = new KeyChain({ HDPublicKey: this.HDPublicKey }); + this.keyChainStore = new KeyChainStore(); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.spec.js index 96a1c9cf847..fca3387d34e 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.spec.js @@ -30,9 +30,10 @@ describe('Wallet - HDPublicKey', function suite() { expect(mockOpts1.mnemonic).to.equal(null); expect(mockOpts1.HDPublicKey.toString()).to.equal(gatherTestnet.external.hdpubkey); expect(new Dashcore.HDPublicKey(mockOpts1.HDPublicKey)).to.equal(mockOpts1.HDPublicKey); - expect(mockOpts1.keyChain.type).to.equal('HDPublicKey'); - expect(mockOpts1.keyChain.HDPublicKey).to.deep.equal(Dashcore.HDPublicKey(gatherTestnet.external.hdpubkey)); - expect(mockOpts1.keyChain.keys).to.deep.equal({}); + + const keyChain = mockOpts1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('HDPublicKey'); + expect(keyChain.rootKey).to.deep.equal(Dashcore.HDPublicKey(gatherTestnet.external.hdpubkey)); }); it('should work from a HDPubKey', () => { const wallet1 = new Wallet( @@ -45,7 +46,9 @@ describe('Wallet - HDPublicKey', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet1.keyChain.type).to.be.deep.equal('HDPublicKey'); + + const keyChain = wallet1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.be.deep.equal('HDPublicKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -54,22 +57,28 @@ describe('Wallet - HDPublicKey', function suite() { expect(wallet1.exportWallet()).to.be.equal(gatherTestnet.external.hdpubkey); - wallet1.getAccount().then((account)=>{ - const unusedAddress = account.getUnusedAddress(); - const expectedUnused = { - path: "m/44'/1'/0'/0/0", - index: 0, - address: 'yNJ3xxTXXBBf39VfMBbBuLH2k57uAwxBxj', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false, - }; - expect(unusedAddress).to.deep.equal(expectedUnused); + // FIXME: it appears we had introduced a bug here, + // as it is not possible to have a HDPublicKey derivation with hardened + // Either our path is m/44/1/0/0/0 or it is m/0/0. + // We should clarify this before merging TODO + wallet1 + .getAccount() + .then((account)=>{ + const unusedAddress = account.getUnusedAddress(); + const expectedUnused = { + path: "m/0/0", + index: 0, + address: 'yNJ3xxTXXBBf39VfMBbBuLH2k57uAwxBxj', + transactions: [], + balanceSat: 0, + unconfirmedBalanceSat: 0, + utxos: {}, + fetchedLast: 0, + used: false, + }; + expect(unusedAddress).to.deep.equal(expectedUnused); - wallet1.disconnect(); - }) + wallet1.disconnect(); + }) }); }); diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.js b/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.js index 4bdeaefefa8..1e2cbdda7a3 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.js @@ -3,19 +3,25 @@ const { is, } = require('../../../utils'); const KeyChain = require('../../KeyChain/KeyChain'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); const { WALLET_TYPES } = require('../../../CONSTANTS'); /** * Will set a wallet to work with a mnemonic (keychain, walletType & HDPrivateKey) * @param mnemonic */ -module.exports = function fromMnemonic(mnemonic) { +module.exports = function fromMnemonic(mnemonic, network, passphrase = '') { if (!is.mnemonic(mnemonic)) { throw new Error('Expected a valid mnemonic (typeof String or Mnemonic)'); } const trimmedMnemonic = mnemonic.toString().trim(); this.walletType = WALLET_TYPES.HDWALLET; - this.mnemonic = trimmedMnemonic; // todo : What about without this ? - this.HDPrivateKey = mnemonicToHDPrivateKey(trimmedMnemonic, this.network, this.passphrase); - this.keyChain = new KeyChain({ HDPrivateKey: this.HDPrivateKey }); + // As we do not require the mnemonic except in this.exportWallet + // users of wallet-lib are free to clear this prop at anytime. + this.mnemonic = trimmedMnemonic; + this.HDPrivateKey = mnemonicToHDPrivateKey(trimmedMnemonic, network, passphrase); + + this.keyChainStore = new KeyChainStore(); + const keyChain = new KeyChain({ HDPrivateKey: this.HDPrivateKey }); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.spec.js index 14e81028618..85f498c6172 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.spec.js @@ -15,27 +15,29 @@ describe('Wallet - fromMnemonic', function suite() { const self1 = { network: 'livenet', }; - fromMnemonic.call(self1, knifeFixture.mnemonic); + fromMnemonic.call(self1, knifeFixture.mnemonic, 'livenet'); expect(self1.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self1.mnemonic).to.equal(knifeFixture.mnemonic); expect(self1.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); expect(new Dashcore.HDPrivateKey(self1.HDPrivateKey)).to.equal(self1.HDPrivateKey); - expect(self1.keyChain.type).to.equal('HDPrivateKey'); - expect(self1.keyChain.network.name).to.equal('livenet'); - expect(self1.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); - expect(self1.keyChain.keys).to.deep.equal({}); + + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain.network).to.equal('livenet'); + expect(keyChain.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); const self2 = {}; fromMnemonic.call(self2, knifeFixture.mnemonic); expect(self2.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self2.mnemonic).to.equal(knifeFixture.mnemonic); - expect(self2.keyChain.network.name).to.equal('testnet'); + + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain2.network).to.equal('testnet'); expect(self2.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); expect(new Dashcore.HDPrivateKey(self2.HDPrivateKey)).to.equal(self2.HDPrivateKey); - expect(self2.keyChain.type).to.equal('HDPrivateKey'); - expect(self2.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); - expect(self2.keyChain.keys).to.deep.equal({}); + expect(keyChain2.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain2.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ @@ -55,38 +57,35 @@ describe('Wallet - fromMnemonic - with passphrase', function suite() { this.timeout(10000); it('should correctly works with passphrase', () => { const self1 = { - network: 'livenet', - passphrase: knifeFixture.passphrase, }; - fromMnemonic.call(self1, knifeFixture.mnemonic); + fromMnemonic.call(self1, knifeFixture.mnemonic, 'livenet', knifeFixture.passphrase); expect(self1.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self1.mnemonic).to.equal(knifeFixture.mnemonic); expect(self1.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyMainnet); expect(new Dashcore.HDPrivateKey(self1.HDPrivateKey)).to.equal(self1.HDPrivateKey); - expect(self1.keyChain.type).to.equal('HDPrivateKey'); - expect(self1.keyChain.network.name).to.equal('livenet'); - expect(self1.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyMainnet); - expect(self1.keyChain.keys).to.deep.equal({}); + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain.network).to.equal('livenet'); + expect(keyChain.rootKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyMainnet); const path1 = 'm/44\'/5\'/0\'/0/0'; - const pubKey1 = self1.keyChain.getKeyForPath(path1).publicKey.toAddress(); + const pubKey1 = keyChain.getForPath(path1).key.publicKey.toAddress(); expect(new Dashcore.Address(pubKey1).toString()).to.equal('Xq3zjky18WjwAHpLgGLasvX5g8TeLRKaxt'); const self2 = { - passphrase: knifeFixture.passphrase, }; - fromMnemonic.call(self2, knifeFixture.mnemonic); + fromMnemonic.call(self2, knifeFixture.mnemonic, 'testnet', knifeFixture.passphrase); expect(self2.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self2.mnemonic).to.equal(knifeFixture.mnemonic); expect(self2.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyTestnet); expect(new Dashcore.HDPrivateKey(self2.HDPrivateKey)).to.equal(self2.HDPrivateKey); - expect(self2.keyChain.type).to.equal('HDPrivateKey'); - expect(self2.keyChain.network.name).to.equal('testnet'); - expect(self2.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyTestnet); - expect(self2.keyChain.keys).to.deep.equal({}); + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain2.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain2.network).to.equal('testnet'); + expect(keyChain2.rootKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyTestnet); const path2 = 'm/44\'/1\'/0\'/0/0'; - const pubKey2 = self2.keyChain.getKeyForPath(path2).publicKey.toAddress(); + const pubKey2 = keyChain2.getForPath(path2).key.publicKey.toAddress(); expect(new Dashcore.Address(pubKey2, 'testnet').toString()).to.equal('yWYCH9XDRnpdNxh67jQJFkovToBVwWr8Ck'); }); }); diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.js b/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.js index 2f8f919bc7e..870af9937d8 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.js @@ -1,15 +1,19 @@ const { is } = require('../../../utils'); const KeyChain = require('../../KeyChain/KeyChain'); const { WALLET_TYPES } = require('../../../CONSTANTS'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); /** * Will set a wallet to work with a mnemonic (keychain, walletType & HDPrivateKey) * @param privateKey */ -module.exports = function fromPrivateKey(privateKey) { +module.exports = function fromPrivateKey(privateKey, network) { if (!is.privateKey(privateKey)) throw new Error('Expected a valid private key (typeof PrivateKey or String)'); this.walletType = WALLET_TYPES.PRIVATEKEY; this.mnemonic = null; this.privateKey = privateKey; - this.keyChain = new KeyChain({ privateKey }); + + const keyChain = new KeyChain({ privateKey, network }); + this.keyChainStore = new KeyChainStore(); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.spec.js index c707c2faddc..4b6ca5e89ab 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.spec.js @@ -16,18 +16,18 @@ describe('Wallet - fromPrivateKey', function suite() { expect(self1.walletType).to.equal(WALLET_TYPES.PRIVATEKEY); expect(self1.mnemonic).to.equal(null); expect(self1.privateKey).to.equal(cR4t6eFixture.privateKey); - expect(self1.keyChain.type).to.equal('privateKey'); - expect(self1.keyChain.privateKey).to.equal(cR4t6eFixture.privateKey); - expect(self1.keyChain.keys).to.deep.equal({}); + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('privateKey'); + expect(keyChain.rootKey.toWIF()).to.equal(cR4t6eFixture.privateKey); const self2 = {}; fromPrivateKey.call(self2, cR4t6eFixture.privateKey); expect(self2.walletType).to.equal(WALLET_TYPES.PRIVATEKEY); expect(self2.mnemonic).to.equal(null); expect(self2.privateKey).to.equal(cR4t6eFixture.privateKey); - expect(self2.keyChain.type).to.equal('privateKey'); - expect(self2.keyChain.privateKey).to.equal(cR4t6eFixture.privateKey); - expect(self2.keyChain.keys).to.deep.equal({}); + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain2.rootKeyType).to.equal('privateKey'); + expect(keyChain2.rootKey.toWIF()).to.equal(cR4t6eFixture.privateKey); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.js b/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.js index 9e6867ac78f..ffbf41344c9 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.js @@ -1,15 +1,19 @@ const { is } = require('../../../utils'); const KeyChain = require('../../KeyChain/KeyChain'); const { WALLET_TYPES } = require('../../../CONSTANTS'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); /** * Will set a wallet to work with a mnemonic (keychain, walletType & HDPrivateKey) * @param privateKey */ -module.exports = function fromPublicKey(publicKey) { +module.exports = function fromPublicKey(publicKey, network) { if (!is.publicKey(publicKey)) throw new Error('Expected a valid public key (typeof PublicKey or String)'); this.walletType = WALLET_TYPES.PUBLICKEY; this.mnemonic = null; this.publicKey = publicKey; - this.keyChain = new KeyChain({ publicKey }); + + const keyChain = new KeyChain({ publicKey, network }); + this.keyChainStore = new KeyChainStore(); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.spec.js index 8852ee6c6ee..07890f2d45d 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.spec.js @@ -18,18 +18,18 @@ describe('Wallet - fromPublicKey', function suite() { expect(self1.walletType).to.equal(WALLET_TYPES.PUBLICKEY); expect(self1.mnemonic).to.equal(null); expect(self1.publicKey).to.equal(cR4t6ePublicKey); - expect(self1.keyChain.type).to.equal('publicKey'); - expect(self1.keyChain.publicKey).to.equal(cR4t6ePublicKey); - expect(self1.keyChain.keys).to.deep.equal({}); + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('publicKey'); + expect(keyChain.rootKey.toString()).to.equal(cR4t6ePublicKey.toString()); const self2 = {}; fromPublicKey.call(self2, cR4t6ePublicKey.toString()); expect(self2.walletType).to.equal(WALLET_TYPES.PUBLICKEY); expect(self2.mnemonic).to.equal(null); expect(self2.publicKey).to.equal(cR4t6ePublicKey.toString()); - expect(self2.keyChain.type).to.equal('publicKey'); - expect(self2.keyChain.publicKey).to.equal(cR4t6ePublicKey.toString()); - expect(self2.keyChain.keys).to.deep.equal({}); + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain2.rootKeyType).to.equal('publicKey'); + expect(keyChain2.rootKey.toString()).to.equal(cR4t6ePublicKey.toString()); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromSeed.js b/packages/wallet-lib/src/types/Wallet/methods/fromSeed.js index 978adc9ecda..038fc138e6b 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromSeed.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromSeed.js @@ -8,7 +8,7 @@ const { * fixme: Term seed is often use, but we might want to rename to fromHDPrivateKey * @param seed */ -module.exports = function fromSeed(seed) { +module.exports = function fromSeed(seed, network) { if (!is.seed(seed)) throw new Error('Expected a valid seed (typeof string)'); - return this.fromHDPrivateKey(seedToHDPrivateKey(seed, this.network)); + return this.fromHDPrivateKey(seedToHDPrivateKey(seed, network)); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromSeed.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromSeed.spec.js index 75e9d32cda1..a0a9ec827ca 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromSeed.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromSeed.spec.js @@ -19,22 +19,22 @@ describe('Wallet - fromSeed', function suite() { expect(self1.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self1.mnemonic).to.equal(null); expect(self1.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); - expect(self1.keyChain.type).to.equal('HDPrivateKey'); - expect(self1.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); - expect(self1.keyChain.keys).to.deep.equal({}); + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); const self2 = { fromHDPrivateKey, network: 'mainnet', }; - fromSeed.call(self2, knifeFixture.seed); + fromSeed.call(self2, knifeFixture.seed, self2.network); expect(self2.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self2.mnemonic).to.equal(null); expect(self2.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); - expect(self2.keyChain.type).to.equal('HDPrivateKey'); - expect(self2.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); - expect(self2.keyChain.keys).to.deep.equal({}); + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain2.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain2.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ diff --git a/packages/wallet-lib/src/utils/bip44/ensureAddressesToGapLimit.js b/packages/wallet-lib/src/utils/bip44/ensureAddressesToGapLimit.js index 318d7959f8f..a4c84cbc784 100644 --- a/packages/wallet-lib/src/utils/bip44/ensureAddressesToGapLimit.js +++ b/packages/wallet-lib/src/utils/bip44/ensureAddressesToGapLimit.js @@ -1,5 +1,5 @@ const logger = require('../../logger'); -const { BIP44_ADDRESS_GAP } = require('../../CONSTANTS'); +const { BIP44_ADDRESS_GAP, WALLET_TYPES } = require('../../CONSTANTS'); const is = require('../is'); const getMissingIndexes = require('./getMissingIndexes'); @@ -77,13 +77,15 @@ function ensureAccountAddressesToGapLimit(walletStore, walletType, accountIndex, const gapBetweenLastUsedAndLastGenerated = { external: lastGeneratedIndexes.external - lastUsedIndexes.external, - internal: lastGeneratedIndexes.internal - lastUsedIndexes.internal, }; const addressesToGenerate = { external: BIP44_ADDRESS_GAP - gapBetweenLastUsedAndLastGenerated.external, - internal: BIP44_ADDRESS_GAP - gapBetweenLastUsedAndLastGenerated.internal, }; - + if (walletType.includes(WALLET_TYPES.HDWALLET)) { + // eslint-disable-next-line max-len + gapBetweenLastUsedAndLastGenerated.internal = lastGeneratedIndexes.internal - lastUsedIndexes.internal; + addressesToGenerate.internal = BIP44_ADDRESS_GAP - gapBetweenLastUsedAndLastGenerated.internal; + } Object.entries(addressesToGenerate) .forEach(([typeToGenerate, numberToGenerate]) => { if (numberToGenerate > 0) { From 0078cd6d820f950cfd2e6c4170f7abdafe2b2441 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Mon, 31 Jan 2022 01:21:55 +0100 Subject: [PATCH 02/27] feat: rename KeyChain to DerivableKeyChain and reinstate old KeyChain --- .../src/types/Account/methods/encrypt.spec.js | 2 +- .../types/Account/methods/generateAddress.js | 72 ++----- .../src/types/Account/methods/getAddress.js | 46 +++-- .../DerivableKeyChain/DerivableKeyChain.d.ts | 50 +++++ .../DerivableKeyChain/DerivableKeyChain.js | 107 +++++++++++ .../DerivableKeyChain.spec.js | 178 ++++++++++++++++++ .../methods/getDIP15ExtendedKey.js | 26 +++ .../methods/getFirstUnusedAddress.js | 12 ++ .../methods/getForAddress.js | 0 .../methods/getForPath.js | 0 .../methods/getHardenedBIP44HDKey.js | 11 ++ .../methods/getHardenedDIP15AccountKey.js | 14 ++ .../methods/getHardenedDIP9FeatureHDKey.js | 11 ++ .../methods/getIssuedPaths.js | 0 .../methods/getRootKey.js | 0 .../methods/getWatchedAddresses.js | 0 .../methods/markAddressAsUsed.js | 3 +- .../methods/maybeLookAhead.js | 2 - .../types/DerivableKeyChain/methods/sign.js | 29 +++ .../src/types/Identities/Identities.js | 2 +- .../methods/getIdentityHDKeyById.js | 5 +- .../methods/getIdentityHDKeyById.spec.js | 8 +- .../src/types/KeyChain/KeyChain.d.ts | 62 +++--- .../wallet-lib/src/types/KeyChain/KeyChain.js | 131 ++++--------- .../src/types/KeyChain/KeyChain.spec.js | 121 ++++-------- .../KeyChain/methods/generateKeyForChild.js | 19 ++ .../KeyChain/methods/generateKeyForPath.js | 19 ++ .../KeyChain/methods/getDIP15ExtendedKey.js | 2 +- .../KeyChain/methods/getFirstUnusedAddress.js | 14 -- .../KeyChain/methods/getHardenedBIP44HDKey.js | 5 +- .../methods/getHardenedDIP9FeatureHDKey.js | 5 +- .../types/KeyChain/methods/getKeyForChild.js | 10 + .../types/KeyChain/methods/getKeyForPath.js | 29 +++ .../types/KeyChain/methods/getPrivateKey.js | 18 ++ .../types/KeyChainStore/KeyChainStore.spec.js | 8 +- .../methods/makeChildKeyChainStore.js | 6 +- .../src/types/Wallet/Wallet.spec.js | 17 +- .../src/types/Wallet/methods/fromAddress.js | 8 +- .../types/Wallet/methods/fromAddress.spec.js | 14 +- .../types/Wallet/methods/fromHDPrivateKey.js | 6 +- .../Wallet/methods/fromHDPrivateKey.spec.js | 7 +- .../types/Wallet/methods/fromHDPublicKey.js | 6 +- .../Wallet/methods/fromHDPublicKey.spec.js | 49 ++--- .../src/types/Wallet/methods/fromMnemonic.js | 14 +- .../types/Wallet/methods/fromMnemonic.spec.js | 47 ++--- .../types/Wallet/methods/fromPrivateKey.js | 8 +- .../Wallet/methods/fromPrivateKey.spec.js | 12 +- .../src/types/Wallet/methods/fromPublicKey.js | 8 +- .../Wallet/methods/fromPublicKey.spec.js | 12 +- .../src/types/Wallet/methods/fromSeed.js | 4 +- .../src/types/Wallet/methods/fromSeed.spec.js | 14 +- .../utils/bip44/ensureAddressesToGapLimit.js | 10 +- 52 files changed, 802 insertions(+), 461 deletions(-) create mode 100644 packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.d.ts create mode 100644 packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.js create mode 100644 packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.spec.js create mode 100644 packages/wallet-lib/src/types/DerivableKeyChain/methods/getDIP15ExtendedKey.js create mode 100644 packages/wallet-lib/src/types/DerivableKeyChain/methods/getFirstUnusedAddress.js rename packages/wallet-lib/src/types/{KeyChain => DerivableKeyChain}/methods/getForAddress.js (100%) rename packages/wallet-lib/src/types/{KeyChain => DerivableKeyChain}/methods/getForPath.js (100%) create mode 100644 packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedBIP44HDKey.js create mode 100644 packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedDIP15AccountKey.js create mode 100644 packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedDIP9FeatureHDKey.js rename packages/wallet-lib/src/types/{KeyChain => DerivableKeyChain}/methods/getIssuedPaths.js (100%) rename packages/wallet-lib/src/types/{KeyChain => DerivableKeyChain}/methods/getRootKey.js (100%) rename packages/wallet-lib/src/types/{KeyChain => DerivableKeyChain}/methods/getWatchedAddresses.js (100%) rename packages/wallet-lib/src/types/{KeyChain => DerivableKeyChain}/methods/markAddressAsUsed.js (96%) rename packages/wallet-lib/src/types/{KeyChain => DerivableKeyChain}/methods/maybeLookAhead.js (97%) create mode 100644 packages/wallet-lib/src/types/DerivableKeyChain/methods/sign.js create mode 100644 packages/wallet-lib/src/types/KeyChain/methods/generateKeyForChild.js create mode 100644 packages/wallet-lib/src/types/KeyChain/methods/generateKeyForPath.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getFirstUnusedAddress.js create mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getKeyForChild.js create mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getKeyForPath.js create mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getPrivateKey.js diff --git a/packages/wallet-lib/src/types/Account/methods/encrypt.spec.js b/packages/wallet-lib/src/types/Account/methods/encrypt.spec.js index facc317ea96..89653f84bdc 100644 --- a/packages/wallet-lib/src/types/Account/methods/encrypt.spec.js +++ b/packages/wallet-lib/src/types/Account/methods/encrypt.spec.js @@ -29,7 +29,7 @@ describe('Account - encrypt', function suite() { const secret = 'secret'; it('should encrypt extPubKey with aes', () => { - const extPubKey = account.keyChainStore.getMasterKeyChain().getForPath(derivationPath).key.toString(); + const extPubKey = account.keyChain.getKeyForPath(derivationPath, 'HDPublicKey').toString(); const encryptedExtPubKey = account.encrypt('aes', extPubKey, secret).toString(); const bytes = CryptoJS.AES.decrypt(encryptedExtPubKey, secret); const decrypted = bytes.toString(CryptoJS.enc.Utf8); diff --git a/packages/wallet-lib/src/types/Account/methods/generateAddress.js b/packages/wallet-lib/src/types/Account/methods/generateAddress.js index 6d19109c108..450f582fe73 100644 --- a/packages/wallet-lib/src/types/Account/methods/generateAddress.js +++ b/packages/wallet-lib/src/types/Account/methods/generateAddress.js @@ -1,94 +1,58 @@ +const { PublicKey } = require('@dashevo/dashcore-lib'); const EVENTS = require('../../../EVENTS'); const { WALLET_TYPES } = require('../../../CONSTANTS'); const { is } = require('../../../utils'); - /** * Generate an address from a path and import it to the store * @param {string} path - * @param {boolean} [isWatchedAddress=true] - if the address will be watched - * @return {AddressInfo} Address information + * @return {AddressObj} Address information * */ -function generateAddress(path, isWatchedAddress = true) { +function generateAddress(path) { if (is.undefOrNull(path)) throw new Error('Expected path to generate an address'); let index = 0; + let privateKey; let address; - let keyPathData; const { network } = this; switch (this.walletType) { case WALLET_TYPES.ADDRESS: - address = this.keyChainStore.getMasterKeyChain().rootKey; - if (isWatchedAddress) { - this.keyChainStore.issuedPaths.set(0, { - path: 0, - address, - isUsed: false, - isWatched: true, - }); - } + address = this.keyChain.address; break; case WALLET_TYPES.PUBLICKEY: - // eslint-disable-next-line no-case-declarations - const { rootKey } = this.keyChainStore.getMasterKeyChain(); - address = rootKey.toAddress(network).toString(); - if (isWatchedAddress) { - this.keyChainStore.issuedPaths.set(0, { - key: rootKey, - path: 0, - address, - isUsed: false, - isWatched: true, - }); - } + address = new PublicKey(this.keyChain.publicKey.toString()).toAddress(network).toString(); break; - case WALLET_TYPES.HDPRIVATE: case WALLET_TYPES.HDWALLET: // eslint-disable-next-line prefer-destructuring - index = parseInt(path.toString().split('/')[2], 10); - keyPathData = this.keyChainStore - .getMasterKeyChain() - .getForPath(path, { isWatched: isWatchedAddress }); - address = keyPathData.address.toString(); + index = parseInt(path.toString().split('/')[5], 10); + privateKey = this.keyChain.getKeyForPath(path); + address = privateKey.publicKey.toAddress(network).toString(); break; case WALLET_TYPES.HDPUBLIC: index = parseInt(path.toString().split('/')[5], 10); - // eslint-disable-next-line no-case-declarations - keyPathData = this.keyChainStore - .getMasterKeyChain() - .getForPath(path, { isWatched: isWatchedAddress }); - address = keyPathData.address.toString(); + privateKey = this.keyChain.getKeyForChild(index); + address = privateKey.publicKey.toAddress(network).toString(); break; - // TODO: DEPRECATE USAGE OF SINGLE_ADDRESS in favor or PRIVATEKEY - case WALLET_TYPES.PRIVATEKEY: case WALLET_TYPES.SINGLE_ADDRESS: default: - keyPathData = this.keyChainStore - .getMasterKeyChain() - .getForPath(path, { isWatched: isWatchedAddress }); - address = keyPathData.address.toString(); - break; + privateKey = this.keyChain.getKeyForPath(path.toString()); + address = privateKey.publicKey.toAddress(network).toString(); } const addressData = { path: path.toString(), index, address, + // privateKey, transactions: [], - utxos: {}, balanceSat: 0, unconfirmedBalanceSat: 0, + utxos: {}, + fetchedLast: 0, + used: false, }; - const accountStore = this.storage - .getWalletStore(this.walletId) - .getPathState(this.accountPath); - - const chainStore = this.storage.getChainStore(this.network); - - accountStore.addresses[addressData.path] = addressData.address.toString(); - chainStore.importAddress(addressData.address.toString()); + this.storage.importAddresses(addressData, this.walletId); this.emit(EVENTS.GENERATED_ADDRESS, { type: EVENTS.GENERATED_ADDRESS, payload: addressData }); return addressData; } - module.exports = generateAddress; diff --git a/packages/wallet-lib/src/types/Account/methods/getAddress.js b/packages/wallet-lib/src/types/Account/methods/getAddress.js index 86efad07386..d4771dc7738 100644 --- a/packages/wallet-lib/src/types/Account/methods/getAddress.js +++ b/packages/wallet-lib/src/types/Account/methods/getAddress.js @@ -1,26 +1,40 @@ const { WALLET_TYPES } = require('../../../CONSTANTS'); +const getTypePathFromWalletType = (walletType, addressType = 'external', index, BIP44PATH) => { + let type; + let path; + + const addressTypeIndex = (addressType === 'external') ? 0 : 1; + switch (walletType) { + case WALLET_TYPES.HDWALLET: + type = addressType; + path = `${BIP44PATH}/${addressTypeIndex}/${index}`; + break; + case WALLET_TYPES.HDPUBLIC: + type = 'external'; + path = `${BIP44PATH}/${addressTypeIndex}/${index}`; + break; + case WALLET_TYPES.PUBLICKEY: + case WALLET_TYPES.ADDRESS: + case WALLET_TYPES.PRIVATEKEY: + case WALLET_TYPES.SINGLE_ADDRESS: + default: + type = 'misc'; + path = '0'; + } + return { type, path }; +}; /** * Get a specific addresss based on the index and type of address. * @param {number} index - The index on the type - * @param {AddressType} [addressType="external"] - Type of the address (external, internal, misc) + * @param {AddressType} [_type="external"] - Type of the address (external, internal, misc) * @return */ -function getAddress(addressIndex = 0, addressType = 'external') { - const addressTypeIndex = (addressType === 'external') ? 0 : 1; - - const { addresses } = this.storage.getWalletStore(this.walletId).getPathState(this.accountPath); - const addressPath = ([WALLET_TYPES.HDPUBLIC, WALLET_TYPES.HDWALLET].includes(this.walletType)) - ? `m/${addressTypeIndex}/${addressIndex}` : '0'; - - const address = addresses[addressPath]; - if (!address) return this.generateAddress(addressPath); +function getAddress(index = 0, _type = 'external') { + const { type, path } = getTypePathFromWalletType(this.walletType, _type, index, this.BIP44PATH); - const chainStore = this.storage.getChainStore(this.network); - return { - index: addressIndex, - path: addressPath, - ...chainStore.getAddress(address), - }; + const { wallets } = this.storage.getStore(); + const matchingTypeAddresses = wallets[this.walletId].addresses[type]; + return (matchingTypeAddresses[path]) ? matchingTypeAddresses[path] : this.generateAddress(path); } module.exports = getAddress; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.d.ts b/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.d.ts new file mode 100644 index 00000000000..406d2baf5ff --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.d.ts @@ -0,0 +1,50 @@ +import {PrivateKey, Network,} from "../types"; +import {HDPrivateKey, HDPublicKey} from "@dashevo/dashcore-lib"; +import {Transaction} from "@dashevo/dashcore-lib/typings/transaction/Transaction"; + +export declare namespace DerivableKeyChain { + interface IDerivableKeyChainOptions { + network?: Network; + keys?: [Keys] + } +} + +export declare class DerivableKeyChain { + constructor(options?: DerivableKeyChain.IDerivableKeyChainOptions); + network: Network; + keys: [Keys]; + + type: HDKeyTypesParam|PrivateKeyTypeParam; + HDPrivateKey?: HDPrivateKey; + privateKey?: PrivateKey; + + generateKeyForChild(index: number, type?: HDKeyTypesParam): HDPrivateKey|HDPublicKey; + generateKeyForPath(path: string, type?: HDKeyTypesParam): HDPrivateKey|HDPublicKey; + + getDIP15ExtendedKey(userUniqueId: string, contactUniqueId: string, index?: number, accountIndex?: number, type?: HDKeyTypesParam): HDKeyTypes; + getHardenedDIP15AccountKey(index?: number, type?: HDKeyTypesParam): HDKeyTypes; + getHardenedBIP44HDKey(type?: HDKeyTypesParam): HDKeyTypes; + getHardenedDIP9FeatureHDKey(type?: HDKeyTypesParam): HDKeyTypes; + getKeyForChild(index: number, type?: HDKeyTypesParam): HDKeyTypes; + getKeyForPath(path: string, type?: HDKeyTypesParam): HDKeyTypes; + getPrivateKey(): PrivateKey; + + sign(object: Transaction|any, privateKeys:[PrivateKey], sigType: number): any; +} + +type HDKeyTypes = HDPublicKey | HDPrivateKey; + +export declare enum HDKeyTypesParam { + HDPrivateKey="HDPrivateKey", + HDPublicKey="HDPrivateKey", +} +export declare enum PrivateKeyTypeParam { + privateKey='privateKey' +} +export declare interface Keys { + [path: string]: { + path: string + }; +} + + diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.js b/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.js new file mode 100644 index 00000000000..9191f40d7e2 --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.js @@ -0,0 +1,107 @@ +const { Networks, HDPrivateKey, HDPublicKey } = require('@dashevo/dashcore-lib'); +const { PrivateKey, PublicKey } = require('@dashevo/dashcore-lib'); +const { doubleSha256 } = require('../../utils/crypto'); +const { mnemonicToHDPrivateKey } = require('../../utils/mnemonic'); + +function generateKeyChainId(key) { + const keyChainIdSuffix = doubleSha256(key.toString()).toString('hex').slice(0, 10); + return `kc${keyChainIdSuffix}`; +} + +function fromOptions(opts) { + let rootKey; + let rootKeyType; + let network = Networks.testnet.toString(); + let passphrase = ''; + + if (opts) { + if (opts.passphrase) { + passphrase = opts.passphrase; + } + if (opts.mnemonic) { + rootKeyType = 'HDPrivateKey'; + rootKey = (typeof opts.mnemonic === 'string') ? HDPrivateKey(opts.HDPrivateKey) : opts.HDPrivateKey; + } + if (opts.network) { + network = opts.network; + } + if (opts.HDPrivateKey) { + rootKeyType = 'HDPrivateKey'; + rootKey = (typeof opts.HDPrivateKey === 'string') ? HDPrivateKey(opts.HDPrivateKey) : opts.HDPrivateKey; + network = rootKey.network.toString(); + } else if (opts.HDPublicKey) { + rootKeyType = 'HDPublicKey'; + rootKey = (typeof opts.HDPublicKey === 'string') ? HDPublicKey(opts.HDPublicKey) : opts.HDPublicKey; + network = rootKey.network.toString(); + } else if (opts.privateKey) { + rootKeyType = 'privateKey'; + rootKey = (typeof opts.privateKey === 'string') ? new PrivateKey(opts.privateKey, opts.network) : opts.privateKey; + network = rootKey.network.toString(); + } else if (opts.publicKey) { + rootKeyType = 'publicKey'; + rootKey = (typeof opts.publicKey === 'string') ? new PublicKey(opts.publicKey, opts.network) : opts.publicKey; + network = rootKey.network.toString(); + } else if (opts.address) { + rootKeyType = 'address'; + rootKey = opts.address.toString(); + } else if (opts.mnemonic) { + return fromOptions({ + ...opts, + HDPrivateKey: mnemonicToHDPrivateKey(opts.mnemonic, network, passphrase), + }); + } + } + + const lookAheadOpts = { + isWatched: true, + paths: {}, + ...opts.lookAheadOpts, + }; + + return { + rootKeyType, + rootKey, + network, + passphrase, + lookAheadOpts, + }; +} + +class DerivableKeyChain { + constructor(opts = {}) { + const { + rootKey, + rootKeyType, + network, + lookAheadOpts, + } = fromOptions(opts); + if (!rootKeyType || !rootKey) { + throw new Error('Expect one of [mnemonic, HDPrivateKey, HDPublicKey, privateKey, publicKey, address] to be provided.'); + } + this.keyChainId = generateKeyChainId(rootKey); + + this.rootKey = rootKey; + this.network = network; + this.rootKeyType = rootKeyType; + this.lookAheadOpts = { isWatched: true, ...lookAheadOpts }; + + this.issuedPaths = new Map(); + + this.maybeLookAhead(); + } +} +DerivableKeyChain.prototype.getForPath = require('./methods/getForPath'); +DerivableKeyChain.prototype.getForAddress = require('./methods/getForAddress'); +DerivableKeyChain.prototype.getDIP15ExtendedKey = require('./methods/getDIP15ExtendedKey'); +DerivableKeyChain.prototype.getFirstUnusedAddress = require('./methods/getFirstUnusedAddress'); +DerivableKeyChain.prototype.getHardenedBIP44HDKey = require('./methods/getHardenedBIP44HDKey'); +DerivableKeyChain.prototype.getHardenedDIP9FeatureHDKey = require('./methods/getHardenedDIP9FeatureHDKey'); +DerivableKeyChain.prototype.getHardenedDIP15AccountKey = require('./methods/getHardenedDIP15AccountKey'); +DerivableKeyChain.prototype.getRootKey = require('./methods/getRootKey'); +DerivableKeyChain.prototype.getWatchedAddresses = require('./methods/getWatchedAddresses'); +DerivableKeyChain.prototype.getIssuedPaths = require('./methods/getIssuedPaths'); +DerivableKeyChain.prototype.maybeLookAhead = require('./methods/maybeLookAhead'); +DerivableKeyChain.prototype.markAddressAsUsed = require('./methods/markAddressAsUsed'); +DerivableKeyChain.prototype.sign = require('./methods/sign'); + +module.exports = DerivableKeyChain; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.spec.js b/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.spec.js new file mode 100644 index 00000000000..98dfc69bb5b --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.spec.js @@ -0,0 +1,178 @@ +const Dashcore = require('@dashevo/dashcore-lib'); +const { expect } = require('chai'); +const DerivableKeyChain = require('./DerivableKeyChain'); +const { mnemonicToHDPrivateKey } = require('../../utils/mnemonic'); + +let derivableKeyChain; +let derivableKeyChain2; +const mnemonic = 'during develop before curtain hazard rare job language become verb message travel'; +const mnemonic2 = 'birth kingdom trash renew flavor utility donkey gasp regular alert pave layer'; +const pk = '4226d5e2fe8cbfe6f5beb7adf5a5b08b310f6c4a67fc27826779073be6f5699e'; +const hdPublicKey = 'xpub661MyMwAqRbcFGB6XSWBsD725rJDUbFUpy4zWe2u22nJ2BxpoHFxtVDfKnTnvVQHohnY7AsVpRTHDv6PyPQTYu1KxFPKw29MAVXPEpz1G7V'; +const expectedRootDIP15AccountKey_0 = 'tprv8hRzmheQujhJN5XP2dj955nAFCKeEoSifJRWuutdbwWRtusdDQ426jbp75EqErUSuTxmPyxYmP1TpcF5qdxGhXLNXRLMGsRLG6NFCv1WnaQ'; +const expectedRootDIP15AccountKey_1 = 'tprv8hRzmheQujhJQyCtFTuUFHxB3Ag5VLB994zhH4CfxbA41cq73HT2mpYq5M33V54oJyn6g514saxxVJB886G55eYX56J6D6x87UNNT6iQHkR'; +const expectedKeyForChild_0 = 'tprv8d4podc2Tg459CH2bwLHXj3vdJFBT2rdsk5Nr1djH7hzHdt5LRdvN6QyFwMiDy7ffRdik7fEVRKKgsHB4F18sh8xF6jFXpKq4sUgGBoSbKw'; +describe('DerivableKeyChain', function suite() { + this.timeout(1000); + it('should create a DerivableKeyChain', () => { + const expectedException1 = 'Expect one of [mnemonic, HDPrivateKey, HDPublicKey, privateKey, publicKey, address] to be provided.'; + expect(() => new DerivableKeyChain()).to.throw(expectedException1); + + derivableKeyChain = new DerivableKeyChain({ mnemonic: mnemonic, network: 'testnet' }); + expect(derivableKeyChain.rootKeyType).to.equal('HDPrivateKey'); + expect(derivableKeyChain.network.toString()).to.equal('testnet'); + expect(derivableKeyChain.rootKey.network.toString()).to.equal('testnet'); + + derivableKeyChain2 = new DerivableKeyChain({ mnemonic: mnemonic2, network: 'livenet' }); + }); + it('should generate key for full path', () => { + const path = 'm/44\'/1\'/0\'/0/0'; + const pk2 = derivableKeyChain.getForPath(path).key; + const address = new Dashcore.Address(pk2.publicKey.toAddress()).toString(); + expect(address).to.equal('yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT'); + }); + it('should get hardened feature path', () => { + const hardenedPk = derivableKeyChain.getHardenedBIP44HDKey(); + const pk2 = derivableKeyChain.getForPath('m/44\'/1\'').key; + expect(pk2.toString()).to.equal(hardenedPk.toString()); + }); + it('should get DIP15 account key', function () { + const rootDIP15AccountKey_0 = derivableKeyChain.getHardenedDIP15AccountKey(0); + expect(rootDIP15AccountKey_0.toString()).to.deep.equal(expectedRootDIP15AccountKey_0); + const rootDIP15AccountKey_1 = derivableKeyChain.getHardenedDIP15AccountKey(1); + expect(rootDIP15AccountKey_1.toString()).to.deep.equal(expectedRootDIP15AccountKey_1); + }); + + it('should get DIP15 extended key', function () { + const userUniqueId = '0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'; + const contactUniqueId = '0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'; + + // m/9'/5'/15'/0'/0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'/0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'/0 + const DIP15ExtPubKey_0 = derivableKeyChain2.getDIP15ExtendedKey(userUniqueId, contactUniqueId, 0, 0, type='HDPublicKey'); + expect(DIP15ExtPubKey_0.toString()).to.equal('xpub6LTkTQFSb8KMgMSz4B6sMZLpkQAY6wSTDprDkHDmLwWLpnjxazuxZn13FrSLKUafitsxuaaffM5a49P6aswhpppWUuYW6eFnwBXshR2W2eY'); + expect(DIP15ExtPubKey_0.publicKey.toString()).to.equal('038030c88ab0106e1f4af3b939db2bafc56f892554106f08da1ce1f9ef10f807bd') + + const DIP15ExtPrivKey_0 = derivableKeyChain2.getDIP15ExtendedKey(userUniqueId, contactUniqueId, 0, 0); + expect(DIP15ExtPrivKey_0.toString()).to.equal('xprvA7UQ3tiYkkm4TsNWx9ZrzRQ6CNL3hUibrbvcwtp9nbyMwzQp3Tbi1ygZQaPoigDhCf8XUjMmGK2NbnB2kLXPYg99Lp6e3iki318sdWcFN3q'); + expect(DIP15ExtPrivKey_0.privateKey.toString()).to.equal('fac40790776d171ee1db90899b5eb2df2f7d2aaf35ad56f07ffb8ed2c57f8e60') + expect(DIP15ExtPrivKey_0.publicKey.toString()).to.equal('038030c88ab0106e1f4af3b939db2bafc56f892554106f08da1ce1f9ef10f807bd') + + const userAhash = "0xa11ce14f698b32e9bb306dba7bbbee831263dcf658abeebb39930460ead117e5"; + const userBhash = "0xb0b052ff075c5ca3c16c3e20e9ac8223834475cc1324ab07889cb24ce6a62793"; + const DIP15ExtKey_1 = derivableKeyChain.getDIP15ExtendedKey(userAhash, userBhash, 0, 0); + expect(DIP15ExtKey_1.privateKey.toString()).to.equal('60581b6dca8244d3fb3cfe619b5a22277e5423b01e5285f356981f247e0f4a60') + expect(DIP15ExtKey_1.publicKey.toString()).to.equal('03deaac00f721151307fbc7bf80d7b8afab98c1f026d67e5f56b21e2013f551ce6') + }); + it('should derive from hardened feature path', () => { + const hardenedHDKey = derivableKeyChain.getHardenedBIP44HDKey(); + const pk2 = derivableKeyChain.getForPath(`m/44'/1'`).key; + expect(pk2.toString()).to.equal(hardenedHDKey.toString()); + expect(hardenedHDKey.toString()).to.deep.equal('tprv8dtrJNytYHRiZY585hmHGbguS6VjGpK49puSB7oXZjLHcQfrAzQkF4ZCxM2DkEbyY85J4EYcZ8EjT5ZCU8ozB727TDdodbfXet5GkGau2RQ'); + const derivedPk = hardenedHDKey.deriveChild(0, true).deriveChild(0).deriveChild(0); + const address = new Dashcore.Address(derivedPk.publicKey.toAddress()).toString(); + expect(address).to.equal('yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT'); + }); + it('should get hardened DIP9FeatureHDKey', function () { + const hardenedHDKey = derivableKeyChain.getHardenedDIP9FeatureHDKey(); + const pk2 = derivableKeyChain.getForPath(`m/9'/1'`).key; + expect(pk2.toString()).to.equal(hardenedHDKey.toString()); + expect(hardenedHDKey.toString()).to.deep.equal('tprv8fBJjWoGgCpGRCbyzE9RUA59rmoN1RUijhLnXGL4VHnLxvSe523yVg4GrGzbR6TyXtdynAEh5z8UX55EXt2Cb3xjvrsx2PgTY9BHxzFVkWn'); + }); + it('should get key for path', () => { + const derivableKeyChain2 = new DerivableKeyChain({ HDPrivateKey: mnemonicToHDPrivateKey(mnemonic, 'testnet') }); + const keyForChild = derivableKeyChain2.getForPath('m/0').key; + expect(keyForChild.toString()).to.equal(expectedKeyForChild_0); + }); + + it('should mark address watched and get watched addresses', function () { + const key0 = derivableKeyChain.getForPath('m/0'); + derivableKeyChain.getForPath('m/0').isWatched = true + key0.isWatched = true; + const key1 = derivableKeyChain.getForPath('m/1', { isWatched: true }); + const key2 = derivableKeyChain.getForPath('m/2', { isWatched: true }); + + const watchedAddresses = derivableKeyChain.getWatchedAddresses(); + let expectedWatchedAddresses = [ + derivableKeyChain.getForPath('m/0').address.toString(), + derivableKeyChain.getForPath('m/1').address.toString(), + derivableKeyChain.getForPath('m/2').address.toString() + ]; + expect(watchedAddresses).to.deep.equal(expectedWatchedAddresses); + }); + it('should get watched addresses', function () { + const watchedAddresses = derivableKeyChain.getWatchedAddresses(); + const expectedWatchedAddresses = [ + 'ybQDfNwiDjk8ZH5UUmHQzAMEmjbrbK5dAj', + 'yhFX5rseJPitV45HUCaa9haeGHtLuooBaq', + 'yhqxsmYk6jfoGWf1hJKq7d4U2cGHCgzpFU' + ] + expect(watchedAddresses).to.deep.equal(expectedWatchedAddresses); + }); + // it('should get watched public keys', function () { + // const watchedPubKeys = derivableKeyChain.getWatchedPublicKeys(); + // const expectedWatchedPubKeys = [ + // '03e6ab8177a7ca2699da4f83ca3c27768fb88b70ae9d6bde1cba8de88355ccf199', + // '0246a870b65153b98e453ff08f7198d06bce2a790286f44d90929aceafafa0673f', + // '025ad16f78f67801e52abe5b512d66e3896d4c2fa3ca3150349437a5dd13519967' + // ] + // expect(watchedPubKeys).to.deep.equal(expectedWatchedPubKeys) + // }); + it('should remove an address from watched addresses', function () { + const data0 = derivableKeyChain.getForPath('m/0', { isWatched: false }); + const data1 = derivableKeyChain.getForPath('m/1'); + const data2 = derivableKeyChain.getForPath('m/2'); + data2.isWatched = false; + + expect(derivableKeyChain.getWatchedAddresses().length).to.equal(1); + }); + it('should get address for path', function (){ + const address0_1 = derivableKeyChain.getForPath('m/1').address; + expect(address0_1.toString()).to.equal('yhFX5rseJPitV45HUCaa9haeGHtLuooBaq') + }) + it('should mark address as used', function () { + const address0_0 = derivableKeyChain.getForPath('m/0').address; + derivableKeyChain.markAddressAsUsed(address0_0); + expect(derivableKeyChain.issuedPaths.get('m/0').isUsed).to.equal(true) + }); +}); +describe('DerivableKeyChain - HDPublicKey', function suite(){ + let hdpubDerivableKeyChain; + it('should initiate from a HDPublicKey', function () { + hdpubDerivableKeyChain = new DerivableKeyChain({ + HDPublicKey: new Dashcore.HDPublicKey(hdPublicKey), + network: 'testnet' + }); + // As the HDPublicKey starts with xpub, it's livenet and should take priority over our network being set. + expect(hdpubDerivableKeyChain.network.toString()).to.equal('livenet'); + expect(hdpubDerivableKeyChain.keyChainId).to.equal('kc5059442d66'); + expect(hdpubDerivableKeyChain.getRootKey().toString()).to.equal(hdPublicKey); + }); + it('should derivate', function () { + const key0_1 = hdpubDerivableKeyChain.getForPath('m/1').key; + expect(key0_1.publicKey.toAddress(hdpubDerivableKeyChain.network).toString()).to.equal('XoL5LcBiDWcj6L7fFwytsFoX5Vz7BVXw9w') + }); + it('should get address for path', function (){ + const address0_1 = hdpubDerivableKeyChain.getForPath('m/2').address; + expect(address0_1.toString()).to.equal('XwAzpxQKbgebaLiadq1c6rDeFJ4FKPUufy') + }) +}) +describe('DerivableKeyChain - single privateKey', function suite() { + this.timeout(10000); + it('should correctly errors out when not a HDPublicKey (privateKey)', () => { + const privateKey = Dashcore.PrivateKey().toString(); + const network = 'livenet'; + const pkDerivableKeyChain = new DerivableKeyChain({ privateKey, network }); + expect(pkDerivableKeyChain.network).to.equal(network); + expect(pkDerivableKeyChain.rootKeyType).to.equal('privateKey'); + expect(pkDerivableKeyChain.rootKey.toString()).to.equal(privateKey); + + const expectedException1 = 'Wallet is not loaded from a mnemonic or a HDPrivateKey, impossible to derivate keys for path m/0'; + expect(() => pkDerivableKeyChain.getForPath('m/0')).to.throw(expectedException1); + }); + it('should get private key', () => { + const privateKey = Dashcore.PrivateKey().toString(); + const pkDerivableKeyChain = new DerivableKeyChain({ privateKey, network: 'livenet' }); + expect(pkDerivableKeyChain.getRootKey().toString()).to.equal(privateKey); + expect(pkDerivableKeyChain.rootKey.toString()).to.equal(privateKey); + }); +}); diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/getDIP15ExtendedKey.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getDIP15ExtendedKey.js new file mode 100644 index 00000000000..b65dc10d7da --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getDIP15ExtendedKey.js @@ -0,0 +1,26 @@ +/** + * Return the extended key of the relationship between two dashpay contacts. + * @param userUniqueId - Current userID + * @param contactUniqueId - Contact userID + * @param index - the key index. + * @param accountIndex[=0] - the internal wallet account from which derivation is done + * @param type {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys + * @return {HDPrivateKey|HDPublicKey} + */ +function getDIP15ExtendedKey(userUniqueId, contactUniqueId, index = 0, accountIndex = 0, type = 'HDPrivateKey') { + if (!['HDPrivateKey', 'HDPublicKey'].includes(this.rootKeyType)) { + throw new Error('Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate keys'); + } + if (!userUniqueId || !contactUniqueId) throw new Error('Required userUniqueId and contactUniqueId to be defined'); + + // Require a HDPrivateKey for hardened derivation + const extendedPrivateKey = this + .getHardenedDIP15AccountKey(accountIndex, 'HDPrivateKey') + .deriveChild((userUniqueId), true) + .deriveChild((contactUniqueId), true) + .deriveChild(index, false); + + return (type === 'HDPublicKey' ? extendedPrivateKey.hdPublicKey : extendedPrivateKey); +} + +module.exports = getDIP15ExtendedKey; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/getFirstUnusedAddress.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getFirstUnusedAddress.js new file mode 100644 index 00000000000..ab7f3fefeeb --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getFirstUnusedAddress.js @@ -0,0 +1,12 @@ +function getFirstUnusedAddress() { + const allUnused = this.getIssuedPaths() + .filter((path) => path.isUsed === false); + + const firstUnused = allUnused.slice(0, 1)[0]; + + return { + path: firstUnused.path, + address: firstUnused.address.toString(), + }; +} +module.exports = getFirstUnusedAddress; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getForAddress.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getForAddress.js similarity index 100% rename from packages/wallet-lib/src/types/KeyChain/methods/getForAddress.js rename to packages/wallet-lib/src/types/DerivableKeyChain/methods/getForAddress.js diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getForPath.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getForPath.js similarity index 100% rename from packages/wallet-lib/src/types/KeyChain/methods/getForPath.js rename to packages/wallet-lib/src/types/DerivableKeyChain/methods/getForPath.js diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedBIP44HDKey.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedBIP44HDKey.js new file mode 100644 index 00000000000..48da61c1616 --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedBIP44HDKey.js @@ -0,0 +1,11 @@ +const { BIP44_TESTNET_ROOT_PATH, BIP44_LIVENET_ROOT_PATH } = require('../../../CONSTANTS'); + +/** + * Return a safier root keys to derivate from + * @return {HDPrivateKey|HDPublicKey} + */ +function getHardenedBIP44HDKey() { + const pathRoot = (this.network.toString() === 'testnet') ? BIP44_TESTNET_ROOT_PATH : BIP44_LIVENET_ROOT_PATH; + return this.getForPath(pathRoot).key; +} +module.exports = getHardenedBIP44HDKey; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedDIP15AccountKey.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedDIP15AccountKey.js new file mode 100644 index 00000000000..c01f240fc85 --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedDIP15AccountKey.js @@ -0,0 +1,14 @@ +/** + * Return a safier root path to derivate from + * @param {number} [accountIndex=0] - set the account index + * @param {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys + * @return {HDPrivateKey|HDPublicKey} + */ +function getHardenedDIP15AccountKey(accountIndex = 0, type = 'HDPrivateKey') { + const hardenedFeatureRootKey = this.getHardenedDIP9FeatureHDKey(type); + + // Feature is set to 15' for all DashPay Incoming Funds derivation paths (see DIP15). + const featureKey = hardenedFeatureRootKey.deriveChild(15, true); + return featureKey.deriveChild(accountIndex, true); +} +module.exports = getHardenedDIP15AccountKey; diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedDIP9FeatureHDKey.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedDIP9FeatureHDKey.js new file mode 100644 index 00000000000..422c9fab667 --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getHardenedDIP9FeatureHDKey.js @@ -0,0 +1,11 @@ +const { DIP9_LIVENET_ROOT_PATH, DIP9_TESTNET_ROOT_PATH } = require('../../../CONSTANTS'); + +/** + * Return a safier root path to derivate from + * @return {HDPrivateKey|HDPublicKey} + */ +function getHardenedDIP9FeatureHDKey() { + const pathRoot = (this.network.toString() === 'testnet') ? DIP9_TESTNET_ROOT_PATH : DIP9_LIVENET_ROOT_PATH; + return this.getForPath(pathRoot).key; +} +module.exports = getHardenedDIP9FeatureHDKey; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getIssuedPaths.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getIssuedPaths.js similarity index 100% rename from packages/wallet-lib/src/types/KeyChain/methods/getIssuedPaths.js rename to packages/wallet-lib/src/types/DerivableKeyChain/methods/getIssuedPaths.js diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getRootKey.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getRootKey.js similarity index 100% rename from packages/wallet-lib/src/types/KeyChain/methods/getRootKey.js rename to packages/wallet-lib/src/types/DerivableKeyChain/methods/getRootKey.js diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getWatchedAddresses.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/getWatchedAddresses.js similarity index 100% rename from packages/wallet-lib/src/types/KeyChain/methods/getWatchedAddresses.js rename to packages/wallet-lib/src/types/DerivableKeyChain/methods/getWatchedAddresses.js diff --git a/packages/wallet-lib/src/types/KeyChain/methods/markAddressAsUsed.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/markAddressAsUsed.js similarity index 96% rename from packages/wallet-lib/src/types/KeyChain/methods/markAddressAsUsed.js rename to packages/wallet-lib/src/types/DerivableKeyChain/methods/markAddressAsUsed.js index 9df149d7dd4..cecf2922e1f 100644 --- a/packages/wallet-lib/src/types/KeyChain/methods/markAddressAsUsed.js +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/markAddressAsUsed.js @@ -1,12 +1,10 @@ const logger = require('../../../logger'); function markAddressAsUsed(address) { - const searchResult = [...this.issuedPaths.entries()] .find(([, el]) => el.address.toString() === address.toString()); if (searchResult) { - const [, addressData] = searchResult; logger.silly(`KeyChain - Marking ${address} ${addressData.path} as used`); addressData.isUsed = true; @@ -14,5 +12,6 @@ function markAddressAsUsed(address) { return this.maybeLookAhead(); } + return false; } module.exports = markAddressAsUsed; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/maybeLookAhead.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/maybeLookAhead.js similarity index 97% rename from packages/wallet-lib/src/types/KeyChain/methods/maybeLookAhead.js rename to packages/wallet-lib/src/types/DerivableKeyChain/methods/maybeLookAhead.js index e84db9fd842..fd3841f40bd 100644 --- a/packages/wallet-lib/src/types/KeyChain/methods/maybeLookAhead.js +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/maybeLookAhead.js @@ -38,7 +38,6 @@ function maybeLookAhead() { // and set it up to watched or used outside of lookAhead bounds const sortedBasePaths = basePaths.sort((a, b) => a.split('/').splice(-1) - b.split('/').splice(-1)); - let prevIndex; sortedBasePaths.forEach((path) => { const addressData = this.issuedPaths.get(path); @@ -49,7 +48,6 @@ function maybeLookAhead() { } lastGeneratedIndexes[basePath] = currentIndex; - prevIndex = currentIndex; }); }); diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/methods/sign.js b/packages/wallet-lib/src/types/DerivableKeyChain/methods/sign.js new file mode 100644 index 00000000000..b10b561f2c2 --- /dev/null +++ b/packages/wallet-lib/src/types/DerivableKeyChain/methods/sign.js @@ -0,0 +1,29 @@ +const { + crypto, Transaction, Message, +} = require('@dashevo/dashcore-lib'); + +/** + * Allow to sign any transaction or a transition object from a valid privateKeys list + * @param {Transaction|any} object + * @param {[PrivateKey]} privateKeys + * @param {number} [sigType=crypto.Signature.SIGHASH_ALL] + */ +function sign(object, privateKeys, sigType = crypto.Signature.SIGHASH_ALL) { + const handledTypes = [Transaction.name, Transaction.Payload.SubTxRegisterPayload, Message.name]; + if (!privateKeys) throw new Error('Require one or multiple privateKeys to sign'); + if (!object) throw new Error('Nothing to sign'); + if (!handledTypes.includes(object.constructor.name)) { + throw new Error(`Keychain sign : Unhandled object of type ${object.constructor.name}`); + } + const obj = object.sign(privateKeys, sigType); + + if (obj.isFullySigned && !obj.isFullySigned()) { + throw new Error('Not fully signed transaction'); + } + if (object.constructor.name === 'Message') { + // When signed, message are in string form. + return Message(obj); + } + return obj; +} +module.exports = sign; diff --git a/packages/wallet-lib/src/types/Identities/Identities.js b/packages/wallet-lib/src/types/Identities/Identities.js index f457fe9fc99..f721356dcce 100644 --- a/packages/wallet-lib/src/types/Identities/Identities.js +++ b/packages/wallet-lib/src/types/Identities/Identities.js @@ -10,7 +10,7 @@ class Identities { this.storage = wallet.storage; - this.keyChain = wallet.keyChainStore.getMasterKeyChain(); + this.keyChain = wallet.keyChain; } } diff --git a/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.js b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.js index 79cd6eedc77..374925a14a3 100644 --- a/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.js +++ b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.js @@ -5,10 +5,7 @@ * @return {HDPrivateKey} */ function getIdentityHDKeyById(identityId, keyIndex) { - const identityIndex = this.storage - .getWalletStore(this.walletId) - .getIndexedIdentityIds() - .indexOf(identityId); + const identityIndex = this.storage.getIndexedIdentityIds(this.walletId).indexOf(identityId); if (identityIndex === -1) { throw new Error(`Identity with ID ${identityId} is not associated with wallet, or it's not synced`); diff --git a/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.spec.js b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.spec.js index 2d8f3a2b829..e13dc087569 100644 --- a/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.spec.js +++ b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.spec.js @@ -1,6 +1,7 @@ const { expect } = require('chai'); const mockedStore = require('../../../../fixtures/sirentonight-fullstore-snapshot-1562711703'); const getIdentityHDKeyById = require('./getIdentityHDKeyById'); +const searchTransaction = require('../../Storage/methods/searchTransaction'); let walletMock; let fetchTransactionInfoCalledNb = 0; @@ -9,15 +10,12 @@ describe('Wallet#getIdentityHDKeyById', function suite() { this.timeout(10000); before(() => { expectedKeyMock = "123"; - const walletStoreMock = { - getIndexedIdentityIds: () => mockedStore.wallets[Object.keys(mockedStore.wallets)].identityIds - } - const storageMock = { store: mockedStore, getStore: () => mockedStore, mappedAddress: {}, - getWalletStore: () => walletStoreMock, + searchTransaction, + getIndexedIdentityIds: () => mockedStore.wallets[Object.keys(mockedStore.wallets)].identityIds, }; const walletId = Object.keys(mockedStore.wallets)[0]; walletMock = { diff --git a/packages/wallet-lib/src/types/KeyChain/KeyChain.d.ts b/packages/wallet-lib/src/types/KeyChain/KeyChain.d.ts index 0c1ee65a322..0d774d60b6f 100644 --- a/packages/wallet-lib/src/types/KeyChain/KeyChain.d.ts +++ b/packages/wallet-lib/src/types/KeyChain/KeyChain.d.ts @@ -3,48 +3,46 @@ import {HDPrivateKey, HDPublicKey} from "@dashevo/dashcore-lib"; import {Transaction} from "@dashevo/dashcore-lib/typings/transaction/Transaction"; export declare namespace KeyChain { - interface IKeyChainOptions { - network?: Network; - keys?: [Keys] - } + interface IKeyChainOptions { + network?: Network; + keys?: [Keys] + } } export declare class KeyChain { - constructor(options?: KeyChain.IKeyChainOptions); - network: Network; - keys: [Keys]; - - type: HDKeyTypesParam|PrivateKeyTypeParam; - HDPrivateKey?: HDPrivateKey; - privateKey?: PrivateKey; - - generateKeyForChild(index: number, type?: HDKeyTypesParam): HDPrivateKey|HDPublicKey; - generateKeyForPath(path: string, type?: HDKeyTypesParam): HDPrivateKey|HDPublicKey; - - getDIP15ExtendedKey(userUniqueId: string, contactUniqueId: string, index?: number, accountIndex?: number, type?: HDKeyTypesParam): HDKeyTypes; - getHardenedDIP15AccountKey(index?: number, type?: HDKeyTypesParam): HDKeyTypes; - getHardenedBIP44HDKey(type?: HDKeyTypesParam): HDKeyTypes; - getHardenedDIP9FeatureHDKey(type?: HDKeyTypesParam): HDKeyTypes; - getKeyForChild(index: number, type?: HDKeyTypesParam): HDKeyTypes; - getKeyForPath(path: string, type?: HDKeyTypesParam): HDKeyTypes; - getPrivateKey(): PrivateKey; - - sign(object: Transaction|any, privateKeys:[PrivateKey], sigType: number): any; + constructor(options?: KeyChain.IKeyChainOptions); + network: Network; + keys: [Keys]; + + type: HDKeyTypesParam|PrivateKeyTypeParam; + HDPrivateKey?: HDPrivateKey; + privateKey?: PrivateKey; + + generateKeyForChild(index: number, type?: HDKeyTypesParam): HDPrivateKey|HDPublicKey; + generateKeyForPath(path: string, type?: HDKeyTypesParam): HDPrivateKey|HDPublicKey; + + getDIP15ExtendedKey(userUniqueId: string, contactUniqueId: string, index?: number, accountIndex?: number, type?: HDKeyTypesParam): HDKeyTypes; + getHardenedDIP15AccountKey(index?: number, type?: HDKeyTypesParam): HDKeyTypes; + getHardenedBIP44HDKey(type?: HDKeyTypesParam): HDKeyTypes; + getHardenedDIP9FeatureHDKey(type?: HDKeyTypesParam): HDKeyTypes; + getKeyForChild(index: number, type?: HDKeyTypesParam): HDKeyTypes; + getKeyForPath(path: string, type?: HDKeyTypesParam): HDKeyTypes; + getPrivateKey(): PrivateKey; + + sign(object: Transaction|any, privateKeys:[PrivateKey], sigType: number): any; } type HDKeyTypes = HDPublicKey | HDPrivateKey; export declare enum HDKeyTypesParam { - HDPrivateKey="HDPrivateKey", - HDPublicKey="HDPrivateKey", + HDPrivateKey="HDPrivateKey", + HDPublicKey="HDPrivateKey", } export declare enum PrivateKeyTypeParam { - privateKey='privateKey' + privateKey='privateKey' } export declare interface Keys { - [path: string]: { - path: string - }; + [path: string]: { + path: string + }; } - - diff --git a/packages/wallet-lib/src/types/KeyChain/KeyChain.js b/packages/wallet-lib/src/types/KeyChain/KeyChain.js index 74931778764..643dcc62737 100644 --- a/packages/wallet-lib/src/types/KeyChain/KeyChain.js +++ b/packages/wallet-lib/src/types/KeyChain/KeyChain.js @@ -1,107 +1,52 @@ const { Networks, HDPrivateKey, HDPublicKey } = require('@dashevo/dashcore-lib'); -const { PrivateKey, PublicKey } = require('@dashevo/dashcore-lib'); -const { doubleSha256 } = require('../../utils/crypto'); -const { mnemonicToHDPrivateKey } = require('../../utils/mnemonic'); +const { has } = require('lodash'); -function generateKeyChainId(key) { - const keyChainIdSuffix = doubleSha256(key.toString()).toString('hex').slice(0, 10); - return `kc${keyChainIdSuffix}`; -} - -function fromOptions(opts) { - let rootKey; - let rootKeyType; - let network = Networks.testnet.toString(); - let passphrase = ''; - - if (opts) { - if (opts.passphrase) { - passphrase = opts.passphrase; - } - if (opts.mnemonic) { - rootKeyType = 'HDPrivateKey'; - rootKey = (typeof opts.mnemonic === 'string') ? HDPrivateKey(opts.HDPrivateKey) : opts.HDPrivateKey; - } - if (opts.network) { - network = opts.network; - } - if (opts.HDPrivateKey) { - rootKeyType = 'HDPrivateKey'; - rootKey = (typeof opts.HDPrivateKey === 'string') ? HDPrivateKey(opts.HDPrivateKey) : opts.HDPrivateKey; - network = rootKey.network.toString(); - } else if (opts.HDPublicKey) { - rootKeyType = 'HDPublicKey'; - rootKey = (typeof opts.HDPublicKey === 'string') ? HDPublicKey(opts.HDPublicKey) : opts.HDPublicKey; - network = rootKey.network.toString(); - } else if (opts.privateKey) { - rootKeyType = 'privateKey'; - rootKey = (typeof opts.privateKey === 'string') ? new PrivateKey(opts.privateKey, opts.network) : opts.privateKey; - network = rootKey.network.toString(); - } else if (opts.publicKey) { - rootKeyType = 'publicKey'; - rootKey = (typeof opts.publicKey === 'string') ? new PublicKey(opts.publicKey, opts.network) : opts.publicKey; - network = rootKey.network.toString(); - } else if (opts.address) { - rootKeyType = 'address'; - rootKey = opts.address.toString(); - } else if (opts.mnemonic) { - return fromOptions({ - ...opts, - HDPrivateKey: mnemonicToHDPrivateKey(opts.mnemonic, network, passphrase), - }); - } - } - - const lookAheadOpts = { - isWatched: true, - paths: {}, - ...opts.lookAheadOpts, - }; - - return { - rootKeyType, - rootKey, - network, - passphrase, - lookAheadOpts, - }; -} +// eslint-disable-next-line no-underscore-dangle +const _defaultOpts = { + network: Networks.testnet.toString(), + keys: {}, +}; class KeyChain { - constructor(opts = {}) { - const { - rootKey, - rootKeyType, - network, - lookAheadOpts, - } = fromOptions(opts); - if (!rootKeyType || !rootKey) { - throw new Error('Expect one of [mnemonic, HDPrivateKey, HDPublicKey, privateKey, publicKey, address] to be provided.'); + constructor(opts = JSON.parse(JSON.stringify(_defaultOpts))) { + const defaultOpts = JSON.parse(JSON.stringify(_defaultOpts)); + this.network = defaultOpts.network; + this.keys = { ...defaultOpts.keys }; + + if (has(opts, 'HDPrivateKey')) { + this.type = 'HDPrivateKey'; + this.HDPrivateKey = (typeof opts.HDPrivateKey === 'string') ? HDPrivateKey(opts.HDPrivateKey) : opts.HDPrivateKey; + this.network = this.HDPrivateKey.network; + } else if (has(opts, 'HDPublicKey')) { + this.type = 'HDPublicKey'; + this.HDPublicKey = (typeof opts.HDPublicKey === 'string') ? HDPublicKey(opts.HDPublicKey) : opts.HDPublicKey; + this.network = this.HDPublicKey.network; + } else if (has(opts, 'privateKey')) { + this.type = 'privateKey'; + this.privateKey = opts.privateKey; + } else if (has(opts, 'publicKey')) { + this.type = 'publicKey'; + this.publicKey = opts.publicKey; + } else if (has(opts, 'address')) { + this.type = 'address'; + this.address = opts.address.toString(); + } else { + throw new Error('Expect privateKey, publicKey, HDPublicKey, HDPrivateKey or Address'); } - this.keyChainId = generateKeyChainId(rootKey); - - this.rootKey = rootKey; - this.network = network; - this.rootKeyType = rootKeyType; - this.lookAheadOpts = { isWatched: true, ...lookAheadOpts }; - - this.issuedPaths = new Map(); - - this.maybeLookAhead(); + if (opts.network) this.network = opts.network; + if (opts.keys) this.keys = { ...opts.keys }; } } -KeyChain.prototype.getForPath = require('./methods/getForPath'); -KeyChain.prototype.getForAddress = require('./methods/getForAddress'); + +KeyChain.prototype.generateKeyForChild = require('./methods/generateKeyForChild'); +KeyChain.prototype.generateKeyForPath = require('./methods/generateKeyForPath'); KeyChain.prototype.getDIP15ExtendedKey = require('./methods/getDIP15ExtendedKey'); -KeyChain.prototype.getFirstUnusedAddress = require('./methods/getFirstUnusedAddress'); KeyChain.prototype.getHardenedBIP44HDKey = require('./methods/getHardenedBIP44HDKey'); KeyChain.prototype.getHardenedDIP9FeatureHDKey = require('./methods/getHardenedDIP9FeatureHDKey'); KeyChain.prototype.getHardenedDIP15AccountKey = require('./methods/getHardenedDIP15AccountKey'); -KeyChain.prototype.getRootKey = require('./methods/getRootKey'); -KeyChain.prototype.getWatchedAddresses = require('./methods/getWatchedAddresses'); -KeyChain.prototype.getIssuedPaths = require('./methods/getIssuedPaths'); -KeyChain.prototype.maybeLookAhead = require('./methods/maybeLookAhead'); -KeyChain.prototype.markAddressAsUsed = require('./methods/markAddressAsUsed'); +KeyChain.prototype.getKeyForChild = require('./methods/getKeyForChild'); +KeyChain.prototype.getKeyForPath = require('./methods/getKeyForPath'); +KeyChain.prototype.getPrivateKey = require('./methods/getPrivateKey'); KeyChain.prototype.sign = require('./methods/sign'); module.exports = KeyChain; diff --git a/packages/wallet-lib/src/types/KeyChain/KeyChain.spec.js b/packages/wallet-lib/src/types/KeyChain/KeyChain.spec.js index f392769cdda..3cf9bb0b5a1 100644 --- a/packages/wallet-lib/src/types/KeyChain/KeyChain.spec.js +++ b/packages/wallet-lib/src/types/KeyChain/KeyChain.spec.js @@ -8,32 +8,36 @@ let keychain2; const mnemonic = 'during develop before curtain hazard rare job language become verb message travel'; const mnemonic2 = 'birth kingdom trash renew flavor utility donkey gasp regular alert pave layer'; const pk = '4226d5e2fe8cbfe6f5beb7adf5a5b08b310f6c4a67fc27826779073be6f5699e'; -const hdPublicKey = 'xpub661MyMwAqRbcFGB6XSWBsD725rJDUbFUpy4zWe2u22nJ2BxpoHFxtVDfKnTnvVQHohnY7AsVpRTHDv6PyPQTYu1KxFPKw29MAVXPEpz1G7V'; + const expectedRootDIP15AccountKey_0 = 'tprv8hRzmheQujhJN5XP2dj955nAFCKeEoSifJRWuutdbwWRtusdDQ426jbp75EqErUSuTxmPyxYmP1TpcF5qdxGhXLNXRLMGsRLG6NFCv1WnaQ'; const expectedRootDIP15AccountKey_1 = 'tprv8hRzmheQujhJQyCtFTuUFHxB3Ag5VLB994zhH4CfxbA41cq73HT2mpYq5M33V54oJyn6g514saxxVJB886G55eYX56J6D6x87UNNT6iQHkR'; const expectedKeyForChild_0 = 'tprv8d4podc2Tg459CH2bwLHXj3vdJFBT2rdsk5Nr1djH7hzHdt5LRdvN6QyFwMiDy7ffRdik7fEVRKKgsHB4F18sh8xF6jFXpKq4sUgGBoSbKw'; describe('Keychain', function suite() { - this.timeout(1000); + this.timeout(10000); it('should create a keychain', () => { - const expectedException1 = 'Expect one of [mnemonic, HDPrivateKey, HDPublicKey, privateKey, publicKey, address] to be provided.'; + const expectedException1 = 'Expect privateKey, publicKey, HDPublicKey, HDPrivateKey or Address'; expect(() => new KeyChain()).to.throw(expectedException1); + expect(() => new KeyChain(mnemonic)).to.throw(expectedException1); - keychain = new KeyChain({ mnemonic: mnemonic, network: 'testnet' }); - expect(keychain.rootKeyType).to.equal('HDPrivateKey'); + keychain = new KeyChain({ HDPrivateKey: mnemonicToHDPrivateKey(mnemonic, 'testnet') }); + expect(keychain.type).to.equal('HDPrivateKey'); expect(keychain.network.toString()).to.equal('testnet'); - expect(keychain.rootKey.network.toString()).to.equal('testnet'); + expect(keychain.keys).to.deep.equal({}); - keychain2 = new KeyChain({ mnemonic: mnemonic2, network: 'livenet' }); + keychain2 = new KeyChain({ HDPrivateKey: mnemonicToHDPrivateKey(mnemonic2, 'mainnet') }); + }); + it('should get private key', () => { + expect(keychain.getPrivateKey().toString()).to.equal(pk); }); it('should generate key for full path', () => { const path = 'm/44\'/1\'/0\'/0/0'; - const pk2 = keychain.getForPath(path).key; + const pk2 = keychain.getKeyForPath(path); const address = new Dashcore.Address(pk2.publicKey.toAddress()).toString(); expect(address).to.equal('yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT'); }); it('should get hardened feature path', () => { const hardenedPk = keychain.getHardenedBIP44HDKey(); - const pk2 = keychain.getForPath('m/44\'/1\'').key; + const pk2 = keychain.getKeyForPath('m/44\'/1\''); expect(pk2.toString()).to.equal(hardenedPk.toString()); }); it('should get DIP15 account key', function () { @@ -42,7 +46,6 @@ describe('Keychain', function suite() { const rootDIP15AccountKey_1 = keychain.getHardenedDIP15AccountKey(1); expect(rootDIP15AccountKey_1.toString()).to.deep.equal(expectedRootDIP15AccountKey_1); }); - it('should get DIP15 extended key', function () { const userUniqueId = '0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'; const contactUniqueId = '0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'; @@ -62,10 +65,11 @@ describe('Keychain', function suite() { const DIP15ExtKey_1 = keychain.getDIP15ExtendedKey(userAhash, userBhash, 0, 0); expect(DIP15ExtKey_1.privateKey.toString()).to.equal('60581b6dca8244d3fb3cfe619b5a22277e5423b01e5285f356981f247e0f4a60') expect(DIP15ExtKey_1.publicKey.toString()).to.equal('03deaac00f721151307fbc7bf80d7b8afab98c1f026d67e5f56b21e2013f551ce6') + }); it('should derive from hardened feature path', () => { const hardenedHDKey = keychain.getHardenedBIP44HDKey(); - const pk2 = keychain.getForPath(`m/44'/1'`).key; + const pk2 = keychain.getKeyForPath(`m/44'/1'`); expect(pk2.toString()).to.equal(hardenedHDKey.toString()); expect(hardenedHDKey.toString()).to.deep.equal('tprv8dtrJNytYHRiZY585hmHGbguS6VjGpK49puSB7oXZjLHcQfrAzQkF4ZCxM2DkEbyY85J4EYcZ8EjT5ZCU8ozB727TDdodbfXet5GkGau2RQ'); const derivedPk = hardenedHDKey.deriveChild(0, true).deriveChild(0).deriveChild(0); @@ -74,88 +78,29 @@ describe('Keychain', function suite() { }); it('should get hardened DIP9FeatureHDKey', function () { const hardenedHDKey = keychain.getHardenedDIP9FeatureHDKey(); - const pk2 = keychain.getForPath(`m/9'/1'`).key; + const pk2 = keychain.getKeyForPath(`m/9'/1'`); expect(pk2.toString()).to.equal(hardenedHDKey.toString()); expect(hardenedHDKey.toString()).to.deep.equal('tprv8fBJjWoGgCpGRCbyzE9RUA59rmoN1RUijhLnXGL4VHnLxvSe523yVg4GrGzbR6TyXtdynAEh5z8UX55EXt2Cb3xjvrsx2PgTY9BHxzFVkWn'); }); - it('should get key for path', () => { + it('should generate key for child', () => { const keychain2 = new KeyChain({ HDPrivateKey: mnemonicToHDPrivateKey(mnemonic, 'testnet') }); - const keyForChild = keychain2.getForPath('m/0').key; + const keyForChild = keychain2.generateKeyForChild(0); expect(keyForChild.toString()).to.equal(expectedKeyForChild_0); }); - it('should mark address watched and get watched addresses', function () { - const key0 = keychain.getForPath('m/0'); - keychain.getForPath('m/0').isWatched = true - key0.isWatched = true; - const key1 = keychain.getForPath('m/1', { isWatched: true }); - const key2 = keychain.getForPath('m/2', { isWatched: true }); - const watchedAddresses = keychain.getWatchedAddresses(); - let expectedWatchedAddresses = [ - keychain.getForPath('m/0').address.toString(), - keychain.getForPath('m/1').address.toString(), - keychain.getForPath('m/2').address.toString() - ]; - expect(watchedAddresses).to.deep.equal(expectedWatchedAddresses); - }); - it('should get watched addresses', function () { - const watchedAddresses = keychain.getWatchedAddresses(); - const expectedWatchedAddresses = [ - 'ybQDfNwiDjk8ZH5UUmHQzAMEmjbrbK5dAj', - 'yhFX5rseJPitV45HUCaa9haeGHtLuooBaq', - 'yhqxsmYk6jfoGWf1hJKq7d4U2cGHCgzpFU' - ] - expect(watchedAddresses).to.deep.equal(expectedWatchedAddresses); - }); - // it('should get watched public keys', function () { - // const watchedPubKeys = keychain.getWatchedPublicKeys(); - // const expectedWatchedPubKeys = [ - // '03e6ab8177a7ca2699da4f83ca3c27768fb88b70ae9d6bde1cba8de88355ccf199', - // '0246a870b65153b98e453ff08f7198d06bce2a790286f44d90929aceafafa0673f', - // '025ad16f78f67801e52abe5b512d66e3896d4c2fa3ca3150349437a5dd13519967' - // ] - // expect(watchedPubKeys).to.deep.equal(expectedWatchedPubKeys) - // }); - it('should remove an address from watched addresses', function () { - const data0 = keychain.getForPath('m/0', { isWatched: false }); - const data1 = keychain.getForPath('m/1'); - const data2 = keychain.getForPath('m/2'); - data2.isWatched = false; + it('should sign', () => { - expect(keychain.getWatchedAddresses().length).to.equal(1); - }); - it('should get address for path', function (){ - const address0_1 = keychain.getForPath('m/1').address; - expect(address0_1.toString()).to.equal('yhFX5rseJPitV45HUCaa9haeGHtLuooBaq') - }) - it('should mark address as used', function () { - const address0_0 = keychain.getForPath('m/0').address; - keychain.markAddressAsUsed(address0_0); - expect(keychain.issuedPaths.get('m/0').isUsed).to.equal(true) }); }); -describe('Keychain - HDPublicKey', function suite(){ - let hdpubKeyChain; - it('should initiate from a HDPublicKey', function () { - hdpubKeyChain = new KeyChain({ - HDPublicKey: new Dashcore.HDPublicKey(hdPublicKey), - network: 'testnet' - }); - // As the HDPublicKey starts with xpub, it's livenet and should take priority over our network being set. - expect(hdpubKeyChain.network.toString()).to.equal('livenet'); - expect(hdpubKeyChain.keyChainId).to.equal('kc5059442d66'); - expect(hdpubKeyChain.getRootKey().toString()).to.equal(hdPublicKey); - }); - it('should derivate', function () { - const key0_1 = hdpubKeyChain.getForPath('m/1').key; - expect(key0_1.publicKey.toAddress(hdpubKeyChain.network).toString()).to.equal('XoL5LcBiDWcj6L7fFwytsFoX5Vz7BVXw9w') +describe('Keychain - clone', function suite() { + this.timeout(10000); + it('should clone', () => { + const keychain2 = new KeyChain(keychain); + expect(keychain2).to.deep.equal(keychain); + expect(keychain2.keys).to.deep.equal(keychain.keys); }); - it('should get address for path', function (){ - const address0_1 = hdpubKeyChain.getForPath('m/2').address; - expect(address0_1.toString()).to.equal('XwAzpxQKbgebaLiadq1c6rDeFJ4FKPUufy') - }) -}) +}); describe('Keychain - single privateKey', function suite() { this.timeout(10000); it('should correctly errors out when not a HDPublicKey (privateKey)', () => { @@ -163,16 +108,18 @@ describe('Keychain - single privateKey', function suite() { const network = 'livenet'; const pkKeyChain = new KeyChain({ privateKey, network }); expect(pkKeyChain.network).to.equal(network); - expect(pkKeyChain.rootKeyType).to.equal('privateKey'); - expect(pkKeyChain.rootKey.toString()).to.equal(privateKey); + expect(pkKeyChain.keys).to.deep.equal({}); + expect(pkKeyChain.type).to.equal('privateKey'); + expect(pkKeyChain.privateKey).to.equal(privateKey); - const expectedException1 = 'Wallet is not loaded from a mnemonic or a HDPrivateKey, impossible to derivate keys for path m/0'; - expect(() => pkKeyChain.getForPath('m/0')).to.throw(expectedException1); + const expectedException1 = 'Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate keys'; + const expectedException2 = 'Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate child'; + expect(() => pkKeyChain.generateKeyForPath()).to.throw(expectedException1); + expect(() => pkKeyChain.generateKeyForChild()).to.throw(expectedException2); }); it('should get private key', () => { const privateKey = Dashcore.PrivateKey().toString(); const pkKeyChain = new KeyChain({ privateKey, network: 'livenet' }); - expect(pkKeyChain.getRootKey().toString()).to.equal(privateKey); - expect(pkKeyChain.rootKey.toString()).to.equal(privateKey); + expect(pkKeyChain.getPrivateKey().toString()).to.equal(privateKey); }); }); diff --git a/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForChild.js b/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForChild.js new file mode 100644 index 00000000000..5d6dcaeba0f --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForChild.js @@ -0,0 +1,19 @@ +const { + HDPublicKey, +} = require('@dashevo/dashcore-lib'); +/** + * Derive from HDPrivateKey to a child + * @param {number} index - Child index to derivee to + * @param {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys + * @return {HDPrivateKey|HDPublicKey} + */ +function generateKeyForChild(index, type = 'HDPrivateKey') { + if (!['HDPrivateKey', 'HDPublicKey'].includes(this.type)) { + throw new Error('Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate child'); + } + const HDKey = this[this.type]; + const hdPublicKey = HDKey.deriveChild(index); + if (type === 'HDPublicKey') return HDPublicKey(hdPublicKey); + return hdPublicKey; +} +module.exports = generateKeyForChild; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForPath.js b/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForPath.js new file mode 100644 index 00000000000..e266c96d757 --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForPath.js @@ -0,0 +1,19 @@ +const { + HDPublicKey, +} = require('@dashevo/dashcore-lib'); +/** + * Derive from HDPrivateKey to a specific path + * @param {string} path + * @param {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys + * @return {HDPrivateKey|HDPublicKey} + */ +function generateKeyForPath(path, type = 'HDPrivateKey') { + if (!['HDPrivateKey', 'HDPublicKey'].includes(this.type)) { + throw new Error('Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate keys'); + } + const HDKey = this[this.type]; + const hdPrivateKey = HDKey.derive(path); + if (type === 'HDPublicKey') return HDPublicKey(hdPrivateKey); + return hdPrivateKey; +} +module.exports = generateKeyForPath; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getDIP15ExtendedKey.js b/packages/wallet-lib/src/types/KeyChain/methods/getDIP15ExtendedKey.js index b65dc10d7da..a7eed1547dc 100644 --- a/packages/wallet-lib/src/types/KeyChain/methods/getDIP15ExtendedKey.js +++ b/packages/wallet-lib/src/types/KeyChain/methods/getDIP15ExtendedKey.js @@ -8,7 +8,7 @@ * @return {HDPrivateKey|HDPublicKey} */ function getDIP15ExtendedKey(userUniqueId, contactUniqueId, index = 0, accountIndex = 0, type = 'HDPrivateKey') { - if (!['HDPrivateKey', 'HDPublicKey'].includes(this.rootKeyType)) { + if (!['HDPrivateKey', 'HDPublicKey'].includes(this.type)) { throw new Error('Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate keys'); } if (!userUniqueId || !contactUniqueId) throw new Error('Required userUniqueId and contactUniqueId to be defined'); diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getFirstUnusedAddress.js b/packages/wallet-lib/src/types/KeyChain/methods/getFirstUnusedAddress.js deleted file mode 100644 index cb72d60c493..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/getFirstUnusedAddress.js +++ /dev/null @@ -1,14 +0,0 @@ -function getFirstUnusedAddress() { - const allUnused = this.getIssuedPaths() - .filter((path)=>{ - return path.isUsed === false - }); - - const firstUnused = allUnused.slice(0,1)[0] - - return { - path: firstUnused.path, - address: firstUnused.address.toString() - } -} -module.exports = getFirstUnusedAddress; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedBIP44HDKey.js b/packages/wallet-lib/src/types/KeyChain/methods/getHardenedBIP44HDKey.js index 48da61c1616..75a6628475f 100644 --- a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedBIP44HDKey.js +++ b/packages/wallet-lib/src/types/KeyChain/methods/getHardenedBIP44HDKey.js @@ -2,10 +2,11 @@ const { BIP44_TESTNET_ROOT_PATH, BIP44_LIVENET_ROOT_PATH } = require('../../../C /** * Return a safier root keys to derivate from + * @param {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys * @return {HDPrivateKey|HDPublicKey} */ -function getHardenedBIP44HDKey() { +function getHardenedBIP44HDKey(type = 'HDPrivateKey') { const pathRoot = (this.network.toString() === 'testnet') ? BIP44_TESTNET_ROOT_PATH : BIP44_LIVENET_ROOT_PATH; - return this.getForPath(pathRoot).key; + return this.generateKeyForPath(pathRoot, type); } module.exports = getHardenedBIP44HDKey; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP9FeatureHDKey.js b/packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP9FeatureHDKey.js index 422c9fab667..06a4733afb0 100644 --- a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP9FeatureHDKey.js +++ b/packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP9FeatureHDKey.js @@ -2,10 +2,11 @@ const { DIP9_LIVENET_ROOT_PATH, DIP9_TESTNET_ROOT_PATH } = require('../../../CON /** * Return a safier root path to derivate from + * @param {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys * @return {HDPrivateKey|HDPublicKey} */ -function getHardenedDIP9FeatureHDKey() { +function getHardenedDIP9FeatureHDKey(type = 'HDPrivateKey') { const pathRoot = (this.network.toString() === 'testnet') ? DIP9_TESTNET_ROOT_PATH : DIP9_LIVENET_ROOT_PATH; - return this.getForPath(pathRoot).key; + return this.generateKeyForPath(pathRoot, type); } module.exports = getHardenedDIP9FeatureHDKey; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getKeyForChild.js b/packages/wallet-lib/src/types/KeyChain/methods/getKeyForChild.js new file mode 100644 index 00000000000..768f1740d55 --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChain/methods/getKeyForChild.js @@ -0,0 +1,10 @@ +/** + * Generate a key by deriving it's direct child + * @param index - {Number} + * @return {HDPrivateKey | HDPublicKey} + */ +function getKeyForChild(index = 0, type = 'HDPrivateKey') { + return this.generateKeyForChild(index, type); +} + +module.exports = getKeyForChild; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getKeyForPath.js b/packages/wallet-lib/src/types/KeyChain/methods/getKeyForPath.js new file mode 100644 index 00000000000..4f7bb180d1d --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChain/methods/getKeyForPath.js @@ -0,0 +1,29 @@ +const { HDPrivateKey, PrivateKey } = require('@dashevo/dashcore-lib'); +/** + * Get a key from the cache or generate if none + * @param path + * @param type - def : HDPrivateKey - Expected return datatype of the keys + * @return {HDPrivateKey | HDPublicKey} + */ +function getKeyForPath(path, type = 'HDPrivateKey') { + if (type === 'HDPublicKey') { + // In this case, we do not generate or keep in cache. + return this.generateKeyForPath(path, type); + } + + if (this.type === 'HDPrivateKey') { + if (!this.keys[path]) { + this.keys[path] = this.generateKeyForPath(path, type).toString(); + } + return new HDPrivateKey(this.keys[path]); + } + if (this.type === 'privateKey') { + if (!this.keys[path]) { + this.keys[path] = this.getPrivateKey(path).toString(); + return new PrivateKey(this.keys[path]); + } + return new PrivateKey(this.keys[path]); + } + return new HDPrivateKey(this.keys[path]); +} +module.exports = getKeyForPath; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getPrivateKey.js b/packages/wallet-lib/src/types/KeyChain/methods/getPrivateKey.js new file mode 100644 index 00000000000..602c287daf7 --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChain/methods/getPrivateKey.js @@ -0,0 +1,18 @@ +const { + PrivateKey, +} = require('@dashevo/dashcore-lib'); + +/** + * @return {PrivateKey} + */ +function getPrivateKey() { + let pk; + if (this.type === 'HDPrivateKey') { + pk = PrivateKey(this.HDPrivateKey.privateKey); + } + if (this.type === 'privateKey') { + pk = PrivateKey(this.privateKey); + } + return pk; +} +module.exports = getPrivateKey; diff --git a/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.spec.js b/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.spec.js index b32737bb7a2..bfb74a6cd81 100644 --- a/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.spec.js +++ b/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.spec.js @@ -1,15 +1,15 @@ const {HDPrivateKey} = require("@dashevo/dashcore-lib"); const KeyChainsStore = require('./KeyChainStore'); -const KeyChain = require("../KeyChain/KeyChain"); +const DerivableKeyChain = require("../DerivableKeyChain/DerivableKeyChain"); const { expect } = require('chai'); describe('KeyChainStore', function suite() { let keyChainsStore; let hdPrivateKey = new HDPrivateKey() let hdPublicKey = new HDPrivateKey().hdPublicKey - let keyChain = new KeyChain({HDPrivateKey: hdPrivateKey}) - let keyChainPublic = new KeyChain({HDPublicKey: hdPublicKey}) - let walletKeyChain = new KeyChain({HDPrivateKey:new HDPrivateKey()}); + let keyChain = new DerivableKeyChain({HDPrivateKey: hdPrivateKey}) + let keyChainPublic = new DerivableKeyChain({HDPublicKey: hdPublicKey}) + let walletKeyChain = new DerivableKeyChain({HDPrivateKey:new HDPrivateKey()}); it('should create a KeyChainStore', () => { keyChainsStore = new KeyChainsStore(); expect(keyChainsStore).to.exist; diff --git a/packages/wallet-lib/src/types/KeyChainStore/methods/makeChildKeyChainStore.js b/packages/wallet-lib/src/types/KeyChainStore/methods/makeChildKeyChainStore.js index b341184f90d..383baee3ce0 100644 --- a/packages/wallet-lib/src/types/KeyChainStore/methods/makeChildKeyChainStore.js +++ b/packages/wallet-lib/src/types/KeyChainStore/methods/makeChildKeyChainStore.js @@ -1,6 +1,4 @@ -const { Networks } = require('@dashevo/dashcore-lib'); -const { BIP44_LIVENET_ROOT_PATH, BIP44_TESTNET_ROOT_PATH } = require('../../../CONSTANTS'); -const KeyChain = require('../../KeyChain/KeyChain'); +const DerivableKeyChain = require('../../DerivableKeyChain/DerivableKeyChain'); const logger = require('../../../logger'); function makeChildKeyChainStore(path, opts) { @@ -13,7 +11,7 @@ function makeChildKeyChainStore(path, opts) { // Accessing the type from getKeyForPath would behave on browser differently due to mangling. keyChainOpts[masterKeyChain.rootKeyType] = masterKeyChain.getForPath(path).key; - const childKeyChain = new KeyChain(keyChainOpts); + const childKeyChain = new DerivableKeyChain(keyChainOpts); childKeyChainStore.addKeyChain(childKeyChain, { isMasterKeyChain: true }); return childKeyChainStore; } diff --git a/packages/wallet-lib/src/types/Wallet/Wallet.spec.js b/packages/wallet-lib/src/types/Wallet/Wallet.spec.js index a6d416bfbee..5f4a4368e20 100644 --- a/packages/wallet-lib/src/types/Wallet/Wallet.spec.js +++ b/packages/wallet-lib/src/types/Wallet/Wallet.spec.js @@ -23,7 +23,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); - expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('HDPrivateKey'); + expect(wallet1.keyChain.type).to.be.deep.equal('HDPrivateKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -50,7 +50,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('HDPrivateKey'); + expect(wallet1.keyChain.type).to.be.deep.equal('HDPrivateKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -77,7 +77,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('HDPrivateKey'); + expect(wallet1.keyChain.type).to.be.deep.equal('HDPrivateKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -95,7 +95,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('HDPublicKey'); + expect(wallet1.keyChain.type).to.be.deep.equal('HDPublicKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -112,7 +112,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('privateKey'); + expect(wallet1.keyChain.type).to.be.deep.equal('privateKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -132,7 +132,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('publicKey'); + expect(wallet1.keyChain.type).to.be.deep.equal('publicKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -149,7 +149,7 @@ describe('Wallet - class', function suite() { expect(wallet2.plugins).to.be.deep.equal({}); expect(wallet2.accounts).to.be.deep.equal([]); expect(wallet2.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet2.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('publicKey'); + expect(wallet2.keyChain.type).to.be.deep.equal('publicKey'); expect(wallet2.passphrase).to.be.deep.equal(null); expect(wallet2.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet2.injectDefaultPlugins).to.be.deep.equal(true); @@ -170,7 +170,7 @@ describe('Wallet - class', function suite() { }); }); describe('Wallet - Get/Create Account', function suite() { - this.timeout(10000); + this.timeout(15000); const wallet1 = new Wallet({ mnemonic: fluidMnemonic.mnemonic, ...mocks }); it('should be able to create/get a wallet', async () => { @@ -246,5 +246,6 @@ describe('Wallet - Get/Create Account', function suite() { it('should not leak', () => { const mockOpts1 = { }; fromHDPublicKey.call(mockOpts1, gatherSail.testnet.external.hdpubkey); + expect(mockOpts1.keyChain.keys).to.deep.equal({}); }); }); diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromAddress.js b/packages/wallet-lib/src/types/Wallet/methods/fromAddress.js index 2397a91aab9..c69c7749b9d 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromAddress.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromAddress.js @@ -1,18 +1,14 @@ const { is } = require('../../../utils'); const KeyChain = require('../../KeyChain/KeyChain'); const { WALLET_TYPES } = require('../../../CONSTANTS'); -const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); /** * @param address */ -module.exports = function fromAddress(address, network) { +module.exports = function fromAddress(address) { if (!is.address(address)) throw new Error('Expected a valid address (typeof Address or String)'); this.walletType = WALLET_TYPES.ADDRESS; this.mnemonic = null; this.address = address.toString(); - - const keyChain = new KeyChain({ address, network }); - this.keyChainStore = new KeyChainStore(); - this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); + this.keyChain = new KeyChain({ address }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromAddress.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromAddress.spec.js index f0839308fc9..44bd996a4d2 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromAddress.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromAddress.spec.js @@ -18,20 +18,18 @@ describe('Wallet - fromAddress', function suite() { expect(self1.walletType).to.equal(WALLET_TYPES.ADDRESS); expect(self1.mnemonic).to.equal(null); expect(self1.address).to.equal(cR4t6ePublicKey.toAddress().toString()); - - const keyChain = self1.keyChainStore.getMasterKeyChain() - expect(keyChain.rootKeyType).to.equal('address'); - expect(keyChain.rootKey).to.equal(cR4t6ePublicKey.toAddress().toString()); + expect(self1.keyChain.type).to.equal('address'); + expect(self1.keyChain.address).to.equal(cR4t6ePublicKey.toAddress().toString()); + expect(self1.keyChain.keys).to.deep.equal({}); const self2 = {}; fromAddress.call(self2, cR4t6ePublicKey.toAddress().toString()); expect(self2.walletType).to.equal(WALLET_TYPES.ADDRESS); expect(self2.mnemonic).to.equal(null); expect(self2.address).to.equal(cR4t6ePublicKey.toAddress().toString()); - - const keyChain2 = self2.keyChainStore.getMasterKeyChain() - expect(keyChain.rootKeyType).to.equal('address'); - expect(keyChain.rootKey).to.equal(cR4t6ePublicKey.toAddress().toString()); + expect(self2.keyChain.type).to.equal('address'); + expect(self2.keyChain.address).to.equal(cR4t6ePublicKey.toAddress().toString()); + expect(self2.keyChain.keys).to.deep.equal({}); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.js b/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.js index 90b6f1a9061..da2aba6e047 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.js @@ -3,7 +3,6 @@ const { is, } = require('../../../utils'); const KeyChain = require('../../KeyChain/KeyChain'); -const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); const { WALLET_TYPES } = require('../../../CONSTANTS'); /** @@ -15,8 +14,5 @@ module.exports = function fromHDPrivateKey(hdPrivateKey) { this.walletType = WALLET_TYPES.HDWALLET; this.mnemonic = null; this.HDPrivateKey = HDPrivateKey(hdPrivateKey); - - const keyChain = new KeyChain({ HDPrivateKey: this.HDPrivateKey }); - this.keyChainStore = new KeyChainStore(); - this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); + this.keyChain = new KeyChain({ HDPrivateKey: hdPrivateKey }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.spec.js index c947a076b4c..5c277483190 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.spec.js @@ -16,10 +16,9 @@ describe('Wallet - fromHDPrivateKey', function suite() { expect(self1.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self1.mnemonic).to.equal(null); expect(self1.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); - - const keyChain = self1.keyChainStore.getMasterKeyChain() - expect(keyChain.rootKeyType).to.equal('HDPrivateKey'); - expect(keyChain.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); + expect(self1.keyChain.type).to.equal('HDPrivateKey'); + expect(self1.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); + expect(self1.keyChain.keys).to.deep.equal({}); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.js b/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.js index aa6a84078a1..0631c051151 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.js @@ -2,7 +2,6 @@ const Dashcore = require('@dashevo/dashcore-lib'); const { is } = require('../../../utils'); const KeyChain = require('../../KeyChain/KeyChain'); const { WALLET_TYPES } = require('../../../CONSTANTS'); -const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); const normalizeHDPubKey = (key) => (is.string(key) ? Dashcore.HDPublicKey(key) : key); /** @@ -14,8 +13,5 @@ module.exports = function fromHDPublicKey(_hdPublicKey) { this.walletType = WALLET_TYPES.HDPUBLIC; this.mnemonic = null; this.HDPublicKey = normalizeHDPubKey(_hdPublicKey); - - const keyChain = new KeyChain({ HDPublicKey: this.HDPublicKey }); - this.keyChainStore = new KeyChainStore(); - this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); + this.keyChain = new KeyChain({ HDPublicKey: this.HDPublicKey }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.spec.js index fca3387d34e..96a1c9cf847 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.spec.js @@ -30,10 +30,9 @@ describe('Wallet - HDPublicKey', function suite() { expect(mockOpts1.mnemonic).to.equal(null); expect(mockOpts1.HDPublicKey.toString()).to.equal(gatherTestnet.external.hdpubkey); expect(new Dashcore.HDPublicKey(mockOpts1.HDPublicKey)).to.equal(mockOpts1.HDPublicKey); - - const keyChain = mockOpts1.keyChainStore.getMasterKeyChain() - expect(keyChain.rootKeyType).to.equal('HDPublicKey'); - expect(keyChain.rootKey).to.deep.equal(Dashcore.HDPublicKey(gatherTestnet.external.hdpubkey)); + expect(mockOpts1.keyChain.type).to.equal('HDPublicKey'); + expect(mockOpts1.keyChain.HDPublicKey).to.deep.equal(Dashcore.HDPublicKey(gatherTestnet.external.hdpubkey)); + expect(mockOpts1.keyChain.keys).to.deep.equal({}); }); it('should work from a HDPubKey', () => { const wallet1 = new Wallet( @@ -46,9 +45,7 @@ describe('Wallet - HDPublicKey', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - - const keyChain = wallet1.keyChainStore.getMasterKeyChain() - expect(keyChain.rootKeyType).to.be.deep.equal('HDPublicKey'); + expect(wallet1.keyChain.type).to.be.deep.equal('HDPublicKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -57,28 +54,22 @@ describe('Wallet - HDPublicKey', function suite() { expect(wallet1.exportWallet()).to.be.equal(gatherTestnet.external.hdpubkey); - // FIXME: it appears we had introduced a bug here, - // as it is not possible to have a HDPublicKey derivation with hardened - // Either our path is m/44/1/0/0/0 or it is m/0/0. - // We should clarify this before merging TODO - wallet1 - .getAccount() - .then((account)=>{ - const unusedAddress = account.getUnusedAddress(); - const expectedUnused = { - path: "m/0/0", - index: 0, - address: 'yNJ3xxTXXBBf39VfMBbBuLH2k57uAwxBxj', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false, - }; - expect(unusedAddress).to.deep.equal(expectedUnused); + wallet1.getAccount().then((account)=>{ + const unusedAddress = account.getUnusedAddress(); + const expectedUnused = { + path: "m/44'/1'/0'/0/0", + index: 0, + address: 'yNJ3xxTXXBBf39VfMBbBuLH2k57uAwxBxj', + transactions: [], + balanceSat: 0, + unconfirmedBalanceSat: 0, + utxos: {}, + fetchedLast: 0, + used: false, + }; + expect(unusedAddress).to.deep.equal(expectedUnused); - wallet1.disconnect(); - }) + wallet1.disconnect(); + }) }); }); diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.js b/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.js index 1e2cbdda7a3..4bdeaefefa8 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.js @@ -3,25 +3,19 @@ const { is, } = require('../../../utils'); const KeyChain = require('../../KeyChain/KeyChain'); -const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); const { WALLET_TYPES } = require('../../../CONSTANTS'); /** * Will set a wallet to work with a mnemonic (keychain, walletType & HDPrivateKey) * @param mnemonic */ -module.exports = function fromMnemonic(mnemonic, network, passphrase = '') { +module.exports = function fromMnemonic(mnemonic) { if (!is.mnemonic(mnemonic)) { throw new Error('Expected a valid mnemonic (typeof String or Mnemonic)'); } const trimmedMnemonic = mnemonic.toString().trim(); this.walletType = WALLET_TYPES.HDWALLET; - // As we do not require the mnemonic except in this.exportWallet - // users of wallet-lib are free to clear this prop at anytime. - this.mnemonic = trimmedMnemonic; - this.HDPrivateKey = mnemonicToHDPrivateKey(trimmedMnemonic, network, passphrase); - - this.keyChainStore = new KeyChainStore(); - const keyChain = new KeyChain({ HDPrivateKey: this.HDPrivateKey }); - this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); + this.mnemonic = trimmedMnemonic; // todo : What about without this ? + this.HDPrivateKey = mnemonicToHDPrivateKey(trimmedMnemonic, this.network, this.passphrase); + this.keyChain = new KeyChain({ HDPrivateKey: this.HDPrivateKey }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.spec.js index 85f498c6172..14e81028618 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.spec.js @@ -15,29 +15,27 @@ describe('Wallet - fromMnemonic', function suite() { const self1 = { network: 'livenet', }; - fromMnemonic.call(self1, knifeFixture.mnemonic, 'livenet'); + fromMnemonic.call(self1, knifeFixture.mnemonic); expect(self1.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self1.mnemonic).to.equal(knifeFixture.mnemonic); expect(self1.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); expect(new Dashcore.HDPrivateKey(self1.HDPrivateKey)).to.equal(self1.HDPrivateKey); - - const keyChain = self1.keyChainStore.getMasterKeyChain() - expect(keyChain.rootKeyType).to.equal('HDPrivateKey'); - expect(keyChain.network).to.equal('livenet'); - expect(keyChain.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); + expect(self1.keyChain.type).to.equal('HDPrivateKey'); + expect(self1.keyChain.network.name).to.equal('livenet'); + expect(self1.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); + expect(self1.keyChain.keys).to.deep.equal({}); const self2 = {}; fromMnemonic.call(self2, knifeFixture.mnemonic); expect(self2.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self2.mnemonic).to.equal(knifeFixture.mnemonic); - - const keyChain2 = self2.keyChainStore.getMasterKeyChain() - expect(keyChain2.network).to.equal('testnet'); + expect(self2.keyChain.network.name).to.equal('testnet'); expect(self2.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); expect(new Dashcore.HDPrivateKey(self2.HDPrivateKey)).to.equal(self2.HDPrivateKey); - expect(keyChain2.rootKeyType).to.equal('HDPrivateKey'); - expect(keyChain2.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); + expect(self2.keyChain.type).to.equal('HDPrivateKey'); + expect(self2.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); + expect(self2.keyChain.keys).to.deep.equal({}); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ @@ -57,35 +55,38 @@ describe('Wallet - fromMnemonic - with passphrase', function suite() { this.timeout(10000); it('should correctly works with passphrase', () => { const self1 = { + network: 'livenet', + passphrase: knifeFixture.passphrase, }; - fromMnemonic.call(self1, knifeFixture.mnemonic, 'livenet', knifeFixture.passphrase); + fromMnemonic.call(self1, knifeFixture.mnemonic); expect(self1.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self1.mnemonic).to.equal(knifeFixture.mnemonic); expect(self1.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyMainnet); expect(new Dashcore.HDPrivateKey(self1.HDPrivateKey)).to.equal(self1.HDPrivateKey); - const keyChain = self1.keyChainStore.getMasterKeyChain() - expect(keyChain.rootKeyType).to.equal('HDPrivateKey'); - expect(keyChain.network).to.equal('livenet'); - expect(keyChain.rootKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyMainnet); + expect(self1.keyChain.type).to.equal('HDPrivateKey'); + expect(self1.keyChain.network.name).to.equal('livenet'); + expect(self1.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyMainnet); + expect(self1.keyChain.keys).to.deep.equal({}); const path1 = 'm/44\'/5\'/0\'/0/0'; - const pubKey1 = keyChain.getForPath(path1).key.publicKey.toAddress(); + const pubKey1 = self1.keyChain.getKeyForPath(path1).publicKey.toAddress(); expect(new Dashcore.Address(pubKey1).toString()).to.equal('Xq3zjky18WjwAHpLgGLasvX5g8TeLRKaxt'); const self2 = { + passphrase: knifeFixture.passphrase, }; - fromMnemonic.call(self2, knifeFixture.mnemonic, 'testnet', knifeFixture.passphrase); + fromMnemonic.call(self2, knifeFixture.mnemonic); expect(self2.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self2.mnemonic).to.equal(knifeFixture.mnemonic); expect(self2.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyTestnet); expect(new Dashcore.HDPrivateKey(self2.HDPrivateKey)).to.equal(self2.HDPrivateKey); - const keyChain2 = self2.keyChainStore.getMasterKeyChain() - expect(keyChain2.rootKeyType).to.equal('HDPrivateKey'); - expect(keyChain2.network).to.equal('testnet'); - expect(keyChain2.rootKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyTestnet); + expect(self2.keyChain.type).to.equal('HDPrivateKey'); + expect(self2.keyChain.network.name).to.equal('testnet'); + expect(self2.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyTestnet); + expect(self2.keyChain.keys).to.deep.equal({}); const path2 = 'm/44\'/1\'/0\'/0/0'; - const pubKey2 = keyChain2.getForPath(path2).key.publicKey.toAddress(); + const pubKey2 = self2.keyChain.getKeyForPath(path2).publicKey.toAddress(); expect(new Dashcore.Address(pubKey2, 'testnet').toString()).to.equal('yWYCH9XDRnpdNxh67jQJFkovToBVwWr8Ck'); }); }); diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.js b/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.js index 870af9937d8..2f8f919bc7e 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.js @@ -1,19 +1,15 @@ const { is } = require('../../../utils'); const KeyChain = require('../../KeyChain/KeyChain'); const { WALLET_TYPES } = require('../../../CONSTANTS'); -const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); /** * Will set a wallet to work with a mnemonic (keychain, walletType & HDPrivateKey) * @param privateKey */ -module.exports = function fromPrivateKey(privateKey, network) { +module.exports = function fromPrivateKey(privateKey) { if (!is.privateKey(privateKey)) throw new Error('Expected a valid private key (typeof PrivateKey or String)'); this.walletType = WALLET_TYPES.PRIVATEKEY; this.mnemonic = null; this.privateKey = privateKey; - - const keyChain = new KeyChain({ privateKey, network }); - this.keyChainStore = new KeyChainStore(); - this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); + this.keyChain = new KeyChain({ privateKey }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.spec.js index 4b6ca5e89ab..c707c2faddc 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.spec.js @@ -16,18 +16,18 @@ describe('Wallet - fromPrivateKey', function suite() { expect(self1.walletType).to.equal(WALLET_TYPES.PRIVATEKEY); expect(self1.mnemonic).to.equal(null); expect(self1.privateKey).to.equal(cR4t6eFixture.privateKey); - const keyChain = self1.keyChainStore.getMasterKeyChain() - expect(keyChain.rootKeyType).to.equal('privateKey'); - expect(keyChain.rootKey.toWIF()).to.equal(cR4t6eFixture.privateKey); + expect(self1.keyChain.type).to.equal('privateKey'); + expect(self1.keyChain.privateKey).to.equal(cR4t6eFixture.privateKey); + expect(self1.keyChain.keys).to.deep.equal({}); const self2 = {}; fromPrivateKey.call(self2, cR4t6eFixture.privateKey); expect(self2.walletType).to.equal(WALLET_TYPES.PRIVATEKEY); expect(self2.mnemonic).to.equal(null); expect(self2.privateKey).to.equal(cR4t6eFixture.privateKey); - const keyChain2 = self2.keyChainStore.getMasterKeyChain() - expect(keyChain2.rootKeyType).to.equal('privateKey'); - expect(keyChain2.rootKey.toWIF()).to.equal(cR4t6eFixture.privateKey); + expect(self2.keyChain.type).to.equal('privateKey'); + expect(self2.keyChain.privateKey).to.equal(cR4t6eFixture.privateKey); + expect(self2.keyChain.keys).to.deep.equal({}); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.js b/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.js index ffbf41344c9..9e6867ac78f 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.js @@ -1,19 +1,15 @@ const { is } = require('../../../utils'); const KeyChain = require('../../KeyChain/KeyChain'); const { WALLET_TYPES } = require('../../../CONSTANTS'); -const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); /** * Will set a wallet to work with a mnemonic (keychain, walletType & HDPrivateKey) * @param privateKey */ -module.exports = function fromPublicKey(publicKey, network) { +module.exports = function fromPublicKey(publicKey) { if (!is.publicKey(publicKey)) throw new Error('Expected a valid public key (typeof PublicKey or String)'); this.walletType = WALLET_TYPES.PUBLICKEY; this.mnemonic = null; this.publicKey = publicKey; - - const keyChain = new KeyChain({ publicKey, network }); - this.keyChainStore = new KeyChainStore(); - this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); + this.keyChain = new KeyChain({ publicKey }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.spec.js index 07890f2d45d..8852ee6c6ee 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.spec.js @@ -18,18 +18,18 @@ describe('Wallet - fromPublicKey', function suite() { expect(self1.walletType).to.equal(WALLET_TYPES.PUBLICKEY); expect(self1.mnemonic).to.equal(null); expect(self1.publicKey).to.equal(cR4t6ePublicKey); - const keyChain = self1.keyChainStore.getMasterKeyChain() - expect(keyChain.rootKeyType).to.equal('publicKey'); - expect(keyChain.rootKey.toString()).to.equal(cR4t6ePublicKey.toString()); + expect(self1.keyChain.type).to.equal('publicKey'); + expect(self1.keyChain.publicKey).to.equal(cR4t6ePublicKey); + expect(self1.keyChain.keys).to.deep.equal({}); const self2 = {}; fromPublicKey.call(self2, cR4t6ePublicKey.toString()); expect(self2.walletType).to.equal(WALLET_TYPES.PUBLICKEY); expect(self2.mnemonic).to.equal(null); expect(self2.publicKey).to.equal(cR4t6ePublicKey.toString()); - const keyChain2 = self2.keyChainStore.getMasterKeyChain() - expect(keyChain2.rootKeyType).to.equal('publicKey'); - expect(keyChain2.rootKey.toString()).to.equal(cR4t6ePublicKey.toString()); + expect(self2.keyChain.type).to.equal('publicKey'); + expect(self2.keyChain.publicKey).to.equal(cR4t6ePublicKey.toString()); + expect(self2.keyChain.keys).to.deep.equal({}); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromSeed.js b/packages/wallet-lib/src/types/Wallet/methods/fromSeed.js index 038fc138e6b..978adc9ecda 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromSeed.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromSeed.js @@ -8,7 +8,7 @@ const { * fixme: Term seed is often use, but we might want to rename to fromHDPrivateKey * @param seed */ -module.exports = function fromSeed(seed, network) { +module.exports = function fromSeed(seed) { if (!is.seed(seed)) throw new Error('Expected a valid seed (typeof string)'); - return this.fromHDPrivateKey(seedToHDPrivateKey(seed, network)); + return this.fromHDPrivateKey(seedToHDPrivateKey(seed, this.network)); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromSeed.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromSeed.spec.js index a0a9ec827ca..75e9d32cda1 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromSeed.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromSeed.spec.js @@ -19,22 +19,22 @@ describe('Wallet - fromSeed', function suite() { expect(self1.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self1.mnemonic).to.equal(null); expect(self1.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); - const keyChain = self1.keyChainStore.getMasterKeyChain() - expect(keyChain.rootKeyType).to.equal('HDPrivateKey'); - expect(keyChain.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); + expect(self1.keyChain.type).to.equal('HDPrivateKey'); + expect(self1.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); + expect(self1.keyChain.keys).to.deep.equal({}); const self2 = { fromHDPrivateKey, network: 'mainnet', }; - fromSeed.call(self2, knifeFixture.seed, self2.network); + fromSeed.call(self2, knifeFixture.seed); expect(self2.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self2.mnemonic).to.equal(null); expect(self2.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); - const keyChain2 = self2.keyChainStore.getMasterKeyChain() - expect(keyChain2.rootKeyType).to.equal('HDPrivateKey'); - expect(keyChain2.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); + expect(self2.keyChain.type).to.equal('HDPrivateKey'); + expect(self2.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); + expect(self2.keyChain.keys).to.deep.equal({}); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ diff --git a/packages/wallet-lib/src/utils/bip44/ensureAddressesToGapLimit.js b/packages/wallet-lib/src/utils/bip44/ensureAddressesToGapLimit.js index a4c84cbc784..318d7959f8f 100644 --- a/packages/wallet-lib/src/utils/bip44/ensureAddressesToGapLimit.js +++ b/packages/wallet-lib/src/utils/bip44/ensureAddressesToGapLimit.js @@ -1,5 +1,5 @@ const logger = require('../../logger'); -const { BIP44_ADDRESS_GAP, WALLET_TYPES } = require('../../CONSTANTS'); +const { BIP44_ADDRESS_GAP } = require('../../CONSTANTS'); const is = require('../is'); const getMissingIndexes = require('./getMissingIndexes'); @@ -77,15 +77,13 @@ function ensureAccountAddressesToGapLimit(walletStore, walletType, accountIndex, const gapBetweenLastUsedAndLastGenerated = { external: lastGeneratedIndexes.external - lastUsedIndexes.external, + internal: lastGeneratedIndexes.internal - lastUsedIndexes.internal, }; const addressesToGenerate = { external: BIP44_ADDRESS_GAP - gapBetweenLastUsedAndLastGenerated.external, + internal: BIP44_ADDRESS_GAP - gapBetweenLastUsedAndLastGenerated.internal, }; - if (walletType.includes(WALLET_TYPES.HDWALLET)) { - // eslint-disable-next-line max-len - gapBetweenLastUsedAndLastGenerated.internal = lastGeneratedIndexes.internal - lastUsedIndexes.internal; - addressesToGenerate.internal = BIP44_ADDRESS_GAP - gapBetweenLastUsedAndLastGenerated.internal; - } + Object.entries(addressesToGenerate) .forEach(([typeToGenerate, numberToGenerate]) => { if (numberToGenerate > 0) { From 99100f00a247772c6154b1b1641b8912d8b47273 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Mon, 31 Jan 2022 03:56:27 +0100 Subject: [PATCH 03/27] feat: connect with KeyChainStore --- packages/wallet-lib/src/CONSTANTS.js | 3 +- packages/wallet-lib/src/EVENTS.js | 1 + packages/wallet-lib/src/index.d.ts | 21 +-- packages/wallet-lib/src/index.js | 16 ++- .../src/test/mocks/mockAccountWithStorage.js | 32 +++++ .../wallet-lib/src/types/Account/Account.d.ts | 4 +- .../wallet-lib/src/types/Account/Account.js | 132 +++++++++++------- .../src/types/Account/Account.spec.js | 27 ++-- .../Account/methods/createTransaction.js | 2 +- .../Account/methods/createTransaction.spec.js | 30 ++-- .../src/types/Account/methods/encrypt.spec.js | 2 +- .../types/Account/methods/generateAddress.js | 72 +++++++--- .../types/Account/methods/getPrivateKeys.js | 23 ++- .../types/Account/methods/getUnusedAddress.js | 54 ++++--- .../Account/methods/getUnusedAddress.spec.js | 59 ++------ .../Account/methods/importTransactions.js | 56 +++++--- .../src/types/Account/methods/sign.js | 2 +- .../src/types/Account/methods/sign.spec.js | 2 +- .../src/types/Identities/Identities.js | 2 +- .../methods/getIdentityHDKeyById.js | 5 +- .../wallet-lib/src/types/Wallet/Wallet.js | 10 +- .../src/types/Wallet/Wallet.spec.js | 17 ++- .../src/types/Wallet/methods/exportWallet.js | 4 +- .../types/Wallet/methods/exportWallet.spec.js | 6 +- .../src/types/Wallet/methods/fromAddress.js | 10 +- .../types/Wallet/methods/fromAddress.spec.js | 14 +- .../types/Wallet/methods/fromHDPrivateKey.js | 8 +- .../Wallet/methods/fromHDPrivateKey.spec.js | 7 +- .../types/Wallet/methods/fromHDPublicKey.js | 8 +- .../Wallet/methods/fromHDPublicKey.spec.js | 49 ++++--- .../src/types/Wallet/methods/fromMnemonic.js | 16 ++- .../types/Wallet/methods/fromMnemonic.spec.js | 47 +++---- .../types/Wallet/methods/fromPrivateKey.js | 10 +- .../Wallet/methods/fromPrivateKey.spec.js | 12 +- .../src/types/Wallet/methods/fromPublicKey.js | 10 +- .../Wallet/methods/fromPublicKey.spec.js | 12 +- .../src/types/Wallet/methods/fromSeed.js | 4 +- .../src/types/Wallet/methods/fromSeed.spec.js | 14 +- 38 files changed, 485 insertions(+), 318 deletions(-) create mode 100644 packages/wallet-lib/src/test/mocks/mockAccountWithStorage.js diff --git a/packages/wallet-lib/src/CONSTANTS.js b/packages/wallet-lib/src/CONSTANTS.js index 87edf80f4bd..838efb8ef15 100644 --- a/packages/wallet-lib/src/CONSTANTS.js +++ b/packages/wallet-lib/src/CONSTANTS.js @@ -46,7 +46,9 @@ const CONSTANTS = { PRIVATEKEY: 'privateKey', // TODO: DEPRECATE. SINGLE_ADDRESS: 'single_address', + // TODO: DEPRECATE. HDWALLET: 'hdwallet', + HDPRIVATE: 'hdprivate', HDPUBLIC: 'hdpublic', }, // List of account function and properties that can be injected in a plugin @@ -79,7 +81,6 @@ const CONSTANTS = { ], UNSAFE_PROPERTIES: [ 'storage', - 'keyChain', 'identities', ], SAFE_PROPERTIES: [ diff --git a/packages/wallet-lib/src/EVENTS.js b/packages/wallet-lib/src/EVENTS.js index 7be5a5edd6d..1f6448ffc82 100644 --- a/packages/wallet-lib/src/EVENTS.js +++ b/packages/wallet-lib/src/EVENTS.js @@ -10,6 +10,7 @@ module.exports = { TRANSACTION: 'transaction', BLOCKHEADER: 'blockheader', FETCHED_ADDRESS: 'FETCHED/ADDRESS', + UPDATED_ADDRESS: 'UPDATED/ADDRESS', ERROR_UPDATE_ADDRESS: 'ERROR/UPDATE_ADDRESS', FETCHED_TRANSACTION: 'FETCHED/TRANSACTION', FETCHED_UNCONFIRMED_TRANSACTION: 'FETCHED/UNCONFIRMED_TRANSACTION', diff --git a/packages/wallet-lib/src/index.d.ts b/packages/wallet-lib/src/index.d.ts index 9ea8f7c7434..0b4606bec8a 100644 --- a/packages/wallet-lib/src/index.d.ts +++ b/packages/wallet-lib/src/index.d.ts @@ -4,20 +4,23 @@ import { Account } from "./types/Account/Account"; import { Wallet } from "./types/Wallet/Wallet"; import { Identities } from "./types/Identities/Identities"; -import { KeyChain } from "./types/KeyChain/KeyChain"; +import { DerivableKeyChain } from "./types/DerivableKeyChain/DerivableKeyChain"; import CONSTANTS from "./CONSTANTS"; import EVENTS from "./EVENTS"; import utils from "./utils"; import plugins from "./plugins"; export { - Account, - Wallet, - KeyChain, - Identities, - EVENTS, - CONSTANTS, - utils, - plugins, + Account, + Wallet, + ChainStore, + DerivableKeyChain, + KeyChainStore, + Identities, + IdentitiesStore, + EVENTS, + CONSTANTS, + utils, + plugins, }; declare module '@dashevo/wallet-lib'; diff --git a/packages/wallet-lib/src/index.js b/packages/wallet-lib/src/index.js index 2fa9d8a9e30..71610cdc033 100644 --- a/packages/wallet-lib/src/index.js +++ b/packages/wallet-lib/src/index.js @@ -2,20 +2,28 @@ // polyfill included here. Making it work with webpack is rather tricky, so it is used as per // documentation: https://github.com/YuzuJS/setImmediate#usage require('setimmediate'); -const Wallet = require('./types/Wallet/Wallet'); const Account = require('./types/Account/Account'); +const ChainStore = require('./types/ChainStore/ChainStore'); const Identities = require('./types/Identities/Identities'); -const KeyChain = require('./types/KeyChain/KeyChain'); +const DerivableKeyChain = require('./types/DerivableKeyChain/DerivableKeyChain'); +const KeyChainStore = require('./types/KeyChainStore/KeyChainStore'); +const Storage = require('./types/Storage/Storage'); +const Wallet = require('./types/Wallet/Wallet'); +const WalletStore = require('./types/WalletStore/WalletStore'); const EVENTS = require('./EVENTS'); const CONSTANTS = require('./CONSTANTS'); const utils = require('./utils'); const plugins = require('./plugins'); module.exports = { - Wallet, Account, + ChainStore, Identities, - KeyChain, + DerivableKeyChain, + KeyChainStore, + Storage, + Wallet, + WalletStore, EVENTS, CONSTANTS, utils, diff --git a/packages/wallet-lib/src/test/mocks/mockAccountWithStorage.js b/packages/wallet-lib/src/test/mocks/mockAccountWithStorage.js new file mode 100644 index 00000000000..1e5b300ddf6 --- /dev/null +++ b/packages/wallet-lib/src/test/mocks/mockAccountWithStorage.js @@ -0,0 +1,32 @@ +const walletStoreMock = require('../../../fixtures/wallets/c922713eac.json'); +const chainStoreMock = require('../../../fixtures/chains/for_wallet_c922713eac.json'); +const Storage = require('../../types/Storage/Storage'); +const { KeyChainStore, DerivableKeyChain } = require('../../index'); + +module.exports = (opts = {}) => { + const { walletId } = walletStoreMock; + + const mockedAccount = { + walletId, + index: 0, + storage: new Storage(), + accountPath: "m/44'/1'/0'", + network: 'testnet', + ...opts, + }; + + mockedAccount.keyChainStore = new KeyChainStore(); + mockedAccount.keyChainStore.addKeyChain(new DerivableKeyChain({ + HDPrivateKey: 'tprv8gpcZgdXPzdXKBjSzieMyfwr6KidKucLiiA9VbCLCx1spyJNd38a5KdjtVuc9bVUNpFM2LdFCrYSyUXHx1RCTdr6qQen1HTECwAZ1p8yqiB', + lookAheadOpts: { + 'm/0': 40, + 'm/1': 40, + }, + }), { isMasterKeyChain: true }); + mockedAccount.storage.createWalletStore(walletId); + mockedAccount.storage.createChainStore('testnet'); + mockedAccount.storage.getWalletStore(walletId).importState(walletStoreMock); + mockedAccount.storage.getChainStore('testnet').importState(chainStoreMock); + + return mockedAccount; +}; diff --git a/packages/wallet-lib/src/types/Account/Account.d.ts b/packages/wallet-lib/src/types/Account/Account.d.ts index 5310566bc86..04afda34f3d 100644 --- a/packages/wallet-lib/src/types/Account/Account.d.ts +++ b/packages/wallet-lib/src/types/Account/Account.d.ts @@ -11,7 +11,7 @@ import { broadcastTransactionOpts, Plugins, RawTransaction, TransactionsMap, WalletObj, StatusInfo, TransactionsHistory } from "../types"; -import { KeyChain } from "../KeyChain/KeyChain"; +import { DerivableKeyChain } from "../DerivableKeyChain/DerivableKeyChain"; import { InstantLock } from "@dashevo/dashcore-lib"; import { Identities, Wallet} from "../../index"; import { Transport } from "../../transport/Transport"; @@ -30,7 +30,7 @@ export declare class Account { cacheBlockHeaders?: boolean; label?: string | null; strategy?: Strategy; - keyChain: KeyChain; + keyChainSore: KeyChainStore; state: any; storage: Storage; store: Storage.store; diff --git a/packages/wallet-lib/src/types/Account/Account.js b/packages/wallet-lib/src/types/Account/Account.js index 248d0d52539..4f963a79880 100644 --- a/packages/wallet-lib/src/types/Account/Account.js +++ b/packages/wallet-lib/src/types/Account/Account.js @@ -1,7 +1,7 @@ const _ = require('lodash'); const EventEmitter = require('events'); const logger = require('../../logger'); -const { WALLET_TYPES } = require('../../CONSTANTS'); +const { WALLET_TYPES, BIP44_ADDRESS_GAP } = require('../../CONSTANTS'); const { is } = require('../../utils'); const EVENTS = require('../../EVENTS'); const Wallet = require('../Wallet/Wallet'); @@ -90,10 +90,10 @@ class Account extends EventEmitter { // If transport is null or invalid, we won't try to fetch anything this.transport = wallet.transport; - this.store = wallet.storage.store; this.storage = wallet.storage; // Forward all storage event + this.storage.on(EVENTS.UPDATED_ADDRESS, (ev) => this.emit(ev.type, ev)); this.storage.on(EVENTS.CONFIGURED, (ev) => this.emit(ev.type, ev)); this.storage.on(EVENTS.REHYDRATE_STATE_FAILED, (ev) => this.emit(ev.type, ev)); this.storage.on(EVENTS.REHYDRATE_STATE_SUCCESS, (ev) => this.emit(ev.type, ev)); @@ -114,29 +114,76 @@ class Account extends EventEmitter { } switch (this.walletType) { case WALLET_TYPES.HDWALLET: - case WALLET_TYPES.HDPUBLIC: - this.storage.createAccount( - this.walletId, - this.BIP44PATH, - this.network, - this.label, - ); + this.accountPath = getBIP44Path(this.network, this.index); + // this.storage + // .getWalletStore(this.walletId) + // .createPathState(this.BIP44PATH); + // this.storage.createAccount( + // this.walletId, + // this.BIP44PATH, + // this.network, + // this.label, + // ); break; + case WALLET_TYPES.HDPUBLIC: case WALLET_TYPES.PRIVATEKEY: case WALLET_TYPES.PUBLICKEY: case WALLET_TYPES.ADDRESS: case WALLET_TYPES.SINGLE_ADDRESS: - this.storage.createSingleAddress( - this.walletId, - this.network, - this.label, - ); + this.accountPath = 'm/0'; + // this.storage + // .getWalletStore(this.walletId) + // .createPathState(this); + // this.storage.createSingleAddress( + // this.walletId, + // this.network, + // this.label, + // ); break; default: throw new Error(`Invalid wallet type ${this.walletType}`); } - this.keyChain = wallet.keyChain; + this.storage + .getWalletStore(this.walletId) + .createPathState(this.accountPath); + + let keyChainStorePath = this.index; + const keyChainStoreOpts = {}; + + switch (this.walletType) { + case WALLET_TYPES.HDPUBLIC: + keyChainStorePath = this.accountPath; + keyChainStoreOpts.lookAheadOpts = { + paths: { + 'm/0': BIP44_ADDRESS_GAP, + }, + }; + break; + case WALLET_TYPES.HDWALLET: + case WALLET_TYPES.HDPRIVATE: + keyChainStorePath = this.BIP44PATH; + keyChainStoreOpts.lookAheadOpts = { + paths: { + 'm/0': BIP44_ADDRESS_GAP, + 'm/1': BIP44_ADDRESS_GAP, + }, + }; + break; + default: + break; + } + + this.keyChainStore = wallet + .keyChainStore + .makeChildKeyChainStore(keyChainStorePath, keyChainStoreOpts); + + // This forces keychainStore to set to issued key what is already its masterkey + if ([WALLET_TYPES.PUBLICKEY, WALLET_TYPES.PRIVATEKEY].includes(this.walletType)) { + this.keyChainStore + .getMasterKeyChain() + .getForPath('0', { isWatched: true }); + } this.cacheTx = (opts.cacheTx) ? opts.cacheTx : defaultOptions.cacheTx; this.cacheBlockHeaders = (opts.cacheBlockHeaders) @@ -149,36 +196,26 @@ class Account extends EventEmitter { watchers: {}, }; - // Handle import of cache - if (opts.cache) { - if (opts.cache.addresses) { - try { - this.storage.importAddresses(opts.cache.addresses, this.walletId); - } catch (e) { - this.disconnect(); - throw e; - } - } - if (opts.cache.transactions) { - try { - this.storage.importTransactions(opts.cache.transactions); - } catch (e) { - this.disconnect(); - throw e; - } - } - } + // // Handle import of cache + // if (opts.cache) { + // if (opts.cache.addresses) { + // try { + // this.storage.importAddresses(opts.cache.addresses, this.walletId); + // } catch (e) { + // this.disconnect(); + // throw e; + // } + // } + // if (opts.cache.transactions) { + // try { + // this.storage.importTransactions(opts.cache.transactions); + // } catch (e) { + // this.disconnect(); + // throw e; + // } + // } + // } this.emit(EVENTS.CREATED, { type: EVENTS.CREATED, payload: null }); - - /** - * Stores promise that waits for the IS lock of the particular transaction - * @type {Promise} - */ - this.txISLockListener = null; - - // Increases a limit of max listeners for transactions related events - // 25 - mempool limit - this.setMaxListeners(25); } static getInstantLockTopicName(transactionHash) { @@ -215,7 +252,8 @@ class Account extends EventEmitter { * @param {InstantLock} instantLock */ importInstantLock(instantLock) { - this.storage.importInstantLock(instantLock); + const chainStore = this.storage.getChainStore(this.network); + chainStore.importInstantLock(instantLock); this.emit(Account.getInstantLockTopicName(instantLock.txid), instantLock); } @@ -235,10 +273,10 @@ class Account extends EventEmitter { */ waitForInstantLock(transactionHash, timeout = this.waitForInstantLockTimeout) { let rejectTimeout; - + const chainStore = this.storage.getChainStore(this.network); return Promise.race([ new Promise((resolve) => { - const instantLock = this.storage.getInstantLock(transactionHash); + const instantLock = chainStore.getInstantLock(transactionHash); if (instantLock != null) { clearTimeout(rejectTimeout); resolve(instantLock); diff --git a/packages/wallet-lib/src/types/Account/Account.spec.js b/packages/wallet-lib/src/types/Account/Account.spec.js index a7b01b6ff7b..b7c2462b8e7 100644 --- a/packages/wallet-lib/src/types/Account/Account.spec.js +++ b/packages/wallet-lib/src/types/Account/Account.spec.js @@ -7,6 +7,10 @@ const { WALLET_TYPES } = require('../../CONSTANTS'); const { Account, EVENTS } = require('../../index'); const EventEmitter = require('events'); const inMem = require('../../adapters/InMem'); +const Storage = require('../Storage/Storage'); +const {mock} = require("sinon"); +const KeyChainStore = require("../KeyChainStore/KeyChainStore"); +const DerivableKeyChain = require("../DerivableKeyChain/DerivableKeyChain"); const blockHeader = new Dashcore.BlockHeader.fromObject({ hash: '00000ac3a0c9df709260e41290d6902e5a4a073099f11fe8c1ce80aadc4bb331', version: 2, @@ -29,10 +33,9 @@ describe('Account - class', function suite() { const mockStorage = { on: emitter.on, emit: emitter.emit, - store: {}, + storage: new Storage(), getStore: () => {}, saveState: () => {}, - stopWorker: () => {}, createAccount: () => {}, importBlockHeader: (blockheader)=>{ mockStorage.emit(EVENTS.BLOCKHEADER, {type: EVENTS.BLOCKHEADER, payload:blockheader}); @@ -43,8 +46,13 @@ describe('Account - class', function suite() { this.walletType = WALLET_TYPES.HDWALLET; this.accounts = []; this.network = Dashcore.Networks.testnet; - this.storage = mockStorage; + this.storage = new Storage(); })()); + mocks.wallet.storage.application.network = mocks.wallet.network; + mocks.wallet.storage.createWalletStore(mocks.wallet.walletId); + mocks.wallet.storage.createChainStore(mocks.wallet.network); + mocks.wallet.keyChainStore = new KeyChainStore() + mocks.wallet.keyChainStore.addKeyChain(new DerivableKeyChain({mnemonic: fluidMnemonic.mnemonic}), { isMasterKeyChain: true }) }); it('should be specify on missing params', () => { const expectedException1 = 'Expected wallet to be passed as param'; @@ -92,12 +100,13 @@ describe('Account - class', function suite() { it('should forward events', function (done) { const mockWallet = mocks.wallet; const account = new Account(mockWallet, { injectDefaultPlugins: false }); - account.init(mockWallet).then(async ()=>{ - await account.on(EVENTS.BLOCKHEADER, ()=>{ - done(); - }); - account.storage.importBlockHeader(blockHeader); - }) + account.init(mockWallet) + .then(async ()=>{ + account.on(EVENTS.BLOCKHEADER, ()=>{ + done(); + }); + account.importBlockHeader(blockHeader); + }) }); }); diff --git a/packages/wallet-lib/src/types/Account/methods/createTransaction.js b/packages/wallet-lib/src/types/Account/methods/createTransaction.js index 5aca76c0730..71334ed4be5 100644 --- a/packages/wallet-lib/src/types/Account/methods/createTransaction.js +++ b/packages/wallet-lib/src/types/Account/methods/createTransaction.js @@ -117,7 +117,7 @@ function createTransaction(opts = {}) { } }); try { - const signedTx = this.keyChain.sign( + const signedTx = this.keyChainStore.getMasterKeyChain().sign( tx, transformedPrivateKeys, crypto.Signature.SIGHASH_ALL, diff --git a/packages/wallet-lib/src/types/Account/methods/createTransaction.spec.js b/packages/wallet-lib/src/types/Account/methods/createTransaction.spec.js index 7dedef8c951..4f2c796b65b 100644 --- a/packages/wallet-lib/src/types/Account/methods/createTransaction.spec.js +++ b/packages/wallet-lib/src/types/Account/methods/createTransaction.spec.js @@ -3,15 +3,16 @@ const { expect } = require('chai'); const { HDPrivateKey, Transaction } = require('@dashevo/dashcore-lib'); const createTransaction = require('./createTransaction'); -const { mnemonic } = require('../../../../fixtures/wallets/mnemonics/during-develop-before'); const FixtureTransport = require('../../../transport/FixtureTransport/FixtureTransport'); const getUTXOS = require('./getUTXOS'); -const { simpleDescendingAccumulator } = require('../../../utils/coinSelections/strategies'); +const getPrivateKeys = require('./getPrivateKeys'); +const getUnusedAddress = require('./getUnusedAddress'); + +const getFixtureHDAccountWithStorage = require('../../../../fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage'); const addressesFixtures = require('../../../../fixtures/addresses.json'); const fixtureUTXOS = require('../../../transport/FixtureTransport/data/utxos/yQ1fb64aeLfgqFKyeV9Hg9KTaTq5ehHm22.json'); -const validStore = require('../../../../fixtures/walletStore').valid.orange.store; const craftedGenerousMinerStrategy = require('../../../../fixtures/strategies/craftedGenerousMinerStrategy'); @@ -20,13 +21,13 @@ describe('Account - createTransaction', function suite() { let mockWallet; it('sould warn on missing inputs', function () { - const self = { - store: validStore, - walletId: 'a3771aaf93', - getUTXOS, - network: 'testnet' - }; + const self = getFixtureHDAccountWithStorage() + self.getUTXOS = getUTXOS; + self.getUnusedAddress = getUnusedAddress; + self.getPrivateKeys = getPrivateKeys; + const selfWithNoUTXOS = { ...self }; + selfWithNoUTXOS.getUTXOS = () => { return []}; const mockOpts1 = {}; const mockOpts2 = { satoshis: 1000, @@ -40,9 +41,8 @@ describe('Account - createTransaction', function suite() { const expectedException3 = 'Error: utxosList must contain at least 1 utxo'; expect(() => createTransaction.call(self, mockOpts1)).to.throw(expectedException1); expect(() => createTransaction.call(self, mockOpts2)).to.throw(expectedException2); - expect(() => createTransaction.call(self, mockOpts3)).to.throw(expectedException3); + expect(() => createTransaction.call(selfWithNoUTXOS, mockOpts3)).to.throw(expectedException3); }); - it('should create valid and deterministic transactions', async function () { if(process.browser){ // FixtureTransport relies heavily on fs.existSync and fs.readFile which are not available on browser @@ -62,8 +62,12 @@ describe('Account - createTransaction', function suite() { return [new HDPrivateKey('tprv8jG3ctd1DEVADnLP3hwS1Gfzjxf5E4WL2UutfJkhAQs7rVu2b3Ryv4WQ46mddZyMbGaSUYnY9wFeuFRAejapjoB1LGzTfM55mxMhZ1X4eGX')] } }, - keyChain: { - sign: (tx, privateKeys) => tx.sign(privateKeys), + keyChainStore: { + getMasterKeyChain:() => { + return { + sign: (tx, privateKeys) => tx.sign(privateKeys), + } + } }, storage: { searchTransaction: (txId) => { diff --git a/packages/wallet-lib/src/types/Account/methods/encrypt.spec.js b/packages/wallet-lib/src/types/Account/methods/encrypt.spec.js index 89653f84bdc..facc317ea96 100644 --- a/packages/wallet-lib/src/types/Account/methods/encrypt.spec.js +++ b/packages/wallet-lib/src/types/Account/methods/encrypt.spec.js @@ -29,7 +29,7 @@ describe('Account - encrypt', function suite() { const secret = 'secret'; it('should encrypt extPubKey with aes', () => { - const extPubKey = account.keyChain.getKeyForPath(derivationPath, 'HDPublicKey').toString(); + const extPubKey = account.keyChainStore.getMasterKeyChain().getForPath(derivationPath).key.toString(); const encryptedExtPubKey = account.encrypt('aes', extPubKey, secret).toString(); const bytes = CryptoJS.AES.decrypt(encryptedExtPubKey, secret); const decrypted = bytes.toString(CryptoJS.enc.Utf8); diff --git a/packages/wallet-lib/src/types/Account/methods/generateAddress.js b/packages/wallet-lib/src/types/Account/methods/generateAddress.js index 450f582fe73..6d19109c108 100644 --- a/packages/wallet-lib/src/types/Account/methods/generateAddress.js +++ b/packages/wallet-lib/src/types/Account/methods/generateAddress.js @@ -1,58 +1,94 @@ -const { PublicKey } = require('@dashevo/dashcore-lib'); const EVENTS = require('../../../EVENTS'); const { WALLET_TYPES } = require('../../../CONSTANTS'); const { is } = require('../../../utils'); + /** * Generate an address from a path and import it to the store * @param {string} path - * @return {AddressObj} Address information + * @param {boolean} [isWatchedAddress=true] - if the address will be watched + * @return {AddressInfo} Address information * */ -function generateAddress(path) { +function generateAddress(path, isWatchedAddress = true) { if (is.undefOrNull(path)) throw new Error('Expected path to generate an address'); let index = 0; - let privateKey; let address; + let keyPathData; const { network } = this; switch (this.walletType) { case WALLET_TYPES.ADDRESS: - address = this.keyChain.address; + address = this.keyChainStore.getMasterKeyChain().rootKey; + if (isWatchedAddress) { + this.keyChainStore.issuedPaths.set(0, { + path: 0, + address, + isUsed: false, + isWatched: true, + }); + } break; case WALLET_TYPES.PUBLICKEY: - address = new PublicKey(this.keyChain.publicKey.toString()).toAddress(network).toString(); + // eslint-disable-next-line no-case-declarations + const { rootKey } = this.keyChainStore.getMasterKeyChain(); + address = rootKey.toAddress(network).toString(); + if (isWatchedAddress) { + this.keyChainStore.issuedPaths.set(0, { + key: rootKey, + path: 0, + address, + isUsed: false, + isWatched: true, + }); + } break; + case WALLET_TYPES.HDPRIVATE: case WALLET_TYPES.HDWALLET: // eslint-disable-next-line prefer-destructuring - index = parseInt(path.toString().split('/')[5], 10); - privateKey = this.keyChain.getKeyForPath(path); - address = privateKey.publicKey.toAddress(network).toString(); + index = parseInt(path.toString().split('/')[2], 10); + keyPathData = this.keyChainStore + .getMasterKeyChain() + .getForPath(path, { isWatched: isWatchedAddress }); + address = keyPathData.address.toString(); break; case WALLET_TYPES.HDPUBLIC: index = parseInt(path.toString().split('/')[5], 10); - privateKey = this.keyChain.getKeyForChild(index); - address = privateKey.publicKey.toAddress(network).toString(); + // eslint-disable-next-line no-case-declarations + keyPathData = this.keyChainStore + .getMasterKeyChain() + .getForPath(path, { isWatched: isWatchedAddress }); + address = keyPathData.address.toString(); break; + // TODO: DEPRECATE USAGE OF SINGLE_ADDRESS in favor or PRIVATEKEY + case WALLET_TYPES.PRIVATEKEY: case WALLET_TYPES.SINGLE_ADDRESS: default: - privateKey = this.keyChain.getKeyForPath(path.toString()); - address = privateKey.publicKey.toAddress(network).toString(); + keyPathData = this.keyChainStore + .getMasterKeyChain() + .getForPath(path, { isWatched: isWatchedAddress }); + address = keyPathData.address.toString(); + break; } const addressData = { path: path.toString(), index, address, - // privateKey, transactions: [], + utxos: {}, balanceSat: 0, unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false, }; - this.storage.importAddresses(addressData, this.walletId); + const accountStore = this.storage + .getWalletStore(this.walletId) + .getPathState(this.accountPath); + + const chainStore = this.storage.getChainStore(this.network); + + accountStore.addresses[addressData.path] = addressData.address.toString(); + chainStore.importAddress(addressData.address.toString()); this.emit(EVENTS.GENERATED_ADDRESS, { type: EVENTS.GENERATED_ADDRESS, payload: addressData }); return addressData; } + module.exports = generateAddress; diff --git a/packages/wallet-lib/src/types/Account/methods/getPrivateKeys.js b/packages/wallet-lib/src/types/Account/methods/getPrivateKeys.js index 9c8392af502..f7a068f8e17 100644 --- a/packages/wallet-lib/src/types/Account/methods/getPrivateKeys.js +++ b/packages/wallet-lib/src/types/Account/methods/getPrivateKeys.js @@ -5,23 +5,20 @@ */ function getPrivateKeys(addressList) { let addresses = []; - let privKeys = []; + const privKeys = []; if (addressList.constructor.name === Object.name) { addresses = [addressList]; } else { addresses = addressList; } - const { walletId } = this; - const self = this; - const subwallets = Object.keys(this.store.wallets[walletId].addresses); - subwallets.forEach((subwallet) => { - const paths = Object.keys(self.store.wallets[walletId].addresses[subwallet]); - paths.forEach((path) => { - const address = self.store.wallets[walletId].addresses[subwallet][path]; - if (addresses.includes(address.address)) { - const privateKey = self.keyChain.getKeyForPath(path); - privKeys = privKeys.concat([privateKey]); - } - }); + const { keyChainStore } = this; + + const keyChain = keyChainStore.getMasterKeyChain(); + + addresses.forEach((address) => { + const addressData = keyChain.getForAddress(address); + if (addressData) { + privKeys.push(addressData.key); + } }); return privKeys; diff --git a/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.js b/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.js index 7341d40b4a6..9a544719938 100644 --- a/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.js +++ b/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.js @@ -10,27 +10,44 @@ function getUnusedAddress(type = 'external', skip = 0) { let unused = { address: '', }; - let skipped = 0; + const skipped = 0; const { walletId } = this; const accountIndex = this.index; - const keys = Object.keys(this.store.wallets[walletId].addresses[type]) - // We filter out other potential account - .filter((el) => parseInt(el.split('/')[3], 10) === accountIndex); - for (let i = 0; i < keys.length; i += 1) { - const key = keys[i]; - const el = (this.store.wallets[walletId].addresses[type][key]); + const { addresses } = this.storage.getWalletStore(walletId).getPathState(this.accountPath); - if (!el || !el.address || el.address === '') { - logger.warn('getUnusedAddress received an empty one.', el, i, skipped); - } - unused = el; - if (el.used === false) { - if (skipped < skip) { - skipped += 1; - } else { - break; + const chainStore = this.storage.getChainStore(this.network); + + // We sort by type + const sortedAddresses = { + external: {}, + internal: {}, + }; + Object + .keys(addresses) + .forEach((path) => { + const splittedPath = path.split('/'); + let pathType = 'external'; + if (splittedPath.length > 1) { + pathType = (splittedPath[splittedPath.length - 2] === '0') ? 'external' : 'internal'; } + sortedAddresses[pathType][path] = addresses[path]; + }); + + const keys = Object.keys(sortedAddresses[type]); + + for (let i = 0; i < keys.length; i += 1) { + const key = keys[i]; + const address = (sortedAddresses[type][key]); + const addressState = chainStore.getAddress(address); + if (!addressState || addressState.transactions.length === 0) { + const keychainData = this.keyChainStore.getMasterKeyChain().getForPath(key); + unused = { + address: keychainData.address.toString(), + path: key, + index: parseInt(key.split('/').splice(-1)[0], 10), + }; + break; } } @@ -40,11 +57,6 @@ function getUnusedAddress(type = 'external', skip = 0) { if (unused.address === '') { return this.getAddress(accountIndex, type); } - - if (!this.storage.mappedAddress[unused.address]) { - this.storage.mappedAddress[unused.address] = { walletId, type, path: unused.path }; - } - return unused; } diff --git a/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.spec.js b/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.spec.js index dd6552a07b0..1f94b0442b3 100644 --- a/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.spec.js +++ b/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.spec.js @@ -1,59 +1,28 @@ const { expect } = require('chai'); -const Dashcore = require('@dashevo/dashcore-lib'); const getUnusedAddress = require('./getUnusedAddress'); -const getAddress = require('./getAddress'); -const generateAddress = require('./generateAddress'); -const KeyChain = require('../../KeyChain/KeyChain'); +const getFixtureHDAccountWithStorage = require('../../../../fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage'); -const mockedStore = require('../../../../fixtures/duringdevelop-fullstore-snapshot-1548538361'); - -const HDRootKeyMockedStore = 'tprv8ZgxMBicQKsPfEan1JB7NF4STbvnjGvP9318CN7FPGZp5nsUTBqmerxtDVpsJjFufyfkTgoe6QfHcDhMqjN3ZoFKtb8SnXFeubNjQreZSq6'; +const mockedHDSelf = { + ...getFixtureHDAccountWithStorage(), +} describe('Account - getUnusedAddress', function suite() { this.timeout(10000); - it('should get the proper unused address', () => { - const self = { - store: mockedStore, - storage: { - getStore: () => mockedStore, - importAddresses: (_) => (_), - mappedAddress: {} - }, - emit: (_) => (_), - keyChain: new KeyChain({ - type: 'HDPrivateKey', - HDPrivateKey: Dashcore.HDPrivateKey(HDRootKeyMockedStore), - }), - BIP44PATH: 'm/44\'/1\'/0\'', - walletId: '5061b8276c', - index: 0, - }; - self.getAddress = getAddress.bind(self); - self.generateAddress = generateAddress.bind(self); - const unusedAddressExternal = getUnusedAddress.call(self); - const unusedAddressInternal = getUnusedAddress.call(self, 'internal'); + it('should get the proper unused address', () => { + const unusedAddressExternal = getUnusedAddress.call(mockedHDSelf); + const unusedAddressInternal = getUnusedAddress.call(mockedHDSelf, 'internal'); - // console.log(mockedStore.wallets[self.walletId].addresses.internal) expect(unusedAddressExternal).to.be.deep.equal({ - address: 'yaVrJ5dgELFkYwv6AydDyGPAJQ5kTJXyAN', - balanceSat: 0, - fetchedLast: 1548538385006, - path: 'm/44\'/1\'/0\'/0/5', - transactions: [], - unconfirmedBalanceSat: 0, - utxos: {}, - used: false, + address: 'yTrBDbJ6635RuBMRYGvUs2s68jwhpSMyQD', + index: 3, + path: 'm/0/3' }); + expect(unusedAddressInternal).to.be.deep.equal({ - address: 'yaZFt1VnAbi72mtyjDNV4AwTECqdg5Bv95', - balanceSat: 0, - fetchedLast: 1548538385164, - path: 'm/44\'/1\'/0\'/1/8', - transactions: [], - unconfirmedBalanceSat: 0, - utxos: {}, - used: false, + address: 'ycxSmwSVQsRmuq2ivzU6NowuB11Wi9kioj', + path: 'm/1/4', + index: 4 }); }); }); diff --git a/packages/wallet-lib/src/types/Account/methods/importTransactions.js b/packages/wallet-lib/src/types/Account/methods/importTransactions.js index 2e3bb0dd123..e657a1b642c 100644 --- a/packages/wallet-lib/src/types/Account/methods/importTransactions.js +++ b/packages/wallet-lib/src/types/Account/methods/importTransactions.js @@ -1,3 +1,4 @@ +const { chain } = require('lodash/seq'); const logger = require('../../../logger'); const { WALLET_TYPES } = require('../../../CONSTANTS'); const ensureAddressesToGapLimit = require('../../../utils/bip44/ensureAddressesToGapLimit'); @@ -8,30 +9,47 @@ const ensureAddressesToGapLimit = require('../../../utils/bip44/ensureAddressesT * @param transactions * @returns {Promise} */ -module.exports = async function importTransactions(transactions) { +module.exports = async function importTransactions(transactionsWithMayBeMetadata) { const { - walletType, - walletId, - index, - store, storage, - getAddress, + network, + walletId, + accountPath, + keyChainStore, } = this; - const localWalletStore = store.wallets[walletId]; - - storage.importTransactions(transactions); - logger.silly(`Account.importTransactions(len: ${transactions.length})`); + const chainStore = storage.getChainStore(network); + const accountStore = storage + .getWalletStore(walletId) + .getPathState(accountPath); - if ([WALLET_TYPES.HDWALLET, WALLET_TYPES.HDPUBLIC].includes(walletType)) { - // After each imports, we will need to ensure we keep our gap of 20 unused addresses - return ensureAddressesToGapLimit( - localWalletStore, - walletType, - index, - getAddress.bind(this), - ); - } + const masterKeyChain = keyChainStore.getMasterKeyChain(); + const keyChains = keyChainStore.getKeyChains(); + transactionsWithMayBeMetadata.forEach((transactionWithMetadata) => { + if (!Array.isArray(transactionWithMetadata)) { + throw new Error('Expecting transactions to be an array of transaction and metadata elements'); + } + const [transaction, metadata] = transactionWithMetadata; + // Affected addresses might not be from our master keychain (account) + const affectedAddressesData = chainStore.importTransaction(transaction, metadata); + const affectedAddresses = Object.keys(affectedAddressesData); + logger.silly(`Account.importTransactions - Import ${transaction.hash} to chainStore. ${affectedAddresses.length} addresses affected.`); + affectedAddresses.forEach((address) => { + keyChains.forEach((keyChain) => { + const issuedPaths = keyChain.markAddressAsUsed(address); + if (issuedPaths) { + issuedPaths.forEach((issuedPath) => { + if (keyChain.keyChainId === masterKeyChain.keyChainId) { + logger.silly(`Account.importTransactions - newly issued paths ${issuedPath.length}`); + accountStore.addresses[issuedPath.path] = issuedPath.address.toString(); + } + chainStore.importAddress(issuedPath.address.toString()); + }); + } + }); + }); + }); + logger.silly(`Account.importTransactions(len: ${transactionsWithMayBeMetadata.length})`); return 0; }; diff --git a/packages/wallet-lib/src/types/Account/methods/sign.js b/packages/wallet-lib/src/types/Account/methods/sign.js index 33509703a97..33756ea29a0 100644 --- a/packages/wallet-lib/src/types/Account/methods/sign.js +++ b/packages/wallet-lib/src/types/Account/methods/sign.js @@ -30,5 +30,5 @@ module.exports = function sign(object, privateKeys = [], sigType) { }); } - return this.keyChain.sign(object, privateKeys, sigType); + return this.keyChainStore.getMasterKeyChain().sign(object, privateKeys, sigType); }; diff --git a/packages/wallet-lib/src/types/Account/methods/sign.spec.js b/packages/wallet-lib/src/types/Account/methods/sign.spec.js index 6e3b6e8e44b..e2de5251eb0 100644 --- a/packages/wallet-lib/src/types/Account/methods/sign.spec.js +++ b/packages/wallet-lib/src/types/Account/methods/sign.spec.js @@ -18,7 +18,7 @@ describe('Account - sign', function suite() { wallet.disconnect(); }); it('should sign a transaction with a message', function () { - account.importTransactions(transactions); + account.importTransactions([[new Dashcore.Transaction(transactions["4e2a8b05a805fcee959b8ecfd5557e196a9b8490dd280d6f599b391d650407c8"])]]); const transaction = account.createTransaction({ recipient: 'yNPbcFfabtNmmxKdGwhHomdYfVs6gikbPf', // Evonet faucet satoshis: 1000000, // 1 Dash diff --git a/packages/wallet-lib/src/types/Identities/Identities.js b/packages/wallet-lib/src/types/Identities/Identities.js index f721356dcce..f457fe9fc99 100644 --- a/packages/wallet-lib/src/types/Identities/Identities.js +++ b/packages/wallet-lib/src/types/Identities/Identities.js @@ -10,7 +10,7 @@ class Identities { this.storage = wallet.storage; - this.keyChain = wallet.keyChain; + this.keyChain = wallet.keyChainStore.getMasterKeyChain(); } } diff --git a/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.js b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.js index 374925a14a3..79cd6eedc77 100644 --- a/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.js +++ b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.js @@ -5,7 +5,10 @@ * @return {HDPrivateKey} */ function getIdentityHDKeyById(identityId, keyIndex) { - const identityIndex = this.storage.getIndexedIdentityIds(this.walletId).indexOf(identityId); + const identityIndex = this.storage + .getWalletStore(this.walletId) + .getIndexedIdentityIds() + .indexOf(identityId); if (identityIndex === -1) { throw new Error(`Identity with ID ${identityId} is not associated with wallet, or it's not synced`); diff --git a/packages/wallet-lib/src/types/Wallet/Wallet.js b/packages/wallet-lib/src/types/Wallet/Wallet.js index bffff130bd1..32738df9f71 100644 --- a/packages/wallet-lib/src/types/Wallet/Wallet.js +++ b/packages/wallet-lib/src/types/Wallet/Wallet.js @@ -86,21 +86,21 @@ class Wallet extends EventEmitter { mnemonic = generateNewMnemonic(); createdFromNewMnemonic = true; } - this.fromMnemonic(mnemonic); + this.fromMnemonic(mnemonic, this.network, this.passphrase); } else if ('seed' in opts) { - this.fromSeed(opts.seed); + this.fromSeed(opts.seed, this.network); } else if ('HDPrivateKey' in opts) { this.fromHDPrivateKey(opts.HDPrivateKey); } else if ('privateKey' in opts) { this.fromPrivateKey((opts.privateKey === null) ? new PrivateKey(network).toString() - : opts.privateKey); + : opts.privateKey, this.network); } else if ('publicKey' in opts) { - this.fromPublicKey(opts.publicKey); + this.fromPublicKey(opts.publicKey, this.network); } else if ('HDPublicKey' in opts) { this.fromHDPublicKey(opts.HDPublicKey); } else if ('address' in opts) { - this.fromAddress(opts.address); + this.fromAddress(opts.address, this.network); } else { this.fromMnemonic(generateNewMnemonic()); createdFromNewMnemonic = true; diff --git a/packages/wallet-lib/src/types/Wallet/Wallet.spec.js b/packages/wallet-lib/src/types/Wallet/Wallet.spec.js index 5f4a4368e20..a6d416bfbee 100644 --- a/packages/wallet-lib/src/types/Wallet/Wallet.spec.js +++ b/packages/wallet-lib/src/types/Wallet/Wallet.spec.js @@ -23,7 +23,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); - expect(wallet1.keyChain.type).to.be.deep.equal('HDPrivateKey'); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('HDPrivateKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -50,7 +50,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet1.keyChain.type).to.be.deep.equal('HDPrivateKey'); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('HDPrivateKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -77,7 +77,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet1.keyChain.type).to.be.deep.equal('HDPrivateKey'); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('HDPrivateKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -95,7 +95,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet1.keyChain.type).to.be.deep.equal('HDPublicKey'); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('HDPublicKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -112,7 +112,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet1.keyChain.type).to.be.deep.equal('privateKey'); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('privateKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -132,7 +132,7 @@ describe('Wallet - class', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet1.keyChain.type).to.be.deep.equal('publicKey'); + expect(wallet1.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('publicKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -149,7 +149,7 @@ describe('Wallet - class', function suite() { expect(wallet2.plugins).to.be.deep.equal({}); expect(wallet2.accounts).to.be.deep.equal([]); expect(wallet2.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet2.keyChain.type).to.be.deep.equal('publicKey'); + expect(wallet2.keyChainStore.getMasterKeyChain().rootKeyType).to.be.deep.equal('publicKey'); expect(wallet2.passphrase).to.be.deep.equal(null); expect(wallet2.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet2.injectDefaultPlugins).to.be.deep.equal(true); @@ -170,7 +170,7 @@ describe('Wallet - class', function suite() { }); }); describe('Wallet - Get/Create Account', function suite() { - this.timeout(15000); + this.timeout(10000); const wallet1 = new Wallet({ mnemonic: fluidMnemonic.mnemonic, ...mocks }); it('should be able to create/get a wallet', async () => { @@ -246,6 +246,5 @@ describe('Wallet - Get/Create Account', function suite() { it('should not leak', () => { const mockOpts1 = { }; fromHDPublicKey.call(mockOpts1, gatherSail.testnet.external.hdpubkey); - expect(mockOpts1.keyChain.keys).to.deep.equal({}); }); }); diff --git a/packages/wallet-lib/src/types/Wallet/methods/exportWallet.js b/packages/wallet-lib/src/types/Wallet/methods/exportWallet.js index f64c4afeca9..39cc12bc41f 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/exportWallet.js +++ b/packages/wallet-lib/src/types/Wallet/methods/exportWallet.js @@ -25,7 +25,7 @@ function exportAddressWallet(outputType = 'address') { } } -function exportSingleAddressWallet(outputType = 'privateKey') { +function exportPrivateKeyWallet(outputType = 'privateKey') { switch (outputType) { case 'privateKey': if (!this.privateKey) throw new Error('No PrivateKey to export'); @@ -81,7 +81,7 @@ module.exports = function exportWallet(outputType) { switch (this.walletType) { case WALLET_TYPES.PRIVATEKEY: case WALLET_TYPES.SINGLE_ADDRESS: - return exportSingleAddressWallet.call(this, outputType); + return exportPrivateKeyWallet.call(this, outputType); case WALLET_TYPES.ADDRESS: return exportAddressWallet.call(this, outputType); case WALLET_TYPES.PUBLICKEY: diff --git a/packages/wallet-lib/src/types/Wallet/methods/exportWallet.spec.js b/packages/wallet-lib/src/types/Wallet/methods/exportWallet.spec.js index d7396d6f2c7..b0af3f6c3a5 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/exportWallet.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/exportWallet.spec.js @@ -11,9 +11,9 @@ const cR4t6ePublicKey = new PrivateKey(cR4t6eFixture.privateKey).toPublicKey(); describe('Wallet - export Wallet', function suite() { this.timeout(10000); it('should indicate on missing data', () => { - const mockOpts1 = {}; - const mockOpts2 = { walletType: WALLET_TYPES.SINGLE_ADDRESS }; - const mockOpts3 = { walletType: WALLET_TYPES.HDWALLET }; + const mockOpts1 = { }; + const mockOpts2 = { walletType: WALLET_TYPES.PRIVATEKEY }; + const mockOpts3 = { walletType: WALLET_TYPES.HDWALLET }; const exceptedException1 = 'Trying to export from an unknown wallet type'; const exceptedException2 = 'No PrivateKey to export'; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromAddress.js b/packages/wallet-lib/src/types/Wallet/methods/fromAddress.js index c69c7749b9d..ebf0aba2925 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromAddress.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromAddress.js @@ -1,14 +1,18 @@ const { is } = require('../../../utils'); -const KeyChain = require('../../KeyChain/KeyChain'); +const DerivableKeyChain = require('../../DerivableKeyChain/DerivableKeyChain'); const { WALLET_TYPES } = require('../../../CONSTANTS'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); /** * @param address */ -module.exports = function fromAddress(address) { +module.exports = function fromAddress(address, network) { if (!is.address(address)) throw new Error('Expected a valid address (typeof Address or String)'); this.walletType = WALLET_TYPES.ADDRESS; this.mnemonic = null; this.address = address.toString(); - this.keyChain = new KeyChain({ address }); + + const keyChain = new DerivableKeyChain({ address, network }); + this.keyChainStore = new KeyChainStore(); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromAddress.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromAddress.spec.js index 44bd996a4d2..f0839308fc9 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromAddress.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromAddress.spec.js @@ -18,18 +18,20 @@ describe('Wallet - fromAddress', function suite() { expect(self1.walletType).to.equal(WALLET_TYPES.ADDRESS); expect(self1.mnemonic).to.equal(null); expect(self1.address).to.equal(cR4t6ePublicKey.toAddress().toString()); - expect(self1.keyChain.type).to.equal('address'); - expect(self1.keyChain.address).to.equal(cR4t6ePublicKey.toAddress().toString()); - expect(self1.keyChain.keys).to.deep.equal({}); + + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('address'); + expect(keyChain.rootKey).to.equal(cR4t6ePublicKey.toAddress().toString()); const self2 = {}; fromAddress.call(self2, cR4t6ePublicKey.toAddress().toString()); expect(self2.walletType).to.equal(WALLET_TYPES.ADDRESS); expect(self2.mnemonic).to.equal(null); expect(self2.address).to.equal(cR4t6ePublicKey.toAddress().toString()); - expect(self2.keyChain.type).to.equal('address'); - expect(self2.keyChain.address).to.equal(cR4t6ePublicKey.toAddress().toString()); - expect(self2.keyChain.keys).to.deep.equal({}); + + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('address'); + expect(keyChain.rootKey).to.equal(cR4t6ePublicKey.toAddress().toString()); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.js b/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.js index da2aba6e047..a90dd2ca516 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.js @@ -2,7 +2,8 @@ const { HDPrivateKey } = require('@dashevo/dashcore-lib'); const { is, } = require('../../../utils'); -const KeyChain = require('../../KeyChain/KeyChain'); +const DerivableKeyChain = require('../../DerivableKeyChain/DerivableKeyChain'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); const { WALLET_TYPES } = require('../../../CONSTANTS'); /** @@ -14,5 +15,8 @@ module.exports = function fromHDPrivateKey(hdPrivateKey) { this.walletType = WALLET_TYPES.HDWALLET; this.mnemonic = null; this.HDPrivateKey = HDPrivateKey(hdPrivateKey); - this.keyChain = new KeyChain({ HDPrivateKey: hdPrivateKey }); + + const keyChain = new DerivableKeyChain({ HDPrivateKey: this.HDPrivateKey }); + this.keyChainStore = new KeyChainStore(); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.spec.js index 5c277483190..c947a076b4c 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromHDPrivateKey.spec.js @@ -16,9 +16,10 @@ describe('Wallet - fromHDPrivateKey', function suite() { expect(self1.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self1.mnemonic).to.equal(null); expect(self1.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); - expect(self1.keyChain.type).to.equal('HDPrivateKey'); - expect(self1.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); - expect(self1.keyChain.keys).to.deep.equal({}); + + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.js b/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.js index 0631c051151..1a6b5aacdfc 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.js @@ -1,7 +1,8 @@ const Dashcore = require('@dashevo/dashcore-lib'); const { is } = require('../../../utils'); -const KeyChain = require('../../KeyChain/KeyChain'); +const DerivableKeyChain = require('../../DerivableKeyChain/DerivableKeyChain'); const { WALLET_TYPES } = require('../../../CONSTANTS'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); const normalizeHDPubKey = (key) => (is.string(key) ? Dashcore.HDPublicKey(key) : key); /** @@ -13,5 +14,8 @@ module.exports = function fromHDPublicKey(_hdPublicKey) { this.walletType = WALLET_TYPES.HDPUBLIC; this.mnemonic = null; this.HDPublicKey = normalizeHDPubKey(_hdPublicKey); - this.keyChain = new KeyChain({ HDPublicKey: this.HDPublicKey }); + + const keyChain = new DerivableKeyChain({ HDPublicKey: this.HDPublicKey }); + this.keyChainStore = new KeyChainStore(); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.spec.js index 96a1c9cf847..8a8086b7cd9 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromHDPublicKey.spec.js @@ -30,9 +30,10 @@ describe('Wallet - HDPublicKey', function suite() { expect(mockOpts1.mnemonic).to.equal(null); expect(mockOpts1.HDPublicKey.toString()).to.equal(gatherTestnet.external.hdpubkey); expect(new Dashcore.HDPublicKey(mockOpts1.HDPublicKey)).to.equal(mockOpts1.HDPublicKey); - expect(mockOpts1.keyChain.type).to.equal('HDPublicKey'); - expect(mockOpts1.keyChain.HDPublicKey).to.deep.equal(Dashcore.HDPublicKey(gatherTestnet.external.hdpubkey)); - expect(mockOpts1.keyChain.keys).to.deep.equal({}); + + const keyChain = mockOpts1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('HDPublicKey'); + expect(keyChain.rootKey).to.deep.equal(Dashcore.HDPublicKey(gatherTestnet.external.hdpubkey)); }); it('should work from a HDPubKey', () => { const wallet1 = new Wallet( @@ -45,7 +46,9 @@ describe('Wallet - HDPublicKey', function suite() { expect(wallet1.plugins).to.be.deep.equal({}); expect(wallet1.accounts).to.be.deep.equal([]); expect(wallet1.network).to.be.deep.equal(Dashcore.Networks.testnet.toString()); - expect(wallet1.keyChain.type).to.be.deep.equal('HDPublicKey'); + + const keyChain = wallet1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.be.deep.equal('HDPublicKey'); expect(wallet1.passphrase).to.be.deep.equal(null); expect(wallet1.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet1.injectDefaultPlugins).to.be.deep.equal(true); @@ -54,22 +57,28 @@ describe('Wallet - HDPublicKey', function suite() { expect(wallet1.exportWallet()).to.be.equal(gatherTestnet.external.hdpubkey); - wallet1.getAccount().then((account)=>{ - const unusedAddress = account.getUnusedAddress(); - const expectedUnused = { - path: "m/44'/1'/0'/0/0", - index: 0, - address: 'yNJ3xxTXXBBf39VfMBbBuLH2k57uAwxBxj', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false, - }; - expect(unusedAddress).to.deep.equal(expectedUnused); + // FIXME: it appears we had introduced a bug here, + // as it is not possible to have a HDPublicKey derivation with hardened + // Either our path is m/44/1/0/0/0 or it is m/0/0. + // We should clarify this before merging TODO + wallet1 + .getAccount() + .then((account)=>{ + const unusedAddress = account.getUnusedAddress(); + const expectedUnused = { + path: "m/0/0", + index: 0, + address: 'yNJ3xxTXXBBf39VfMBbBuLH2k57uAwxBxj', + transactions: [], + balanceSat: 0, + unconfirmedBalanceSat: 0, + utxos: {}, + fetchedLast: 0, + used: false, + }; + expect(unusedAddress).to.deep.equal(expectedUnused); - wallet1.disconnect(); - }) + wallet1.disconnect(); + }) }); }); diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.js b/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.js index 4bdeaefefa8..b0b436fbbf4 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.js @@ -2,20 +2,26 @@ const { mnemonicToHDPrivateKey, is, } = require('../../../utils'); -const KeyChain = require('../../KeyChain/KeyChain'); +const DerivableKeyChain = require('../../DerivableKeyChain/DerivableKeyChain'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); const { WALLET_TYPES } = require('../../../CONSTANTS'); /** * Will set a wallet to work with a mnemonic (keychain, walletType & HDPrivateKey) * @param mnemonic */ -module.exports = function fromMnemonic(mnemonic) { +module.exports = function fromMnemonic(mnemonic, network, passphrase = '') { if (!is.mnemonic(mnemonic)) { throw new Error('Expected a valid mnemonic (typeof String or Mnemonic)'); } const trimmedMnemonic = mnemonic.toString().trim(); this.walletType = WALLET_TYPES.HDWALLET; - this.mnemonic = trimmedMnemonic; // todo : What about without this ? - this.HDPrivateKey = mnemonicToHDPrivateKey(trimmedMnemonic, this.network, this.passphrase); - this.keyChain = new KeyChain({ HDPrivateKey: this.HDPrivateKey }); + // As we do not require the mnemonic except in this.exportWallet + // users of wallet-lib are free to clear this prop at anytime. + this.mnemonic = trimmedMnemonic; + this.HDPrivateKey = mnemonicToHDPrivateKey(trimmedMnemonic, network, passphrase); + + this.keyChainStore = new KeyChainStore(); + const keyChain = new DerivableKeyChain({ HDPrivateKey: this.HDPrivateKey }); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.spec.js index 14e81028618..85f498c6172 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromMnemonic.spec.js @@ -15,27 +15,29 @@ describe('Wallet - fromMnemonic', function suite() { const self1 = { network: 'livenet', }; - fromMnemonic.call(self1, knifeFixture.mnemonic); + fromMnemonic.call(self1, knifeFixture.mnemonic, 'livenet'); expect(self1.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self1.mnemonic).to.equal(knifeFixture.mnemonic); expect(self1.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); expect(new Dashcore.HDPrivateKey(self1.HDPrivateKey)).to.equal(self1.HDPrivateKey); - expect(self1.keyChain.type).to.equal('HDPrivateKey'); - expect(self1.keyChain.network.name).to.equal('livenet'); - expect(self1.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); - expect(self1.keyChain.keys).to.deep.equal({}); + + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain.network).to.equal('livenet'); + expect(keyChain.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); const self2 = {}; fromMnemonic.call(self2, knifeFixture.mnemonic); expect(self2.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self2.mnemonic).to.equal(knifeFixture.mnemonic); - expect(self2.keyChain.network.name).to.equal('testnet'); + + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain2.network).to.equal('testnet'); expect(self2.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); expect(new Dashcore.HDPrivateKey(self2.HDPrivateKey)).to.equal(self2.HDPrivateKey); - expect(self2.keyChain.type).to.equal('HDPrivateKey'); - expect(self2.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); - expect(self2.keyChain.keys).to.deep.equal({}); + expect(keyChain2.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain2.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ @@ -55,38 +57,35 @@ describe('Wallet - fromMnemonic - with passphrase', function suite() { this.timeout(10000); it('should correctly works with passphrase', () => { const self1 = { - network: 'livenet', - passphrase: knifeFixture.passphrase, }; - fromMnemonic.call(self1, knifeFixture.mnemonic); + fromMnemonic.call(self1, knifeFixture.mnemonic, 'livenet', knifeFixture.passphrase); expect(self1.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self1.mnemonic).to.equal(knifeFixture.mnemonic); expect(self1.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyMainnet); expect(new Dashcore.HDPrivateKey(self1.HDPrivateKey)).to.equal(self1.HDPrivateKey); - expect(self1.keyChain.type).to.equal('HDPrivateKey'); - expect(self1.keyChain.network.name).to.equal('livenet'); - expect(self1.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyMainnet); - expect(self1.keyChain.keys).to.deep.equal({}); + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain.network).to.equal('livenet'); + expect(keyChain.rootKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyMainnet); const path1 = 'm/44\'/5\'/0\'/0/0'; - const pubKey1 = self1.keyChain.getKeyForPath(path1).publicKey.toAddress(); + const pubKey1 = keyChain.getForPath(path1).key.publicKey.toAddress(); expect(new Dashcore.Address(pubKey1).toString()).to.equal('Xq3zjky18WjwAHpLgGLasvX5g8TeLRKaxt'); const self2 = { - passphrase: knifeFixture.passphrase, }; - fromMnemonic.call(self2, knifeFixture.mnemonic); + fromMnemonic.call(self2, knifeFixture.mnemonic, 'testnet', knifeFixture.passphrase); expect(self2.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self2.mnemonic).to.equal(knifeFixture.mnemonic); expect(self2.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyTestnet); expect(new Dashcore.HDPrivateKey(self2.HDPrivateKey)).to.equal(self2.HDPrivateKey); - expect(self2.keyChain.type).to.equal('HDPrivateKey'); - expect(self2.keyChain.network.name).to.equal('testnet'); - expect(self2.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyTestnet); - expect(self2.keyChain.keys).to.deep.equal({}); + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain2.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain2.network).to.equal('testnet'); + expect(keyChain2.rootKey.toString()).to.equal(knifeFixture.HDRootEncryptedPrivateKeyTestnet); const path2 = 'm/44\'/1\'/0\'/0/0'; - const pubKey2 = self2.keyChain.getKeyForPath(path2).publicKey.toAddress(); + const pubKey2 = keyChain2.getForPath(path2).key.publicKey.toAddress(); expect(new Dashcore.Address(pubKey2, 'testnet').toString()).to.equal('yWYCH9XDRnpdNxh67jQJFkovToBVwWr8Ck'); }); }); diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.js b/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.js index 2f8f919bc7e..60912dcac17 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.js @@ -1,15 +1,19 @@ const { is } = require('../../../utils'); -const KeyChain = require('../../KeyChain/KeyChain'); +const DerivableKeyChain = require('../../DerivableKeyChain/DerivableKeyChain'); const { WALLET_TYPES } = require('../../../CONSTANTS'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); /** * Will set a wallet to work with a mnemonic (keychain, walletType & HDPrivateKey) * @param privateKey */ -module.exports = function fromPrivateKey(privateKey) { +module.exports = function fromPrivateKey(privateKey, network) { if (!is.privateKey(privateKey)) throw new Error('Expected a valid private key (typeof PrivateKey or String)'); this.walletType = WALLET_TYPES.PRIVATEKEY; this.mnemonic = null; this.privateKey = privateKey; - this.keyChain = new KeyChain({ privateKey }); + + const keyChain = new DerivableKeyChain({ privateKey, network }); + this.keyChainStore = new KeyChainStore(); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.spec.js index c707c2faddc..4b6ca5e89ab 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromPrivateKey.spec.js @@ -16,18 +16,18 @@ describe('Wallet - fromPrivateKey', function suite() { expect(self1.walletType).to.equal(WALLET_TYPES.PRIVATEKEY); expect(self1.mnemonic).to.equal(null); expect(self1.privateKey).to.equal(cR4t6eFixture.privateKey); - expect(self1.keyChain.type).to.equal('privateKey'); - expect(self1.keyChain.privateKey).to.equal(cR4t6eFixture.privateKey); - expect(self1.keyChain.keys).to.deep.equal({}); + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('privateKey'); + expect(keyChain.rootKey.toWIF()).to.equal(cR4t6eFixture.privateKey); const self2 = {}; fromPrivateKey.call(self2, cR4t6eFixture.privateKey); expect(self2.walletType).to.equal(WALLET_TYPES.PRIVATEKEY); expect(self2.mnemonic).to.equal(null); expect(self2.privateKey).to.equal(cR4t6eFixture.privateKey); - expect(self2.keyChain.type).to.equal('privateKey'); - expect(self2.keyChain.privateKey).to.equal(cR4t6eFixture.privateKey); - expect(self2.keyChain.keys).to.deep.equal({}); + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain2.rootKeyType).to.equal('privateKey'); + expect(keyChain2.rootKey.toWIF()).to.equal(cR4t6eFixture.privateKey); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.js b/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.js index 9e6867ac78f..c86dc6d6e37 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.js @@ -1,15 +1,19 @@ const { is } = require('../../../utils'); -const KeyChain = require('../../KeyChain/KeyChain'); +const DerivableKeyChain = require('../../DerivableKeyChain/DerivableKeyChain'); const { WALLET_TYPES } = require('../../../CONSTANTS'); +const KeyChainStore = require('../../KeyChainStore/KeyChainStore'); /** * Will set a wallet to work with a mnemonic (keychain, walletType & HDPrivateKey) * @param privateKey */ -module.exports = function fromPublicKey(publicKey) { +module.exports = function fromPublicKey(publicKey, network) { if (!is.publicKey(publicKey)) throw new Error('Expected a valid public key (typeof PublicKey or String)'); this.walletType = WALLET_TYPES.PUBLICKEY; this.mnemonic = null; this.publicKey = publicKey; - this.keyChain = new KeyChain({ publicKey }); + + const keyChain = new DerivableKeyChain({ publicKey, network }); + this.keyChainStore = new KeyChainStore(); + this.keyChainStore.addKeyChain(keyChain, { isMasterKeyChain: true }); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.spec.js index 8852ee6c6ee..07890f2d45d 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromPublicKey.spec.js @@ -18,18 +18,18 @@ describe('Wallet - fromPublicKey', function suite() { expect(self1.walletType).to.equal(WALLET_TYPES.PUBLICKEY); expect(self1.mnemonic).to.equal(null); expect(self1.publicKey).to.equal(cR4t6ePublicKey); - expect(self1.keyChain.type).to.equal('publicKey'); - expect(self1.keyChain.publicKey).to.equal(cR4t6ePublicKey); - expect(self1.keyChain.keys).to.deep.equal({}); + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('publicKey'); + expect(keyChain.rootKey.toString()).to.equal(cR4t6ePublicKey.toString()); const self2 = {}; fromPublicKey.call(self2, cR4t6ePublicKey.toString()); expect(self2.walletType).to.equal(WALLET_TYPES.PUBLICKEY); expect(self2.mnemonic).to.equal(null); expect(self2.publicKey).to.equal(cR4t6ePublicKey.toString()); - expect(self2.keyChain.type).to.equal('publicKey'); - expect(self2.keyChain.publicKey).to.equal(cR4t6ePublicKey.toString()); - expect(self2.keyChain.keys).to.deep.equal({}); + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain2.rootKeyType).to.equal('publicKey'); + expect(keyChain2.rootKey.toString()).to.equal(cR4t6ePublicKey.toString()); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromSeed.js b/packages/wallet-lib/src/types/Wallet/methods/fromSeed.js index 978adc9ecda..038fc138e6b 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromSeed.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromSeed.js @@ -8,7 +8,7 @@ const { * fixme: Term seed is often use, but we might want to rename to fromHDPrivateKey * @param seed */ -module.exports = function fromSeed(seed) { +module.exports = function fromSeed(seed, network) { if (!is.seed(seed)) throw new Error('Expected a valid seed (typeof string)'); - return this.fromHDPrivateKey(seedToHDPrivateKey(seed, this.network)); + return this.fromHDPrivateKey(seedToHDPrivateKey(seed, network)); }; diff --git a/packages/wallet-lib/src/types/Wallet/methods/fromSeed.spec.js b/packages/wallet-lib/src/types/Wallet/methods/fromSeed.spec.js index 75e9d32cda1..a0a9ec827ca 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/fromSeed.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/fromSeed.spec.js @@ -19,22 +19,22 @@ describe('Wallet - fromSeed', function suite() { expect(self1.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self1.mnemonic).to.equal(null); expect(self1.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); - expect(self1.keyChain.type).to.equal('HDPrivateKey'); - expect(self1.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); - expect(self1.keyChain.keys).to.deep.equal({}); + const keyChain = self1.keyChainStore.getMasterKeyChain() + expect(keyChain.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyTestnet); const self2 = { fromHDPrivateKey, network: 'mainnet', }; - fromSeed.call(self2, knifeFixture.seed); + fromSeed.call(self2, knifeFixture.seed, self2.network); expect(self2.walletType).to.equal(WALLET_TYPES.HDWALLET); expect(self2.mnemonic).to.equal(null); expect(self2.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); - expect(self2.keyChain.type).to.equal('HDPrivateKey'); - expect(self2.keyChain.HDPrivateKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); - expect(self2.keyChain.keys).to.deep.equal({}); + const keyChain2 = self2.keyChainStore.getMasterKeyChain() + expect(keyChain2.rootKeyType).to.equal('HDPrivateKey'); + expect(keyChain2.rootKey.toString()).to.equal(knifeFixture.HDRootPrivateKeyMainnet); }); it('should reject invalid mnemonic', () => { const invalidInputs = [ From a69d65483f4163ba01be285612df8cf7b6edd490 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Mon, 31 Jan 2022 03:56:42 +0100 Subject: [PATCH 04/27] tests: provide fixtures --- .../chains/for_wallet_c922713eac.json | 1087 +++++++++++++++++ .../fixtures/chains/testnet/blockheaders.json | 12 + .../fixtures/chains/testnet/transactions.json | 14 + .../getFixtureAccountWithStorage.js | 32 + .../2a331817b9d6bf85100ef0/wallet-store.json | 15 + .../categorizeTransactions.expectedResults.js | 2 +- .../getFixtureAccountWithStorage.js | 33 + .../transactions-with-metadata.json | 44 +- .../apart-trip-dignity/wallet-store.json | 116 ++ .../fixtures/wallets/c922713eac.json | 97 ++ 10 files changed, 1429 insertions(+), 23 deletions(-) create mode 100644 packages/wallet-lib/fixtures/chains/for_wallet_c922713eac.json create mode 100644 packages/wallet-lib/fixtures/chains/testnet/blockheaders.json create mode 100644 packages/wallet-lib/fixtures/chains/testnet/transactions.json create mode 100644 packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/getFixtureAccountWithStorage.js create mode 100644 packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/wallet-store.json create mode 100644 packages/wallet-lib/fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage.js create mode 100644 packages/wallet-lib/fixtures/wallets/apart-trip-dignity/wallet-store.json create mode 100644 packages/wallet-lib/fixtures/wallets/c922713eac.json diff --git a/packages/wallet-lib/fixtures/chains/for_wallet_c922713eac.json b/packages/wallet-lib/fixtures/chains/for_wallet_c922713eac.json new file mode 100644 index 00000000000..fba5af71c98 --- /dev/null +++ b/packages/wallet-lib/fixtures/chains/for_wallet_c922713eac.json @@ -0,0 +1,1087 @@ +{ + "network": "testnet", + "state": { + "fees": { + "minRelay": 1000 + }, + "blockHeight": 623235, + "blockHeaders": { + "000000b0e2931614db83139686c4b95b30f4351fc1c8265081da235d1ef1ed07": "0000002014b19c3f28e3ea5b8a3764cd76ad322ef1a3e7ca12cf7e72f9625f9d080000004a46458c0f1e647a4c5c0cde93c1349d846c33d143eb5e439ee7e9650da5b9ed4138a761d127011e26e70000", + "000001810cf3b49ed94ae033f007923d3c243077f5f9e24b559b536087e8b960": "000000206c529bed8f7d56831dbfbe2c87a3c6188bbed1841002926c018993ddf60000008e4b077f4978057981ab7ced5fa3749b8559b627efcdfc7513b7892520cca9c55f4a9761d0dd011e31a50000", + "00000141c85d1b1b5a1313a139c6ebc9e748f1677d3a5a72e5c6c0635c0c07df": "0000002060b9e88760539b554be2f9f57730243c3d9207f033e04ad99eb4f30c810100004aeddaefb9b9a124e9cc154102dd189a3db7ef6bab61d78bb5013d1907798151404b9761160f021e967a0000", + "000002092c76130d20419ca56ea02cc111a121ceb0996715b37e213e00bed874": "000000206b7c87ccac5d5b836c3f3c951ef34b231d51d214ce7fbb68e1ce5474dc00000050f9e99a67a7bceca14f1c20379a7531a01d7aa8a9142ac5e400c2cf4fbddc22bf4b97618b38021eb89b0c00", + "0000011021296590288241b6d23eba1276c50dcada8dc69f2a237fc458b17dc1": "000000200e44bcf1de3f37892c24004d1835f31689c341d411c92750a4f91626490000005581a10aa3891b2e1c163b6372c172c299b037f7443d97e2ba90c85ee8dc76f4d04c9761f08d011e55560000", + "000000d672c7929c0f570e4793670f7695c848f8bb98d910aade929b41b38ce2": "00000020cd8db43289d1574b2938b0b87bfef9c9cf1d78a7f5626159e506e079490100009cbfb07f675787ebcd3cf8329344212919ee495b471a7af6c944dd3fc07bdad0904f97617c5d011e090e0c00", + "00000105e03b663923fa2b2da60404113cdffa1ca7a19df32188f4add3308452": "00000020e28cb3419b92deaa10d998bbf848c895760f6793470e570f9c92c772d60000000b6ceb13eccadbdb1d4d53b4503dc17b5b02a595b7ef87c5866d25150718acb5365097613a5c011e40c40400", + "0000005546b84ccf1a71cbd58e2bc0f76b14f98034729c0d3095fe51cb4b4554": "00000020d11c62bf5e2d31c9caf71dec9095aa956bddb9cdff7d8a8364c8afea3e00000079e6a9a6c22641abf7f2e2a49a4819327be5a83cf11ec44d51468ddba47f9987f2519761ed51011e981c0500" + }, + "transactions": { + "a43f20bcb46fef22926745a27ca38f63c773f2c7c4cbb55aaf184b58b3755965": { + "transaction": "0300000001481116b70180090112d22598c4962e2034ea269c755a8445410ecdd0299a388a010000006a47304402202be55c1f32da1dd6f408e9a945d38670f64258716044da4314ffda738dbcefd2022050555557347a78209556b1399443ab4685aac8f9c579f209038f470db434c8f50121035cf01fcf7ab699115a5ace61cc818cedd6287a3ff3d2178b44bf93bdcea09c3cffffffff0199e1610d000000001976a914b0b7c8c3637ec294f583738a46d1aa07b0f6ed8c88ac00000000", + "metadata": { + "blockHash": "000001810cf3b49ed94ae033f007923d3c243077f5f9e24b559b536087e8b960", + "height": 615751, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "c8f0d780e6cedb9c37724f98cc3fecd6d5ad314db28e3cd439184bf25196ceb4": { + "transaction": "0300000001655975b3584b18af5ab5cbc4c7f273c7638fa37ca245679222ef6fb4bc203fa4000000006b48304502210080185b616b2f8cb013e264b66146954cdc1597053ea8238467b896413b836039022071e71147dc45fc2dbde666c2774912c5bfd00b761268a3a4f8951375eb895c310121029671ae86f7eeb5c127568fddc977a1cce1f76ad64efa83c8ec9fcaef08ea9738ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac92b9610d000000001976a9147f6f4280f91e00126d927466cd48629439b763fa88ac00000000", + "metadata": { + "blockHash": "000001810cf3b49ed94ae033f007923d3c243077f5f9e24b559b536087e8b960", + "height": 615751, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "ca2afbe06b0187777f7e1aa8ae90c16ea094fbf1ed1b2ae8372e34c31a2cf67d": { + "transaction": "0300000001b4ce9651f24b1839d43c8eb24d31add5d6ec3fcc984f72379cdbcee680d7f0c8010000006a47304402206c93144ccd1816503fa13e36c5cc7aa4bc834dafb7543d306b0ac122902e3b55022076c756c3ab6ce341f55674f77a21a995264028f5891c1e257691bd743260f7fb01210208ef0d91b0377fd629082192d55ac831b6c9ff0603ed6dc19cbb3d6e21913917ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac8b91610d000000001976a9144b99b817a9caa57fc141cbfa68e2dc86d54ca5ee88ac00000000", + "metadata": { + "blockHash": "000001810cf3b49ed94ae033f007923d3c243077f5f9e24b559b536087e8b960", + "height": 615751, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "3096b29ee16f6ec1b8e8b5f3d01beb5a231d87b51b6fa4e637e0f45b6b8b83b6": { + "transaction": "03000000017df62c1ac3342e37e82a1bedf1fb94a06ec190aea81a7e7f7787016be0fb2aca010000006a47304402200122dda1789ce203582718e77afac10cb4e33acffe728d57b54263e4c007655f0220633b18a0260c731e59bbf69e353ab824d5693d5ea7dd863093adf5916de7bbe901210381e52ecbce2dbd36931346483a2a90b48fcd6d077504ff6ef172e16b1b1104e1ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac8469610d000000001976a914cc7406fdd5c28fbea82d60e2ab1307179c474cd088ac00000000", + "metadata": { + "blockHash": "000001810cf3b49ed94ae033f007923d3c243077f5f9e24b559b536087e8b960", + "height": 615751, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "95fe8be7b264aa71a1a55478abce7233311b7f3a22135a6a2543988294ddbc68": { + "transaction": "0300000001b6838b6b5bf4e037e6a46f1bb5871d235aeb1bd0f3b5e8b8c16e6fe19eb29630010000006b483045022100f03d0e357ac3253f4799061814fe1fe4dad95784240b7aa4b83dadc6d06657c102205e1f81d5e9fe12fb6bd73bc4cdc23468fb9f5ea76c7a37d93281850f2f9f3bc2012103d8505ef3e6bdb5c5cfdadbce5b512979cfaac497642d8406c05bbe9d8dc1f245ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac7d41610d000000001976a914bc6604d7be005be0ed9106d923fdeb13ecd8da3288ac00000000", + "metadata": { + "blockHash": "00000141c85d1b1b5a1313a139c6ebc9e748f1677d3a5a72e5c6c0635c0c07df", + "height": 615752, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "267c4191e3b32241bf00cf15ff7e02460e4d6cc892afaaf3a28f88601b5b6a38": { + "transaction": "030000000168bcdd94829843256a5a13223a7f1b313372ceab7854a5a171aa64b2e78bfe95010000006b483045022100ce097043895c2c32f34be8010790f6c1c86b392be0368c52ece87c7233c8b1cd0220040d2ba2647d337f34ffc422b2d2edb24c9395bd0c19c680afa0fd3013493eee0121035d2cd2b5154fe849c3a9856397c26064c52b033a382b1054635ccb96290cbaa3ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac7619610d000000001976a914744e5f4f8661dcc51e88f4615fe64a1d6b26e6dd88ac00000000", + "metadata": { + "blockHash": "00000141c85d1b1b5a1313a139c6ebc9e748f1677d3a5a72e5c6c0635c0c07df", + "height": 615752, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "eb39c1c9e0e5dd8df5ec1f822a4fd96469749f56443377262d933f888568047d": { + "transaction": "0300000001386a5b1b60888fa2f3aaaf92c86c4d0e46027eff15cf00bf4122b3e391417c26010000006b483045022100cd1ff3e404263423fcea33895ee550d5fd3ae848082402748b9f31b2a3e2071902202e98c8b1c73a84a3f3b03854842aa2590d95881d5af3697b47c36f2df040e6120121031dc2909f3f80373b7b6383e61adfc8a312954b7d420ee8f9a17bddf271999d95ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac6ff1600d000000001976a914a1cd6944da97ed00333c224679234a799d7de43f88ac00000000", + "metadata": { + "blockHash": "000002092c76130d20419ca56ea02cc111a121ceb0996715b37e213e00bed874", + "height": 615754, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "d066ac08dc743bfe5a29fd541802f9269b3da17c71f6b2f64aabd6c68a671566": { + "transaction": "03000000017d046885883f932d26773344569f746964d94f2a821fecf58ddde5e0c9c139eb010000006b48304502210082037c36c8acde9120e5b5234f2b1625698b599966ab65f24498145e7076821a022057b8b2ac8056e93a5753b3c1ad95eff8b9ac0cf6135425d349f765b8db0bd501012103eae3d568169c0c7944c27623a7295ef1f3a563890123c3c33450d8ce4e60117dffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac68c9600d000000001976a914a1ecca0e966e04bc0e6d26466eedc56237eeebc188ac00000000", + "metadata": { + "blockHash": "000002092c76130d20419ca56ea02cc111a121ceb0996715b37e213e00bed874", + "height": 615754, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "a3f4b8c9c9f49434e0437ad1aa0fa9f9c621d3a8742cee174402863a5b23fe4a": { + "transaction": "03000000016615678ac6d6ab4af6b2f6717ca13d9b26f9021854fd295afe3b74dc08ac66d0010000006b483045022100db5523499661badbec84dca3ee03f701de09982497e34fd8df284532ed0e09c902202a5da05c4378f1d878558103bdabcd1801dd2aac9d782c7f7eaa31348d6d00a7012103310974f905740f426e2c21af561d442d198f474344ad79b7071fdafd92fceee4ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac61a1600d000000001976a914c6be1292e6c212259bbf760e509c7cf3e8ce15a888ac00000000", + "metadata": { + "blockHash": "0000011021296590288241b6d23eba1276c50dcada8dc69f2a237fc458b17dc1", + "height": 615757, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "eee88d2a03a0452dfe0abc9a84915f33d552110088c4382f0c459960494bf2ab": { + "transaction": "03000000014afe235b3a86024417ee2c74a8d321c6f9a90faad17a43e03494f4c9c9b8f4a3010000006b483045022100c222770356e4157a7926454701483d26f8ad2a1595d4fe0898eb9ce1ab2b216202202994cfb2563f53f28ab21959289a890fcd2cfdcaf7cc576be8c5930b76ade1d90121036ef5809736958928e5e369a805a7f3236369dcda8cb0ad8b41c02ec12de92b36ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac5a79600d000000001976a914526a5726b41e7f178f9c6ef94e63a321b61db0c688ac00000000", + "metadata": { + "blockHash": "0000011021296590288241b6d23eba1276c50dcada8dc69f2a237fc458b17dc1", + "height": 615757, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "0fd66f212549dbb84d42a745e9e0bef5e6772acfaf8b6d06579cffa15ac9fa1d": { + "transaction": "0300000001abf24b496099450c2f38c488001152d5335f91849abc0afe2d45a0032a8de8ee010000006a47304402203ff02fcd8551e4525522a37d6cc7202daea3be57b7d2a34dbb1d2cc06a38a6cc0220456f04bd6fa328e53f66167ea1c2c18f27d80838f4306e17e633410afa0aefed0121037be066d359ad86dba34a084754a9a79a63626e7ab9df7dd185505c6465969d1affffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac5351600d000000001976a914c96c81e8a992e95eade60a4e3ab967de672c4e6288ac00000000", + "metadata": { + "blockHash": "0000011021296590288241b6d23eba1276c50dcada8dc69f2a237fc458b17dc1", + "height": 615757, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "92be5095443f506771d6f145716ab33a12b20239b5d363c9b09d5e5cd7fb3f96": { + "transaction": "03000000011dfac95aa1ff9c57066d8bafcf2a77e6f5bee0e945a7424db8db4925216fd60f010000006a47304402210097facc4725c831d0afc70828d18c128868fddcdec91479cb3d242658bfbee887021f361e45fe35cfada131d376e290ac4ab0a9710839d49f96690b1a3a5dc160a0012102df67e08e44684da52bd8b648344a00ee6ffae73a7efdfcfb5503af799c3d069cffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac4c29600d000000001976a914c0c6b5c4f8f775acbd6de28c5aafcdb785d6d08588ac00000000", + "metadata": { + "blockHash": "0000011021296590288241b6d23eba1276c50dcada8dc69f2a237fc458b17dc1", + "height": 615757, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "ea704ab4f38d786116875270c141b628f182684fd39dcd57c969237cf92f5589": { + "transaction": "0300000001963ffbd75c5e9db0c963d3b53902b2123ab36a7145f1d67167503f449550be92010000006a473044022065fdcd2e579ae58af2634386d1f92da1617a0ab16bea34e5a5900b9454a528920220496e59ef90d5d79e0dcaf9b97f962e63e41a71cf336e8fdf0bdbabb72deffae801210273e7e7d1fe3d3cb5038f058bb1d5076b0781d61085f5c658ffd6246632b03f9affffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac4501600d000000001976a9145c6bb6833b27ca6252e64faa48b8a29e783df3e788ac00000000", + "metadata": { + "blockHash": "0000011021296590288241b6d23eba1276c50dcada8dc69f2a237fc458b17dc1", + "height": 615757, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "f86445d20beccda3749058d90a605f4179a51a04b09f1262a9aefb382fe21bfd": { + "transaction": "030000000189552ff97c2369c957cd9dd34f6882f128b641c17052871661788df3b44a70ea010000006b483045022100b671fc3bc69567298f61f5d033b7746985fe517b83e30f38dac0f1306aba0a5902201a9e509d96ab9f57c74cca218171f49b7a3d56a66afde0bf53481ebd68892e19012103108df8bbcd37ace008ec407ca8cdc95ebdb220db3df8527418a0d86c95c1af3bffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac3ed95f0d000000001976a91444fcf081778ce018b167d007d910f3c8993c4c7f88ac00000000", + "metadata": { + "blockHash": "0000011021296590288241b6d23eba1276c50dcada8dc69f2a237fc458b17dc1", + "height": 615757, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "b2420b554dad82bffb22f10bda0124876fc6d02b4892d01833b413cad275c351": { + "transaction": "0300000001fd1be22f38fbaea962129fb0041aa579415f600ad9589074a3cdec0bd24564f8010000006b483045022100c19616212adbade24c4322520e20218a6518947cb420025b6cda0560194b3c1c0220208e1132a95674160069cd02007200acfa7568d7b4e7af5c63bbbdbe1add2f3b012102cd31f5a37c8032f34f6babb216a48d8ee5de54bc212e86a4d76bd49d10f18e91ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac37b15f0d000000001976a9148d1adb237d7f960b5d6912e21169ca9f15a2ebe988ac00000000", + "metadata": { + "blockHash": "0000011021296590288241b6d23eba1276c50dcada8dc69f2a237fc458b17dc1", + "height": 615757, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "174bbb78437049150cfe8c00ec53cd9f0ee038d0e9451e85eef25b5256a2e95e": { + "transaction": "030000000151c375d2ca13b43318d092482bd0c66f872401da0bf122fbbf82ad4d550b42b2010000006b483045022100c41c6a7c30c3479bccc020cf468b5c8be4188caef840b4a65f9a3bf16e8c3738022008458d62093d180bbee74e5839820189fa7fbe3160364ae69ddffe42ace22e3b01210234c8bb381c51a9de5253d77b7f5758dc73c832c2750332e62882b131c73e4955ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac30895f0d000000001976a914ea923f5fcaba8406dbbc2074262923653f2b9da588ac00000000", + "metadata": { + "blockHash": "0000011021296590288241b6d23eba1276c50dcada8dc69f2a237fc458b17dc1", + "height": 615757, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "fec6553adbe93a1702b43a9a871979ea0294bcd8d776ae684ff7aca0d22fe0e9": { + "transaction": "03000000015ee9a256525bf2ee851e45e9d038e00e9fcd53ec008cfe0c1549704378bb4b17010000006a47304402203abb41d61f4eaecf5ac9ffe350bd6fef1e7e6ff5733c5f3399f09d191e1b8cc602206623acc6bf4ea2219e20183fd952f42c21efc64daf53049983509dddbd633c74012103cb4ce869ba12c894772aa8300dd362af3b8eee63ef394b3d86210b4d3044a70bffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac29615f0d000000001976a91493e02288f0c39d5927425ee77428696592678f5588ac00000000", + "metadata": { + "blockHash": "0000011021296590288241b6d23eba1276c50dcada8dc69f2a237fc458b17dc1", + "height": 615757, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "fc9863caf91f51da5ae67cd8bdada46435db0e940164bee87248026c79ccebd9": { + "transaction": "0300000001e9e02fd2a0acf74f68ae76d7d8bc9402ea7919879a3ab402173ae9db3a55c6fe010000006a4730440220273eee107fc493e599ce014f7087371e9ec28199a8a4676a588e0e7a96efaa2f02202c55628295bd3d3b1b895dc60dd7b4b98d9558393fcdb22106878721f554b5e7012102db98cb80d13a6e575589c641f22cfb75278e2b664ce1b7765fca4f16f3be8bb8ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac22395f0d000000001976a9148f8dea282edef6cdfb5c03cfe80cb139d8380d2688ac00000000", + "metadata": { + "blockHash": "0000011021296590288241b6d23eba1276c50dcada8dc69f2a237fc458b17dc1", + "height": 615757, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "74789930fe5d4bab8e485f1432a79704d3f111eec959aed55584c04b7f7da2dc": { + "transaction": "0300000001d9ebcc796c024872e8be6401940edb3564a4adbdd87ce65ada511ff9ca6398fc010000006a473044022009f48d0032c8190278d37cb3374edf2681cba880d4fabdeeeb041d8414c2b6dd02205b26b9ac50262c1f5d0b4369264383014a209293e5f0a26acc0322736f29d60f0121038cd3c39953611f8273ad74bbbd3e919a71c5e7481f4841f6ddc9ac0427d8297bffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac1b115f0d000000001976a914299b3526d93433676cd3aaaf28839a8f83db97f988ac00000000", + "metadata": { + "blockHash": "0000011021296590288241b6d23eba1276c50dcada8dc69f2a237fc458b17dc1", + "height": 615757, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "4b1ec1877f9c2eaa4fbfeb034b38b21156aab4955d597fc328b6c30be1acd10e": { + "transaction": "0300000001dca27d7f4bc08455d5ae59c9ee11f1d30497a732145f488eab4b5dfe30997874010000006a473044022057fb09fa6301445cd72ec36c9bdbc1748eada3098f41227d10d258c152bc06eb02207ca0e41d447bbea34af8d3ee1ea879e03e5daf0241777e942e08a8d3450cfdb10121035a0a22c2a6d893fbdbe7c2cc7ad2d09292eb0323b78e6a64503b9f09adc7b888ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac14e95e0d000000001976a9149c7f9e354bf6e4b3e8fcff6ebeebed179464397f88ac00000000", + "metadata": { + "blockHash": "000000d672c7929c0f570e4793670f7695c848f8bb98d910aade929b41b38ce2", + "height": 615766, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "c0cbf437c0f8e980b8d64a3148a258645a821dc51631c4dfdb3dcd8324d6819e": { + "transaction": "03000000010ed1ace10bc3b628c37f595d95b4aa5611b2384b03ebbf4faa2e9c7f87c11e4b010000006b483045022100d4968053278a5e0d7d177de6aacb8d5b955b155e8a7fcf0b263c990b4058be0102201702447e79c98812aa34588d8f5f5cf955f2a92ce0c22b1eafa0abafdf95b6e0012103c921989753f9a5c4b656aa3e64da167eb4c44b20848db06f8dd0857b2468f5cbffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac0dc15e0d000000001976a914ff4c2b2c85b85a2e4ae8078587cbba81f3880c1988ac00000000", + "metadata": { + "blockHash": "00000105e03b663923fa2b2da60404113cdffa1ca7a19df32188f4add3308452", + "height": 615767, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "5ec24edb2394c13db2d2b3fb1502cd5a4db8006b283eb6868cc44828df30148c": { + "transaction": "03000000019e81d62483cd3ddbdfc43116c51d825a6458a248314ad6b880e9f8c037f4cbc0010000006a4730440220624a0ca69ff3730cf2107945ffb629209fc8cefbe32abf5eecff76e8efa110f2022059f3b834178bbb360c9a473f896d7eda2ad9fff1af2c7f478ee3364824f4163d012102b997bc2dec7eebe4c504f95e0373bbd25e8c9970d4d9a84a36c701e4d1b50bcaffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac06995e0d000000001976a914315c783a94c3cb762a141cd949b631111caad00488ac00000000", + "metadata": { + "blockHash": "00000105e03b663923fa2b2da60404113cdffa1ca7a19df32188f4add3308452", + "height": 615767, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "f95dbbc93352dd0cfe0350ce7407eabc5ead0cd5b3265873c5544447047eddf1": { + "transaction": "03000000018c1430df2848c48c86b63e286b00b84d5acd0215fbb3d2b23dc19423db4ec25e010000006a4730440220508d1d7cc4a3d961186913c3e17df787a8338342f8e47354c98a28b7091fec01022003aa09800685591628478d62eeee34b45d6f5dd3138f6455fd0decc83a0780a0012102bb5fbf33eed8fd6fe472584bce8551e8082ecf4d445f562b5b20b60b018e8043ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088acff705e0d000000001976a9141a34cc7c74c833c38a10c390b77f7fcced77af0d88ac00000000", + "metadata": { + "blockHash": "00000105e03b663923fa2b2da60404113cdffa1ca7a19df32188f4add3308452", + "height": 615767, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "5cfbb3fd2dfdd75f590278a1749fe2dae2d5395eef5e87690756adb44cb5745d": { + "transaction": "0300000001f1dd7e04474454c5735826b3d50cad5ebcea0774ce5003fe0cdd5233c9bb5df9010000006b483045022100f39957205230341508c414b17d4961f52c5dc0a426280381a407c6c46954ab09022006c8ea46b28f076211dc8d00c7fd16863285711102d2b073946323a61264a1080121023e6748994e6df1a568a0bc42fe903f7abedbe4b9161bbb4e60e0c30996df1401ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088acf8485e0d000000001976a91463da38cac685abf6975de7b8d54415f9cb0f1e3088ac00000000", + "metadata": { + "blockHash": "00000105e03b663923fa2b2da60404113cdffa1ca7a19df32188f4add3308452", + "height": 615767, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "34ba3e041c3026bab4f2c2c0ec1e1bb496ec72042aba9b03a324b74d1ec1d1af": { + "transaction": "03000000015d74b54cb4ad560769875eef5e39d5e2dae29f74a17802595fd7fd2dfdb3fb5c010000006a473044022000cf20af647df385691ffee7c20de78b05cd264645f784b3bd94bfa847bcd34402204ab594edbc06d4f7bc883eec99ff554a014e7bc9ae1d821e48ed75bd41ba176201210299f14e8558141cd4b58eff7da5d62bc5d12fc35ec7ca62fb577361b7b39323d5ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088acf1205e0d000000001976a914cd5ff7587af9ce7762fb73ee132b392a7734691688ac00000000", + "metadata": { + "blockHash": "00000105e03b663923fa2b2da60404113cdffa1ca7a19df32188f4add3308452", + "height": 615767, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "0d61420bb114d9d16b424e2822ab8412f3dec63e0b8da8df8f0fd5786782abd0": { + "transaction": "0300000001afd1c11e4db724a3039bba2a0472ec96b41b1eecc0c2f2b4ba26301c043eba34010000006a473044022046f78fed1260cc82d6c2c8be5b724d2a7c85687009c893cb464506106c40ce7c0220406ecc013482039460cd724bdabd39e6e407129b0f42367a8b8a5f6a0ac853a801210298dd86b2cef1adc0011c67fd29c314bb1b57c3f1be4d815f604d598ae22e55dcffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088aceaf85d0d000000001976a9145246323c87caa68e41f818bd2a78ffa969b8ecd288ac00000000", + "metadata": { + "blockHash": "00000105e03b663923fa2b2da60404113cdffa1ca7a19df32188f4add3308452", + "height": 615767, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "0e62d7c53e01bd3a61065ca279bd77a6e1354c0c6a373502d7cc9328be3812a8": { + "transaction": "0300000001d0ab826778d50f8fdfa88d0b3ec6def31284ab22284e426bd1d914b10b42610d010000006b483045022100b7dec3519f1a1fbdd57f4cc4625a522b2cbd0980b577a79285d0e3995230129b022060af9411ce03cf9e17d30fc544ab8f31f068fd10a6b27def605e3d03c75c66d7012102c877e9a908ff2532da285c49e4f5fbb4b4fe9890167e8f53926f8f0405893ce9ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ace3d05d0d000000001976a914fd17b92a259f10b059e3b961e2245fced0ffeaf088ac00000000", + "metadata": { + "blockHash": "0000005546b84ccf1a71cbd58e2bc0f76b14f98034729c0d3095fe51cb4b4554", + "height": 615769, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "49c214eab55a17f80ec6e136861cbc56d507ea0c491864706b3cfa0a7f175634": { + "transaction": "0300000001a81238be2893ccd70235376a0c4c35e1a677bd79a25c06613abd013ec5d7620e010000006a47304402203f21c9573085b88848d22b894e5cfad783e7bb94bdd073c677b8a49495aa8f40022036cc89a2b492b1bde71d5fbc9c9a5825314669c9b4af635e34ba0d807edaf5e70121034bce811ced77c6a27f970af6ac6e5ee6a2efd713273774f3039490bc26f19f49ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088acdca85d0d000000001976a914dfe1744ed3efe1c41ce02d92d6f4b3c789f3594e88ac00000000", + "metadata": { + "blockHash": "0000005546b84ccf1a71cbd58e2bc0f76b14f98034729c0d3095fe51cb4b4554", + "height": 615769, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "d17f579a4ead6a798618d3b567c2f130cac22a0830219d03404d68d9b6a5d8ce": { + "transaction": "03000000013456177f0afa3c6b706418490cea07d556bc1c8636e1c60ef8175ab5ea14c249010000006b483045022100853ad2f6f1fb290020e0c703dfea5d8ad476b671631ee3ff3b123045eca5a4d802206f81f7dc3ecd86b7ca2dd51243ec18562a9d935813b25c766839317b1b178b9b0121022a56bb1ffad84cd344cf40b5c968cb5879c1a2eff24035604a21e9acb18db570ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088acd5805d0d000000001976a9145707912cce7499bd9f594536d9ef6a9442161ce388ac00000000", + "metadata": { + "blockHash": "0000005546b84ccf1a71cbd58e2bc0f76b14f98034729c0d3095fe51cb4b4554", + "height": 615769, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "9594a1c5a05fa24a78e9b93f03ee62b56d9c90e62e62298a3cf54629a355750a": { + "transaction": "0300000001ced8a5b6d9684d40039d2130082ac2ca30f1c267b5d31886796aad4e9a577fd1010000006b483045022100a321cac2bb16cbc30682700ef402c98d0c603b948d38e8d5a3b3368021bfc91e02202fb7cc27a5f701f9f15fa2a790b351e462dcd56bb3e33c959a2f78e53a5f9e400121030132d760b2488626feba05f7317a4ce255cfb5ed4bd23a78366bf0846bf666b1ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088acce585d0d000000001976a914f8f4d2ba9d26ceb5b237ab220c5fa09e12495ca488ac00000000", + "metadata": { + "blockHash": "0000005546b84ccf1a71cbd58e2bc0f76b14f98034729c0d3095fe51cb4b4554", + "height": 615769, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "d522f8a9406dc497893ec3e99c69a05a1b88e0a010e3f12495628e412b85663a": { + "transaction": "03000000010a7555a32946f53c8a29622ee6909c6db562ee033fb9e9784aa25fa0c5a19495010000006a4730440220025799d9fde964000894e13249a7a856aa4f7402392e29a90a8560093f4d179402200db0ac7f4cbfeb1ec1e8ffa2eddf00cabcd53a6d51a9c7e2af90c6cd9e3d805401210372c3b4139803029186ffdaace2acc48bb3131b24b19643c7919e4c994eacd4d7ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088acc7305d0d000000001976a914aefcf82febe1bd937263b53bc5f93708616665f988ac00000000", + "metadata": { + "blockHash": "0000005546b84ccf1a71cbd58e2bc0f76b14f98034729c0d3095fe51cb4b4554", + "height": 615769, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "3ee6bec9dbe57574305a25d72362434b8f3d02a79c6228d12f0a957891f5e52d": { + "transaction": "03000000013a66852b418e629524f1e310a0e0881b5aa0699ce9c33e8997c46d40a9f822d5010000006b483045022100fc74a5f633c25a152195b7ffc2e59309af554cbf26ac176f35b8607742f01854022031d4edc7ba4851e1fa98c761f2389251532943632ac6ed0fd88f23d5781535b9012102bfa11d27e6cb225d549ec6677c49c4ca84dc463648bc541f91d1f1fdd3b11a37ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088acc0085d0d000000001976a914f9dfb4ac40a2d2aff83d58d78d5eeb98eaed432588ac00000000", + "metadata": { + "blockHash": "0000005546b84ccf1a71cbd58e2bc0f76b14f98034729c0d3095fe51cb4b4554", + "height": 615769, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "89181c1b627d32ea7dceda330c4c44d4992e98f7cacc392691220bba800ec6a7": { + "transaction": "03000000012de5f59178950a2fd128629ca7023d8f4b436223d7255a307475e5dbc9bee63e010000006b483045022100d9cb2fa10a5f04e17d170509732408a2026712908b296f79f545923c1c80deec022012b08498ff70108cf48be3633b539f59a572ecc121d2cdf69782f34570c71ce2012102c82f336f78045e7ba70295560bd674b5edf004a80a7ea6aef3c5fd110fc2fabbffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088acb9e05c0d000000001976a914adbd1ff2c75b70c94865cb4bb0afc78c1e36d78688ac00000000", + "metadata": { + "blockHash": "0000005546b84ccf1a71cbd58e2bc0f76b14f98034729c0d3095fe51cb4b4554", + "height": 615769, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "5b02921dda2f33def0951615466578b3cac8fd19ae9a9e75ee0176bf3c8e4821": { + "transaction": "0300000001a7c60e80ba0b22912639cccaf7982e99d4444c0c33dace7dea327d621b1c1889010000006b4830450221008d15bc527302c6b330f89c34add456d702e67dfa3b633e605e434332305e308c02205a7860004ec9a7c5703febccef584b39d5a29c770b19b0bb74072514013eb1b501210351ab77c8b2b4cf0430b9d8324e46d0d672117f174bab7487e3e601e60cbc731cffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088acb2b85c0d000000001976a91427d236621e716361c0253902e192b3c1ee2bd79388ac00000000", + "metadata": { + "blockHash": "0000005546b84ccf1a71cbd58e2bc0f76b14f98034729c0d3095fe51cb4b4554", + "height": 615769, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "3d2e84e85560f835084d6e6514563a16453ded77d8474244b1a5dc95c8891faf": { + "transaction": "030000000121488e3cbf7601ee759e9aae19fdc8cab3786546151695f0de332fda1d92025b010000006a47304402206b8006112d2ccf477e23673d3decfa4314af671e03fed8eda958fe6a78317b8e022014aa1dfd05e43fca8fdc233aabee6701b5bd32ee9af983497cab93bb960ca9c70121023d72e96a3287e997680e62a16120a395e3f68bd96d56291aa58eed08a5121dbfffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088acab905c0d000000001976a9140c5d1982e36bd9b5f4aa08d8e4c51a5535e2a8ad88ac00000000", + "metadata": { + "blockHash": "0000005546b84ccf1a71cbd58e2bc0f76b14f98034729c0d3095fe51cb4b4554", + "height": 615769, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "2a035c18d1a51c7164beac80f374bef6b49d084681e8035071fc0adc903b606c": { + "transaction": "0300000001af1f89c895dca5b1444247d877ed3d45163a5614656e4d0835f86055e8842e3d010000006a473044022027389c256710a7b70fe3cf0b8d04c486f1f6f2e3b94662f68dae7be2e8ec741d022071d3514613d9bcef688339addd86a0103667ae997f170c82f8eceba5b7c0aae201210306fa7efecfe3e7e4bc0d71e333ad9b330ba5027f0bcbcf9209dc6dc6d1d214a6ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088aca4685c0d000000001976a914681ebc28972f2e23f88a6b8f71a30f45fe08b45f88ac00000000", + "metadata": { + "blockHash": "0000005546b84ccf1a71cbd58e2bc0f76b14f98034729c0d3095fe51cb4b4554", + "height": 615769, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "11d14e8be169305b49e5a080f0570d2be6783c4e8bbe0c32451b97e35b188ee9": { + "transaction": "03000000016c603b90dc0afc715003e88146089db4f6be74f380acbe64711ca5d1185c032a010000006a47304402203f685c0d8dc11f74c153d93d895e0bf0a0b0ec31120345a9b98baa8e28808c02022011530b45fb4a53b33fc4201fd4b2834b672931c4e5f7097f852c126146d9e6140121038977b564fcf729b468572015dcd11f2475e28fc2f86dd1d3699a6873134ceabfffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac9d405c0d000000001976a91454eee81f9d2650a8162dea7cf0f444b270a685b688ac00000000", + "metadata": { + "blockHash": "0000005546b84ccf1a71cbd58e2bc0f76b14f98034729c0d3095fe51cb4b4554", + "height": 615769, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "b8c506c24e66aabc59b409b2ab37cd97893dae1ef671c5e839dd8649a65586a0": { + "transaction": "0300000001e98e185be3971b45320cbe8b4e3c78e62b0d57f080a0e5495b3069e18b4ed111010000006b483045022100a12b68d57f6c591ffa5001987c8b2b246e396965cbb24452a2931d3837d6dd710220554d1a231a29aac6cb68b0278b1039521ab3f08ebb53ed78e4319648fcb00538012103cbb5e729a4dfbb2607e4c2ec0afae068b21b76d907b859e0980b98d5e0212b0bffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac96185c0d000000001976a914f050e5615a87152533df496a5fdc7867c327e77888ac00000000", + "metadata": { + "blockHash": "0000005546b84ccf1a71cbd58e2bc0f76b14f98034729c0d3095fe51cb4b4554", + "height": 615769, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "1b33390deac7e4c58761f048ee6d04ad2770277d78f2111951347c318088f4bd": { + "transaction": "0300000001a08655a64986dd39e8c571f61eae3d8997cd37abb209b459bcaa664ec206c5b8010000006b483045022100e11f02c0571a2beac3993191d31936d9fbd4f7b4e71ebefc195763ad66c0ff680220565006889d022b790f57a6a9e8fbd404b353365e73e29b8152cf1a7f7bacf3bf012103d55102102e4a446c4f76fd26a1052ca304b1523fe8fefdc81a21bdb37a593d26ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac8ff05b0d000000001976a91404ee78127bf986f1abf07b35e30613949811410588ac00000000", + "metadata": { + "blockHash": "0000005546b84ccf1a71cbd58e2bc0f76b14f98034729c0d3095fe51cb4b4554", + "height": 615769, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "1219e136566a370d2b302059a03b77f0ad0cfd98d6767e8a877157cbf6ffdccc": { + "transaction": "0300000001bdf48880317c34511911f2787d277027ad046dee48f06187c5e4c7ea0d39331b010000006b4830450221009cf93733d4ea0de4bb5b2b7c15a146ca7798bdc9c03e305c0a30fff9d9b86c5a0220653ea274a79540674ca92d08f89543a6a0e15dc2ba53bc7e9d29a694f5088dbb0121024f2a07e71f7e37ff5bcce315b2af83b32f1730c63a1de8f3bc2a3f624e15d6e4ffffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac88c85b0d000000001976a91430fcee94aa13b13de63fd0b52e384cb5590c973588ac00000000", + "metadata": { + "blockHash": "0000005546b84ccf1a71cbd58e2bc0f76b14f98034729c0d3095fe51cb4b4554", + "height": 615769, + "isInstantLocked": null, + "isChainLocked": null + } + }, + "33b14c6bc960c5717d734d5a15dc86b2060bf6e746cc509863344204d356cee4": { + "transaction": "0300000001ccdcfff6cb5771878a7e76d698fd0cadf0773ba05920302b0d376a5636e11912010000006b483045022100e3a3d166cdb9cab262a80b8c85a3ca82f837997669ff7b3eef5be3573a1c06e202207330cfaed20b37fd63e88e1e5395383e7242c4ad91e71493ccc8a51504a8a9fd0121038d955c7bd1805614e5a7d1641433915a0833b96d96ba25375da4f217cc9fdb7affffffff0210270000000000001976a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac81a05b0d000000001976a9140a163cfcba43b87e58b1996f61376d7bd8d9805288ac00000000", + "metadata": { + "blockHash": "0000005546b84ccf1a71cbd58e2bc0f76b14f98034729c0d3095fe51cb4b4554", + "height": 615769, + "isInstantLocked": null, + "isChainLocked": null + } + } + }, + "instantLocks": {}, + "addresses": { + "ycRqwWy2pNzwNumX79MmRyvQYVpy6w3AeX": { + "address": "ycRqwWy2pNzwNumX79MmRyvQYVpy6w3AeX", + "transactions": [ + "a43f20bcb46fef22926745a27ca38f63c773f2c7c4cbb55aaf184b58b3755965", + "c8f0d780e6cedb9c37724f98cc3fecd6d5ad314db28e3cd439184bf25196ceb4" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "ycuRYGdNudwRxKNDQBqHDW7aGbJU6uqhXo": { + "address": "ycuRYGdNudwRxKNDQBqHDW7aGbJU6uqhXo", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yfNFUv1vfx3EVyVGtfZNXqvFR7HLP5MGRq": { + "address": "yfNFUv1vfx3EVyVGtfZNXqvFR7HLP5MGRq", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yjDWQWhNixjKLMjzmXxWqLdJUEcgyjQh1o": { + "address": "yjDWQWhNixjKLMjzmXxWqLdJUEcgyjQh1o", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "ygCCNzQM2Uj7Dfyi6sxPGBmRY6hqHGEFP9": { + "address": "ygCCNzQM2Uj7Dfyi6sxPGBmRY6hqHGEFP9", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yX5c33WWgLacu7Z1hnBTLKPSXbbGt4BFNt": { + "address": "yX5c33WWgLacu7Z1hnBTLKPSXbbGt4BFNt", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yYeyVQqiejQsFmkQjtbcigLLPVGJKHxQBL": { + "address": "yYeyVQqiejQsFmkQjtbcigLLPVGJKHxQBL", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yTFXnvNARXGPNL1ZKFHyVah3xY6JxzQaPD": { + "address": "yTFXnvNARXGPNL1ZKFHyVah3xY6JxzQaPD", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yNGpfTzZkJNhEQUk6Wj1VdS8RUVpq6qA2g": { + "address": "yNGpfTzZkJNhEQUk6Wj1VdS8RUVpq6qA2g", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "ySW43fHX7uTEjJWjozu3EF3bsXywFoo1gf": { + "address": "ySW43fHX7uTEjJWjozu3EF3bsXywFoo1gf", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yM3LdfhFcMQBQoECRrh2WZwy6c8xnsA1rq": { + "address": "yM3LdfhFcMQBQoECRrh2WZwy6c8xnsA1rq", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "ybDdvzUVYvqSPnvdzqgQo1ictZxTjoNTeY": { + "address": "ybDdvzUVYvqSPnvdzqgQo1ictZxTjoNTeY", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yZLvsg1LuC1q9xa1FqNoKWKHZJTLb1Rs5U": { + "address": "yZLvsg1LuC1q9xa1FqNoKWKHZJTLb1Rs5U", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yeGtNDLgTEfSYtYK84kXwvHXwRu3AQprku": { + "address": "yeGtNDLgTEfSYtYK84kXwvHXwRu3AQprku", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yPBq6vmEXnz2FtHVztLHD9HPQf8FMbnRPr": { + "address": "yPBq6vmEXnz2FtHVztLHD9HPQf8FMbnRPr", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yjcBeyf1r8EkjNFXGgX4NmNQEwk4NNH4Dp": { + "address": "yjcBeyf1r8EkjNFXGgX4NmNQEwk4NNH4Dp", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "ySCxSmKAfjo8fk15TCM9chiS1dAQfJ54z2": { + "address": "ySCxSmKAfjo8fk15TCM9chiS1dAQfJ54z2", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "ySBWPfBDnN2iWKt4uRoFaVPGDoRDQpo5i1": { + "address": "ySBWPfBDnN2iWKt4uRoFaVPGDoRDQpo5i1", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yXMhEgMKAknAUTNR31nYr4uSP7GMpmCgsm": { + "address": "yXMhEgMKAknAUTNR31nYr4uSP7GMpmCgsm", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yf9Zudcaof9c3KrdPVz3r7YPZJ1U2ECbXE": { + "address": "yf9Zudcaof9c3KrdPVz3r7YPZJ1U2ECbXE", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yXwFx2jAMxxntn421z9PsdzytmHRJGjyU4": { + "address": "yXwFx2jAMxxntn421z9PsdzytmHRJGjyU4", + "transactions": [ + "c8f0d780e6cedb9c37724f98cc3fecd6d5ad314db28e3cd439184bf25196ceb4", + "ca2afbe06b0187777f7e1aa8ae90c16ea094fbf1ed1b2ae8372e34c31a2cf67d" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yTDBgBUYbPq2XWkrnEWXTZGDcvbPxH1chY": { + "address": "yTDBgBUYbPq2XWkrnEWXTZGDcvbPxH1chY", + "transactions": [ + "ca2afbe06b0187777f7e1aa8ae90c16ea094fbf1ed1b2ae8372e34c31a2cf67d", + "3096b29ee16f6ec1b8e8b5f3d01beb5a231d87b51b6fa4e637e0f45b6b8b83b6" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yexVh4ARXbi7ZCUEEGEDde63t91N5e6kfC": { + "address": "yexVh4ARXbi7ZCUEEGEDde63t91N5e6kfC", + "transactions": [ + "3096b29ee16f6ec1b8e8b5f3d01beb5a231d87b51b6fa4e637e0f45b6b8b83b6", + "95fe8be7b264aa71a1a55478abce7233311b7f3a22135a6a2543988294ddbc68" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "ydVc6fZz46csMbAea6xtd1AmW3Zohk9DX5": { + "address": "ydVc6fZz46csMbAea6xtd1AmW3Zohk9DX5", + "transactions": [ + "95fe8be7b264aa71a1a55478abce7233311b7f3a22135a6a2543988294ddbc68", + "267c4191e3b32241bf00cf15ff7e02460e4d6cc892afaaf3a28f88601b5b6a38" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yWvR7rm6s28SePpVi24AX1pNcBoTubsEcC": { + "address": "yWvR7rm6s28SePpVi24AX1pNcBoTubsEcC", + "transactions": [ + "267c4191e3b32241bf00cf15ff7e02460e4d6cc892afaaf3a28f88601b5b6a38", + "eb39c1c9e0e5dd8df5ec1f822a4fd96469749f56443377262d933f888568047d" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yb4yiJrSTstkTrm4Ah9cfkyYmbC3H2TPoS": { + "address": "yb4yiJrSTstkTrm4Ah9cfkyYmbC3H2TPoS", + "transactions": [ + "eb39c1c9e0e5dd8df5ec1f822a4fd96469749f56443377262d933f888568047d", + "d066ac08dc743bfe5a29fd541802f9269b3da17c71f6b2f64aabd6c68a671566" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yb5dJVhq2pi68Qm1ZfzUsux9fCqPdw5GuM": { + "address": "yb5dJVhq2pi68Qm1ZfzUsux9fCqPdw5GuM", + "transactions": [ + "d066ac08dc743bfe5a29fd541802f9269b3da17c71f6b2f64aabd6c68a671566", + "a3f4b8c9c9f49434e0437ad1aa0fa9f9c621d3a8742cee174402863a5b23fe4a" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yeSJLf7xsfenJwmwAb28suXpHLwd8bgc2K": { + "address": "yeSJLf7xsfenJwmwAb28suXpHLwd8bgc2K", + "transactions": [ + "a3f4b8c9c9f49434e0437ad1aa0fa9f9c621d3a8742cee174402863a5b23fe4a", + "eee88d2a03a0452dfe0abc9a84915f33d552110088c4382f0c459960494bf2ab" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yTqDebXPzENywmQiciNjn6U5Hkv7uBYvgm": { + "address": "yTqDebXPzENywmQiciNjn6U5Hkv7uBYvgm", + "transactions": [ + "eee88d2a03a0452dfe0abc9a84915f33d552110088c4382f0c459960494bf2ab", + "0fd66f212549dbb84d42a745e9e0bef5e6772acfaf8b6d06579cffa15ac9fa1d" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yegUezShAWzyZfM8WVSj5yzaKzVTRQC9n7": { + "address": "yegUezShAWzyZfM8WVSj5yzaKzVTRQC9n7", + "transactions": [ + "0fd66f212549dbb84d42a745e9e0bef5e6772acfaf8b6d06579cffa15ac9fa1d", + "92be5095443f506771d6f145716ab33a12b20239b5d363c9b09d5e5cd7fb3f96" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "ydtkdeeX8NfAR7thLnzDPwrsiZ7c3a7bVL": { + "address": "ydtkdeeX8NfAR7thLnzDPwrsiZ7c3a7bVL", + "transactions": [ + "92be5095443f506771d6f145716ab33a12b20239b5d363c9b09d5e5cd7fb3f96", + "ea704ab4f38d786116875270c141b628f182684fd39dcd57c969237cf92f5589" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yUk83srvLCbpjkiYTpcPpftjGroSeYdHTF": { + "address": "yUk83srvLCbpjkiYTpcPpftjGroSeYdHTF", + "transactions": [ + "ea704ab4f38d786116875270c141b628f182684fd39dcd57c969237cf92f5589", + "f86445d20beccda3749058d90a605f4179a51a04b09f1262a9aefb382fe21bfd" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yScDono7QjZPqNJo27eCsrNwUAXFdTLK9P": { + "address": "yScDono7QjZPqNJo27eCsrNwUAXFdTLK9P", + "transactions": [ + "f86445d20beccda3749058d90a605f4179a51a04b09f1262a9aefb382fe21bfd", + "b2420b554dad82bffb22f10bda0124876fc6d02b4892d01833b413cad275c351" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yZBYJEqmt8rE57unfDRseXBLkdCyfDF9VX": { + "address": "yZBYJEqmt8rE57unfDRseXBLkdCyfDF9VX", + "transactions": [ + "b2420b554dad82bffb22f10bda0124876fc6d02b4892d01833b413cad275c351", + "174bbb78437049150cfe8c00ec53cd9f0ee038d0e9451e85eef25b5256a2e95e" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yhhkAYqLNJQyu5XNAT61b2b8Qo7k5Uib1b": { + "address": "yhhkAYqLNJQyu5XNAT61b2b8Qo7k5Uib1b", + "transactions": [ + "174bbb78437049150cfe8c00ec53cd9f0ee038d0e9451e85eef25b5256a2e95e", + "fec6553adbe93a1702b43a9a871979ea0294bcd8d776ae684ff7aca0d22fe0e9" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yZoLgZuUEmFANWFA8PE2zNVH1Yaz9fciGT": { + "address": "yZoLgZuUEmFANWFA8PE2zNVH1Yaz9fciGT", + "transactions": [ + "fec6553adbe93a1702b43a9a871979ea0294bcd8d776ae684ff7aca0d22fe0e9", + "fc9863caf91f51da5ae67cd8bdada46435db0e940164bee87248026c79ccebd9" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yZQVV3EaLpAehKMpPiGy7w3NRmgKD7xJX1": { + "address": "yZQVV3EaLpAehKMpPiGy7w3NRmgKD7xJX1", + "transactions": [ + "fc9863caf91f51da5ae67cd8bdada46435db0e940164bee87248026c79ccebd9", + "74789930fe5d4bab8e485f1432a79704d3f111eec959aed55584c04b7f7da2dc" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yQ7SV5m46ck1fVQrJinvA982aBAUv3TCq2": { + "address": "yQ7SV5m46ck1fVQrJinvA982aBAUv3TCq2", + "transactions": [ + "74789930fe5d4bab8e485f1432a79704d3f111eec959aed55584c04b7f7da2dc", + "4b1ec1877f9c2eaa4fbfeb034b38b21156aab4955d597fc328b6c30be1acd10e" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yaaw9BZ16eoXv4RAGBA9qbSrjvTTbqbUKq": { + "address": "yaaw9BZ16eoXv4RAGBA9qbSrjvTTbqbUKq", + "transactions": [ + "4b1ec1877f9c2eaa4fbfeb034b38b21156aab4955d597fc328b6c30be1acd10e", + "c0cbf437c0f8e980b8d64a3148a258645a821dc51631c4dfdb3dcd8324d6819e" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yjbLQQkP5tZ3aZ734vFXscFG14vDPnE1rx": { + "address": "yjbLQQkP5tZ3aZ734vFXscFG14vDPnE1rx", + "transactions": [ + "c0cbf437c0f8e980b8d64a3148a258645a821dc51631c4dfdb3dcd8324d6819e", + "5ec24edb2394c13db2d2b3fb1502cd5a4db8006b283eb6868cc44828df30148c" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yYqmE6MCitNpgyL7iRdNheG2ZP4bNCxoMV": { + "address": "yYqmE6MCitNpgyL7iRdNheG2ZP4bNCxoMV", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yQpSjWKF6HabXAMizRvMETDd6EbQixXQZ7": { + "address": "yQpSjWKF6HabXAMizRvMETDd6EbQixXQZ7", + "transactions": [ + "5ec24edb2394c13db2d2b3fb1502cd5a4db8006b283eb6868cc44828df30148c", + "f95dbbc93352dd0cfe0350ce7407eabc5ead0cd5b3265873c5544447047eddf1" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yNi1fiMR554w6MehxFoESXwUHk3v6AJELy": { + "address": "yNi1fiMR554w6MehxFoESXwUHk3v6AJELy", + "transactions": [ + "f95dbbc93352dd0cfe0350ce7407eabc5ead0cd5b3265873c5544447047eddf1", + "5cfbb3fd2dfdd75f590278a1749fe2dae2d5395eef5e87690756adb44cb5745d" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yVRRAWki91bieh6eAYjwZZpJLcgmY14D79": { + "address": "yVRRAWki91bieh6eAYjwZZpJLcgmY14D79", + "transactions": [ + "5cfbb3fd2dfdd75f590278a1749fe2dae2d5395eef5e87690756adb44cb5745d", + "34ba3e041c3026bab4f2c2c0ec1e1bb496ec72042aba9b03a324b74d1ec1d1af" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yf3NLNnFSnZQuQEFHrAU3Kzdip3AfNXBy3": { + "address": "yf3NLNnFSnZQuQEFHrAU3Kzdip3AfNXBy3", + "transactions": [ + "34ba3e041c3026bab4f2c2c0ec1e1bb496ec72042aba9b03a324b74d1ec1d1af", + "0d61420bb114d9d16b424e2822ab8412f3dec63e0b8da8df8f0fd5786782abd0" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yTpUMFdpe2WnMz9cjJp3jcDR4G6gXjW5jh": { + "address": "yTpUMFdpe2WnMz9cjJp3jcDR4G6gXjW5jh", + "transactions": [ + "0d61420bb114d9d16b424e2822ab8412f3dec63e0b8da8df8f0fd5786782abd0", + "0e62d7c53e01bd3a61065ca279bd77a6e1354c0c6a373502d7cc9328be3812a8" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yjPgE4RimwUGZFJpDZjANyioWd1SbZpcaS": { + "address": "yjPgE4RimwUGZFJpDZjANyioWd1SbZpcaS", + "transactions": [ + "0e62d7c53e01bd3a61065ca279bd77a6e1354c0c6a373502d7cc9328be3812a8", + "49c214eab55a17f80ec6e136861cbc56d507ea0c491864706b3cfa0a7f175634" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "ygjDcpfLxzs8pweCQ6Ty8GioNDTgfQX3GQ": { + "address": "ygjDcpfLxzs8pweCQ6Ty8GioNDTgfQX3GQ", + "transactions": [ + "49c214eab55a17f80ec6e136861cbc56d507ea0c491864706b3cfa0a7f175634", + "d17f579a4ead6a798618d3b567c2f130cac22a0830219d03404d68d9b6a5d8ce" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yUFchfQMACPyoy6HtqNRv8gjEYKvjd3Wd6": { + "address": "yUFchfQMACPyoy6HtqNRv8gjEYKvjd3Wd6", + "transactions": [ + "d17f579a4ead6a798618d3b567c2f130cac22a0830219d03404d68d9b6a5d8ce", + "9594a1c5a05fa24a78e9b93f03ee62b56d9c90e62e62298a3cf54629a355750a" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yj1oiPUjyo2RLvpbyigfb7pLiX1piritz1": { + "address": "yj1oiPUjyo2RLvpbyigfb7pLiX1piritz1", + "transactions": [ + "9594a1c5a05fa24a78e9b93f03ee62b56d9c90e62e62298a3cf54629a355750a", + "d522f8a9406dc497893ec3e99c69a05a1b88e0a010e3f12495628e412b85663a" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "ycGhUD69dS2V18LFy4qrfYhG1qJ8JnVvX7": { + "address": "ycGhUD69dS2V18LFy4qrfYhG1qJ8JnVvX7", + "transactions": [ + "d522f8a9406dc497893ec3e99c69a05a1b88e0a010e3f12495628e412b85663a", + "3ee6bec9dbe57574305a25d72362434b8f3d02a79c6228d12f0a957891f5e52d" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yj6f6KLsQHFoVCUmfbpjSGmdqnB3j9NouZ": { + "address": "yj6f6KLsQHFoVCUmfbpjSGmdqnB3j9NouZ", + "transactions": [ + "3ee6bec9dbe57574305a25d72362434b8f3d02a79c6228d12f0a957891f5e52d", + "89181c1b627d32ea7dceda330c4c44d4992e98f7cacc392691220bba800ec6a7" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "ycA6K1bW4nxYH8UBvYaXpxmdnMoXPQxp8L": { + "address": "ycA6K1bW4nxYH8UBvYaXpxmdnMoXPQxp8L", + "transactions": [ + "89181c1b627d32ea7dceda330c4c44d4992e98f7cacc392691220bba800ec6a7", + "5b02921dda2f33def0951615466578b3cac8fd19ae9a9e75ee0176bf3c8e4821" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yPx12VyoeVLhMCVDiwK7Js7zDWNtNbq8JV": { + "address": "yPx12VyoeVLhMCVDiwK7Js7zDWNtNbq8JV", + "transactions": [ + "5b02921dda2f33def0951615466578b3cac8fd19ae9a9e75ee0176bf3c8e4821", + "3d2e84e85560f835084d6e6514563a16453ded77d8474244b1a5dc95c8891faf" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yMSpVBXff6XoLMwmP7N4nX4Mjo21ewkYkE": { + "address": "yMSpVBXff6XoLMwmP7N4nX4Mjo21ewkYkE", + "transactions": [ + "3d2e84e85560f835084d6e6514563a16453ded77d8474244b1a5dc95c8891faf", + "2a035c18d1a51c7164beac80f374bef6b49d084681e8035071fc0adc903b606c" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yVoywf8fK8VtPEiLyx6zknqWBjE4izUXvD": { + "address": "yVoywf8fK8VtPEiLyx6zknqWBjE4izUXvD", + "transactions": [ + "2a035c18d1a51c7164beac80f374bef6b49d084681e8035071fc0adc903b606c", + "11d14e8be169305b49e5a080f0570d2be6783c4e8bbe0c32451b97e35b188ee9" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yU4Xor7Y8XjXFNYmVebqKeaCPqDmKNxbYd": { + "address": "yU4Xor7Y8XjXFNYmVebqKeaCPqDmKNxbYd", + "transactions": [ + "11d14e8be169305b49e5a080f0570d2be6783c4e8bbe0c32451b97e35b188ee9", + "b8c506c24e66aabc59b409b2ab37cd97893dae1ef671c5e839dd8649a65586a0" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yiE7vzjGNseTtEwtXZTmjcf99hfwLziJPq": { + "address": "yiE7vzjGNseTtEwtXZTmjcf99hfwLziJPq", + "transactions": [ + "b8c506c24e66aabc59b409b2ab37cd97893dae1ef671c5e839dd8649a65586a0", + "1b33390deac7e4c58761f048ee6d04ad2770277d78f2111951347c318088f4bd" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yLmXE68Kq8UrK42QjyNWi8gFqf1ihd3UiW": { + "address": "yLmXE68Kq8UrK42QjyNWi8gFqf1ihd3UiW", + "transactions": [ + "1b33390deac7e4c58761f048ee6d04ad2770277d78f2111951347c318088f4bd", + "1219e136566a370d2b302059a03b77f0ad0cfd98d6767e8a877157cbf6ffdccc" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yQnUHRyB768tawz3iLDs5VVLYg5dGEJYQi": { + "address": "yQnUHRyB768tawz3iLDs5VVLYg5dGEJYQi", + "transactions": [ + "1219e136566a370d2b302059a03b77f0ad0cfd98d6767e8a877157cbf6ffdccc", + "33b14c6bc960c5717d734d5a15dc86b2060bf6e746cc509863344204d356cee4" + ], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yMEnFG5TBqEZXYXTg3PhENtZgGbwhw6qbX": { + "address": "yMEnFG5TBqEZXYXTg3PhENtZgGbwhw6qbX", + "transactions": [ + "33b14c6bc960c5717d734d5a15dc86b2060bf6e746cc509863344204d356cee4" + ], + "utxos": { + "33b14c6bc960c5717d734d5a15dc86b2060bf6e746cc509863344204d356cee4-1": { + "satoshis": 224108673, + "script": "76a9140a163cfcba43b87e58b1996f61376d7bd8d9805288ac" + } + }, + "balanceSat": 224108673, + "unconfirmedBalanceSat": 0 + }, + "ybPPRHGDK6HUjAapixJaHjpFrBP7p1eNHX": { + "address": "ybPPRHGDK6HUjAapixJaHjpFrBP7p1eNHX", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yeKckmFWSPq4ZgxrKtqgWLfspT5YSuinAw": { + "address": "yeKckmFWSPq4ZgxrKtqgWLfspT5YSuinAw", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yX5fXtxaF5Umi3wkCvFCCbYjQniex8YfZb": { + "address": "yX5fXtxaF5Umi3wkCvFCCbYjQniex8YfZb", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yYXYNLd3pwDnZMo8VvFZWtuTaW97yMBtvh": { + "address": "yYXYNLd3pwDnZMo8VvFZWtuTaW97yMBtvh", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yPxjUsH2X9SFnpXt4ke7boWdVJb1jLRU2U": { + "address": "yPxjUsH2X9SFnpXt4ke7boWdVJb1jLRU2U", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yPaJs6aVAxsSsjsaeoWEEf1KWNJHA4osTX": { + "address": "yPaJs6aVAxsSsjsaeoWEEf1KWNJHA4osTX", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yRtTcfySvuZHmnyTGnSVFJiuXQLAMtA8Zp": { + "address": "yRtTcfySvuZHmnyTGnSVFJiuXQLAMtA8Zp", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yWniGBz9g5WzC8SNw2Yz6rTiw8gXCwviHH": { + "address": "yWniGBz9g5WzC8SNw2Yz6rTiw8gXCwviHH", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yTBBLUse991FY17xVQxTW6gELhDPLTbrfg": { + "address": "yTBBLUse991FY17xVQxTW6gELhDPLTbrfg", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yW8ZcyB43XEaNb1rC3vU8B787pCFd5A92G": { + "address": "yW8ZcyB43XEaNb1rC3vU8B787pCFd5A92G", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yZFMjHZLcw8VLZSfBDamfaowiespUADi8x": { + "address": "yZFMjHZLcw8VLZSfBDamfaowiespUADi8x", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yS4CoPF6Bb6vZAorsTQGddhdqzUNA4NQkq": { + "address": "yS4CoPF6Bb6vZAorsTQGddhdqzUNA4NQkq", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yeKFhYBcoDB5BFQVatGEY6xAWWgyAH2znL": { + "address": "yeKFhYBcoDB5BFQVatGEY6xAWWgyAH2znL", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yVtwa9msBFy3s5YW4zpKaRFEAS3CK5h1sd": { + "address": "yVtwa9msBFy3s5YW4zpKaRFEAS3CK5h1sd", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "ySdqTcWTqRHJBwsymifjg4j6hBTUtrhjCo": { + "address": "ySdqTcWTqRHJBwsymifjg4j6hBTUtrhjCo", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yXgzDeQ6Fa3gzthByHYeiwyVKYzDov7EtR": { + "address": "yXgzDeQ6Fa3gzthByHYeiwyVKYzDov7EtR", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yZVUpv1RfnH4gTiTtuarXVpAcK1h9irJ7q": { + "address": "yZVUpv1RfnH4gTiTtuarXVpAcK1h9irJ7q", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "yS4osisWi5dpSmUZFav56Uxj4Uhc3i7RaL": { + "address": "yS4osisWi5dpSmUZFav56Uxj4Uhc3i7RaL", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "ygAb2f6gztLoPdZrma8BRjm3MtNGZFrUbX": { + "address": "ygAb2f6gztLoPdZrma8BRjm3MtNGZFrUbX", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + }, + "ygukA9r4bM3CDS1gkFfTfVNozC7c5FvnGj": { + "address": "ygukA9r4bM3CDS1gkFfTfVNozC7c5FvnGj", + "transactions": [], + "utxos": {}, + "balanceSat": 0, + "unconfirmedBalanceSat": 0 + } + } + } +} diff --git a/packages/wallet-lib/fixtures/chains/testnet/blockheaders.json b/packages/wallet-lib/fixtures/chains/testnet/blockheaders.json new file mode 100644 index 00000000000..d58e0885f60 --- /dev/null +++ b/packages/wallet-lib/fixtures/chains/testnet/blockheaders.json @@ -0,0 +1,12 @@ +[ + { + "height": 600000, + "blockheader": "00000020a69faca490012f4371e4bd40a40167809e6fe1b03c9c5dcb7df247f2fa000000b4b8c2d4a78fb35e50a4eda0bd3a5b9b32f4d739b28a4266f24775b3b79d5b61c73275610e04021ea74d0000", + "hash":"000000de786e659950e0f27681faf1a91871d15de264d0b769cb5941c1d807c3" + }, + { + "height": 610000, + "blockheader": "00000020af5155e1f8d5b60fb247ac9bb8badbdce1fa72302336c7daab53585caf0000006d495b9629989ac3699d528956b1f617fb51a199ef4277c3f45f4606aba8e461d1c78a61210b011e94ef0000", + "hash":"00000094d124cfb68d6d59ffaec9f7d63965cb894855684e23a586274b49708f" + } +] \ No newline at end of file diff --git a/packages/wallet-lib/fixtures/chains/testnet/transactions.json b/packages/wallet-lib/fixtures/chains/testnet/transactions.json new file mode 100644 index 00000000000..5fd376a7a61 --- /dev/null +++ b/packages/wallet-lib/fixtures/chains/testnet/transactions.json @@ -0,0 +1,14 @@ +[ + { + "blockHash": "000000b2537d9b3468e02dc6f49fb8bbb5c9d8b77895df1e76816fbad5555d7d", + "transaction": "02000000019583d226737edfad682cc1f0297f688bfca4c0046062ed3e88fa47dd47eab2b2010000006a473044022021550232cc659fa412deb2ea98956faf9ff0efea3a04029bb63022f6809105ce0220230a3f955a3ee7563f5b006342d8db940f3b29f4e312701bf51720575c7a7a8a012103f3d2b2ddfe6ffad2b8fc1708f2eafadfed36bf31a05dfa04b1fc176526475b0ffeffffff02a003d806000000001976a91464220a1c12690ec26d837b3be0a2e3588bb4b79188ac4c5bc92b250000001976a9143e89746b9aa52703ab784bc0df467b160406ffb988acb56b0900", + "height": 617398, + "isInstantLocked": true, + "isChainLocked": true + }, + { + "transaction": "0200000001c7d66bb85e0069c221b44b07f49f52cc4f2e54f70e14430b94888327763a66a9010000006b483045022100da5b319f73e6adfee751f33308f5a8c1fceeab2683e15e132d79053b3118639602204262022fb85f88d9802649a289a1134b678efcf708faaeae8f101e8eab785054012102bc626898b49f31f5194de7bc68004401639a20cfa82e4c2eac9684a91fc47a57feffffff0270f2f605000000001976a91464220a1c12690ec26d837b3be0a2e3588bb4b79188ac912e250c250000001976a91415e1edb5c5d9e67d0e36f94343b3eff26bb76d1088ac266e0900", + "blockHash":"0000005c81a683007e86e75c76b4b2feca229f806702ca92953562f2ae628ce7", + "height": 618023 + } +] \ No newline at end of file diff --git a/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/getFixtureAccountWithStorage.js b/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/getFixtureAccountWithStorage.js new file mode 100644 index 00000000000..79a622b07de --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/getFixtureAccountWithStorage.js @@ -0,0 +1,32 @@ +const walletStoreMock = require('./wallet-store.json'); +const chainStoreMock = require('./chain-store.json'); +const Storage = require('../../../src/types/Storage/Storage'); +const { KeyChainStore, DerivableKeyChain } = require('../../../src/index'); + +module.exports = (opts = {}) => { + const { walletId } = walletStoreMock; + + const mockedAccount = { + walletId, + index: 0, + storage: new Storage(), + accountPath: 'm/0', + network: 'testnet', + walletType: 'privateKey', + ...opts, + }; + + mockedAccount.keyChainStore = new KeyChainStore(); + mockedAccount.keyChainStore.addKeyChain(new DerivableKeyChain({ + privateKey: '2a331817b9d6bf85100ef05503d16f9f57c8855dbf13766b2f26c382b716d396', + lookAheadOpts: { + 'm/0': 1, + }, + }), { isMasterKeyChain: true }); + mockedAccount.storage.createWalletStore(walletId); + mockedAccount.storage.createChainStore('testnet'); + mockedAccount.storage.getWalletStore(walletId).importState(walletStoreMock); + mockedAccount.storage.getChainStore('testnet').importState(chainStoreMock); + + return mockedAccount; +}; diff --git a/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/wallet-store.json b/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/wallet-store.json new file mode 100644 index 00000000000..3c472465951 --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/2a331817b9d6bf85100ef0/wallet-store.json @@ -0,0 +1,15 @@ +{ + "walletId": "6101b44d50", + "state": { + "mnemonic": null, + "paths": { + "m/0": { + "path": "m/0", + "addresses": { + "0": "ycDeuTfs4U77bTb5cq17dame28zdWHVYfk" + } + } + }, + "identities": {} + } +} diff --git a/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/categorizeTransactions.expectedResults.js b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/categorizeTransactions.expectedResults.js index 11f2d1f2c69..14c900525c4 100644 --- a/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/categorizeTransactions.expectedResults.js +++ b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/categorizeTransactions.expectedResults.js @@ -187,7 +187,7 @@ const expectedResultTx9 = { } ], transaction: transactionsWithMetadataFixtures[10][0], - type: 'unknown', + type: 'account_transfer', blockHash: '0000016fb685b4b1efed743d2263de34a9f8323ed75e732654b1b951c5cb4dde', height: 558236, isInstantLocked: true, diff --git a/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage.js b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage.js new file mode 100644 index 00000000000..46955463144 --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage.js @@ -0,0 +1,33 @@ +const walletStoreMock = require('./wallet-store.json'); +const chainStoreMock = require('./chain-store.json'); +const Storage = require('../../../src/types/Storage/Storage'); +const { KeyChainStore, DerivableKeyChain } = require('../../../src/index'); + +module.exports = (opts = {}) => { + const { walletId } = walletStoreMock; + + const mockedAccount = { + walletId, + index: 0, + storage: new Storage(), + accountPath: "m/44'/1'/0'", + network: 'testnet', + walletType: 'hdwallet', + ...opts, + }; + + mockedAccount.keyChainStore = new KeyChainStore(); + mockedAccount.keyChainStore.addKeyChain(new DerivableKeyChain({ + mnemonic: 'apart trip dignity try point rocket damp reflect raw ten normal young', + lookAheadOpts: { + 'm/0': 40, + 'm/1': 40, + }, + }), { isMasterKeyChain: true }); + mockedAccount.storage.createWalletStore(walletId); + mockedAccount.storage.createChainStore('testnet'); + mockedAccount.storage.getWalletStore(walletId).importState(walletStoreMock); + mockedAccount.storage.getChainStore('testnet').importState(chainStoreMock); + + return mockedAccount; +}; diff --git a/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/transactions-with-metadata.json b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/transactions-with-metadata.json index 5ae5f2850c2..2e6847d4b7c 100644 --- a/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/transactions-with-metadata.json +++ b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/transactions-with-metadata.json @@ -4,8 +4,8 @@ { "blockHash": "00000c1e4556add15119392ed36ec6af2640569409abfa23a9972bc3be1b3717", "height": 558036, - "instantLocked": true, - "chainLocked": true + "isInstantLocked": true, + "isChainLocked": true } ], [ @@ -13,8 +13,8 @@ { "blockHash": "00000221952c2a60adcb929de837f659308cb5c6bb7783016479381fb550fbad", "height": 557481, - "instantLocked": true, - "chainLocked": true + "isInstantLocked": true, + "isChainLocked": true } ], [ @@ -22,8 +22,8 @@ { "blockHash": "000001deee9f99e8219a9abcaaea135dbaae8a9b0f1ea214e6b6a37a5c5b115d", "height": 555506, - "instantLocked": true, - "chainLocked": true + "isInstantLocked": true, + "isChainLocked": true } ], [ @@ -31,8 +31,8 @@ { "blockHash": "00000084b4d9e887a6ad3f37c576a17d79c35ec9301e55210eded519e8cdcd3a", "height": 558102, - "instantLocked": true, - "chainLocked": true + "isInstantLocked": true, + "isChainLocked": true } ], [ @@ -40,8 +40,8 @@ { "blockHash": "000000b6006c758eda23ec7e2a640a0bf2c6a0c44827be216faff6bf4fd388e8", "height": 555507, - "instantLocked": true, - "chainLocked": true + "isInstantLocked": true, + "isChainLocked": true } ], [ @@ -49,8 +49,8 @@ { "blockHash": "0000012cf6377c6cf2b317a4deed46573c09f04f6880dca731cc9ccea6691e19", "height": 555508, - "instantLocked": true, - "chainLocked": true + "isInstantLocked": true, + "isChainLocked": true } ], [ @@ -58,8 +58,8 @@ { "blockHash": "000001953ea0bbb8ad04a9a1a2a707fef207ad22a712d7d3c619f0f9b63fa98c", "height": 558229, - "instantLocked": true, - "chainLocked": true + "isInstantLocked": true, + "isChainLocked": true } ], [ @@ -67,8 +67,8 @@ { "blockHash": "000000dffb05c071a8c05082a475b7ce9c1e403f3b89895a6c448fe08535a5f5", "height": 558230, - "instantLocked": true, - "chainLocked": true + "isInstantLocked": true, + "isChainLocked": true } ], [ @@ -76,8 +76,8 @@ { "blockHash": "000000444b3f2f02085f8befe72da5442c865c290658766cf935e1a71a4f4ba7", "height": 558242, - "instantLocked": true, - "chainLocked": true + "isInstantLocked": true, + "isChainLocked": true } ], [ @@ -85,8 +85,8 @@ { "blockHash": "000001f9c5de4d2b258a975bfbf7b9a3346890af6389512bea3cb6926b9be330", "height": 558246, - "instantLocked": true, - "chainLocked": true + "isInstantLocked": true, + "isChainLocked": true } ], [ @@ -94,8 +94,8 @@ { "blockHash": "0000016fb685b4b1efed743d2263de34a9f8323ed75e732654b1b951c5cb4dde", "height": 558236, - "instantLocked": true, - "chainLocked": true + "isInstantLocked": true, + "isChainLocked": true } ] ] diff --git a/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/wallet-store.json b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/wallet-store.json new file mode 100644 index 00000000000..e6858bf2dc3 --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/apart-trip-dignity/wallet-store.json @@ -0,0 +1,116 @@ +{ + "walletId": "d6143ef4e6", + "state": { + "mnemonic": null, + "paths": { + "m/44'/1'/1'": { + "path": "m/44'/1'/1'", + "addresses": { + "m/0/0": "yYJmzWey5kNecAThet5BFxAga1F4b4DKQ2", + "m/0/1": "yNCqctyQaq51WU1hN5aNwsgMsZ5fRiB7GY", + "m/0/2": "yNPbYz5cZKw2EwxtkL3VSVzPi2FYp9VKjQ", + "m/0/3": "ybsGWzsnSCAZufgSeUjScVxqEdved99UM2", + "m/0/4": "yfNHuPojk8XKWP5nuueDptX4nM7qToudgx", + "m/0/5": "yXxLnDkk6s8h1PSnYaFM6MAyRarc1Kc1rY", + "m/0/6": "yipResSzN2zUvL7UYkmptKKmQTv7sNssRn", + "m/0/7": "yZPtNwimHdRiKYbNQW49qezw1Kc1YwUJeT", + "m/0/8": "yPMjYYfQbga2nBiuqqfUyX41U1vwRZ8fG8", + "m/0/9": "yLueLWWcLQsaXQ8D5o9tcyo8tfTxMWXvG4", + "m/0/10": "yN8gzgsc1RVjXThMQT5qZH2jjpnMymz6zP", + "m/0/11": "yPQLWBNwMdLxUW2oUwHGwQtfyYxD41BARJ", + "m/0/12": "yg5g2AfWFdwWexWGfbSXYbUHf1y5WWrFPs", + "m/0/13": "yWyABu4naV1Jzw7w9sn1gqhebPRSkCndsS", + "m/0/14": "ycuUPzUBjhKyUjezQR1LNot79a6C4aRLaR", + "m/0/15": "yQ7YjvAXgDAUCekveHVjr6NBveXrUemVno", + "m/0/16": "yi8bghcw627cMGpuH4bJqH6bqR5ywv1NLH", + "m/0/17": "yizHu8i2rfwzwBgnJ62s2WUe6wLoDjne6N", + "m/0/18": "yW1u3tySeUKAKJsz7sjZFyjUiTyKLB6xBv", + "m/0/19": "yNaSkdy1Q8JNubUdbLMGsGf7sTRofEJYZq", + "m/1/0": "yXMrw79LPgu78EJsfGGYpm6fXKc1EMnQ49", + "m/1/1": "yh6Hcyipdvp6WJpQxjNbaXP4kzPQUJpY3n", + "m/1/2": "yNphpXuaTZRpU9FBh2W7NkUYcr3kBDE8me", + "m/1/3": "yXFppDT59xYD41mT2pmAdnvr7aZEFdgdrN", + "m/1/4": "yeKGAiiEHBGRujvLoYewA77jDDpeDamxvF", + "m/1/5": "yaxTG66CVzKgHhHZXojRHC9ztLTvz3fwdT", + "m/1/6": "yYw6qU7dwGoELZkSTj3oSKRpM4U8qTMc1U", + "m/1/7": "yQE2MksEnSfbeNre19oja9Jj8tvpj64C5a", + "m/1/8": "yaRnvHo8oLvVmv46vMj5XPbDJouQSnmcLT", + "m/1/9": "yj5ofWf2uYQQkSavYm2WXgu1QkaZCyP3Cm", + "m/1/10": "yUCjGmEwrHJwNDrE1o2rMre6MkSbiE6yz7", + "m/1/11": "yfJzd1nE2rEqz5XEurD6vs4ykizwmw9xTv", + "m/1/12": "yUk8U3jRZMHKVTa1eFDEtZpa1G4E13FP4d", + "m/1/13": "yMr59YWQFCADq4FbWrtxDUtMwwshSrmAyK", + "m/1/14": "yetSehBupzGS9yps5ogqARUGmTMAs2xVcQ", + "m/1/15": "yNcESKLwriNrhM6EyoSpZEXrzdY3uht92T", + "m/1/16": "yN2FihGU7KdaEspp39bKrhsHypeyeYzoM2", + "m/1/17": "yirpWLxHuhwFzA6LfUPKUh1Ke9RB9BUjit", + "m/1/18": "yVDN66vvdshWdNzhUaQNB6xExAHkzs1zj8", + "m/1/19": "yPzofnEhRVfDisL2nCUJtAoSHkuyMirHZS", + "m/0/20": "yjG4jiCMNbx3MyFCAsaNaU29CBVmgPf8hS", + "m/0/21": "yWrEYTtydaZCyJoKE1vzX4e4RXDNPGdnw9", + "m/1/20": "yM7Smr8HEhbRW4CXga898brATrJSsk1QUh", + "m/1/21": "yUprvaE5KrKVPgABmGtxba9MmZqDzKQHom" + } + }, + "m/44'/1'/0'": { + "path": "m/44'/1'/0'", + "addresses": { + "m/0/0": "yTwEca67QSkZ6axGdpNFzWPaCj8zqYybY7", + "m/0/1": "yercyhdN9oEkZcB9BsW5ktFaDxFEuK6qXN", + "m/0/2": "ygk3GCSba2J3L9G665Snozhj9HSkh5ByVE", + "m/0/3": "ybuL6rM6dgrKzCg8s99f3jxGuv5oz5JcDA", + "m/0/4": "ygHAVkMtYSqoTWHebDv7qkhMV6dHyuRsp2", + "m/0/5": "yMLhEsiP2ajSh8STmXnNmkWXtoHsmawZxd", + "m/0/6": "yj8rRKATAUHcAgXvNZekob58xKm2oNyvhv", + "m/0/7": "yhaAB6e8m3F8zmGX7WAVYa6eEfmSrrnY8x", + "m/0/8": "yiXh4Yo5djG6QH8WzXkKm5EFzqLRJWakXz", + "m/0/9": "yQYv3Um6DsdtANo1ZPTUte75wAGMstLRex", + "m/0/10": "yiYPJmu7eEm1cXUNumQRdjv1fvPhsfgMS4", + "m/0/11": "yii4aUZhNfL6EWN9KAgAFrJzGJmqHnF4wx", + "m/0/12": "yLpTquSct2SGz2Ka45uTPDd81Kzro2Jt2k", + "m/0/13": "yMiJtpzb1Qthy9TGnavsf5NZ6EZZa4j9q3", + "m/0/14": "yacgSfW7RkwWakEZPg8USAVdzCypiG3vxS", + "m/0/15": "yVvrmoRPFLy6nUpCQBT8ZExxF5wF3DhiGU", + "m/0/16": "yaJf2aG6cFUtfv4o6TuEKsh5kr4xq5iAY4", + "m/0/17": "yfardJQ4ucgWLKQPaRHGMRMbSGm5H4ExJR", + "m/0/18": "yLSCqx7dcM5JKR2fG7vHbF2axMvuYqomaw", + "m/0/19": "yVij8XpJ78LM5hepSV1KF7T8vRpUEXCpK5", + "m/1/0": "yNDpPsJqXKM36zHSNEW7c1zSvNnrZ699FY", + "m/1/1": "yLk4Hw3w4zDudrDVP6W8J9TggkY57zQUki", + "m/1/2": "yirJaK8KCE5YAmwvLadizqFw3TCXqBuZXL", + "m/1/3": "yhdRfg5gNr587dtEC4YYMcSHmLVEGqqtHc", + "m/1/4": "yYwKP1FQae5kbjXkmuirGx6Xzf8NzHpLqW", + "m/1/5": "yX9gmsm8aSxZZjYhq4w35aidT7qbhcpNjU", + "m/1/6": "ybgXCTGMHEBbQeUib8c3xAjtGAc12XtWiU", + "m/1/7": "yS31WpdMT2b34uL9C37fbUoACHhiupHCyP", + "m/1/8": "yTSpFqRoX3vyN286AUtKKhgmX5Xb41YKQe", + "m/1/9": "yQU5YsqN7psTTASuYbcMi7N5nNZGaxXb2X", + "m/1/10": "yVGGFj9BLgEab5rucSGLC6UGVLQKB4U1wJ", + "m/1/11": "yQCh5yYCHEbJzgSJE9rdHiqXHidKm3kwr5", + "m/1/12": "yX7T3Ac3yaLk5CTC5UaR93Fc7SjYkeT5hn", + "m/1/13": "yXx3WXq8kYNPbYEg5U6bL8Xfih4g5LCYVo", + "m/1/14": "yYnLMTz3jCi2KKKNuo3TVkEAGyUFg8tgkJ", + "m/1/15": "yiKa1dA6B4tSTNJqJP9Y5pQfQEffnQQDTL", + "m/1/16": "yf7vcuDnE9DVhXdMfBMQQTEi43otYQzkWE", + "m/1/17": "yTmSmocwERCeRHqNNG5SbpYKUra1HTmj8m", + "m/1/18": "yivUe5NeJsGsREwPQZUGYaTSwWB3E1oLcz", + "m/1/19": "ygfsZojdfW9UjCRU4ra95Aq6YgCC7UqZFx", + "m/1/20": "yU9fdXaUVtefwDZvxjJAr9xj1z2MtYi34A", + "m/1/21": "yXgMN6FgrgZCnTN1vhoZMh8afKMBmi3JC4", + "m/1/22": "yiqaCbXscvR8y3VFYMzdaKCaAGuDuZxMzt", + "m/0/20": "ydJpjuJGossAZR7S5oS7cWvjygEwoj8Xwp", + "m/0/21": "yW3TmWnmhvpxRbgFcQ8oXqDRkn3RhRH6jj", + "m/0/22": "yRegVX85DThKRkH8C61TtRacfzrkiBfNy5", + "m/0/23": "yPtDCqDFRe1JuDp8pvdiEMQMz2erGwS3VG", + "m/0/24": "yM9pSw3L4oBfG7uQL5o522Hu3WTvy9awgZ", + "m/0/25": "yNC6qYJYungzuk5XUynDFKCn54Dy8ngox4", + "m/0/26": "yR5KcLr1bceLT4teTk2qoJx6pFLik1zyzL", + "m/0/27": "yRrKLGJa9JmdjBWvrHtedKjHTao6CRDTKf", + "m/0/28": "yP5dShZBydpbEzgGoXL6kcjv2KzervRrYB", + "m/1/23": "ydcgWDxheSxrLAqDBP4JXBndMCzUNf77gq" + } + } + }, + "identities": {} + } +} diff --git a/packages/wallet-lib/fixtures/wallets/c922713eac.json b/packages/wallet-lib/fixtures/wallets/c922713eac.json new file mode 100644 index 00000000000..3fac8277713 --- /dev/null +++ b/packages/wallet-lib/fixtures/wallets/c922713eac.json @@ -0,0 +1,97 @@ +{ + "walletId": "c922713eac", + "state": { + "mnemonic": null, + "paths": { + "m/44'/1'/0'": { + "path": "m/44'/1'/0'", + "addresses": { + "m/0/0": "ycRqwWy2pNzwNumX79MmRyvQYVpy6w3AeX", + "m/0/1": "ycuRYGdNudwRxKNDQBqHDW7aGbJU6uqhXo", + "m/0/2": "yfNFUv1vfx3EVyVGtfZNXqvFR7HLP5MGRq", + "m/0/3": "yjDWQWhNixjKLMjzmXxWqLdJUEcgyjQh1o", + "m/0/4": "ygCCNzQM2Uj7Dfyi6sxPGBmRY6hqHGEFP9", + "m/0/5": "yX5c33WWgLacu7Z1hnBTLKPSXbbGt4BFNt", + "m/0/6": "yYeyVQqiejQsFmkQjtbcigLLPVGJKHxQBL", + "m/0/7": "yTFXnvNARXGPNL1ZKFHyVah3xY6JxzQaPD", + "m/0/8": "yNGpfTzZkJNhEQUk6Wj1VdS8RUVpq6qA2g", + "m/0/9": "ySW43fHX7uTEjJWjozu3EF3bsXywFoo1gf", + "m/0/10": "yM3LdfhFcMQBQoECRrh2WZwy6c8xnsA1rq", + "m/0/11": "ybDdvzUVYvqSPnvdzqgQo1ictZxTjoNTeY", + "m/0/12": "yZLvsg1LuC1q9xa1FqNoKWKHZJTLb1Rs5U", + "m/0/13": "yeGtNDLgTEfSYtYK84kXwvHXwRu3AQprku", + "m/0/14": "yPBq6vmEXnz2FtHVztLHD9HPQf8FMbnRPr", + "m/0/15": "yjcBeyf1r8EkjNFXGgX4NmNQEwk4NNH4Dp", + "m/0/16": "ySCxSmKAfjo8fk15TCM9chiS1dAQfJ54z2", + "m/0/17": "ySBWPfBDnN2iWKt4uRoFaVPGDoRDQpo5i1", + "m/0/18": "yXMhEgMKAknAUTNR31nYr4uSP7GMpmCgsm", + "m/0/19": "yf9Zudcaof9c3KrdPVz3r7YPZJ1U2ECbXE", + "m/1/0": "yXwFx2jAMxxntn421z9PsdzytmHRJGjyU4", + "m/1/1": "yTDBgBUYbPq2XWkrnEWXTZGDcvbPxH1chY", + "m/1/2": "yexVh4ARXbi7ZCUEEGEDde63t91N5e6kfC", + "m/1/3": "ydVc6fZz46csMbAea6xtd1AmW3Zohk9DX5", + "m/1/4": "yWvR7rm6s28SePpVi24AX1pNcBoTubsEcC", + "m/1/5": "yb4yiJrSTstkTrm4Ah9cfkyYmbC3H2TPoS", + "m/1/6": "yb5dJVhq2pi68Qm1ZfzUsux9fCqPdw5GuM", + "m/1/7": "yeSJLf7xsfenJwmwAb28suXpHLwd8bgc2K", + "m/1/8": "yTqDebXPzENywmQiciNjn6U5Hkv7uBYvgm", + "m/1/9": "yegUezShAWzyZfM8WVSj5yzaKzVTRQC9n7", + "m/1/10": "ydtkdeeX8NfAR7thLnzDPwrsiZ7c3a7bVL", + "m/1/11": "yUk83srvLCbpjkiYTpcPpftjGroSeYdHTF", + "m/1/12": "yScDono7QjZPqNJo27eCsrNwUAXFdTLK9P", + "m/1/13": "yZBYJEqmt8rE57unfDRseXBLkdCyfDF9VX", + "m/1/14": "yhhkAYqLNJQyu5XNAT61b2b8Qo7k5Uib1b", + "m/1/15": "yZoLgZuUEmFANWFA8PE2zNVH1Yaz9fciGT", + "m/1/16": "yZQVV3EaLpAehKMpPiGy7w3NRmgKD7xJX1", + "m/1/17": "yQ7SV5m46ck1fVQrJinvA982aBAUv3TCq2", + "m/1/18": "yaaw9BZ16eoXv4RAGBA9qbSrjvTTbqbUKq", + "m/1/19": "yjbLQQkP5tZ3aZ734vFXscFG14vDPnE1rx", + "m/0/20": "yYqmE6MCitNpgyL7iRdNheG2ZP4bNCxoMV", + "m/1/20": "yQpSjWKF6HabXAMizRvMETDd6EbQixXQZ7", + "m/1/21": "yNi1fiMR554w6MehxFoESXwUHk3v6AJELy", + "m/1/22": "yVRRAWki91bieh6eAYjwZZpJLcgmY14D79", + "m/1/23": "yf3NLNnFSnZQuQEFHrAU3Kzdip3AfNXBy3", + "m/1/24": "yTpUMFdpe2WnMz9cjJp3jcDR4G6gXjW5jh", + "m/1/25": "yjPgE4RimwUGZFJpDZjANyioWd1SbZpcaS", + "m/1/26": "ygjDcpfLxzs8pweCQ6Ty8GioNDTgfQX3GQ", + "m/1/27": "yUFchfQMACPyoy6HtqNRv8gjEYKvjd3Wd6", + "m/1/28": "yj1oiPUjyo2RLvpbyigfb7pLiX1piritz1", + "m/1/29": "ycGhUD69dS2V18LFy4qrfYhG1qJ8JnVvX7", + "m/1/30": "yj6f6KLsQHFoVCUmfbpjSGmdqnB3j9NouZ", + "m/1/31": "ycA6K1bW4nxYH8UBvYaXpxmdnMoXPQxp8L", + "m/1/32": "yPx12VyoeVLhMCVDiwK7Js7zDWNtNbq8JV", + "m/1/33": "yMSpVBXff6XoLMwmP7N4nX4Mjo21ewkYkE", + "m/1/34": "yVoywf8fK8VtPEiLyx6zknqWBjE4izUXvD", + "m/1/35": "yU4Xor7Y8XjXFNYmVebqKeaCPqDmKNxbYd", + "m/1/36": "yiE7vzjGNseTtEwtXZTmjcf99hfwLziJPq", + "m/1/37": "yLmXE68Kq8UrK42QjyNWi8gFqf1ihd3UiW", + "m/1/38": "yQnUHRyB768tawz3iLDs5VVLYg5dGEJYQi", + "m/1/39": "yMEnFG5TBqEZXYXTg3PhENtZgGbwhw6qbX", + "m/1/40": "ybPPRHGDK6HUjAapixJaHjpFrBP7p1eNHX", + "m/1/41": "yeKckmFWSPq4ZgxrKtqgWLfspT5YSuinAw", + "m/1/42": "yX5fXtxaF5Umi3wkCvFCCbYjQniex8YfZb", + "m/1/43": "yYXYNLd3pwDnZMo8VvFZWtuTaW97yMBtvh", + "m/1/44": "yPxjUsH2X9SFnpXt4ke7boWdVJb1jLRU2U", + "m/1/45": "yPaJs6aVAxsSsjsaeoWEEf1KWNJHA4osTX", + "m/1/46": "yRtTcfySvuZHmnyTGnSVFJiuXQLAMtA8Zp", + "m/1/47": "yWniGBz9g5WzC8SNw2Yz6rTiw8gXCwviHH", + "m/1/48": "yTBBLUse991FY17xVQxTW6gELhDPLTbrfg", + "m/1/49": "yW8ZcyB43XEaNb1rC3vU8B787pCFd5A92G", + "m/1/50": "yZFMjHZLcw8VLZSfBDamfaowiespUADi8x", + "m/1/51": "yS4CoPF6Bb6vZAorsTQGddhdqzUNA4NQkq", + "m/1/52": "yeKFhYBcoDB5BFQVatGEY6xAWWgyAH2znL", + "m/1/53": "yVtwa9msBFy3s5YW4zpKaRFEAS3CK5h1sd", + "m/1/54": "ySdqTcWTqRHJBwsymifjg4j6hBTUtrhjCo", + "m/1/55": "yXgzDeQ6Fa3gzthByHYeiwyVKYzDov7EtR", + "m/1/56": "yZVUpv1RfnH4gTiTtuarXVpAcK1h9irJ7q", + "m/1/57": "yS4osisWi5dpSmUZFav56Uxj4Uhc3i7RaL", + "m/1/58": "ygAb2f6gztLoPdZrma8BRjm3MtNGZFrUbX", + "m/1/59": "ygukA9r4bM3CDS1gkFfTfVNozC7c5FvnGj" + } + } + }, + "identities": { + "0": "D8oTFRQqiKcLEYXZBUVLsM9jBctq7YcM7MAXXT9GJesK" + } + } +} From 957623d957e1342a188046a8243a5f5c41ca572b Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Mon, 31 Jan 2022 03:57:07 +0100 Subject: [PATCH 05/27] feat: integrate to Storage --- .../wallet-lib/src/types/Storage/.eslintrc | 5 - .../wallet-lib/src/types/Storage/Storage.d.ts | 127 ------ .../wallet-lib/src/types/Storage/Storage.js | 97 +---- .../src/types/Storage/Storage.spec.js | 106 ----- .../src/types/Storage/initialStore.json | 5 - .../Storage/methods/addNewTxToAddress.js | 30 -- .../Storage/methods/addNewTxToAddress.spec.js | 27 -- .../src/types/Storage/methods/announce.js | 27 -- .../types/Storage/methods/announce.spec.js | 6 - .../src/types/Storage/methods/clearAll.js | 11 - .../types/Storage/methods/clearAll.spec.js | 25 -- .../types/Storage/methods/configure.spec.js | 61 --- .../types/Storage/methods/createAccount.js | 28 -- .../Storage/methods/createAccountStore.js | 29 ++ .../src/types/Storage/methods/createChain.js | 25 -- .../types/Storage/methods/createChain.spec.js | 31 -- .../types/Storage/methods/createChainStore.js | 15 + .../Storage/methods/createSingleAddress.js | 27 -- .../src/types/Storage/methods/createWallet.js | 24 -- .../Storage/methods/createWallet.spec.js | 75 ---- .../Storage/methods/createWalletStore.js | 13 + .../types/Storage/methods/exportAccounts.js | 8 - .../src/types/Storage/methods/exportChains.js | 3 - .../Storage/methods/exportTransactions.js | 3 - .../types/Storage/methods/exportWallets.js | 3 - .../types/Storage/methods/getBlockHeader.js | 13 - .../types/Storage/methods/getChainStore.js | 4 + .../Storage/methods/getIdentityIdByIndex.js | 11 - .../Storage/methods/getIndexedIdentityIds.js | 10 - .../types/Storage/methods/getInstantLock.js | 10 - .../src/types/Storage/methods/getStore.js | 8 - .../types/Storage/methods/getTransaction.js | 14 - .../Storage/methods/getTransaction.spec.js | 30 -- .../Storage/methods/getTransactionMetadata.js | 14 - .../methods/getTransactionMetadata.spec.js | 31 -- .../types/Storage/methods/getWalletStore.js | 5 + .../types/Storage/methods/importAccounts.js | 49 --- .../Storage/methods/importAccounts.spec.js | 63 --- .../types/Storage/methods/importAddress.js | 43 -- .../Storage/methods/importAddress.spec.js | 18 - .../types/Storage/methods/importAddresses.js | 31 -- .../Storage/methods/importAddresses.spec.js | 7 - .../Storage/methods/importBlockHeader.js | 30 -- .../src/types/Storage/methods/importChains.js | 10 - .../Storage/methods/importInstantLock.js | 9 - .../Storage/methods/importSingleAddress.js | 28 -- .../methods/importSingleAddress.spec.js | 8 - .../Storage/methods/importTransaction.js | 127 ------ .../Storage/methods/importTransaction.spec.js | 407 ------------------ .../Storage/methods/importTransactions.js | 34 -- .../methods/importTransactions.spec.js | 11 - .../Storage/methods/insertIdentityAtIndex.js | 25 -- .../types/Storage/methods/rehydrateState.js | 119 +---- .../Storage/methods/rehydrateState.spec.js | 226 ---------- .../types/Storage/methods/saveState.spec.js | 8 - .../types/Storage/methods/searchAddress.js | 46 -- .../Storage/methods/searchAddress.spec.js | 8 - .../methods/searchAddressWithTx.spec.js | 8 - .../Storage/methods/searchAddressesWithTx.js | 36 -- .../Storage/methods/searchBlockHeader.js | 25 -- .../Storage/methods/searchBlockHeader.spec.js | 53 --- .../Storage/methods/searchTransaction.js | 22 - .../Storage/methods/searchTransaction.spec.js | 31 -- .../methods/searchTransactionMetadata.js | 22 - .../methods/searchTransactionMetadata.spec.js | 37 -- .../src/types/Storage/methods/searchWallet.js | 18 - .../Storage/methods/searchWallet.spec.js | 33 -- .../types/Storage/methods/startWorker.spec.js | 53 --- .../types/Storage/methods/updateAddress.js | 123 ------ .../Storage/methods/updateAddress.spec.js | 35 -- .../Storage/methods/updateTransaction.js | 18 - .../Storage/methods/updateTransaction.spec.js | 70 --- 72 files changed, 105 insertions(+), 2747 deletions(-) delete mode 100644 packages/wallet-lib/src/types/Storage/.eslintrc delete mode 100644 packages/wallet-lib/src/types/Storage/Storage.d.ts delete mode 100644 packages/wallet-lib/src/types/Storage/Storage.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/initialStore.json delete mode 100644 packages/wallet-lib/src/types/Storage/methods/addNewTxToAddress.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/addNewTxToAddress.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/announce.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/announce.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/clearAll.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/clearAll.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/configure.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/createAccount.js create mode 100644 packages/wallet-lib/src/types/Storage/methods/createAccountStore.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/createChain.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/createChain.spec.js create mode 100644 packages/wallet-lib/src/types/Storage/methods/createChainStore.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/createSingleAddress.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/createWallet.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/createWallet.spec.js create mode 100644 packages/wallet-lib/src/types/Storage/methods/createWalletStore.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/exportAccounts.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/exportChains.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/exportTransactions.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/exportWallets.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/getBlockHeader.js create mode 100644 packages/wallet-lib/src/types/Storage/methods/getChainStore.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/getIdentityIdByIndex.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/getIndexedIdentityIds.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/getInstantLock.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/getStore.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/getTransaction.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/getTransaction.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/getTransactionMetadata.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/getTransactionMetadata.spec.js create mode 100644 packages/wallet-lib/src/types/Storage/methods/getWalletStore.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/importAccounts.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/importAccounts.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/importAddress.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/importAddress.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/importAddresses.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/importAddresses.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/importBlockHeader.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/importChains.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/importInstantLock.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/importSingleAddress.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/importSingleAddress.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/importTransaction.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/importTransaction.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/importTransactions.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/importTransactions.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/insertIdentityAtIndex.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/rehydrateState.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/saveState.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/searchAddress.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/searchAddress.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/searchAddressWithTx.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/searchAddressesWithTx.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/searchBlockHeader.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/searchBlockHeader.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/searchTransaction.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/searchTransaction.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/searchTransactionMetadata.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/searchTransactionMetadata.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/searchWallet.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/searchWallet.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/startWorker.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/updateAddress.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/updateAddress.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/updateTransaction.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/updateTransaction.spec.js diff --git a/packages/wallet-lib/src/types/Storage/.eslintrc b/packages/wallet-lib/src/types/Storage/.eslintrc deleted file mode 100644 index a8022ef2b8a..00000000000 --- a/packages/wallet-lib/src/types/Storage/.eslintrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "rules": { - "import/newline-after-import": "off" - } -} diff --git a/packages/wallet-lib/src/types/Storage/Storage.d.ts b/packages/wallet-lib/src/types/Storage/Storage.d.ts deleted file mode 100644 index c41cc83ba66..00000000000 --- a/packages/wallet-lib/src/types/Storage/Storage.d.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { - AddressObj, - AddressType, - Mnemonic, - Network, - TransactionInfo, TransactionMetaData, TransactionsWithMetaData, - WalletObj, WalletType -} from "../types"; -import { BlockHeader } from "@dashevo/dashcore-lib"; -import { Account } from "../.."; -import { Transaction } from "@dashevo/dashcore-lib/typings/transaction/Transaction"; - - -export declare namespace Storage { - interface IStorageOptions { - rehydrate?: boolean; - autosave?: boolean; - autosaveIntervalTime?: number; - network?: Network; - } - interface store { - wallets: {}, - transactions: {}, - chains: {}, - } -} - - -export declare class Storage { - constructor(options?: Storage.IStorageOptions); - store: {}; - rehydrate: boolean; - autosave: boolean; - autosaveIntervalTime: number; - lastRehydrate: number|null; - lastSave: number|null; - lastModified: number|null; - network: Network; - mappedAddress: MappedAddressMap; - - addNewTxToAddress(tx: TransactionInfo, address: string): boolean; - announce(type: string, el: any): boolean; - calculateDuffBalance(walletId: number, accountId: number, type: string): number; - clearAll(): Promise; - configure(opts: { - rehydrate?: boolean, - autosave?: boolean, - adapter?: any - }): Promise; - createChain(network: string): boolean; - createWallet(walletId: string, network: Network, mnemonic?:Mnemonic, type?: WalletType ): boolean; - getBlockHeader(identifier: string|number): BlockHeader; - getIdentityIdByIndex(walletId: string, identityIndex: number): string|undefined; - getIndexedIdentityIds(walletId: string): Array; - getStore(): Storage.store; - getTransaction(txid: string): Transaction; - getTransactionMetadata(txid: string): TransactionMetaData; - importAccounts(accounts: Account|[Account], walletId: string): boolean; - importAddress(address: AddressObj, walletId: string): boolean; - importAddresses(addresses: [AddressObj], walletId: string): boolean; - importBlockHeader(blockHeader: BlockHeader, height: number): void; - importSingleAddress(singleAddress: AddressObj, walletId: string): boolean; - importTransaction(transaction: Transaction, metadata?: TransactionMetaData): void; - importTransactions(transactions: Transaction|[Transaction]|[TransactionsWithMetaData]): boolean; - insertIdentityIdAtIndex(walletId: string, identityId: string, identityIndex: number): void; - rehydrateState(): Promise; - saveState(): Promise; - searchAddress(address: string, forceLoop: boolean): AddressSearchResult; - searchAddressesWithTx(txid: string): AddressesSearchResult; - searchBlockHeader(identifier: string|number): BlockHeaderSearchResult; - searchTransaction(hash: string): TransactionSearchResult; - searchTransactionMetadata(hash: string): TransactionMetadataSearchResult; - searchWallet(walletId: string): WalletSearchResult; - startWorker(): void; - stopWorker(): boolean; - updateAddress(addressObj: AddressObj, walletId: string): boolean; - updateTransaction(transaction: Transaction): boolean; -} - -interface MappedAddressMap { - [pathName: string]: MappedAddress -} - -interface MappedAddress { - [path: string]: { - walletId: string, - type: AddressType, - path: string - }; -} - -interface WalletSearchResult { - walletId: number, - found: boolean, - result?: WalletObj -} - -interface TransactionSearchResult { - hash: number, - found: boolean, - result?: TransactionInfo -} -interface TransactionMetadataSearchResult { - hash: number, - found: boolean, - result?: TransactionMetaData -} - -interface AddressSearchResult { - address: string, - type: AddressType, - found: boolean, - path?: string, - result: AddressObj, - walletId: number -} -interface BlockHeaderSearchResult { - found: boolean, - result?: BlockHeader, - identifier: number|string -} - -interface AddressesSearchResult { - txid: number, - found: boolean, - results: [AddressObj] -} diff --git a/packages/wallet-lib/src/types/Storage/Storage.js b/packages/wallet-lib/src/types/Storage/Storage.js index cf1668582e6..10ecd29ce3c 100644 --- a/packages/wallet-lib/src/types/Storage/Storage.js +++ b/packages/wallet-lib/src/types/Storage/Storage.js @@ -1,95 +1,30 @@ const EventEmitter = require('events'); -const { cloneDeep, has } = require('lodash'); -const CONSTANTS = require('../../CONSTANTS'); - -const initialStore = { - wallets: {}, - transactions: {}, - transactionsMetadata: {}, - chains: {}, - instantLocks: {}, -}; -// eslint-disable-next-line no-underscore-dangle -const _defaultOpts = { - rehydrate: true, - autosave: true, - autosaveIntervalTime: CONSTANTS.STORAGE.autosaveIntervalTime, - network: 'testnet', -}; /** - * Handle all the storage logic, it's a wrapper around the adapters - * So all the needed methods should be provided by the Storage class and the access to the adapter - * should be limited. - * */ +* Handle all the storage logic, it's a wrapper around the adapters +* So all the needed methods should be provided by the Storage class and the access to the adapter +* should be limited. +* */ class Storage extends EventEmitter { - constructor(opts = JSON.parse(JSON.stringify(_defaultOpts))) { + constructor() { super(); - const defaultOpts = JSON.parse(JSON.stringify(_defaultOpts)); - - this.store = cloneDeep(initialStore); - this.rehydrate = has(opts, 'rehydrate') ? opts.rehydrate : defaultOpts.rehydrate; - this.autosave = has(opts, 'autosave') ? opts.autosave : defaultOpts.autosave; - this.autosaveIntervalTime = has(opts, 'autosaveIntervalTime') - ? opts.autosaveIntervalTime - : defaultOpts.autosaveIntervalTime; - - this.lastRehydrate = null; - this.lastSave = null; - this.lastModified = null; - this.network = has(opts, 'network') ? opts.network.toString() : defaultOpts.network; - - // Map an address to it's walletid/path/type schema (used by searchAddress for speedup) - this.mappedAddress = {}; - - // Map height to transaction ids to facilitate search. - this.mappedTransactionsHeight = {}; + this.wallets = new Map(); + this.chains = new Map(); + this.application = { + blockHeight: 0, + }; } } -Storage.prototype.addNewTxToAddress = require('./methods/addNewTxToAddress'); -Storage.prototype.announce = require('./methods/announce'); -Storage.prototype.calculateDuffBalance = require('./methods/calculateDuffBalance'); -Storage.prototype.clearAll = require('./methods/clearAll'); -Storage.prototype.configure = require('./methods/configure'); -Storage.prototype.createAccount = require('./methods/createAccount'); -Storage.prototype.createChain = require('./methods/createChain'); -Storage.prototype.createSingleAddress = require('./methods/createSingleAddress'); -Storage.prototype.createWallet = require('./methods/createWallet'); - -Storage.prototype.exportAccounts = require('./methods/exportAccounts'); -Storage.prototype.exportChains = require('./methods/exportChains'); -Storage.prototype.exportTransactions = require('./methods/exportTransactions'); -Storage.prototype.exportWallets = require('./methods/exportWallets'); -Storage.prototype.getStore = require('./methods/getStore'); -Storage.prototype.getBlockHeader = require('./methods/getBlockHeader'); -Storage.prototype.getTransaction = require('./methods/getTransaction'); -Storage.prototype.getInstantLock = require('./methods/getInstantLock'); -Storage.prototype.importAccounts = require('./methods/importAccounts'); -Storage.prototype.importAddress = require('./methods/importAddress'); -Storage.prototype.importAddresses = require('./methods/importAddresses'); -Storage.prototype.importBlockHeader = require('./methods/importBlockHeader'); -Storage.prototype.importSingleAddress = require('./methods/importSingleAddress'); -Storage.prototype.importChains = require('./methods/importChains'); -Storage.prototype.importTransaction = require('./methods/importTransaction'); -Storage.prototype.importTransactions = require('./methods/importTransactions'); -Storage.prototype.importInstantLock = require('./methods/importInstantLock'); +Storage.prototype.configure = require('./methods/configure'); +Storage.prototype.createAccountStore = require('./methods/createAccountStore'); +Storage.prototype.createChainStore = require('./methods/createChainStore'); +Storage.prototype.createWalletStore = require('./methods/createWalletStore'); +Storage.prototype.getChainStore = require('./methods/getChainStore'); +Storage.prototype.getWalletStore = require('./methods/getWalletStore'); Storage.prototype.rehydrateState = require('./methods/rehydrateState'); Storage.prototype.saveState = require('./methods/saveState'); -Storage.prototype.searchAddress = require('./methods/searchAddress'); -Storage.prototype.searchAddressesWithTx = require('./methods/searchAddressesWithTx'); -Storage.prototype.searchBlockHeader = require('./methods/searchBlockHeader'); -Storage.prototype.searchTransaction = require('./methods/searchTransaction'); -Storage.prototype.searchTransactionMetadata = require('./methods/searchTransactionMetadata'); -Storage.prototype.searchWallet = require('./methods/searchWallet'); -Storage.prototype.updateAddress = require('./methods/updateAddress'); -Storage.prototype.updateTransaction = require('./methods/updateTransaction'); Storage.prototype.startWorker = require('./methods/startWorker'); Storage.prototype.stopWorker = require('./methods/stopWorker'); -// Identities -Storage.prototype.insertIdentityIdAtIndex = require('./methods/insertIdentityAtIndex'); -Storage.prototype.getIdentityIdByIndex = require('./methods/getIdentityIdByIndex'); -Storage.prototype.getIndexedIdentityIds = require('./methods/getIndexedIdentityIds'); - module.exports = Storage; diff --git a/packages/wallet-lib/src/types/Storage/Storage.spec.js b/packages/wallet-lib/src/types/Storage/Storage.spec.js deleted file mode 100644 index 621a01dc5be..00000000000 --- a/packages/wallet-lib/src/types/Storage/Storage.spec.js +++ /dev/null @@ -1,106 +0,0 @@ -const { expect } = require('chai'); - -const localForage = require('localforage'); -const Dashcore = require('@dashevo/dashcore-lib'); -const Storage = require('./Storage'); -const { CONFIGURED } = require('../../EVENTS'); - -describe('Storage - constructor', function suite() { - this.timeout(10000); - it('It should create a storage', () => { - const storage = new Storage(); - expect(storage.store).to.deep.equal({ wallets: {}, transactions: {}, transactionsMetadata: {}, chains: {}, instantLocks: {} }); - expect(storage.getStore()).to.deep.equal(storage.store); - expect(storage.rehydrate).to.equal(true); - expect(storage.autosave).to.equal(true); - expect(storage.lastRehydrate).to.equal(null); - expect(storage.lastSave).to.equal(null); - expect(storage.lastModified).to.equal(null); - storage.stopWorker(); - }); - it('should configure a storage with default adapter', async () => { - const storage = new Storage(); - let configuredEvent = false; - storage.on(CONFIGURED, () => configuredEvent = true); - await storage.configure(); - expect(storage.adapter).to.exist; - expect(storage.adapter.constructor.name).to.equal('InMem'); - expect(configuredEvent).to.equal(true); - storage.stopWorker(); - }); - it('should handle bad adapter', async function () { - if (process.browser){ - // Local forage is valid adapter on browser. - this.skip('LocalForage is a valid adapter on browser') - return; - } - const expectedException1 = 'Invalid Storage Adapter : No available storage method found.'; - const storageOpts1 = { adapter: localForage }; - const storage = new Storage(); - return storage.configure(storageOpts1).then( - () => Promise.reject(new Error('Expected method to reject.')), - (err) => expect(err).to.be.a('Error').with.property('message', expectedException1), - ).then(() => { - storage.stopWorker(); - }); - }); - it('should work on usage', async () => { - const storage = new Storage(); - await storage.configure(); - await storage.createChain(Dashcore.Networks.testnet); - - const defaultWalletId = 'squawk7700'; - const expectedStore1 = { - wallets: {}, - transactions: {}, - transactionsMetadata:{}, - chains: { - testnet: { - name: 'testnet', - blockHeight: -1, - blockHeaders: {}, - mappedBlockHeaderHeights: {}, - fees: { - minRelay: -1 - } - }, - }, - instantLocks: {} - }; - expect(storage.getStore()).to.deep.equal(expectedStore1); - - await storage.createWallet(); - const expectedStore2 = { - wallets: { - squawk7700: { - accounts: {}, - network: Dashcore.Networks.testnet.toString(), - mnemonic: null, - type: null, - identityIds: [], - addresses: { external: {}, internal: {}, misc: {} }, - }, - }, - transactions: {}, - transactionsMetadata: {}, - chains: { - testnet: { - name: 'testnet', - blockHeight: -1, - blockHeaders: {}, - mappedBlockHeaderHeights: {}, - fees: { - minRelay: -1 - } - }, - }, - instantLocks: {}, - }; - expect(storage.getStore()).to.deep.equal(expectedStore2); - expect(storage.store).to.deep.equal(expectedStore2); - - const account = {}; - await storage.importAccounts(account, defaultWalletId); - storage.stopWorker(); - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/initialStore.json b/packages/wallet-lib/src/types/Storage/initialStore.json deleted file mode 100644 index 8b4f9541fa6..00000000000 --- a/packages/wallet-lib/src/types/Storage/initialStore.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "wallets": {}, - "transactions": {}, - "chains": {} -} \ No newline at end of file diff --git a/packages/wallet-lib/src/types/Storage/methods/addNewTxToAddress.js b/packages/wallet-lib/src/types/Storage/methods/addNewTxToAddress.js deleted file mode 100644 index a2dba1b551e..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/addNewTxToAddress.js +++ /dev/null @@ -1,30 +0,0 @@ -const { InvalidAddress, InvalidTransactionObject, StorageUnableToAddTransaction } = require('../../../errors'); -const { is } = require('../../../utils'); - -/** - * Add a new transaction to an address (push a tx) -* @param {TransactionInfo} tx -* @param {String} address -* @return {boolean} - */ -const addNewTxToAddress = function addNewTxToAddress(tx, address) { - if (!is.address(address)) throw new InvalidAddress(address); - if (!is.transactionObj(tx)) throw new InvalidTransactionObject(tx); - - const searchAddr = this.searchAddress(address); - const { walletId } = searchAddr; - - if (tx.address && tx.txid) { - const { type, path, found } = this.searchAddress(tx.address); - if (!found) { - throw new StorageUnableToAddTransaction(tx); - } - this.store.wallets[walletId].addresses[type][path].transactions.push(tx.txid); - // Because of the unclear state will force a refresh - this.store.wallets[walletId].addresses[type][path].fetchedLast = 0; - this.lastModified = +new Date(); - return true; - } - throw new StorageUnableToAddTransaction(tx); -}; -module.exports = addNewTxToAddress; diff --git a/packages/wallet-lib/src/types/Storage/methods/addNewTxToAddress.spec.js b/packages/wallet-lib/src/types/Storage/methods/addNewTxToAddress.spec.js deleted file mode 100644 index 03b20df4de9..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/addNewTxToAddress.spec.js +++ /dev/null @@ -1,27 +0,0 @@ -const { expect } = require('chai'); -const addNewTxToAddress = require('./addNewTxToAddress'); - -// FIXME : We only use this method in one specific case : From the SyncWorker when receiving from the WSock from insight -// THerefore we might want to remove our dependency on this method ? -describe('Storage - addNewTxToAddress', () => { - it('should add a new transaction to an address in store', () => { - // const self = { - // store: { - // wallets: { - // '123ae': { - // addresses: { - // internal: {}, - // external: {}, - // misc: {}, - // }, - // transactions:{ - // - // } - // }, - // }, - // }, - // }; - // - // addNewTxToAddress.call(self, fd7c727155ef67fd5c1d54b73dea869e9690c439570063d6e96fec1d3bba450e, '123ae'); - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/announce.js b/packages/wallet-lib/src/types/Storage/methods/announce.js deleted file mode 100644 index 8c35e6b7b7a..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/announce.js +++ /dev/null @@ -1,27 +0,0 @@ -const logger = require('../../../logger'); -const EVENTS = require('../../../EVENTS'); - -/** - * Used to announce some events. - * @param type - * @param el - * @return {boolean} - */ -const announce = function announce(type, el) { - switch (type) { - case EVENTS.BLOCK: - case EVENTS.BLOCKHEADER: - case EVENTS.BLOCKHEIGHT_CHANGED: - case EVENTS.CONFIRMED_BALANCE_CHANGED: - case EVENTS.UNCONFIRMED_BALANCE_CHANGED: - case EVENTS.FETCHED_UNCONFIRMED_TRANSACTION: - case EVENTS.FETCHED_CONFIRMED_TRANSACTION: - this.emit(type, { type, payload: el }); - break; - default: - this.emit(type, { type, payload: el }); - logger.warn('Storage - Not implemented, announce of ', type, el); - } - return true; -}; -module.exports = announce; diff --git a/packages/wallet-lib/src/types/Storage/methods/announce.spec.js b/packages/wallet-lib/src/types/Storage/methods/announce.spec.js deleted file mode 100644 index 55b7e6b5e80..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/announce.spec.js +++ /dev/null @@ -1,6 +0,0 @@ -const { expect } = require('chai'); -const announce = require('./announce'); - -describe('Storage - announce', () => { - describe.skip('TODO test'); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/clearAll.js b/packages/wallet-lib/src/types/Storage/methods/clearAll.js deleted file mode 100644 index c22c665bcd6..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/clearAll.js +++ /dev/null @@ -1,11 +0,0 @@ -const { cloneDeep } = require('lodash'); -const initialStore = require('../initialStore.json'); -/** - * Clear all the store and save the cleared store to the persistence adapter - * @return {Promise} - */ -const clearAll = async function clearAll() { - this.store = cloneDeep(initialStore); - return this.saveState(); -}; -module.exports = clearAll; diff --git a/packages/wallet-lib/src/types/Storage/methods/clearAll.spec.js b/packages/wallet-lib/src/types/Storage/methods/clearAll.spec.js deleted file mode 100644 index de4bfdd8870..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/clearAll.spec.js +++ /dev/null @@ -1,25 +0,0 @@ -const { expect } = require('chai'); -const clearState = require('./clearAll'); - -describe('Storage - clearAll', function suite() { - this.timeout(10000); - it('should clear the whole state', () => { - let called = 0; - const self = { - saveState: () => called++, - store: { - stuff: {}, - transactions: { - nope: true, - }, - }, - }; - clearState.call(self); - expect(self.store).to.deep.equal({ - chains: {}, - transactions: {}, - wallets: {}, - }); - expect(called).to.equal(1); - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/configure.spec.js b/packages/wallet-lib/src/types/Storage/methods/configure.spec.js deleted file mode 100644 index 186f5adc496..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/configure.spec.js +++ /dev/null @@ -1,61 +0,0 @@ -const { expect } = require('chai'); -const configure = require('./configure'); - -const noop = () => {}; - -describe('Storage - configure', async function suite() { - this.timeout(10000); - it('should set save/rehydrate settings', () => { - let rehydrated = 0; - - const self = { - emit: noop, - autosaveIntervalTime: 1000, - startWorker: noop, - rehydrateState: () => (rehydrated += 1), - rehydrate: true, - autosave: true, - }; - expect(rehydrated).to.equal(0); - return configure - .call(self) - .then(() => expect(self.autosave).to.equal(true)) - .then(() => expect(self.rehydrate).to.equal(true)) - .then(() => expect(rehydrated).to.equal(1)) - .then(() => configure - .call(self, { rehydrate: false, autosave: false }) - .then(() => expect(self.autosave).to.equal(false)) - .then(() => expect(self.rehydrate).to.equal(false)) - .then(() => expect(rehydrated).to.equal(1))); - }); - it('should successfully emit', () => { - const emitted = []; - const self = { - emit: (emitType) => (emitted.push(emitType)), - autosaveIntervalTime: 1000, - startWorker: noop, - }; - expect(emitted.length).to.equal(0); - - return configure - .call(self) - .then(() => expect(emitted).to.deep.equal(['CONFIGURED'])); - }); - it('should start the autosave worker if autosave is true', () => { - let workerStarted = false; - const self = { - rehydrate: false, - autosave: false, - autosaveIntervalTime: 1000, - startWorker: () => { workerStarted = true; }, - emit: noop, - }; - - return configure - .call(self) - .then(() => expect(workerStarted).to.equal(false)) - .then(() => configure - .call(self, { autosave: true }) - .then(() => expect(workerStarted).to.equal(true))); - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/createAccount.js b/packages/wallet-lib/src/types/Storage/methods/createAccount.js deleted file mode 100644 index 819a1737239..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/createAccount.js +++ /dev/null @@ -1,28 +0,0 @@ -const { hasProp } = require('../../../utils'); - -/** - * Create a new account into a wallet - * @param {string} walletId - * @param {string} path - * @param {string} network - * @param {string|null} [label] - * @return {boolean} - */ -module.exports = function createAccount(walletId, path, network, label = null) { - if (!hasProp(this.store.wallets, walletId.toString())) { - if (!this.searchWallet(walletId).found) { - this.createWallet(walletId, network); - } - } - if (!hasProp(this.store.wallets[walletId].accounts, path.toString())) { - this.store.wallets[walletId].accounts[path.toString()] = { - label, - path, - network, - blockHeight: 0, // Used to keep track of local state sync of the account - }; - - return true; - } - return false; -}; diff --git a/packages/wallet-lib/src/types/Storage/methods/createAccountStore.js b/packages/wallet-lib/src/types/Storage/methods/createAccountStore.js new file mode 100644 index 00000000000..3069a3b0d25 --- /dev/null +++ b/packages/wallet-lib/src/types/Storage/methods/createAccountStore.js @@ -0,0 +1,29 @@ +const { hasProp } = require('../../../utils'); + +/** + * Create a new account store into a wallet + * @param {string} walletId + * @param {string} path + * @param {string} network + * @param {string|null} [label] + * @return {boolean} + */ +module.exports = function createAccount(walletId, path, network, label = null) { + const walletStore = this.getWalletStore(walletId); + // if (!hasProp(this.store.wallets, walletId.toString())) { + // if (!this.searchWallet(walletId).found) { + // this.createWallet(walletId, network); + // } + // } + // if (!hasProp(this.store.wallets[walletId].accounts, path.toString())) { + // this.store.wallets[walletId].accounts[path.toString()] = { + // label, + // path, + // network, + // blockHeight: 0, // Used to keep track of local state sync of the account + // }; + // + // return true; + // } + // return false; +}; diff --git a/packages/wallet-lib/src/types/Storage/methods/createChain.js b/packages/wallet-lib/src/types/Storage/methods/createChain.js deleted file mode 100644 index 0fea70ed618..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/createChain.js +++ /dev/null @@ -1,25 +0,0 @@ -const { hasProp } = require('../../../utils'); - -/** - * Create when does not yet exist a chain in the store - * @param network - * @return {boolean} - */ -const createChain = function createChain(network) { - if (!hasProp(this.store.chains, network.toString())) { - this.store.chains[network.toString()] = { - name: network.toString(), - blockHeaders: {}, - // Map a blockheader to it's height (used by searchBlockheader for speed up the process) - mappedBlockHeaderHeights: {}, - blockHeight: -1, - fees: { - // feeRate expressed per kb - minRelay: -1, - }, - }; - return true; - } - return false; -}; -module.exports = createChain; diff --git a/packages/wallet-lib/src/types/Storage/methods/createChain.spec.js b/packages/wallet-lib/src/types/Storage/methods/createChain.spec.js deleted file mode 100644 index 71d2da156ff..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/createChain.spec.js +++ /dev/null @@ -1,31 +0,0 @@ -const { expect } = require('chai'); -const createChain = require('./createChain'); - -describe('Storage - createChain', function suite() { - this.timeout(10000); - it('should create a chain', () => { - const self = { - store: { chains: {} }, - }; - const testnet = 'testnet'; - - createChain.call(self, testnet); - - const expected = { - store: { - chains: { - testnet: { - name: 'testnet', - blockHeight: -1, - blockHeaders: {}, - mappedBlockHeaderHeights: {}, - fees: { - minRelay: -1 - } - }, - }, - }, - }; - expect(self).to.be.deep.equal(expected); - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/createChainStore.js b/packages/wallet-lib/src/types/Storage/methods/createChainStore.js new file mode 100644 index 00000000000..47ae5c2bde6 --- /dev/null +++ b/packages/wallet-lib/src/types/Storage/methods/createChainStore.js @@ -0,0 +1,15 @@ +const ChainStore = require('../../ChainStore/ChainStore'); + +/** + * Create when does not yet exist a chainStore + * @param network + * @return {boolean} + */ +const createChainStore = function createChain(network) { + if (!this.chains.has(network.toString())) { + this.chains.set(network.toString(), new ChainStore(network.toString())); + return true; + } + return false; +}; +module.exports = createChainStore; diff --git a/packages/wallet-lib/src/types/Storage/methods/createSingleAddress.js b/packages/wallet-lib/src/types/Storage/methods/createSingleAddress.js deleted file mode 100644 index 62421610cd2..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/createSingleAddress.js +++ /dev/null @@ -1,27 +0,0 @@ -const { hasProp } = require('../../../utils'); - -/** - * Create a new account into a wallet - * @param {string} walletId - * @param {string} network - * @param {string|null} [label] - * @return {boolean} - */ -module.exports = function createAccount(walletId, network, label = null) { - if (!hasProp(this.store.wallets, walletId.toString())) { - if (!this.searchWallet(walletId).found) { - this.createWallet(walletId, network); - } - } - - if (!hasProp(this.store.wallets[walletId].accounts, '0')) { - this.store.wallets[walletId].accounts['0'] = { - label, - network, - blockHeight: 0, // Used to keep track of local state sync of the account - }; - - return true; - } - return false; -}; diff --git a/packages/wallet-lib/src/types/Storage/methods/createWallet.js b/packages/wallet-lib/src/types/Storage/methods/createWallet.js deleted file mode 100644 index 7380cdbc5be..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/createWallet.js +++ /dev/null @@ -1,24 +0,0 @@ -const Dashcore = require('@dashevo/dashcore-lib'); -const { hasProp } = require('../../../utils'); - -const { testnet } = Dashcore.Networks; -const createWallet = function createWallet(walletId = 'squawk7700', network = testnet.toString(), mnemonic = null, type = null) { - if (!hasProp(this.store.wallets, walletId)) { - this.store.wallets[walletId] = { - accounts: {}, - network, - mnemonic, - type, - identityIds: [], - addresses: { - external: {}, - internal: {}, - misc: {}, - }, - }; - this.createChain(network); - return true; - } - return false; -}; -module.exports = createWallet; diff --git a/packages/wallet-lib/src/types/Storage/methods/createWallet.spec.js b/packages/wallet-lib/src/types/Storage/methods/createWallet.spec.js deleted file mode 100644 index 9b799c12d04..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/createWallet.spec.js +++ /dev/null @@ -1,75 +0,0 @@ -const { expect } = require('chai'); -const Dashcore = require('@dashevo/dashcore-lib'); -const createWallet = require('./createWallet'); -const createChain = require('./createChain'); - -describe('Storage - createWallet', function suite() { - this.timeout(10000); - it('should create a wallet', () => { - const self = { - store: { wallets: {}, chains: {} }, - createChain, - }; - const walletid = '123ae'; - - createWallet.call(self, walletid); - - const expected = { - wallets: { - '123ae': { - accounts: {}, - network: Dashcore.Networks.testnet.toString(), - mnemonic: null, - type: null, - identityIds: [], - addresses: { external: {}, internal: {}, misc: {} }, - }, - }, - chains: { - testnet: { - name: 'testnet', - blockHeight: -1, - blockHeaders: {}, - mappedBlockHeaderHeights: {}, - fees: { - minRelay: -1 - } - }, - }, - }; - expect(self.store).to.be.deep.equal(expected); - }); - it('should create a wallet without any walletId', () => { - const self = { - store: { wallets: {}, chains: {} }, - createChain, - }; - - createWallet.call(self); - - const expected = { - wallets: { - squawk7700: { - accounts: {}, - network: Dashcore.Networks.testnet.toString(), - mnemonic: null, - type: null, - identityIds: [], - addresses: { external: {}, internal: {}, misc: {} }, - }, - }, - chains: { - testnet: { - name: 'testnet', - blockHeight: -1, - blockHeaders: {}, - mappedBlockHeaderHeights: {}, - fees: { - minRelay: -1 - } - }, - }, - }; - expect(self.store).to.be.deep.equal(expected); - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/createWalletStore.js b/packages/wallet-lib/src/types/Storage/methods/createWalletStore.js new file mode 100644 index 00000000000..d3053b1a889 --- /dev/null +++ b/packages/wallet-lib/src/types/Storage/methods/createWalletStore.js @@ -0,0 +1,13 @@ +const Dashcore = require('@dashevo/dashcore-lib'); +const WalletStore = require('../../WalletStore/WalletStore'); + +const { testnet } = Dashcore.Networks; + +const createWalletStore = function createWallet(walletId = 'squawk7700', network = testnet.toString(), mnemonic = null, type = null) { + if (!this.wallets.has(walletId)) { + this.wallets.set(walletId, new WalletStore(walletId)); + return true; + } + return false; +}; +module.exports = createWalletStore; diff --git a/packages/wallet-lib/src/types/Storage/methods/exportAccounts.js b/packages/wallet-lib/src/types/Storage/methods/exportAccounts.js deleted file mode 100644 index 89969a7384c..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/exportAccounts.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = function exportAccounts(walletId) { - if (!walletId) throw new Error('Expected to export account of a specific walletId'); - - if (!this.store.wallets[walletId]) { - throw new Error(`No wallet with the following walletId found in store : ${walletId}`); - } - return this.store.wallets[walletId].accounts; -}; diff --git a/packages/wallet-lib/src/types/Storage/methods/exportChains.js b/packages/wallet-lib/src/types/Storage/methods/exportChains.js deleted file mode 100644 index 0cef2b28d7c..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/exportChains.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function exportChains() { - return this.store.chains; -}; diff --git a/packages/wallet-lib/src/types/Storage/methods/exportTransactions.js b/packages/wallet-lib/src/types/Storage/methods/exportTransactions.js deleted file mode 100644 index 57daaf8e8e4..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/exportTransactions.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function exportTransactions() { - return this.store.transactions; -}; diff --git a/packages/wallet-lib/src/types/Storage/methods/exportWallets.js b/packages/wallet-lib/src/types/Storage/methods/exportWallets.js deleted file mode 100644 index b2ba79562bd..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/exportWallets.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function exportWallets() { - return this.store.wallets; -}; diff --git a/packages/wallet-lib/src/types/Storage/methods/getBlockHeader.js b/packages/wallet-lib/src/types/Storage/methods/getBlockHeader.js deleted file mode 100644 index 4c69a0a4d4d..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/getBlockHeader.js +++ /dev/null @@ -1,13 +0,0 @@ -const { BlockHeaderNotInStore } = require('../../../errors'); - -/** - * @param identifier - block hash or height - * @return {BlockHeader} - */ -const getBlockHeader = function getBlockHeader(identifier) { - const search = this.searchBlockHeader(identifier); - if (!search.found) throw new BlockHeaderNotInStore(identifier); - return search.result; -}; - -module.exports = getBlockHeader; diff --git a/packages/wallet-lib/src/types/Storage/methods/getChainStore.js b/packages/wallet-lib/src/types/Storage/methods/getChainStore.js new file mode 100644 index 00000000000..5255eb71033 --- /dev/null +++ b/packages/wallet-lib/src/types/Storage/methods/getChainStore.js @@ -0,0 +1,4 @@ +function getChainStore(network) { + return this.chains.get(network); +} +module.exports = getChainStore; diff --git a/packages/wallet-lib/src/types/Storage/methods/getIdentityIdByIndex.js b/packages/wallet-lib/src/types/Storage/methods/getIdentityIdByIndex.js deleted file mode 100644 index 111d3a145b5..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/getIdentityIdByIndex.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * - * @param {string} walletId - * @param {number} identityIndex - * @return {string|undefined} - */ -function getIdentityIdByIndex(walletId, identityIndex) { - return this.store.wallets[walletId].identityIds[identityIndex]; -} - -module.exports = getIdentityIdByIndex; diff --git a/packages/wallet-lib/src/types/Storage/methods/getIndexedIdentityIds.js b/packages/wallet-lib/src/types/Storage/methods/getIndexedIdentityIds.js deleted file mode 100644 index cb82397d31d..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/getIndexedIdentityIds.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * - * @param {string} walletId - * @return {Array} - */ -function getIndexedIdentityIds(walletId) { - return this.store.wallets[walletId].identityIds; -} - -module.exports = getIndexedIdentityIds; diff --git a/packages/wallet-lib/src/types/Storage/methods/getInstantLock.js b/packages/wallet-lib/src/types/Storage/methods/getInstantLock.js deleted file mode 100644 index 333bbd8e61d..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/getInstantLock.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * - * @param {string} transactionHash - * @return {InstantLock} - */ -function getInstantLock(transactionHash) { - return this.store.instantLocks[transactionHash]; -} - -module.exports = getInstantLock; diff --git a/packages/wallet-lib/src/types/Storage/methods/getStore.js b/packages/wallet-lib/src/types/Storage/methods/getStore.js deleted file mode 100644 index 8782c2bcd46..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/getStore.js +++ /dev/null @@ -1,8 +0,0 @@ -const { cloneDeep } = require('lodash'); -/** - * Return the content of the store - * @return {Storage.store} - */ -module.exports = function getStore() { - return cloneDeep(this.store); -}; diff --git a/packages/wallet-lib/src/types/Storage/methods/getTransaction.js b/packages/wallet-lib/src/types/Storage/methods/getTransaction.js deleted file mode 100644 index 601bcf859ae..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/getTransaction.js +++ /dev/null @@ -1,14 +0,0 @@ -const { TransactionNotInStore } = require('../../../errors'); - -/** - * Get a specific transaxtion by it's transaction id - * @param {string} txid - * @return {Transaction} - */ -const getTransaction = function getTransaction(txid) { - const search = this.searchTransaction(txid); - if (!search.found) throw new TransactionNotInStore(txid); - return search.result; -}; - -module.exports = getTransaction; diff --git a/packages/wallet-lib/src/types/Storage/methods/getTransaction.spec.js b/packages/wallet-lib/src/types/Storage/methods/getTransaction.spec.js deleted file mode 100644 index 4134d582033..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/getTransaction.spec.js +++ /dev/null @@ -1,30 +0,0 @@ -const { expect } = require('chai'); -const getTransaction = require('./getTransaction'); -const transactionsFixtures = require('../../../../fixtures/transactions'); - -describe('Storage - getTransaction', function suite() { - this.timeout(10000); - it('should throw on failed fetching', () => { - const validTx = transactionsFixtures.valid.mainnet['4f71db0c4bf3e2769a3ebd2162753b54b33028e3287e45f93c5c7df8bac5ec7e']; - const exceptedException1 = `Transaction is not in store: ${validTx.txid}`; - const self = { - store: { - transactions: {}, - }, - searchTransaction: () => ({ found: false }), - }; - expect(() => getTransaction.call(self, validTx.txid)).to.throw(exceptedException1); - }); - it('should work', () => { - const validTx = transactionsFixtures.valid.mainnet['4f71db0c4bf3e2769a3ebd2162753b54b33028e3287e45f93c5c7df8bac5ec7e']; - const self = { - store: { - transactions: {}, - }, - searchTransaction: () => ({ found: true, result: validTx }), - }; - self.store.transactions[validTx.txid] = validTx; - const { txid } = validTx; - expect(getTransaction.call(self, txid)).to.deep.equal(validTx); - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/getTransactionMetadata.js b/packages/wallet-lib/src/types/Storage/methods/getTransactionMetadata.js deleted file mode 100644 index 63626b05866..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/getTransactionMetadata.js +++ /dev/null @@ -1,14 +0,0 @@ -const { TransactionMetadataNotInStore } = require('../../../errors'); - -/** - * Get a specific transaction metadata by it's transaction id - * @param {string} txid - * @return {TransactionMetaData} - */ -const getTransactionMetadata = function getTransactionMetadata(txid) { - const search = this.searchTransactionMetadata(txid); - if (!search.found) throw new TransactionMetadataNotInStore(txid); - return search.result; -}; - -module.exports = getTransactionMetadata; diff --git a/packages/wallet-lib/src/types/Storage/methods/getTransactionMetadata.spec.js b/packages/wallet-lib/src/types/Storage/methods/getTransactionMetadata.spec.js deleted file mode 100644 index 2624263fb8f..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/getTransactionMetadata.spec.js +++ /dev/null @@ -1,31 +0,0 @@ -const { expect } = require('chai'); -const getTransactionMetadata = require('./getTransactionMetadata'); -const transactionsFixtures = require('../../../../fixtures/transactions'); - -describe('Storage - getTransactionMetadata', function suite() { - this.timeout(10000); - const validTxId = '1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6'; - // const validTx = transactionsFixtures.valid.testnet[validTxId]; - const validMetadata = transactionsFixtures.valid.testnet.metadata[validTxId]; - - it('should throw on failed fetching', () => { - const exceptedException1 = `Transaction metadata is not in store: ${validTxId}`; - const self = { - store: { - transactionsMetadata: {}, - }, - searchTransactionMetadata: () => ({ found: false }), - }; - expect(() => getTransactionMetadata.call(self, validTxId)).to.throw(exceptedException1); - }); - it('should work', () => { - const validTx = transactionsFixtures.valid.mainnet['4f71db0c4bf3e2769a3ebd2162753b54b33028e3287e45f93c5c7df8bac5ec7e']; - const self = { - store: { - transactionsMetadata: {}, - }, - searchTransactionMetadata: () => ({ found: true, result: validMetadata }), - }; - expect(getTransactionMetadata.call(self, validTxId)).to.deep.equal(validMetadata); - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/getWalletStore.js b/packages/wallet-lib/src/types/Storage/methods/getWalletStore.js new file mode 100644 index 00000000000..3f43fc2b25e --- /dev/null +++ b/packages/wallet-lib/src/types/Storage/methods/getWalletStore.js @@ -0,0 +1,5 @@ +function getWalletStore(walletId) { + if (!this.wallets.has(walletId)) return null; + return this.wallets.get(walletId); +} +module.exports = getWalletStore; diff --git a/packages/wallet-lib/src/types/Storage/methods/importAccounts.js b/packages/wallet-lib/src/types/Storage/methods/importAccounts.js deleted file mode 100644 index 51b373b0876..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/importAccounts.js +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Import an array of accounts or a account object to the store - * @param {Account|[Account]} accounts - * @param {string} walletId - * @return {boolean} - */ -const importAccounts = function importAccounts(accounts, walletId) { - if (!walletId) throw new Error('Expected walletId to import addresses'); - if (!this.searchWallet(walletId).found) { - this.createWallet(walletId); - } - const accList = this.store.wallets[walletId].accounts; - - const type = accounts.constructor.name; - if (type === 'Object') { - if (accounts.path) { - if (!accList[accounts.path]) { - accList[accounts.path] = accounts; - this.lastModified = +new Date(); - } - } else { - const accountsPaths = Object.keys(accounts); - accountsPaths.forEach((path) => { - const el = accounts[path]; - if (el.path) { - if (!accList[el.path]) { - accList[el.path] = el; - this.lastModified = +new Date(); - } - } - }); - } - } else if (type === 'Array') { - accounts.forEach((account) => { - importAccounts.call(this, account, walletId); - }); - } else if (type === 'Account') { - const accObj = { - label: accounts.label, - path: accounts.BIP44PATH, - network: accounts.network, - }; - return importAccounts.call(this, accObj, walletId); - } else { - throw new Error('Invalid account. Cannot import.'); - } - return true; -}; -module.exports = importAccounts; diff --git a/packages/wallet-lib/src/types/Storage/methods/importAccounts.spec.js b/packages/wallet-lib/src/types/Storage/methods/importAccounts.spec.js deleted file mode 100644 index 361f8cfbac8..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/importAccounts.spec.js +++ /dev/null @@ -1,63 +0,0 @@ -const { expect } = require('chai'); -const importAccounts = require('./importAccounts'); -const Wallet = require('../../Wallet/Wallet'); -const EVENTS = require('../../../EVENTS'); - -describe('Storage - importAccounts', async function suite() { - this.timeout(15000); - it('should throw on failed import', () => { - const mockOpts1 = { }; - const walletId = '123ae'; - const exceptedException1 = 'Expected walletId to import addresses'; - - expect(() => importAccounts.call({})).to.throw(exceptedException1); - expect(() => importAccounts.call({}, walletId)).to.throw(exceptedException1); - }); - it('should create a wallet if not existing', (done) => { - const wallet = new Wallet({ offlineMode: true }); - wallet.storage.on(EVENTS.CONFIGURED, () => { - wallet.getAccount().then((acc)=>{ - let called = 0; - - const self = { - searchWallet: () => ({ found: false }), - createWallet: () => (called += 1), - store: { wallets: { } }, - }; - self.store.wallets[wallet.walletId] = { accounts: {} }; - importAccounts.call(self, acc, wallet.walletId); - - // Called twice because of recursivity. We have a Acc Instance here. - expect(called).to.be.equal(2); - wallet.disconnect(); - acc.disconnect(); - done(); - }); - }); - }); - it('should import an account', (done) => { - const wallet = new Wallet({ offlineMode: true }); - wallet.storage.on('CONFIGURED', () => { - wallet.getAccount().then((acc)=>{ - let called = 0; - - const self = { - searchWallet: () => ({ found: false }), - createWallet: () => (called += 1), - store: { wallets: { } }, - }; - acc.label = 'Heya!'; - self.store.wallets[wallet.walletId] = { accounts: {} }; - importAccounts.call(self, acc, wallet.walletId); - const walletsKeys = Object.keys(self.store.wallets); - expect(walletsKeys.length).to.equal(1); - expect(self.store.wallets[walletsKeys[0]].accounts['m/44\'/1\'/0\''].label).to.equal('Heya!'); - setTimeout(() => { - wallet.disconnect(); - acc.disconnect(); - done(); - }, 50); - }) - }); - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/importAddress.js b/packages/wallet-lib/src/types/Storage/methods/importAddress.js deleted file mode 100644 index 1788692122c..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/importAddress.js +++ /dev/null @@ -1,43 +0,0 @@ -const { cloneDeep } = require('lodash'); -const { InvalidAddressObject } = require('../../../errors'); -const { is } = require('../../../utils'); -/** - * Import one address to the store - * @param {AddressObj} addressObj - * @param {string} walletId - * @return {boolean} - */ -const importAddress = function importAddress(addressObj, walletId) { - if (!walletId) throw new Error('Expected walletId to import addresses'); - if (!this.searchWallet(walletId).found) { - this.createWallet(walletId); - } - const addressesStore = this.store.wallets[walletId].addresses; - if (is.undef(walletId)) throw new Error('Expected walletId to import an address'); - if (!is.addressObj(addressObj)) throw new InvalidAddressObject(addressObj); - const { path } = addressObj; - const modifiedAddressObject = cloneDeep(addressObj); - const index = (addressObj.index !== undefined) ? addressObj.index : parseInt(path.split('/')[5], 10); - const typeInt = path.split('/')[4]; - let type; - switch (typeInt) { - case '0': - type = 'external'; - break; - case '1': - type = 'internal'; - break; - default: - type = 'misc'; - } - if (!modifiedAddressObject.index) modifiedAddressObject.index = index; - if (addressesStore[type][path]) { - if (addressesStore[type][path].fetchedLast < modifiedAddressObject.fetchedLast) { - this.updateAddress(modifiedAddressObject, walletId); - } - } else { - this.updateAddress(modifiedAddressObject, walletId); - } - return true; -}; -module.exports = importAddress; diff --git a/packages/wallet-lib/src/types/Storage/methods/importAddress.spec.js b/packages/wallet-lib/src/types/Storage/methods/importAddress.spec.js deleted file mode 100644 index 714f3cfd506..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/importAddress.spec.js +++ /dev/null @@ -1,18 +0,0 @@ -const { expect } = require('chai'); -const logger = require('../../../logger'); -const importAddress = require('./importAddress'); - -describe('Storage - importAddress', () => { - it('should throw on failed import', () => { - const walletId = '123ae'; - const exceptedException1 = 'Expected walletId to import addresses'; - - expect(() => importAddress.call({})).to.throw(exceptedException1); - expect(() => importAddress.call({}, walletId)).to.throw(exceptedException1); - }); - it('should import an address', () => { - logger.warn('FIXME'); - // const self = {}; - // importAddress.call(self, {}); - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/importAddresses.js b/packages/wallet-lib/src/types/Storage/methods/importAddresses.js deleted file mode 100644 index 5b2287d0ab0..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/importAddresses.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Import one or multiple addresses to the store - * @param {[AddressObj]} addresses - * @param {string} walletId - * @return {boolean} - */ -const importAddresses = function importAddresses(addresses, walletId) { - if (!walletId) throw new Error('Expected walletId to import addresses'); - if (!this.searchWallet(walletId).found) { - this.createWallet(walletId); - } - const type = addresses.constructor.name; - if (type === 'Object') { - if (addresses.path) { - const address = addresses; - this.importAddress(address, walletId); - } else { - const addressPaths = Object.keys(addresses); - addressPaths.forEach((path) => { - const address = addresses[path]; - this.importAddress(address, walletId); - }); - } - } else if (type === 'Array') { - throw new Error('Not implemented. Please create an issue on github if needed.'); - } else { - throw new Error('Not implemented. Please create an issue on github if needed.'); - } - return true; -}; -module.exports = importAddresses; diff --git a/packages/wallet-lib/src/types/Storage/methods/importAddresses.spec.js b/packages/wallet-lib/src/types/Storage/methods/importAddresses.spec.js deleted file mode 100644 index 7ad30ca8bf0..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/importAddresses.spec.js +++ /dev/null @@ -1,7 +0,0 @@ -const { expect } = require('chai'); -const importAddresses = require('./importAddresses'); - -describe('Storage - importAddresses', () => { - it('should import an array of addresses', () => { - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/importBlockHeader.js b/packages/wallet-lib/src/types/Storage/methods/importBlockHeader.js deleted file mode 100644 index a96ca0f1115..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/importBlockHeader.js +++ /dev/null @@ -1,30 +0,0 @@ -const EVENTS = require('../../../EVENTS'); -/** - * This method is used to import a blockheader in Store. - * @param {BlockHeader} blockHeader - A Blockheader - * @param {number} height - */ -const importBlockHeader = function importBlockHeader(blockHeader, height) { - const self = this; - const { store, network } = this; - - const chainStore = store.chains[network]; - const { blockHeight: currentChainHeight } = store.chains[network]; - - if (!chainStore.blockHeaders[blockHeader.hash]) { - if (height) { - if (height > currentChainHeight) store.chains[network].blockHeight = height; - else { - store.chains[network].blockHeight += 1; - self.announce(EVENTS.BLOCKHEIGHT_CHANGED, store.chains[network].blockHeight); - } - } - const blockHeight = height || currentChainHeight; - - chainStore.blockHeaders[blockHeader.hash] = blockHeader; - chainStore.mappedBlockHeaderHeights[blockHeight] = blockHeader.hash; - - self.announce(EVENTS.BLOCKHEADER, blockHeader); - } -}; -module.exports = importBlockHeader; diff --git a/packages/wallet-lib/src/types/Storage/methods/importChains.js b/packages/wallet-lib/src/types/Storage/methods/importChains.js deleted file mode 100644 index df1afb97899..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/importChains.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Import chains to the store - * - * @param {object} chains - * @return {void} - */ -const importChains = function importChains(chains) { - Object.assign(this.store.chains, chains); -}; -module.exports = importChains; diff --git a/packages/wallet-lib/src/types/Storage/methods/importInstantLock.js b/packages/wallet-lib/src/types/Storage/methods/importInstantLock.js deleted file mode 100644 index 8ce00b63f8c..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/importInstantLock.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Imports instant lock to the storage - * @param {InstantLock} instantLock - */ -function importInstantLock(instantLock) { - this.store.instantLocks[instantLock.txid] = instantLock; -} - -module.exports = importInstantLock; diff --git a/packages/wallet-lib/src/types/Storage/methods/importSingleAddress.js b/packages/wallet-lib/src/types/Storage/methods/importSingleAddress.js deleted file mode 100644 index f408f472be3..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/importSingleAddress.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * SingleAddress differs from importAddress is the type being linked to a - * single PrivateKey (when not a HDWallet). - * @param {AddressObj} singleAddress - * @param {string} walletId - * @returns {boolean} - */ -const importSingleAddress = function importSingleAddress(singleAddress, walletId) { - const type = singleAddress.constructor.name; - if (!walletId) throw new Error('Expected walletId to import single address'); - if (!this.searchWallet(walletId).found) { - this.createWallet(walletId); - } - const accList = this.store.wallets[walletId].accounts; - - if (type === 'Object') { - if (singleAddress.path) { - accList[singleAddress.path] = (singleAddress); - this.lastModified = +new Date(); - } - } else if (type === 'Array') { - throw new Error('Not implemented. Please create an issue on github if needed.'); - } else { - throw new Error('Invalid account. Cannot import.'); - } - return true; -}; -module.exports = importSingleAddress; diff --git a/packages/wallet-lib/src/types/Storage/methods/importSingleAddress.spec.js b/packages/wallet-lib/src/types/Storage/methods/importSingleAddress.spec.js deleted file mode 100644 index 44023d1dbe0..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/importSingleAddress.spec.js +++ /dev/null @@ -1,8 +0,0 @@ -const { expect } = require('chai'); -const importSingleAddress = require('./importSingleAddress'); - -describe('Storage - importSingleAddress', () => { - it('should import a SingleAddress (non-bip44)', () => { - - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/importTransaction.js b/packages/wallet-lib/src/types/Storage/methods/importTransaction.js deleted file mode 100644 index 7cb63861e3d..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/importTransaction.js +++ /dev/null @@ -1,127 +0,0 @@ -const { Transaction } = require('@dashevo/dashcore-lib'); -const { Output } = Transaction; -const { InvalidDashcoreTransaction } = require('../../../errors'); -const { FETCHED_CONFIRMED_TRANSACTION } = require('../../../EVENTS'); - -const parseStringifiedTransaction = (stringified) => new Transaction(stringified); -/** - * This method is used to import a transaction in Store. - * if a transaction is already existing, we verify if the metadata needs an update as well. - * @param {Transaction/String} transaction - A valid Transaction - * @param {TransactionMetaData} transactionMetadata - Transaction Metadata - * @return void - */ -const importTransaction = function importTransaction(transaction, transactionMetadata) { - if (!(transaction instanceof Transaction)) { - try { - // eslint-disable-next-line no-param-reassign - transaction = parseStringifiedTransaction(transaction); - if (!transaction.hash || !transaction.inputs.length || !transaction.outputs.length) { - throw new InvalidDashcoreTransaction(transaction); - } - } catch (e) { - throw new InvalidDashcoreTransaction(transaction); - } - } - const { - store, - network, - mappedAddress, - mappedTransactionsHeight, - } = this; - const { transactions, transactionsMetadata } = store; - const { inputs, outputs } = transaction; - - let hasUpdateStorage = false; - let outputIndex = -1; - const processedAddressesForTx = {}; - - transactions[transaction.hash] = transaction; - if (transactionMetadata) { - const { height } = transactionMetadata; - if (Number.isInteger(height) && height !== 0) { - transactionsMetadata[transaction.hash] = transactionMetadata; - const mappedTransactionObject = { hash: transaction.hash, ...transactionMetadata }; - - if (mappedTransactionsHeight[height]) { - // If we had this transaction locally, and it might have not been final (confirmed) - // We require to look if it previously existed and need replace or to add it - const findIndex = mappedTransactionsHeight[height] - .findIndex((el) => el.hash === transaction.hash); - - if (findIndex >= 0) { - mappedTransactionsHeight[height][findIndex] = mappedTransactionObject; - } else { - mappedTransactionsHeight[height].push(mappedTransactionObject); - } - } else { - mappedTransactionsHeight[height] = ([mappedTransactionObject]); - } - } - } - - // even if we had this transaction locally, we need to - // process it to ensure no new address (BIP44) needs to be generated - [...inputs, ...outputs].forEach((element) => { - const isOutput = (element instanceof Output); - if (isOutput) outputIndex += 1; - - if (element.script) { - const address = element.script.toAddress(network).toString(); - - if (mappedAddress && mappedAddress[address]) { - const { path, type, walletId } = mappedAddress[address]; - const addressObject = store.wallets[walletId].addresses[type][path]; - // If the transactions has already been processed in a previous insertion, - // we can skip the processing now - if (addressObject.transactions.includes(transaction.hash)) { - return; - } - - if (!addressObject.used) addressObject.used = true; - - // We mark our address as affected so we update the tx later on - if (!processedAddressesForTx[addressObject.address]) { - processedAddressesForTx[addressObject.address] = addressObject; - } - - if (!isOutput) { - const vin = element; - const utxoKey = `${vin.prevTxId.toString('hex')}-${vin.outputIndex}`; - if (addressObject.utxos[utxoKey]) { - const previousOutput = addressObject.utxos[utxoKey]; - addressObject.balanceSat -= previousOutput.satoshis; - delete addressObject.utxos[utxoKey]; - hasUpdateStorage = true; - } - } else { - const vout = element; - - const utxoKey = `${transaction.hash}-${outputIndex}`; - if (!addressObject.utxos[utxoKey]) { - addressObject.utxos[utxoKey] = vout; - addressObject.balanceSat += vout.satoshis; - hasUpdateStorage = true; - } else if (addressObject.unconfirmedBalanceSat >= vout.satoshis) { - addressObject.unconfirmedBalanceSat -= vout.satoshis; - addressObject.balanceSat += vout.satoshis; - hasUpdateStorage = true; - } - } - } - } - }); - - // As the same address can have one or more inputs and one or more outputs in the same tx - // we update it's transactions array as last step of importing - Object.values(processedAddressesForTx).forEach((addressObject) => { - addressObject.transactions.push(transaction.hash); - }); - - if (hasUpdateStorage) { - this.lastModified = +new Date(); - // Announce only confirmed transaction imported that are our. - this.announce(FETCHED_CONFIRMED_TRANSACTION, { transaction }); - } -}; -module.exports = importTransaction; diff --git a/packages/wallet-lib/src/types/Storage/methods/importTransaction.spec.js b/packages/wallet-lib/src/types/Storage/methods/importTransaction.spec.js deleted file mode 100644 index 8c37c9f7404..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/importTransaction.spec.js +++ /dev/null @@ -1,407 +0,0 @@ -const {expect} = require('chai'); -const importTransaction = require('./importTransaction'); -const transactionFixtures = require('../../../../fixtures/transactions'); -const {fd7c727155ef67fd5c1d54b73dea869e9690c439570063d6e96fec1d3bba450e} = transactionFixtures.valid.mainnet -const { Transaction, Script } = require('@dashevo/dashcore-lib'); - -const faltyTx = '03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff0602cc0c028800ffffffff0200902f50090000001976a91446e502918c04a65a3830ce89cc364b0cd301793388ac00e40b54020000001976a914ecfd5aaebcbb8f4791e716e188b20d4f0183265c88ac00000000460200cc0c0000be0c7d02ff51a9d30e39873ebb953d763595565fcbe0512a04bfa25ed0455e380000000000000000000000000000000000000000000000000000000000000000'; - -const tx = new Transaction({ - hash: 'ea9c4066394aa09cb7ee8f3997b8dc10b999a8d709c4046f81d8bf9341ae6e5b', - version: 3, - inputs: [ - { - prevTxId: '9f398515b6fc898ebf4e7b49bbfc4359b8c89f508c6cd677e53946bd86064b28', - outputIndex: 0, - sequenceNumber: 4294967295, - script: '47304402205bb4f7880fb0fc13218940ba341c30e817363e5590343d28639af921b2a5f1d40220010920ae4b00bbb657f8653cb44172b8cb13447bb5105ddaf32a2845ea0666b90121025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794b', - scriptString: '71 0x304402205bb4f7880fb0fc13218940ba341c30e817363e5590343d28639af921b2a5f1d40220010920ae4b00bbb657f8653cb44172b8cb13447bb5105ddaf32a2845ea0666b901 33 0x025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794b', - }, - { - prevTxId: 'b812d9345fa8ea06af1d19b935eec65824d53779db74cd325690ad1d38a82757', - outputIndex: 0, - sequenceNumber: 4294967295, - script: '483045022100ea2d17ffc417e1f70c9c9ae11b7d95a07ab359c1d9d634baba145bab7b1deb0802207507296e12acc83ce038e5bbd54c46fa78b9475536f64fb313fedb978d12b73b0121025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794b', - scriptString: '72 0x3045022100ea2d17ffc417e1f70c9c9ae11b7d95a07ab359c1d9d634baba145bab7b1deb0802207507296e12acc83ce038e5bbd54c46fa78b9475536f64fb313fedb978d12b73b01 33 0x025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794b', - }, - { - prevTxId: '370b7bbd5b6e0de42a95d59e3277041ac20e945ffb93f56bb6984ba42f28a2ac', - outputIndex: 0, - sequenceNumber: 4294967295, - script: '47304402207926bf9176bdc88f38dde2140b2b8b0e4f331f33bb48af12c1bcce5efbb2593c022073c188d2149d5a0bfe4adff82b63d0bc62e04f2769cdcfda50a2c5e34ab7cbf60121025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794b', - scriptString: '71 0x304402207926bf9176bdc88f38dde2140b2b8b0e4f331f33bb48af12c1bcce5efbb2593c022073c188d2149d5a0bfe4adff82b63d0bc62e04f2769cdcfda50a2c5e34ab7cbf601 33 0x025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794b', - }, - ], - outputs: [ - { - satoshis: 12999997493, - script: '76a9143ec33076ba72b36b66b7ec571dd7417abdeb76f888ac', - }, - ], - nLockTime: 0, -}); - -describe('Storage - importTransaction', function suite() { - this.timeout(10000); - it('should throw on failed import', () => { - const mockStorage = { - store:{ - transactions:{} - } - } - const mockOpts1 = {}; - const mockOpts2 = '688dd18dea2b6f3c2d3892d13b41922fde7be01cd6040be9f3568dafbf9b1a23'; - const mockOpts3 = {'688dd18dea2b6f3c2d3892d13b41922fde7be01cd6040be9f3568dafbf9b1a23': {}}; - const mockOpts4 = {txid: '688dd18dea2b6f3c2d3892d13b41922fde7be01cd6040be9f3568dafbf9b1a23'}; - const mockOpts5 = {txid: '688dd18dea2b6f3c2d3892d13b41922fde7be01cd6040be9f3568dafbf9b1a23', vin: []}; - - const exceptedException1 = 'A Dashcore Transaction object or valid rawTransaction is required'; - - expect(() => importTransaction.call(mockStorage, mockOpts1)).to.throw(exceptedException1); - expect(() => importTransaction.call(mockStorage, mockOpts2)).to.throw(exceptedException1); - expect(() => importTransaction.call(mockStorage, mockOpts3)).to.throw(exceptedException1); - expect(() => importTransaction.call(mockStorage, mockOpts4)).to.throw(exceptedException1); - expect(() => importTransaction.call(mockStorage, mockOpts5)).to.throw(exceptedException1); - }); - it('should import a transaction', () => { - const mockedSearchAddress = () => ({found: false}); - let announceCalled = 0; - const self = { - store: { - wallets: { - 'db158d08df': { - addresses: { - external: { - "m/44'/1'/0'/0/0": { - path: "m/44'/1'/0'/0/0", - index: 0, - address: 'yS3Ja63BpkH7qHYVQvdEuiBd9xo8ZoPjZB', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - } - } - } - } - }, - transactions: {}, - chains: {testnet: {blockHeight: 50000}}, - }, - mappedAddress: { - 'yS3Ja63BpkH7qHYVQvdEuiBd9xo8ZoPjZB': {walletId: 'db158d08df', type: 'external', path: "m/44'/1'/0'/0/0"} - }, - network: 'testnet', - lastModified: 0, - searchAddress: mockedSearchAddress, - announce: (annType) => { - announceCalled += 1; - expect(annType).to.equal('FETCHED/CONFIRMED_TRANSACTION'); - }, - }; - importTransaction.call(self, tx); - importTransaction.call(self, tx); - const expectedStore = { - wallets: { - 'db158d08df': { - addresses: { - external: { - "m/44'/1'/0'/0/0": { - path: "m/44'/1'/0'/0/0", - index: 0, - address: 'yS3Ja63BpkH7qHYVQvdEuiBd9xo8ZoPjZB', - transactions: ["ea9c4066394aa09cb7ee8f3997b8dc10b999a8d709c4046f81d8bf9341ae6e5b"], - balanceSat: 12999997493, - unconfirmedBalanceSat: 0, - utxos: {"ea9c4066394aa09cb7ee8f3997b8dc10b999a8d709c4046f81d8bf9341ae6e5b-0": tx.outputs[0]}, - fetchedLast: 0, - used: true - } - } - } - } - }, - - transactions: {ea9c4066394aa09cb7ee8f3997b8dc10b999a8d709c4046f81d8bf9341ae6e5b: tx}, - chains: {testnet: {blockHeight: 50000}}, - }; - const expectedMappedAddress = { - 'yS3Ja63BpkH7qHYVQvdEuiBd9xo8ZoPjZB': {walletId: 'db158d08df', type: 'external', path: "m/44'/1'/0'/0/0"} - }; - - expect(self.store).to.be.deep.equal(expectedStore); - expect(self.mappedAddress).to.be.deep.equal(expectedMappedAddress); - expect(self.lastModified).to.be.not.equal(0); - expect(announceCalled).to.be.equal(1); - }); - it('should impact input and output correctly', function () { - let announceCalled = 0; - const tx_79fd_1 = new Transaction('0200000002de85b10c3e4e95e94597969cd7ffda3f8dc9237d36b225326fc8b24ea895039c010000006a47304402206f3b27083662213cadcc8d511f991c6cd57a45374829f32c707f99b046aaa6e8022021bfda9808d3adda06c9535edbdfe419db27ce3cb628ab0e9b1e3eeba01732c1012103a65caff6ca4c0415a3ac182dfc2a6d3a4dceb98e8b831e71501df38aa156f2c1feffffff17bed9b68cd3c1077b1776936b78a3c964bfe27d695708a196d0f35f4dcd3cef000000006a47304402200926de33076dfd2f6a0c6830ff447a9adab4e3143f7f34883e96cb3a9513f20f0220535a42cf40c5ba6095779393f8c702c913ce2f2a62d45a2cd37c56ccdbe60445012102372247aa7ef740c54fd126f3080537be5f834f0c16ba20edfe671d7c9b538c67feffffff0240782715000000001976a914e8f859254d24c98f64253a0388ba81ef2c68712788ac60f72e9e030000001976a914300dccc87c4811311c94525c7b208fc371ab654088ac1b150000'); - const tx_79fd_2 = new Transaction('0300000001853852da3974e3e1f8548256e1930781700b2f2c6bf420c0284033d61e1f4092010000006b483045022100d601a80702d3d599b338992d05f3475688a7c5febdabf372a053aaf0cfccc1d20220284410a6095a9777ca9b0f1ca56b42f536735ab39c7158682e7dba6a47cbd50c0121033807498e192fde6bfe27933365227e262e12fbfcf4d7b37ecff100228a0b04a2ffffffff0210270000000000001976a914e330440072a28e1da250fb63f3cd07e3d5a9b6cc88ac59cf2e9e030000001976a914c0c59ed9a83f91d070876941a362ed18f1b9223788ac00000000'); - const mockedSelf = { - store: { - wallets: { - '79fd90175d': { - addresses: { - external: { - "m/44'/1'/0'/0/0": { - path: "m/44'/1'/0'/0/0", - index: 0, - address: 'yQhXpFHfxk9pLyR1sPDYWZK5xqEMWbXrCd', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/1": { - path: "m/44'/1'/0'/0/1", - index: 1, - address: 'yh2i4JZ51rCFLbgk6RStaGKWB5JkQeeQYr', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/2": { - path: "m/44'/1'/0'/0/2", - index: 2, - address: 'yikykkDREFzxM7gNjxszrw2LYmJGHfJsdv', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - } - }, - internal: { - "m/44'/1'/0'/1/0": { - path: "m/44'/1'/0'/1/0", - index: 0, - address: 'ydtjKwwrxsq2Czeoeqk5ULoSXvdrnKWKWR', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/1": { - path: "m/44'/1'/0'/1/1", - index: 1, - address: 'yZ7zwKLSwuvZyYQXU2UGRPf1qr7nRqdH7b', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - } - } - } - }, - transactions: {}, - chains: {testnet: {blockHeight: 50000}}, - }, - mappedAddress: { - 'yQhXpFHfxk9pLyR1sPDYWZK5xqEMWbXrCd': {walletId: '79fd90175d', type: 'external', path: "m/44'/1'/0'/0/0"}, - 'ydtjKwwrxsq2Czeoeqk5ULoSXvdrnKWKWR': {walletId: '79fd90175d', type: 'internal', path: "m/44'/1'/0'/1/0"}, - 'yh2i4JZ51rCFLbgk6RStaGKWB5JkQeeQYr': {walletId: '79fd90175d', type: 'external', path: "m/44'/1'/0'/0/1"}, - 'yZ7zwKLSwuvZyYQXU2UGRPf1qr7nRqdH7b': {walletId: '79fd90175d', type: 'internal', path: "m/44'/1'/0'/1/1"}, - 'yikykkDREFzxM7gNjxszrw2LYmJGHfJsdv': {walletId: '79fd90175d', type: 'external', path: "m/44'/1'/0'/0/2"} - }, - network: 'testnet', - lastModified: 0, - announce: (annType) => { - announceCalled += 1; - expect(annType).to.equal('FETCHED/CONFIRMED_TRANSACTION'); - }, - }; - importTransaction.call(mockedSelf, tx_79fd_1); - const expectedTransactionsAfterTx1 = { - '92401f1ed6334028c020f46b2c2f0b70810793e1568254f8e1e37439da523885': tx_79fd_1 - }; - const expectedExternalStoreAfterTx1 = { - "m/44'/1'/0'/0/0":{ - "path": "m/44'/1'/0'/0/0", - "index": 0, - "address": "yQhXpFHfxk9pLyR1sPDYWZK5xqEMWbXrCd", - "transactions": ['92401f1ed6334028c020f46b2c2f0b70810793e1568254f8e1e37439da523885'], - "balanceSat": 15538780000, - "unconfirmedBalanceSat": 0, - "utxos": { - "92401f1ed6334028c020f46b2c2f0b70810793e1568254f8e1e37439da523885-1": new Transaction.Output({ - "satoshis": 15538780000, - "script": "76a914300dccc87c4811311c94525c7b208fc371ab654088ac" - }) - }, - "fetchedLast": 0, - "used": true - } - } - expect(mockedSelf.store.transactions).to.deep.equal(expectedTransactionsAfterTx1); - expect(mockedSelf.store.wallets['79fd90175d'].addresses.external["m/44'/1'/0'/0/0"]).to.deep.equal(expectedExternalStoreAfterTx1["m/44'/1'/0'/0/0"]); - - // We need to ensure it do not duplicate UTXO or TXs - importTransaction.call(mockedSelf, tx_79fd_1); - expect(mockedSelf.store.transactions).to.deep.equal(expectedTransactionsAfterTx1); - expect(mockedSelf.store.wallets['79fd90175d'].addresses.external["m/44'/1'/0'/0/0"]).to.deep.equal(expectedExternalStoreAfterTx1["m/44'/1'/0'/0/0"]); - - importTransaction.call(mockedSelf, tx_79fd_2); - const expectedTransactionsAfterTx2 = { - '92401f1ed6334028c020f46b2c2f0b70810793e1568254f8e1e37439da523885': tx_79fd_1, - 'df7792f30588150c94b68e869bcb219b55f66f7ef97d4e5c4bb48c3a6db1250e': tx_79fd_2 - }; - expect(mockedSelf.store.transactions).to.deep.equal(expectedTransactionsAfterTx2); - - const expectedExternalStoreAfterTx2 = { - "m/44'/1'/0'/0/0":{ - "path": "m/44'/1'/0'/0/0", - "index": 0, - "address": "yQhXpFHfxk9pLyR1sPDYWZK5xqEMWbXrCd", - "transactions": [ - '92401f1ed6334028c020f46b2c2f0b70810793e1568254f8e1e37439da523885', - 'df7792f30588150c94b68e869bcb219b55f66f7ef97d4e5c4bb48c3a6db1250e' - ], - "balanceSat": 0, - "unconfirmedBalanceSat": 0, - "utxos": {}, - "fetchedLast": 0, - "used": true - }, - "m/44'/1'/0'/0/1":{ - "path": "m/44'/1'/0'/0/1", - "index": 1, - "address": "yh2i4JZ51rCFLbgk6RStaGKWB5JkQeeQYr", - "transactions": [ - 'df7792f30588150c94b68e869bcb219b55f66f7ef97d4e5c4bb48c3a6db1250e' - ], - "balanceSat": 10000, - "unconfirmedBalanceSat": 0, - "utxos": { - "df7792f30588150c94b68e869bcb219b55f66f7ef97d4e5c4bb48c3a6db1250e-0": new Transaction.Output({ - "satoshis": 10000, - "script": '76a914e330440072a28e1da250fb63f3cd07e3d5a9b6cc88ac' - }), - }, - "fetchedLast": 0, - "used": true - },"m/44'/1'/0'/1/0":{ - "path": "m/44'/1'/0'/1/0", - "index": 0, - "address": "ydtjKwwrxsq2Czeoeqk5ULoSXvdrnKWKWR", - "transactions": [ - 'df7792f30588150c94b68e869bcb219b55f66f7ef97d4e5c4bb48c3a6db1250e' - ], - "balanceSat": 15538769753, - "unconfirmedBalanceSat": 0, - "utxos": { - "df7792f30588150c94b68e869bcb219b55f66f7ef97d4e5c4bb48c3a6db1250e-1": new Transaction.Output({ - "satoshis": 15538769753, - "script": '76a914c0c59ed9a83f91d070876941a362ed18f1b9223788ac' - }), - }, - "fetchedLast": 0, - "used": true - } - }; - expect(mockedSelf.store.wallets['79fd90175d'].addresses.external["m/44'/1'/0'/0/0"]).to.deep.equal(expectedExternalStoreAfterTx2["m/44'/1'/0'/0/0"]); - expect(mockedSelf.store.wallets['79fd90175d'].addresses.external["m/44'/1'/0'/0/1"]).to.deep.equal(expectedExternalStoreAfterTx2["m/44'/1'/0'/0/1"]); - expect(mockedSelf.store.wallets['79fd90175d'].addresses.internal["m/44'/1'/0'/1/0"]).to.deep.equal(expectedExternalStoreAfterTx2["m/44'/1'/0'/1/0"]); - - importTransaction.call(mockedSelf, tx_79fd_2); - importTransaction.call(mockedSelf, tx_79fd_1); - importTransaction.call(mockedSelf, tx_79fd_2); - expect(mockedSelf.store.transactions).to.deep.equal(expectedTransactionsAfterTx2); - expect(mockedSelf.store.wallets['79fd90175d'].addresses.external["m/44'/1'/0'/0/0"]).to.deep.equal(expectedExternalStoreAfterTx2["m/44'/1'/0'/0/0"]); - expect(mockedSelf.store.wallets['79fd90175d'].addresses.external["m/44'/1'/0'/0/1"]).to.deep.equal(expectedExternalStoreAfterTx2["m/44'/1'/0'/0/1"]); - expect(mockedSelf.store.wallets['79fd90175d'].addresses.internal["m/44'/1'/0'/1/0"]).to.deep.equal(expectedExternalStoreAfterTx2["m/44'/1'/0'/1/0"]); - - }); - it('should import transaction metadata', function () { - let announceCalled = 0; - - const mockedSelf = { - store: { - wallets: { - '60ee3a92b6': { - accounts:{ - "m/44'/1'/0'": { - label: null, - path: "m/44'/1'/0'", - network: 'testnet', - blockHeight: 552160 - } - }, - network: 'testnet', - mnemonic: null, - type: null, - identityIds: [], - addresses: { - external: { - "m/44'/1'/0'/0/0": { - path: "m/44'/1'/0'/0/0", - index: 0, - address: 'yd1ohc12LgCYp56CDuckTEHwoa6LbPghMd', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - }, - } - } - }, - transactionsMetadata: {}, - transactions: {}, - - chains: {testnet: { - mappedBlockHeaderHeights: { - '552160': '0000019156265f85695bf62285e32408d6057406b19374a57c009afa9116396f' - }, - blockHeight: 552160 - }}, - }, - mappedAddress: { - 'yd1ohc12LgCYp56CDuckTEHwoa6LbPghMd': {walletId: '60ee3a92b6', type: 'external', path: "m/44'/1'/0'/0/0"}, - }, - mappedTransactionsHeight: { - - }, - network: 'testnet', - lastModified: 0, - announce: (annType) => { - announceCalled += 1; - expect(annType).to.equal('FETCHED/CONFIRMED_TRANSACTION'); - }, - }; - - importTransaction.call(mockedSelf, transactionFixtures.valid.testnet["1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6"], transactionFixtures.valid.testnet.metadata["1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6"]); - - expect(mockedSelf.store.transactions['1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6']).to.exist; - const expectedTransaction = new Transaction(transactionFixtures.valid.testnet["1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6"]); - expect(mockedSelf.store.transactions['1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6'].toString()).to.equal(expectedTransaction.toString()); - - expect(mockedSelf.store.transactionsMetadata).to.exist; - expect(mockedSelf.store.transactionsMetadata['1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6']).to.deep.equal({ - blockHash: '0000007a84abfe1d2b4201f4844bb1e59f24daf965c928281589269f281abc01', - height: 551438, - instantLocked: true, - chainLocked: true - }) - - expect(mockedSelf.mappedTransactionsHeight).to.exist; - const expectedMetadata = transactionFixtures.valid.testnet.metadata["1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6"]; - expectedMetadata.hash = '1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6'; - expect(mockedSelf.mappedTransactionsHeight['551438']).to.deep.equal([expectedMetadata]); - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/importTransactions.js b/packages/wallet-lib/src/types/Storage/methods/importTransactions.js deleted file mode 100644 index a2e29244dee..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/importTransactions.js +++ /dev/null @@ -1,34 +0,0 @@ -const { Transaction } = require('@dashevo/dashcore-lib'); - -/** - * Import an array of transactions or a transaction object to the store - * @param {[TransactionsWithMetaData][Transaction]|Transaction} transactions - * @return {number} - * */ -const importTransactions = function importTransactions(transactions) { - const type = transactions.constructor.name; - const self = this; - if (type === Transaction.name) { - self.importTransaction(transactions); - } else if (type === 'Object') { - const transactionsIds = Object.keys(transactions); - if (transactionsIds.length === 0) { - throw new Error('Invalid transaction'); - } - transactionsIds.forEach((id) => { - const transaction = transactions[id]; - self.importTransaction(transaction); - }); - } else if (type === 'Array') { - transactions.forEach((transactionData) => { - if (transactionData.constructor.name === 'Array') { - self.importTransaction(transactionData[0], transactionData[1]); - } else { - self.importTransaction(transactionData); - } - }); - } else { - throw new Error('Invalid transaction. Cannot import.'); - } -}; -module.exports = importTransactions; diff --git a/packages/wallet-lib/src/types/Storage/methods/importTransactions.spec.js b/packages/wallet-lib/src/types/Storage/methods/importTransactions.spec.js deleted file mode 100644 index 307a5c3263a..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/importTransactions.spec.js +++ /dev/null @@ -1,11 +0,0 @@ -const { expect } = require('chai'); -const importTransactions = require('./importTransactions'); - -describe('Storage - importTransactions', () => { - it('should import an array of transaction', () => { - - }); - it('should import an object with one or multiples transaction', () => { - - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/insertIdentityAtIndex.js b/packages/wallet-lib/src/types/Storage/methods/insertIdentityAtIndex.js deleted file mode 100644 index fd46c1f20ad..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/insertIdentityAtIndex.js +++ /dev/null @@ -1,25 +0,0 @@ -const IdentityReplaceError = require('../../../errors/IndentityIdReplaceError'); - -/** - * - * @param {string} walletId - * @param {string} identityId - * @param {number} identityIndex - * @return void - */ -function insertIdentityAtIndex(walletId, identityId, identityIndex) { - if (!this.store.wallets[walletId].identityIds) { - this.store.wallets[walletId].identityIds = []; - } - - const existingId = this.getIdentityIdByIndex(walletId, identityIndex); - - if (Boolean(existingId) && existingId !== identityId) { - throw new IdentityReplaceError(`Trying to replace identity at index ${identityIndex}`); - } - - this.store.wallets[walletId].identityIds[identityIndex] = identityId; - this.lastModified = Date.now(); -} - -module.exports = insertIdentityAtIndex; diff --git a/packages/wallet-lib/src/types/Storage/methods/rehydrateState.js b/packages/wallet-lib/src/types/Storage/methods/rehydrateState.js index 342489cf2e0..4381086714a 100644 --- a/packages/wallet-lib/src/types/Storage/methods/rehydrateState.js +++ b/packages/wallet-lib/src/types/Storage/methods/rehydrateState.js @@ -1,74 +1,9 @@ const { merge } = require('lodash'); -const { InstantLock, Transaction, BlockHeader } = require('@dashevo/dashcore-lib'); +const { InstantLock } = require('@dashevo/dashcore-lib'); const { hasMethod } = require('../../../utils'); const mergeHelper = (initial = {}, additional = {}) => merge(initial, additional); const { REHYDRATE_STATE_FAILED, REHYDRATE_STATE_SUCCESS } = require('../../../EVENTS'); -const logger = require('../../../logger'); - -const storeTypesSchema = { - transactions: { - '*': Transaction, - }, - instantLocks: { - '*': InstantLock, - }, - wallets: { - '*': { - addresses: { - '*': { - '*': { - utxos: { - // eslint-disable-next-line func-names - '*': function (item) { - // TODO: resolve type inconsistency for address utxos - try { - return new Transaction.UnspentOutput(item); - } catch (e) { - return new Transaction.Output(item); - } - }, - }, - }, - }, - }, - }, - }, - chains: { - '*': { - blockHeaders: { - '*': BlockHeader, - }, - }, - }, -}; - -const castItemTypes = (item, schema) => { - Object.entries(schema).forEach(([schemaKey, schemaValue]) => { - if (schemaValue.constructor.name !== 'Object') { - const Clazz = schemaValue; - if (schemaKey === '*') { - Object.keys(item).forEach((itemKey) => { - // eslint-disable-next-line no-param-reassign - item[itemKey] = new Clazz(item[itemKey]); - }); - } else { - if (!item[schemaKey]) { - throw new Error(`No schema key "${schemaKey}" found for item ${JSON.stringify(item)}`); - } - - // eslint-disable-next-line no-param-reassign - item[schemaKey] = new Clazz(item[schemaKey]); - } - } else if (schemaKey === '*') { - Object.values(item).forEach((itemValue) => castItemTypes(itemValue, schemaValue)); - } else { - castItemTypes(item[schemaKey], schemaValue); - } - }); - - return item; -}; /** * Fetch the state from the persistence adapter @@ -77,39 +12,31 @@ const castItemTypes = (item, schema) => { const rehydrateState = async function rehydrateState() { if (this.rehydrate && this.lastRehydrate === null) { try { - const storeItems = { - transactions: null, - wallets: null, - chains: null, - instantLocks: null, - }; - const keys = Object.keys(storeItems); - // Obtain items from storage adapter - for (let i = 0; i < keys.length; i += 1) { - const itemKey = keys[i]; - - let item; - if (this.adapter && hasMethod(this.adapter, 'getItem')) { - // eslint-disable-next-line no-await-in-loop - item = await this.adapter.getItem(itemKey); + const transactions = (this.adapter && hasMethod(this.adapter, 'getItem')) + ? (await this.adapter.getItem('transactions') || this.store.transactions) + : this.store.transactions; + const wallets = (this.adapter && hasMethod(this.adapter, 'getItem')) + ? (await this.adapter.getItem('wallets') || this.store.wallets) + : this.store.wallets; + const chains = (this.adapter && hasMethod(this.adapter, 'getItem')) + ? (await this.adapter.getItem('chains') || this.store.chains) + : this.store.chains; + const instantLocks = (this.adapter && hasMethod(this.adapter, 'getItem')) + ? (await this.adapter.getItem('instantLocks') || this.store.instantLocks) + : this.store.instantLocks; + + // We need to keep deserialized instant locks + Object.keys(instantLocks).forEach((transactionHash) => { + const instantLock = instantLocks[transactionHash]; + if (!(instantLock instanceof InstantLock)) { + instantLocks[transactionHash] = new InstantLock(instantLock); } - - storeItems[itemKey] = item || this.store[itemKey]; - } - - // Cast store items to correct data types - try { - castItemTypes(storeItems, storeTypesSchema); - } catch (e) { - logger.error('Error casting storage items types: possibly data schema mismatch'); - throw e; - } - - // Merge with the current items in store - Object.keys(storeItems).forEach((itemKey) => { - this.store[itemKey] = mergeHelper(this.store[itemKey], storeItems[itemKey]); }); + this.store.transactions = mergeHelper(this.store.transactions, transactions); + this.store.wallets = mergeHelper(this.store.wallets, wallets); + this.store.chains = mergeHelper(this.store.chains, chains); + this.store.instantLocks = mergeHelper(this.store.instantLocks, instantLocks); this.lastRehydrate = +new Date(); this.emit(REHYDRATE_STATE_SUCCESS, { type: REHYDRATE_STATE_SUCCESS, payload: null }); } catch (e) { diff --git a/packages/wallet-lib/src/types/Storage/methods/rehydrateState.spec.js b/packages/wallet-lib/src/types/Storage/methods/rehydrateState.spec.js deleted file mode 100644 index 5c59971fa66..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/rehydrateState.spec.js +++ /dev/null @@ -1,226 +0,0 @@ -const { expect } = require('chai'); -const rehydrateState = require('./rehydrateState'); -const { Transaction, BlockHeader, InstantLock } = require("@dashevo/dashcore-lib"); - -const storeMock = { - "transactions": { - "d37eb9f1b41c45134926d418486f2d8f57778cb949f4069a0b4c91955934913b": { - "hash": "d37eb9f1b41c45134926d418486f2d8f57778cb949f4069a0b4c91955934913b", - "version": 2, - "inputs": [ - { - "prevTxId": "ba657cdff1bdf2010e2b394f7db81aa429a94574c7fdcc5da4a8c5be751bd419", - "outputIndex": 1, - "sequenceNumber": 4294967294, - "script": "483045022100eb7f103a5755e79a25179f289b74033d13e7b5a252a4911bba1c908d0db4361002203a664917ebbb4f5c1d81409898c05f807e23dd324d4458f32a6fc5b42ad8e3830121024b181f43670ebc67826e07885170029e01fd830bfcfa43862d694224d8c7e162", - "scriptString": "72 0x3045022100eb7f103a5755e79a25179f289b74033d13e7b5a252a4911bba1c908d0db4361002203a664917ebbb4f5c1d81409898c05f807e23dd324d4458f32a6fc5b42ad8e38301 33 0x024b181f43670ebc67826e07885170029e01fd830bfcfa43862d694224d8c7e162" - } - ], - "outputs": [ - { - "satoshis": 82716538, - "script": "76a9142f7d620394f5604854e495e6a78773287651faab88ac" - }, - { - "satoshis": 105580000, - "script": "76a91428a46d7ae4d50ec3a5dbb847b01a60818905e98e88ac" - } - ], - "nLockTime": 627066 - }, - "26faad59f6f3aef120323fb3aa9b0a3aaccf56ae770ff83099e3e86324d3a0da": { - "hash": "26faad59f6f3aef120323fb3aa9b0a3aaccf56ae770ff83099e3e86324d3a0da", - "version": 3, - "inputs": [ - { - "prevTxId": "d37eb9f1b41c45134926d418486f2d8f57778cb949f4069a0b4c91955934913b", - "outputIndex": 1, - "sequenceNumber": 4294967295, - "script": "4730440220645a3cfe6798b2d4e5943209c2f241725c0e809c82bd3985743854bbebfeca48022036fd8bf98960d4d1deea37e5cc5833cb64178bc2e27a1628f83250e9e2ec69290121024c61a9a159104ad5e027754b19514347c5ee4a7f5ede8cc379f5f987f2fdb0b0", - "scriptString": "71 0x30440220645a3cfe6798b2d4e5943209c2f241725c0e809c82bd3985743854bbebfeca48022036fd8bf98960d4d1deea37e5cc5833cb64178bc2e27a1628f83250e9e2ec692901 33 0x024c61a9a159104ad5e027754b19514347c5ee4a7f5ede8cc379f5f987f2fdb0b0" - } - ], - "outputs": [ - { - "satoshis": 10000, - "script": "76a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac" - }, - { - "satoshis": 105569753, - "script": "76a91499c256c6f6724b926b7d903b10547bf712faeebd88ac" - } - ], - "nLockTime": 0 - } - }, - "wallets": { - "799ea77395": { - "accounts": { - "m/44'/1'/0'": { - "label": null, - "path": "m/44'/1'/0'", - "network": "testnet", - "blockHeight": 627626, - "blockHash": "00000053b6c3b0b56f64607ad77524922fbb4560306fa31f165ae36e11551d77" - } - }, - "network": "testnet", - "mnemonic": null, - "type": null, - "identityIds": [], - "addresses": { - "external": { - "m/44'/1'/0'/0/0": { - "path": "m/44'/1'/0'/0/0", - "index": 0, - "address": "yQ2LrWYLRdUdDnofwqscghwSgCghd2BMPv", - "transactions": [ - "d37eb9f1b41c45134926d418486f2d8f57778cb949f4069a0b4c91955934913b", - "26faad59f6f3aef120323fb3aa9b0a3aaccf56ae770ff83099e3e86324d3a0da" - ], - "balanceSat": 0, - "unconfirmedBalanceSat": 0, - "utxos": { - "adfc0ad1dd803fc1541f87711e794936c3fd99b763d0b26e77dd463425382833-1": { - "address": "yQ2LrWYLRdUdDnofwqscghwSgCghd2BMPv", - "txid": "26faad59f6f3aef120323fb3aa9b0a3aaccf56ae770ff83099e3e86324d3a0da", - "vout": 1, - "scriptPubKey": "76a91421b4db2de99d02a16092fa0aa0f9bb32d87353a588ac", - "amount": 0.82706400 - } - }, - "fetchedLast": 0, - "used": true - } - }, - "internal": { - "m/44'/1'/0'/1/0": { - "path": "m/44'/1'/0'/1/0", - "index": 0, - "address": "yaLT3TjS7jdDVgAjDBCRSm1rHbsMeLXAx4", - "transactions": [ - "adfc0ad1dd803fc1541f87711e794936c3fd99b763d0b26e77dd463425382833" - ], - "balanceSat": 0, - "unconfirmedBalanceSat": 0, - "utxos": { - "adfc0ad1dd803fc1541f87711e794936c3fd99b763d0b26e77dd463425382833-1": { - "address": "yaLT3TjS7jdDVgAjDBCRSm1rHbsMeLXAx4", - "txid": "26faad59f6f3aef120323fb3aa9b0a3aaccf56ae770ff83099e3e86324d3a0da", - "vout": 1, - "scriptPubKey": "76a91421b4db2de99d02a16092fa0aa0f9bb32d87353a588ac", - "amount": 0.0001 - } - }, - "fetchedLast": 0, - "used": true - } - }, - "misc": {} - } - } - }, - "chains": { - "testnet": { - "name": "testnet", - "blockHeaders": { - "000000059885815cfc06ba74b814200d29658394dbe5d1e93948a8587947747b": { - "hash": "000000059885815cfc06ba74b814200d29658394dbe5d1e93948a8587947747b", - "version": 536870912, - "prevHash": "000000c520efd2047f0b6f0c1c75e0382f8a9b7d76bb140bde3ada10c62e8b0d", - "merkleRoot": "ef292bfb7965402e57dfeb4ee8bad0055c216c4c5a4e549a0ac17a393ae8617b", - "time": 1638950949, - "bits": 503385436, - "nonce": 351770 - }, - "000000b7d508273169da2e0f1c167554d3d4643759361ab8ca338cabbbf5dee4": { - "hash": "000000b7d508273169da2e0f1c167554d3d4643759361ab8ca338cabbbf5dee4", - "version": 536870912, - "prevHash": "0000009b0bb17530c41a8200868e61e8a2d927b3cce7cfac14c3f393ffca5b06", - "merkleRoot": "664d055f0194e01ac64db1ace3a720f4580533d6d999b1cfab7740f8b9c4d642", - "time": 1638884848, - "bits": 503373836, - "nonce": 13494 - }, - }, - "mappedBlockHeaderHeights": { - "627561": "00000145d8a9ab1a71ffdcd01657d284c67593b27f9ca244fc202632bad7145d", - "627574": "000000801bafea9630d9c6a341963912e7faff067e79b8b9b57910b62f736d3e", - "627626": "00000053b6c3b0b56f64607ad77524922fbb4560306fa31f165ae36e11551d77" - }, - "blockHeight": 627626, - "fees": { - "minRelay": 1000 - } - } - }, - "instantLocks": { - "d37eb9f1b41c45134926d418486f2d8f57778cb949f4069a0b4c91955934913b": { - "inputs": [ - { - "outpointHash": "3188b2bfb238cb07c1822944088fff59d665af1f00a72d2beee8b88457201beb", - "outpointIndex": 1 - } - ], - "txid": "d37eb9f1b41c45134926d418486f2d8f57778cb949f4069a0b4c91955934913b", - "signature": "0628537fd9b44b5d2441724780183bea34f44e82fea7a7057337c562858bc9dabfe3541c8a7c9bd4677887dfaa2b02b90c483fe223590a05d6ab8670221aacf046731aa462a8e9f08271434ed6cbfaa1ec51c2be71f6e0a7d9612795b1a0f050" - }, - "26faad59f6f3aef120323fb3aa9b0a3aaccf56ae770ff83099e3e86324d3a0da": { - "inputs": [ - { - "outpointHash": "b5cdbdae1f43ef324326b5519e2e670afa9203e2f37115c3c6ac51db968b3726", - "outpointIndex": 1 - } - ], - "txid": "26faad59f6f3aef120323fb3aa9b0a3aaccf56ae770ff83099e3e86324d3a0da", - "signature": "913ab2cf9bb6d918f0d2d12c5fef9ba9652ce93f625cebd83241746b36bdc6505e0385e1875576a92a5d49d74ec8098315e90c8688ccd41ab4fdc8df43bd83fa53081a65651bc30ca2957e4c2e98388465b19fac7b61f7b86393d777c2d26e66" - }, - } -} - - -describe('Storage - rehydrateState', () => { - const storage = { - rehydrate: true, - lastRehydrate: null, - adapter: { - getItem: async (key) => { - return storeMock[key]; - } - }, - store: { - transactions: {}, - wallets: {}, - chains: {}, - instantLocks: {} - }, - emit: () => {} - } - - - it('should rehydrate the state', async () => { - await rehydrateState.call(storage); - Object.values(storage.store.transactions).forEach(tx => { - expect(tx instanceof Transaction).to.be.true; - }); - - Object.values(storage.store.instantLocks).forEach(isLock => { - expect(isLock instanceof InstantLock).to.be.true; - }); - - Object.values(storage.store.wallets).forEach(wallet => { - const { internal, external } = wallet.addresses; - Object.values({ ...internal, ...external }).forEach(address => { - Object.values(address.utxos).forEach(utxo => { - expect(utxo instanceof Transaction.UnspentOutput).to.be.true; - }); - }); - }); - - Object.values(storage.store.chains).forEach(chain => { - Object.values(chain.blockHeaders).forEach(blockHeader => { - expect(blockHeader instanceof BlockHeader).to.be.true; - }) - }) - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/saveState.spec.js b/packages/wallet-lib/src/types/Storage/methods/saveState.spec.js deleted file mode 100644 index 261a5aa673c..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/saveState.spec.js +++ /dev/null @@ -1,8 +0,0 @@ -const { expect } = require('chai'); -const saveState = require('./saveState'); - -describe('Storage - saveState', () => { - it('should state the state', () => { - - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/searchAddress.js b/packages/wallet-lib/src/types/Storage/methods/searchAddress.js deleted file mode 100644 index b19382c69d6..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/searchAddress.js +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Search a specific address in the store - * @param {string} address - * @param {boolean} [forceLoop=false] - When set at true, will force a full search - * @return {AddressSearchResult} - */ -const searchAddress = function searchAddress(address, forceLoop = false) { - const search = { - address, - type: null, - found: false, - }; - const { store } = this; - if (forceLoop === true) { - // Look up by looping over all addresses todo:optimisation - const existingWallets = Object.keys(store.wallets); - existingWallets.forEach((walletId) => { - const existingTypes = Object.keys(store.wallets[walletId].addresses); - existingTypes.forEach((type) => { - const existingPaths = Object.keys(store.wallets[walletId].addresses[type]); - existingPaths.forEach((path) => { - const el = store.wallets[walletId].addresses[type][path]; - if (el.address === search.address) { - search.path = path; - search.type = type; - search.found = true; - search.result = el; - search.walletId = walletId; - } - }); - }); - }); - } else if (this.mappedAddress[address]) { - const { path, type, walletId } = this.mappedAddress[address]; - const el = store.wallets[walletId].addresses[type][path]; - - search.path = path; - search.type = type; - search.found = true; - search.result = el; - search.walletId = walletId; - } - - return search; -}; -module.exports = searchAddress; diff --git a/packages/wallet-lib/src/types/Storage/methods/searchAddress.spec.js b/packages/wallet-lib/src/types/Storage/methods/searchAddress.spec.js deleted file mode 100644 index 0d0b92ca73d..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/searchAddress.spec.js +++ /dev/null @@ -1,8 +0,0 @@ -const { expect } = require('chai'); -const searchAddress = require('./searchAddress'); - -describe('Storage - searchAddress', () => { - it('should search an address', () => { - - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/searchAddressWithTx.spec.js b/packages/wallet-lib/src/types/Storage/methods/searchAddressWithTx.spec.js deleted file mode 100644 index f8df278e8cd..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/searchAddressWithTx.spec.js +++ /dev/null @@ -1,8 +0,0 @@ -const { expect } = require('chai'); -const searchAddressesWithTx = require('./searchAddressesWithTx'); - -describe('Storage - searchAddressesWithTx', () => { - it('should search an address having a specific tx', () => { - - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/searchAddressesWithTx.js b/packages/wallet-lib/src/types/Storage/methods/searchAddressesWithTx.js deleted file mode 100644 index 6aa36d625bb..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/searchAddressesWithTx.js +++ /dev/null @@ -1,36 +0,0 @@ -const _ = require('lodash'); -/** - * Search an address having a specific txid - * todo : Handle when multiples one (inbound/outbound) - * @param {string} txid - * @return {AddressesSearchResult} - */ -const searchAddressesWithTx = function searchAddressesWithTx(txid) { - const search = { - txid, - results: [], - found: false, - }; - const store = this.getStore(); - - // Look up by looping over all addresses todo:optimisation - const existingWallets = Object.keys(store.wallets); - existingWallets.forEach((walletId) => { - if (_.has(store.wallets[walletId], 'addresses')) { - const existingTypes = Object.keys(store.wallets[walletId].addresses); - existingTypes.forEach((type) => { - const existingPaths = Object.keys(store.wallets[walletId].addresses[type]); - existingPaths.forEach((path) => { - const el = store.wallets[walletId].addresses[type][path]; - if (el.transactions.includes(search.txid)) { - search.results.push({ type, ...el }); - search.found = true; - } - }); - }); - } - }); - - return search; -}; -module.exports = searchAddressesWithTx; diff --git a/packages/wallet-lib/src/types/Storage/methods/searchBlockHeader.js b/packages/wallet-lib/src/types/Storage/methods/searchBlockHeader.js deleted file mode 100644 index c93463a19cf..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/searchBlockHeader.js +++ /dev/null @@ -1,25 +0,0 @@ -const { is } = require('../../../utils'); -/** - * Search a specific blockheader in the store - * @param {string|number} identifier - block hash or height - * @return {BlockHeaderSearchResult} - */ -const searchBlockHeader = function searchBlockHeader(identifier) { - const store = this.getStore(); - const search = { - identifier, - found: false, - }; - const chainStore = store.chains[this.network.toString()]; - const blockheader = (is.num(identifier) - // eslint-disable-next-line no-underscore-dangle - ? chainStore.blockHeaders[chainStore.mappedBlockHeaderHeights[identifier]] - : chainStore.blockHeaders[identifier]); - - if (blockheader) { - search.found = true; - search.result = blockheader; - } - return search; -}; -module.exports = searchBlockHeader; diff --git a/packages/wallet-lib/src/types/Storage/methods/searchBlockHeader.spec.js b/packages/wallet-lib/src/types/Storage/methods/searchBlockHeader.spec.js deleted file mode 100644 index 137b3b2e902..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/searchBlockHeader.spec.js +++ /dev/null @@ -1,53 +0,0 @@ -const {expect} = require('chai'); -const {BlockHeader} = require('@dashevo/dashcore-lib'); -const searchBlockHeader = require('./searchBlockHeader'); - -const existingBlockHeader = new BlockHeader.fromObject({ - hash: '00000ac3a0c9df709260e41290d6902e5a4a073099f11fe8c1ce80aadc4bb331', - version: 2, - prevHash: '00000ce430de949c85a145b02e33ebbaed3772dc8f3d668f66edc6852c24d002', - merkleRoot: '663360403b5fba9cd8744c3706f9660c7d3fee4e5a9ee98ce0ad5e5ad7824c1d', - time: 1398712821, - bits: 504365040, - nonce: 312363 -}); -const notExistingBlockHeader = new BlockHeader.fromObject({ - hash: '00000b526b34e733532d706c1f4cef93eefe707b87c2c3cb2978e1a84b97c501', - version: 2, - prevHash: '00000ac3a0c9df709260e41290d6902e5a4a073099f11fe8c1ce80aadc4bb331', - merkleRoot: 'c2ed22a3e6712b842359dfbb6f0a133ae122ffb601e4cf60e30b8c99f9438f4f', - time: 1398712821, - bits: 504365040, - nonce: 8325 -}) -describe('Storage - searchBlockHeader', function suite() { - this.timeout(10000); - it('should find a transaction', () => { - const self = { - network: 'testnet', - store: { - chains:{ - 'testnet':{ - blockHeaders: { - '00000ac3a0c9df709260e41290d6902e5a4a073099f11fe8c1ce80aadc4bb331': existingBlockHeader, - }, - } - }, - }, - }; - - self.getStore = () => self.store; - - const existingBlockHash = existingBlockHeader.hash; - const notExistingBlockHash = notExistingBlockHeader.hash; - const search = searchBlockHeader.call(self, existingBlockHash); - - expect(search.found).to.be.equal(true); - expect(search.identifier).to.be.equal(existingBlockHash); - expect(search.result).to.be.deep.equal(existingBlockHeader); - - const search2 = searchBlockHeader.call(self, notExistingBlockHash); - expect(search2.found).to.be.equal(false); - expect(search2.identifier).to.be.equal(notExistingBlockHash); - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/searchTransaction.js b/packages/wallet-lib/src/types/Storage/methods/searchTransaction.js deleted file mode 100644 index 1bd8b729b08..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/searchTransaction.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Search a specific txid in the store - * @param {string} hash - * @return {TransactionSearchResult} - */ -const searchTransaction = function searchTransaction(hash) { - const search = { - hash, - found: false, - }; - const store = this.getStore(); - - if (store.transactions[hash]) { - const tx = store.transactions[hash]; - if (tx.hash === hash) { - search.found = true; - search.result = tx; - } - } - return search; -}; -module.exports = searchTransaction; diff --git a/packages/wallet-lib/src/types/Storage/methods/searchTransaction.spec.js b/packages/wallet-lib/src/types/Storage/methods/searchTransaction.spec.js deleted file mode 100644 index 831a804be5b..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/searchTransaction.spec.js +++ /dev/null @@ -1,31 +0,0 @@ -const { expect } = require('chai'); -const searchTransaction = require('./searchTransaction'); -const transactionsFixtures = require('../../../../fixtures/transactions'); - -const { faa430b0fe84a074d981e6fa3995a13363478415ca029a12f6432bf3d90dfa60, fd7c727155ef67fd5c1d54b73dea869e9690c439570063d6e96fec1d3bba450e } = transactionsFixtures.valid.mainnet; -describe('Storage - searchTransaction', function suite() { - this.timeout(10000); - it('should find a transaction', () => { - const self = { - store: { - transactions: { - faa430b0fe84a074d981e6fa3995a13363478415ca029a12f6432bf3d90dfa60, - }, - }, - }; - - self.getStore = () => self.store; - - const existingTxID = faa430b0fe84a074d981e6fa3995a13363478415ca029a12f6432bf3d90dfa60.hash; - const notExistingTxID = fd7c727155ef67fd5c1d54b73dea869e9690c439570063d6e96fec1d3bba450e.hash; - const search = searchTransaction.call(self, existingTxID); - - expect(search.found).to.be.equal(true); - expect(search.hash).to.be.equal(existingTxID); - expect(search.result).to.be.equal(faa430b0fe84a074d981e6fa3995a13363478415ca029a12f6432bf3d90dfa60); - - const search2 = searchTransaction.call(self, notExistingTxID); - expect(search2.found).to.be.equal(false); - expect(search2.hash).to.be.equal(notExistingTxID); - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/searchTransactionMetadata.js b/packages/wallet-lib/src/types/Storage/methods/searchTransactionMetadata.js deleted file mode 100644 index eae1b9627b6..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/searchTransactionMetadata.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Search a specific txid's metadata in the store - * @param {string} hash - * @return {TransactionMetadataSearchResult} - */ -const searchTransactionMetadata = function searchTransactionMetadata(hash) { - const search = { - hash, - found: false, - }; - const store = this.getStore(); - - if (store.transactionsMetadata[hash]) { - const txMetadata = store.transactionsMetadata[hash]; - if (txMetadata.hash === hash) { - search.found = true; - search.result = txMetadata; - } - } - return search; -}; -module.exports = searchTransactionMetadata; diff --git a/packages/wallet-lib/src/types/Storage/methods/searchTransactionMetadata.spec.js b/packages/wallet-lib/src/types/Storage/methods/searchTransactionMetadata.spec.js deleted file mode 100644 index 6cc013b5a0d..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/searchTransactionMetadata.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -const { expect } = require('chai'); -const searchTransactionMetadata = require('./searchTransactionMetadata'); -const transactionsFixtures = require('../../../../fixtures/transactions'); - -const validTxId = '1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6'; -const validTx = transactionsFixtures.valid.testnet[validTxId]; -const validMetadata = transactionsFixtures.valid.testnet.metadata[validTxId]; -describe('Storage - searchTransactionMetadata', function suite() { - this.timeout(10000); - it('should find a transaction metadata', () => { - const self = { - store: { - transactions: { - "1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6": validTx, - }, - transactionsMetadata: { - "1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6": {hash:"1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6",...validMetadata} - } - }, - }; - - self.getStore = () => self.store; - - const existingTxID = "1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6"; - const notExistingTxID = "fd7c727155ef67fd5c1d54b73dea869e9690c439570063d6e96fec1d3bba450e"; - const search = searchTransactionMetadata.call(self, existingTxID); - - expect(search.found).to.be.equal(true); - expect(search.hash).to.be.equal(existingTxID); - const expectedResult = {hash: "1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6", ...validMetadata}; - expect(search.result).to.be.deep.equal(expectedResult); - - const search2 = searchTransactionMetadata.call(self, notExistingTxID); - expect(search2.found).to.be.equal(false); - expect(search2.hash).to.be.equal(notExistingTxID); - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/searchWallet.js b/packages/wallet-lib/src/types/Storage/methods/searchWallet.js deleted file mode 100644 index 9719d057b92..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/searchWallet.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Search a wallet in store based from it's walletId - * @param {string} walletId - * @return {WalletSearchResult} - */ -const searchWallet = function searchWallet(walletId) { - const search = { - walletId, - found: false, - }; - const store = this.getStore(); - if (store.wallets[walletId]) { - search.found = true; - search.result = store.wallets[walletId]; - } - return search; -}; -module.exports = searchWallet; diff --git a/packages/wallet-lib/src/types/Storage/methods/searchWallet.spec.js b/packages/wallet-lib/src/types/Storage/methods/searchWallet.spec.js deleted file mode 100644 index 4f40be20c63..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/searchWallet.spec.js +++ /dev/null @@ -1,33 +0,0 @@ -const { expect } = require('chai'); -const Dashcore = require('@dashevo/dashcore-lib'); -const searchWallet = require('./searchWallet'); - -describe('Storage - searchWallet', function suite() { - this.timeout(10000); - it('should find a wallet', () => { - const self = { - store: { - wallets: { - '123ae': { - accounts: {}, - network: Dashcore.Networks.testnet, - mnemonic: null, - type: null, - blockHeight: 0, - addresses: { external: {}, internal: {}, misc: {} }, - }, - }, - chains: { testnet: { name: 'testnet', blockHeight: -1 } }, - }, - }; - self.getStore = () => self.store; - - const existingWalletid = '123ae'; - const search = searchWallet.call(self, existingWalletid); - - expect(search.found).to.be.equal(true); - expect(search.walletId).to.be.equal(existingWalletid); - expect(search.result.accounts).to.be.deep.equal({}); - expect(search.result.addresses).to.be.deep.equal({ external: {}, internal: {}, misc: {} }); - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/startWorker.spec.js b/packages/wallet-lib/src/types/Storage/methods/startWorker.spec.js deleted file mode 100644 index 0711f40946e..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/startWorker.spec.js +++ /dev/null @@ -1,53 +0,0 @@ -const { expect } = require('chai'); -const startWorker = require('./startWorker'); - -let testInterval = null; -const simulateChangeEvery = function (ms) { - const self = this; - testInterval = setInterval(() => { - self.lastModified = Date.now(); - }, ms); -}; -describe('Storage - startWorker', function suite() { - this.timeout(60000); - it('should set an interval', function () { - const defaultIntervalValue = 10000; - const self = { - autosaveIntervalTime: defaultIntervalValue, - }; - startWorker.call(self); - if (process.browser){ - this.skip('doesn\'t work in browser') - // Need to clear to not hang-on forever - clearInterval(self.interval); - return; - } - expect(self.interval.constructor.name).to.be.equal('Timeout'); - expect(self.interval._repeat).to.be.equal(defaultIntervalValue); // Timeout are null btw - clearInterval(self.interval); - }); - it('should work', async () => new Promise((res) => { - let saved = 0; - const self = { - saveState: () => { - saved += 1; - self.lastSave = Date.now(); - }, - autosaveIntervalTime: 500, - lastModified: Date.now(), - lastSave: 0, - }; - startWorker.call(self); - simulateChangeEvery.call(self, 200); - - setTimeout(() => { - clearInterval(self.interval); - testInterval = clearInterval(testInterval); - - expect(saved < 11).to.be.equal(true); - // First autosave + 9 induced changes - // However it can be less as we do not hard force the place in the event loop (simple setInterval) - res(expect(saved >= 8).to.be.equal(true)); - }, 5499); - })); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/updateAddress.js b/packages/wallet-lib/src/types/Storage/methods/updateAddress.js deleted file mode 100644 index 5bff96feaa5..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/updateAddress.js +++ /dev/null @@ -1,123 +0,0 @@ -const { cloneDeep, xor } = require('lodash'); -const { InvalidAddressObject, TransactionNotInStore } = require('../../../errors'); -const { is } = require('../../../utils'); -const EVENTS = require('../../../EVENTS'); - -/** -* Update a specific address information in the store -* @param {AddressObj} addressObj -* @param {string} walletId -* @return {boolean} -*/ -const updateAddress = function updateAddress(addressObj, walletId) { - if (!walletId) throw new Error('Expected walletId to update an address'); - if (!is.addressObj(addressObj)) throw new InvalidAddressObject(addressObj); - const { path } = addressObj; - if (!path) throw new Error('Expected path to update an address'); - const accountIndex = parseInt(path.split('/')[3], 10); - - const typeInt = path.split('/')[4]; - let type; - switch (typeInt) { - case '0': - type = 'external'; - break; - case '1': - type = 'internal'; - break; - default: - type = 'misc'; - } - const walletStore = this.store.wallets[walletId]; - const addressesStore = walletStore.addresses; - const previousObject = cloneDeep(addressesStore[type][path]); - - const newObject = cloneDeep(addressObj); - // We do not autorize to alter UTXO using this - // if(newObject.utxos.length==0 && previousObject.utxos.length>0){ - // - // } - // const currentBlockHeight = this.store.chains[walletStore.network].blockHeight; - - // We calculate here the balanceSat and unconfirmedBalanceSat of our addressObj - // We do that to avoid getBalance to be slow, so we have to keep that in mind or then - // Move that to an event type of calculation or somth - const { utxos } = newObject; - - const newObjectUtxosKeys = Object.keys(utxos); - if (newObjectUtxosKeys.length > 0) { - // we compare the diff between the two utxos sets - - const previousUTXOS = (previousObject !== undefined) ? previousObject.utxos : []; - - const newUtxos = xor(newObjectUtxosKeys, Object.keys(previousUTXOS)); - // Then we verify the outputs - newUtxos.forEach((utxoKey) => { - const [txid, outputIndex] = utxoKey.split('-'); - const utxo = utxos[utxoKey]; - utxo.txId = txid; - utxo.outputIndex = parseInt(outputIndex, 10); - - try { - this.getTransaction(txid); - // TODO : We removed here the confirmations verification - // We should ensure we had a locked block before being able to really spend those. - newObject.balanceSat += utxo.satoshis; - } catch (e) { - if (!(e instanceof TransactionNotInStore)) throw e; - } - }); - } - - // Check if there is a balance but no utxo. - addressesStore[type][path] = newObject; - if (previousObject === undefined) { - if (newObject.balanceSat > 0) { - this.announce( - EVENTS.CONFIRMED_BALANCE_CHANGED, - { - delta: newObject.balanceSat, - currentValue: this.calculateDuffBalance(walletId, accountIndex, 'confirmed') || newObject.unconfirmedBalanceSat, - // currentValue: newObject.balanceSat, - }, - ); - } - if (newObject.unconfirmedBalanceSat > 0) { - this.announce( - EVENTS.UNCONFIRMED_BALANCE_CHANGED, - { - delta: newObject.unconfirmedBalanceSat, - // currentValue: newObject.unconfirmedBalanceSat, - currentValue: this.calculateDuffBalance(walletId, accountIndex, 'unconfirmed'), - }, - ); - } - } else { - if (previousObject.balanceSat !== newObject.balanceSat) { - this.announce( - EVENTS.CONFIRMED_BALANCE_CHANGED, - { - delta: newObject.balanceSat - previousObject.balanceSat, - // currentValue: newObject.balanceSat, - currentValue: this.calculateDuffBalance(walletId, accountIndex, 'confirmed'), - }, - ); - } - if (previousObject.unconfirmedBalanceSat !== newObject.unconfirmedBalanceSat) { - this.announce(EVENTS.UNCONFIRMED_BALANCE_CHANGED, - { - delta: newObject.unconfirmedBalanceSat - previousObject.unconfirmedBalanceSat, - // currentValue: newObject.unconfirmedBalanceSat, - currentValue: this.calculateDuffBalance(walletId, accountIndex, 'unconfirmed'), - }); - } - } - - this.lastModified = Date.now(); - - if (!this.mappedAddress[newObject.address]) { - this.mappedAddress[newObject.address] = { walletId, type, path }; - } - return true; -}; -module.exports = updateAddress; diff --git a/packages/wallet-lib/src/types/Storage/methods/updateAddress.spec.js b/packages/wallet-lib/src/types/Storage/methods/updateAddress.spec.js deleted file mode 100644 index 0de48ab1e3d..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/updateAddress.spec.js +++ /dev/null @@ -1,35 +0,0 @@ -const { expect } = require('chai'); -const updateAddress = require('./updateAddress'); -const orangeWStore = require('../../../../fixtures/walletStore').valid.orange.store; - -describe('Storage - updateAddress', function suite() { - this.timeout(10000); - it('should throw errors on failed update', () => { - const self = {}; - expect(() => updateAddress.call(self)).to.throw('Expected walletId to update an address'); - - expect(() => updateAddress.call(self, {}, '123ae')).to.throw('Address should have property path of type string'); - }); - it('should update an address', () => { - const self = { store: orangeWStore, mappedAddress: {} }; - const validAddrObj = { - path: "m/44'/1'/0'/0/0", - index: '0', - address: 'yLhsYLXW5sFHLDPLj2EHgrmQRhP712ANda', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: true, - }; - const validWalletId = Object.keys(orangeWStore.wallets)[0]; - updateAddress.call(self, validAddrObj, validWalletId); - const expectedMappedAddress = { yLhsYLXW5sFHLDPLj2EHgrmQRhP712ANda: { walletId: 'a3771aaf93', type: 'external', path: "m/44'/1'/0'/0/0" } }; - const expectedUpdatedAddress = { - path: "m/44'/1'/0'/0/0", index: '0', address: 'yLhsYLXW5sFHLDPLj2EHgrmQRhP712ANda', transactions: [], balanceSat: 0, unconfirmedBalanceSat: 0, utxos: {}, fetchedLast: 0, used: true, - }; - expect(self.mappedAddress).to.be.deep.equal(expectedMappedAddress); - expect(self.store.wallets.a3771aaf93.addresses.external["m/44'/1'/0'/0/0"]).to.deep.equal(expectedUpdatedAddress); - }); -}); diff --git a/packages/wallet-lib/src/types/Storage/methods/updateTransaction.js b/packages/wallet-lib/src/types/Storage/methods/updateTransaction.js deleted file mode 100644 index 18c3595050a..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/updateTransaction.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Update a specific transaction information in the store - * It do not handle any merging right now and write over previous data. - * @param {Transaction} transaction - * @return {boolean} - */ -const updateTransaction = function updateTransaction(transaction) { - if (!transaction) throw new Error('Expected a transaction to update'); - - const transactionStore = this.store.transactions; - const storeTx = transactionStore[transaction.hash]; - if (JSON.stringify(storeTx) !== JSON.stringify(transaction)) { - transactionStore[transaction.hash] = transaction; - this.lastModified = Date.now(); - } - return true; -}; -module.exports = updateTransaction; diff --git a/packages/wallet-lib/src/types/Storage/methods/updateTransaction.spec.js b/packages/wallet-lib/src/types/Storage/methods/updateTransaction.spec.js deleted file mode 100644 index 04c359171fc..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/updateTransaction.spec.js +++ /dev/null @@ -1,70 +0,0 @@ -const { expect } = require('chai'); -const updateTransaction = require('./updateTransaction'); -const orangeWStore = require('../../../../fixtures/walletStore').valid.orange.store; -const { Transaction } = require('@dashevo/dashcore-lib'); - -const tx = new Transaction({ - hash: 'ea9c4066394aa09cb7ee8f3997b8dc10b999a8d709c4046f81d8bf9341ae6e5b', - version: 3, - inputs: [ - { - prevTxId: '9f398515b6fc898ebf4e7b49bbfc4359b8c89f508c6cd677e53946bd86064b28', - outputIndex: 0, - sequenceNumber: 4294967295, - script: '47304402205bb4f7880fb0fc13218940ba341c30e817363e5590343d28639af921b2a5f1d40220010920ae4b00bbb657f8653cb44172b8cb13447bb5105ddaf32a2845ea0666b90121025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794b', - scriptString: '71 0x304402205bb4f7880fb0fc13218940ba341c30e817363e5590343d28639af921b2a5f1d40220010920ae4b00bbb657f8653cb44172b8cb13447bb5105ddaf32a2845ea0666b901 33 0x025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794b', - }, - { - prevTxId: 'b812d9345fa8ea06af1d19b935eec65824d53779db74cd325690ad1d38a82757', - outputIndex: 0, - sequenceNumber: 4294967295, - script: '483045022100ea2d17ffc417e1f70c9c9ae11b7d95a07ab359c1d9d634baba145bab7b1deb0802207507296e12acc83ce038e5bbd54c46fa78b9475536f64fb313fedb978d12b73b0121025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794b', - scriptString: '72 0x3045022100ea2d17ffc417e1f70c9c9ae11b7d95a07ab359c1d9d634baba145bab7b1deb0802207507296e12acc83ce038e5bbd54c46fa78b9475536f64fb313fedb978d12b73b01 33 0x025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794b', - }, - { - prevTxId: '370b7bbd5b6e0de42a95d59e3277041ac20e945ffb93f56bb6984ba42f28a2ac', - outputIndex: 0, - sequenceNumber: 4294967295, - script: '47304402207926bf9176bdc88f38dde2140b2b8b0e4f331f33bb48af12c1bcce5efbb2593c022073c188d2149d5a0bfe4adff82b63d0bc62e04f2769cdcfda50a2c5e34ab7cbf60121025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794b', - scriptString: '71 0x304402207926bf9176bdc88f38dde2140b2b8b0e4f331f33bb48af12c1bcce5efbb2593c022073c188d2149d5a0bfe4adff82b63d0bc62e04f2769cdcfda50a2c5e34ab7cbf601 33 0x025ae98eff89505fa5ff60f919ae690de638d31f4f2fcab9a9deeaf4d48eda794b', - }, - ], - outputs: [ - { - satoshis: 12999997493, - script: '76a9143ec33076ba72b36b66b7ec571dd7417abdeb76f888ac', - }, - ], -}); -describe('Storage - updateTransaction',function suite() { - this.timeout(10000); - it('should throw on failed update', () => { - const exceptedException1 = 'Expected a transaction to update'; - const self = { - store: { - transactions: {}, - }, - }; - - expect(() => updateTransaction.call(self, null)).to.throw(exceptedException1); - }); - it('should work', () => { - const self = { - store: { - transactions: { - ea9c4066394aa09cb7ee8f3997b8dc10b999a8d709c4046f81d8bf9341ae6e5b: tx, - }, - }, - }; - const txObj = new Transaction(tx); - txObj.nLockTime = 0; - const expected = { - ea9c4066394aa09cb7ee8f3997b8dc10b999a8d709c4046f81d8bf9341ae6e5b: txObj, - }; - - - const update = updateTransaction.call(self, txObj); - expect(update).to.equal(true); - expect(self.store.transactions).to.deep.equal(expected); - }); -}); From 4490aa52452d147c1411b041eb637179735771db Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Mon, 31 Jan 2022 03:57:31 +0100 Subject: [PATCH 06/27] feat: remove unused KeyChain --- .../src/types/KeyChain/KeyChain.d.ts | 48 ------- .../wallet-lib/src/types/KeyChain/KeyChain.js | 52 -------- .../src/types/KeyChain/KeyChain.spec.js | 125 ------------------ .../KeyChain/methods/generateKeyForChild.js | 19 --- .../KeyChain/methods/generateKeyForPath.js | 19 --- .../KeyChain/methods/getDIP15ExtendedKey.js | 26 ---- .../KeyChain/methods/getHardenedBIP44HDKey.js | 12 -- .../methods/getHardenedDIP15AccountKey.js | 14 -- .../methods/getHardenedDIP9FeatureHDKey.js | 12 -- .../types/KeyChain/methods/getKeyForChild.js | 10 -- .../types/KeyChain/methods/getKeyForPath.js | 29 ---- .../types/KeyChain/methods/getPrivateKey.js | 18 --- .../src/types/KeyChain/methods/sign.js | 29 ---- 13 files changed, 413 deletions(-) delete mode 100644 packages/wallet-lib/src/types/KeyChain/KeyChain.d.ts delete mode 100644 packages/wallet-lib/src/types/KeyChain/KeyChain.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/KeyChain.spec.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/generateKeyForChild.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/generateKeyForPath.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getDIP15ExtendedKey.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getHardenedBIP44HDKey.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP15AccountKey.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP9FeatureHDKey.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getKeyForChild.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getKeyForPath.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getPrivateKey.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/sign.js diff --git a/packages/wallet-lib/src/types/KeyChain/KeyChain.d.ts b/packages/wallet-lib/src/types/KeyChain/KeyChain.d.ts deleted file mode 100644 index 0d774d60b6f..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/KeyChain.d.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {PrivateKey, Network,} from "../types"; -import {HDPrivateKey, HDPublicKey} from "@dashevo/dashcore-lib"; -import {Transaction} from "@dashevo/dashcore-lib/typings/transaction/Transaction"; - -export declare namespace KeyChain { - interface IKeyChainOptions { - network?: Network; - keys?: [Keys] - } -} - -export declare class KeyChain { - constructor(options?: KeyChain.IKeyChainOptions); - network: Network; - keys: [Keys]; - - type: HDKeyTypesParam|PrivateKeyTypeParam; - HDPrivateKey?: HDPrivateKey; - privateKey?: PrivateKey; - - generateKeyForChild(index: number, type?: HDKeyTypesParam): HDPrivateKey|HDPublicKey; - generateKeyForPath(path: string, type?: HDKeyTypesParam): HDPrivateKey|HDPublicKey; - - getDIP15ExtendedKey(userUniqueId: string, contactUniqueId: string, index?: number, accountIndex?: number, type?: HDKeyTypesParam): HDKeyTypes; - getHardenedDIP15AccountKey(index?: number, type?: HDKeyTypesParam): HDKeyTypes; - getHardenedBIP44HDKey(type?: HDKeyTypesParam): HDKeyTypes; - getHardenedDIP9FeatureHDKey(type?: HDKeyTypesParam): HDKeyTypes; - getKeyForChild(index: number, type?: HDKeyTypesParam): HDKeyTypes; - getKeyForPath(path: string, type?: HDKeyTypesParam): HDKeyTypes; - getPrivateKey(): PrivateKey; - - sign(object: Transaction|any, privateKeys:[PrivateKey], sigType: number): any; -} - -type HDKeyTypes = HDPublicKey | HDPrivateKey; - -export declare enum HDKeyTypesParam { - HDPrivateKey="HDPrivateKey", - HDPublicKey="HDPrivateKey", -} -export declare enum PrivateKeyTypeParam { - privateKey='privateKey' -} -export declare interface Keys { - [path: string]: { - path: string - }; -} diff --git a/packages/wallet-lib/src/types/KeyChain/KeyChain.js b/packages/wallet-lib/src/types/KeyChain/KeyChain.js deleted file mode 100644 index 643dcc62737..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/KeyChain.js +++ /dev/null @@ -1,52 +0,0 @@ -const { Networks, HDPrivateKey, HDPublicKey } = require('@dashevo/dashcore-lib'); -const { has } = require('lodash'); - -// eslint-disable-next-line no-underscore-dangle -const _defaultOpts = { - network: Networks.testnet.toString(), - keys: {}, -}; - -class KeyChain { - constructor(opts = JSON.parse(JSON.stringify(_defaultOpts))) { - const defaultOpts = JSON.parse(JSON.stringify(_defaultOpts)); - this.network = defaultOpts.network; - this.keys = { ...defaultOpts.keys }; - - if (has(opts, 'HDPrivateKey')) { - this.type = 'HDPrivateKey'; - this.HDPrivateKey = (typeof opts.HDPrivateKey === 'string') ? HDPrivateKey(opts.HDPrivateKey) : opts.HDPrivateKey; - this.network = this.HDPrivateKey.network; - } else if (has(opts, 'HDPublicKey')) { - this.type = 'HDPublicKey'; - this.HDPublicKey = (typeof opts.HDPublicKey === 'string') ? HDPublicKey(opts.HDPublicKey) : opts.HDPublicKey; - this.network = this.HDPublicKey.network; - } else if (has(opts, 'privateKey')) { - this.type = 'privateKey'; - this.privateKey = opts.privateKey; - } else if (has(opts, 'publicKey')) { - this.type = 'publicKey'; - this.publicKey = opts.publicKey; - } else if (has(opts, 'address')) { - this.type = 'address'; - this.address = opts.address.toString(); - } else { - throw new Error('Expect privateKey, publicKey, HDPublicKey, HDPrivateKey or Address'); - } - if (opts.network) this.network = opts.network; - if (opts.keys) this.keys = { ...opts.keys }; - } -} - -KeyChain.prototype.generateKeyForChild = require('./methods/generateKeyForChild'); -KeyChain.prototype.generateKeyForPath = require('./methods/generateKeyForPath'); -KeyChain.prototype.getDIP15ExtendedKey = require('./methods/getDIP15ExtendedKey'); -KeyChain.prototype.getHardenedBIP44HDKey = require('./methods/getHardenedBIP44HDKey'); -KeyChain.prototype.getHardenedDIP9FeatureHDKey = require('./methods/getHardenedDIP9FeatureHDKey'); -KeyChain.prototype.getHardenedDIP15AccountKey = require('./methods/getHardenedDIP15AccountKey'); -KeyChain.prototype.getKeyForChild = require('./methods/getKeyForChild'); -KeyChain.prototype.getKeyForPath = require('./methods/getKeyForPath'); -KeyChain.prototype.getPrivateKey = require('./methods/getPrivateKey'); -KeyChain.prototype.sign = require('./methods/sign'); - -module.exports = KeyChain; diff --git a/packages/wallet-lib/src/types/KeyChain/KeyChain.spec.js b/packages/wallet-lib/src/types/KeyChain/KeyChain.spec.js deleted file mode 100644 index 3cf9bb0b5a1..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/KeyChain.spec.js +++ /dev/null @@ -1,125 +0,0 @@ -const Dashcore = require('@dashevo/dashcore-lib'); -const { expect } = require('chai'); -const KeyChain = require('./KeyChain'); -const { mnemonicToHDPrivateKey } = require('../../utils/mnemonic'); - -let keychain; -let keychain2; -const mnemonic = 'during develop before curtain hazard rare job language become verb message travel'; -const mnemonic2 = 'birth kingdom trash renew flavor utility donkey gasp regular alert pave layer'; -const pk = '4226d5e2fe8cbfe6f5beb7adf5a5b08b310f6c4a67fc27826779073be6f5699e'; - -const expectedRootDIP15AccountKey_0 = 'tprv8hRzmheQujhJN5XP2dj955nAFCKeEoSifJRWuutdbwWRtusdDQ426jbp75EqErUSuTxmPyxYmP1TpcF5qdxGhXLNXRLMGsRLG6NFCv1WnaQ'; -const expectedRootDIP15AccountKey_1 = 'tprv8hRzmheQujhJQyCtFTuUFHxB3Ag5VLB994zhH4CfxbA41cq73HT2mpYq5M33V54oJyn6g514saxxVJB886G55eYX56J6D6x87UNNT6iQHkR'; -const expectedKeyForChild_0 = 'tprv8d4podc2Tg459CH2bwLHXj3vdJFBT2rdsk5Nr1djH7hzHdt5LRdvN6QyFwMiDy7ffRdik7fEVRKKgsHB4F18sh8xF6jFXpKq4sUgGBoSbKw'; -describe('Keychain', function suite() { - this.timeout(10000); - it('should create a keychain', () => { - const expectedException1 = 'Expect privateKey, publicKey, HDPublicKey, HDPrivateKey or Address'; - expect(() => new KeyChain()).to.throw(expectedException1); - expect(() => new KeyChain(mnemonic)).to.throw(expectedException1); - - keychain = new KeyChain({ HDPrivateKey: mnemonicToHDPrivateKey(mnemonic, 'testnet') }); - expect(keychain.type).to.equal('HDPrivateKey'); - expect(keychain.network.toString()).to.equal('testnet'); - expect(keychain.keys).to.deep.equal({}); - - keychain2 = new KeyChain({ HDPrivateKey: mnemonicToHDPrivateKey(mnemonic2, 'mainnet') }); - }); - it('should get private key', () => { - expect(keychain.getPrivateKey().toString()).to.equal(pk); - }); - it('should generate key for full path', () => { - const path = 'm/44\'/1\'/0\'/0/0'; - const pk2 = keychain.getKeyForPath(path); - const address = new Dashcore.Address(pk2.publicKey.toAddress()).toString(); - expect(address).to.equal('yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT'); - }); - it('should get hardened feature path', () => { - const hardenedPk = keychain.getHardenedBIP44HDKey(); - const pk2 = keychain.getKeyForPath('m/44\'/1\''); - expect(pk2.toString()).to.equal(hardenedPk.toString()); - }); - it('should get DIP15 account key', function () { - const rootDIP15AccountKey_0 = keychain.getHardenedDIP15AccountKey(0); - expect(rootDIP15AccountKey_0.toString()).to.deep.equal(expectedRootDIP15AccountKey_0); - const rootDIP15AccountKey_1 = keychain.getHardenedDIP15AccountKey(1); - expect(rootDIP15AccountKey_1.toString()).to.deep.equal(expectedRootDIP15AccountKey_1); - }); - it('should get DIP15 extended key', function () { - const userUniqueId = '0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'; - const contactUniqueId = '0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'; - - // m/9'/5'/15'/0'/0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'/0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'/0 - const DIP15ExtPubKey_0 = keychain2.getDIP15ExtendedKey(userUniqueId, contactUniqueId, 0, 0, type='HDPublicKey'); - expect(DIP15ExtPubKey_0.toString()).to.equal('xpub6LTkTQFSb8KMgMSz4B6sMZLpkQAY6wSTDprDkHDmLwWLpnjxazuxZn13FrSLKUafitsxuaaffM5a49P6aswhpppWUuYW6eFnwBXshR2W2eY'); - expect(DIP15ExtPubKey_0.publicKey.toString()).to.equal('038030c88ab0106e1f4af3b939db2bafc56f892554106f08da1ce1f9ef10f807bd') - - const DIP15ExtPrivKey_0 = keychain2.getDIP15ExtendedKey(userUniqueId, contactUniqueId, 0, 0); - expect(DIP15ExtPrivKey_0.toString()).to.equal('xprvA7UQ3tiYkkm4TsNWx9ZrzRQ6CNL3hUibrbvcwtp9nbyMwzQp3Tbi1ygZQaPoigDhCf8XUjMmGK2NbnB2kLXPYg99Lp6e3iki318sdWcFN3q'); - expect(DIP15ExtPrivKey_0.privateKey.toString()).to.equal('fac40790776d171ee1db90899b5eb2df2f7d2aaf35ad56f07ffb8ed2c57f8e60') - expect(DIP15ExtPrivKey_0.publicKey.toString()).to.equal('038030c88ab0106e1f4af3b939db2bafc56f892554106f08da1ce1f9ef10f807bd') - - const userAhash = "0xa11ce14f698b32e9bb306dba7bbbee831263dcf658abeebb39930460ead117e5"; - const userBhash = "0xb0b052ff075c5ca3c16c3e20e9ac8223834475cc1324ab07889cb24ce6a62793"; - const DIP15ExtKey_1 = keychain.getDIP15ExtendedKey(userAhash, userBhash, 0, 0); - expect(DIP15ExtKey_1.privateKey.toString()).to.equal('60581b6dca8244d3fb3cfe619b5a22277e5423b01e5285f356981f247e0f4a60') - expect(DIP15ExtKey_1.publicKey.toString()).to.equal('03deaac00f721151307fbc7bf80d7b8afab98c1f026d67e5f56b21e2013f551ce6') - - }); - it('should derive from hardened feature path', () => { - const hardenedHDKey = keychain.getHardenedBIP44HDKey(); - const pk2 = keychain.getKeyForPath(`m/44'/1'`); - expect(pk2.toString()).to.equal(hardenedHDKey.toString()); - expect(hardenedHDKey.toString()).to.deep.equal('tprv8dtrJNytYHRiZY585hmHGbguS6VjGpK49puSB7oXZjLHcQfrAzQkF4ZCxM2DkEbyY85J4EYcZ8EjT5ZCU8ozB727TDdodbfXet5GkGau2RQ'); - const derivedPk = hardenedHDKey.deriveChild(0, true).deriveChild(0).deriveChild(0); - const address = new Dashcore.Address(derivedPk.publicKey.toAddress()).toString(); - expect(address).to.equal('yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT'); - }); - it('should get hardened DIP9FeatureHDKey', function () { - const hardenedHDKey = keychain.getHardenedDIP9FeatureHDKey(); - const pk2 = keychain.getKeyForPath(`m/9'/1'`); - expect(pk2.toString()).to.equal(hardenedHDKey.toString()); - expect(hardenedHDKey.toString()).to.deep.equal('tprv8fBJjWoGgCpGRCbyzE9RUA59rmoN1RUijhLnXGL4VHnLxvSe523yVg4GrGzbR6TyXtdynAEh5z8UX55EXt2Cb3xjvrsx2PgTY9BHxzFVkWn'); - }); - it('should generate key for child', () => { - const keychain2 = new KeyChain({ HDPrivateKey: mnemonicToHDPrivateKey(mnemonic, 'testnet') }); - const keyForChild = keychain2.generateKeyForChild(0); - expect(keyForChild.toString()).to.equal(expectedKeyForChild_0); - }); - - - it('should sign', () => { - - }); -}); -describe('Keychain - clone', function suite() { - this.timeout(10000); - it('should clone', () => { - const keychain2 = new KeyChain(keychain); - expect(keychain2).to.deep.equal(keychain); - expect(keychain2.keys).to.deep.equal(keychain.keys); - }); -}); -describe('Keychain - single privateKey', function suite() { - this.timeout(10000); - it('should correctly errors out when not a HDPublicKey (privateKey)', () => { - const privateKey = Dashcore.PrivateKey().toString(); - const network = 'livenet'; - const pkKeyChain = new KeyChain({ privateKey, network }); - expect(pkKeyChain.network).to.equal(network); - expect(pkKeyChain.keys).to.deep.equal({}); - expect(pkKeyChain.type).to.equal('privateKey'); - expect(pkKeyChain.privateKey).to.equal(privateKey); - - const expectedException1 = 'Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate keys'; - const expectedException2 = 'Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate child'; - expect(() => pkKeyChain.generateKeyForPath()).to.throw(expectedException1); - expect(() => pkKeyChain.generateKeyForChild()).to.throw(expectedException2); - }); - it('should get private key', () => { - const privateKey = Dashcore.PrivateKey().toString(); - const pkKeyChain = new KeyChain({ privateKey, network: 'livenet' }); - expect(pkKeyChain.getPrivateKey().toString()).to.equal(privateKey); - }); -}); diff --git a/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForChild.js b/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForChild.js deleted file mode 100644 index 5d6dcaeba0f..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForChild.js +++ /dev/null @@ -1,19 +0,0 @@ -const { - HDPublicKey, -} = require('@dashevo/dashcore-lib'); -/** - * Derive from HDPrivateKey to a child - * @param {number} index - Child index to derivee to - * @param {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys - * @return {HDPrivateKey|HDPublicKey} - */ -function generateKeyForChild(index, type = 'HDPrivateKey') { - if (!['HDPrivateKey', 'HDPublicKey'].includes(this.type)) { - throw new Error('Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate child'); - } - const HDKey = this[this.type]; - const hdPublicKey = HDKey.deriveChild(index); - if (type === 'HDPublicKey') return HDPublicKey(hdPublicKey); - return hdPublicKey; -} -module.exports = generateKeyForChild; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForPath.js b/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForPath.js deleted file mode 100644 index e266c96d757..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/generateKeyForPath.js +++ /dev/null @@ -1,19 +0,0 @@ -const { - HDPublicKey, -} = require('@dashevo/dashcore-lib'); -/** - * Derive from HDPrivateKey to a specific path - * @param {string} path - * @param {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys - * @return {HDPrivateKey|HDPublicKey} - */ -function generateKeyForPath(path, type = 'HDPrivateKey') { - if (!['HDPrivateKey', 'HDPublicKey'].includes(this.type)) { - throw new Error('Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate keys'); - } - const HDKey = this[this.type]; - const hdPrivateKey = HDKey.derive(path); - if (type === 'HDPublicKey') return HDPublicKey(hdPrivateKey); - return hdPrivateKey; -} -module.exports = generateKeyForPath; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getDIP15ExtendedKey.js b/packages/wallet-lib/src/types/KeyChain/methods/getDIP15ExtendedKey.js deleted file mode 100644 index a7eed1547dc..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/getDIP15ExtendedKey.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Return the extended key of the relationship between two dashpay contacts. - * @param userUniqueId - Current userID - * @param contactUniqueId - Contact userID - * @param index - the key index. - * @param accountIndex[=0] - the internal wallet account from which derivation is done - * @param type {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys - * @return {HDPrivateKey|HDPublicKey} - */ -function getDIP15ExtendedKey(userUniqueId, contactUniqueId, index = 0, accountIndex = 0, type = 'HDPrivateKey') { - if (!['HDPrivateKey', 'HDPublicKey'].includes(this.type)) { - throw new Error('Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate keys'); - } - if (!userUniqueId || !contactUniqueId) throw new Error('Required userUniqueId and contactUniqueId to be defined'); - - // Require a HDPrivateKey for hardened derivation - const extendedPrivateKey = this - .getHardenedDIP15AccountKey(accountIndex, 'HDPrivateKey') - .deriveChild((userUniqueId), true) - .deriveChild((contactUniqueId), true) - .deriveChild(index, false); - - return (type === 'HDPublicKey' ? extendedPrivateKey.hdPublicKey : extendedPrivateKey); -} - -module.exports = getDIP15ExtendedKey; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedBIP44HDKey.js b/packages/wallet-lib/src/types/KeyChain/methods/getHardenedBIP44HDKey.js deleted file mode 100644 index 75a6628475f..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedBIP44HDKey.js +++ /dev/null @@ -1,12 +0,0 @@ -const { BIP44_TESTNET_ROOT_PATH, BIP44_LIVENET_ROOT_PATH } = require('../../../CONSTANTS'); - -/** - * Return a safier root keys to derivate from - * @param {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys - * @return {HDPrivateKey|HDPublicKey} - */ -function getHardenedBIP44HDKey(type = 'HDPrivateKey') { - const pathRoot = (this.network.toString() === 'testnet') ? BIP44_TESTNET_ROOT_PATH : BIP44_LIVENET_ROOT_PATH; - return this.generateKeyForPath(pathRoot, type); -} -module.exports = getHardenedBIP44HDKey; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP15AccountKey.js b/packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP15AccountKey.js deleted file mode 100644 index c01f240fc85..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP15AccountKey.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Return a safier root path to derivate from - * @param {number} [accountIndex=0] - set the account index - * @param {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys - * @return {HDPrivateKey|HDPublicKey} - */ -function getHardenedDIP15AccountKey(accountIndex = 0, type = 'HDPrivateKey') { - const hardenedFeatureRootKey = this.getHardenedDIP9FeatureHDKey(type); - - // Feature is set to 15' for all DashPay Incoming Funds derivation paths (see DIP15). - const featureKey = hardenedFeatureRootKey.deriveChild(15, true); - return featureKey.deriveChild(accountIndex, true); -} -module.exports = getHardenedDIP15AccountKey; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP9FeatureHDKey.js b/packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP9FeatureHDKey.js deleted file mode 100644 index 06a4733afb0..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP9FeatureHDKey.js +++ /dev/null @@ -1,12 +0,0 @@ -const { DIP9_LIVENET_ROOT_PATH, DIP9_TESTNET_ROOT_PATH } = require('../../../CONSTANTS'); - -/** - * Return a safier root path to derivate from - * @param {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys - * @return {HDPrivateKey|HDPublicKey} - */ -function getHardenedDIP9FeatureHDKey(type = 'HDPrivateKey') { - const pathRoot = (this.network.toString() === 'testnet') ? DIP9_TESTNET_ROOT_PATH : DIP9_LIVENET_ROOT_PATH; - return this.generateKeyForPath(pathRoot, type); -} -module.exports = getHardenedDIP9FeatureHDKey; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getKeyForChild.js b/packages/wallet-lib/src/types/KeyChain/methods/getKeyForChild.js deleted file mode 100644 index 768f1740d55..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/getKeyForChild.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Generate a key by deriving it's direct child - * @param index - {Number} - * @return {HDPrivateKey | HDPublicKey} - */ -function getKeyForChild(index = 0, type = 'HDPrivateKey') { - return this.generateKeyForChild(index, type); -} - -module.exports = getKeyForChild; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getKeyForPath.js b/packages/wallet-lib/src/types/KeyChain/methods/getKeyForPath.js deleted file mode 100644 index 4f7bb180d1d..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/getKeyForPath.js +++ /dev/null @@ -1,29 +0,0 @@ -const { HDPrivateKey, PrivateKey } = require('@dashevo/dashcore-lib'); -/** - * Get a key from the cache or generate if none - * @param path - * @param type - def : HDPrivateKey - Expected return datatype of the keys - * @return {HDPrivateKey | HDPublicKey} - */ -function getKeyForPath(path, type = 'HDPrivateKey') { - if (type === 'HDPublicKey') { - // In this case, we do not generate or keep in cache. - return this.generateKeyForPath(path, type); - } - - if (this.type === 'HDPrivateKey') { - if (!this.keys[path]) { - this.keys[path] = this.generateKeyForPath(path, type).toString(); - } - return new HDPrivateKey(this.keys[path]); - } - if (this.type === 'privateKey') { - if (!this.keys[path]) { - this.keys[path] = this.getPrivateKey(path).toString(); - return new PrivateKey(this.keys[path]); - } - return new PrivateKey(this.keys[path]); - } - return new HDPrivateKey(this.keys[path]); -} -module.exports = getKeyForPath; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getPrivateKey.js b/packages/wallet-lib/src/types/KeyChain/methods/getPrivateKey.js deleted file mode 100644 index 602c287daf7..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/getPrivateKey.js +++ /dev/null @@ -1,18 +0,0 @@ -const { - PrivateKey, -} = require('@dashevo/dashcore-lib'); - -/** - * @return {PrivateKey} - */ -function getPrivateKey() { - let pk; - if (this.type === 'HDPrivateKey') { - pk = PrivateKey(this.HDPrivateKey.privateKey); - } - if (this.type === 'privateKey') { - pk = PrivateKey(this.privateKey); - } - return pk; -} -module.exports = getPrivateKey; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/sign.js b/packages/wallet-lib/src/types/KeyChain/methods/sign.js deleted file mode 100644 index b10b561f2c2..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/sign.js +++ /dev/null @@ -1,29 +0,0 @@ -const { - crypto, Transaction, Message, -} = require('@dashevo/dashcore-lib'); - -/** - * Allow to sign any transaction or a transition object from a valid privateKeys list - * @param {Transaction|any} object - * @param {[PrivateKey]} privateKeys - * @param {number} [sigType=crypto.Signature.SIGHASH_ALL] - */ -function sign(object, privateKeys, sigType = crypto.Signature.SIGHASH_ALL) { - const handledTypes = [Transaction.name, Transaction.Payload.SubTxRegisterPayload, Message.name]; - if (!privateKeys) throw new Error('Require one or multiple privateKeys to sign'); - if (!object) throw new Error('Nothing to sign'); - if (!handledTypes.includes(object.constructor.name)) { - throw new Error(`Keychain sign : Unhandled object of type ${object.constructor.name}`); - } - const obj = object.sign(privateKeys, sigType); - - if (obj.isFullySigned && !obj.isFullySigned()) { - throw new Error('Not fully signed transaction'); - } - if (object.constructor.name === 'Message') { - // When signed, message are in string form. - return Message(obj); - } - return obj; -} -module.exports = sign; From 744b4bb0347de72ff86286222d8a1424a9e5d42b Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Mon, 31 Jan 2022 03:57:56 +0100 Subject: [PATCH 07/27] feat: change error message for injection --- packages/wallet-lib/src/errors/InjectionToPluginUnallowed.js | 4 ++-- packages/wallet-lib/src/plugins/StandardPlugin.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/wallet-lib/src/errors/InjectionToPluginUnallowed.js b/packages/wallet-lib/src/errors/InjectionToPluginUnallowed.js index 3d7e57f6785..6d6767f0d32 100644 --- a/packages/wallet-lib/src/errors/InjectionToPluginUnallowed.js +++ b/packages/wallet-lib/src/errors/InjectionToPluginUnallowed.js @@ -1,8 +1,8 @@ const WalletLibError = require('./WalletLibError'); class InjectionToPluginUnallowed extends WalletLibError { - constructor(pluginName) { - super(`Injection of plugin : ${pluginName} Unallowed`); + constructor(currentPluginName, injectingPluginName) { + super(`Injection of plugin : ${injectingPluginName} into ${currentPluginName} not allowed`); } } diff --git a/packages/wallet-lib/src/plugins/StandardPlugin.js b/packages/wallet-lib/src/plugins/StandardPlugin.js index 4b2ec8fdf8a..6a5c5a22bb8 100644 --- a/packages/wallet-lib/src/plugins/StandardPlugin.js +++ b/packages/wallet-lib/src/plugins/StandardPlugin.js @@ -63,7 +63,7 @@ class StandardPlugin extends EventEmitter { // this.parentEvents = {on:obj.on, emit:obj.emit}; this.parentEvents = obj; } else { - this.emit('error', new InjectionToPluginUnallowed(name), { + this.emit('error', new InjectionToPluginUnallowed(this.name, name), { type: 'plugin', pluginType: 'plugin', pluginName: this.name, From 35546acd9c2df37dc7ecfcb7a05ca6678255035c Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Mon, 31 Jan 2022 03:58:11 +0100 Subject: [PATCH 08/27] feat: integrate with ChainStore and WalletStore --- .../src/types/Account/_initializeAccount.js | 68 +-- .../src/types/Account/_sortPlugins.spec.js | 8 +- .../Account/methods/broadcastTransaction.js | 90 ++- .../methods/broadcastTransaction.spec.js | 52 +- .../src/types/Account/methods/getAddress.js | 46 +- .../src/types/Account/methods/getAddresses.js | 42 +- .../types/Account/methods/getBalance.spec.js | 42 -- .../Account/methods/getConfirmedBalance.js | 11 +- .../methods/getConfirmedBalance.spec.js | 36 ++ .../types/Account/methods/getTotalBalance.js | 11 +- .../Account/methods/getTotalBalance.spec.js | 30 + .../types/Account/methods/getTransaction.js | 15 +- .../Account/methods/getTransaction.spec.js | 141 ++--- .../Account/methods/getTransactionHistory.js | 44 +- .../methods/getTransactionHistory.spec.js | 133 +++-- .../types/Account/methods/getTransactions.js | 27 +- .../Account/methods/getTransactions.spec.js | 295 +--------- .../src/types/Account/methods/getUTXOS.js | 62 +-- .../types/Account/methods/getUTXOS.spec.js | 177 +----- .../Account/methods/getUnconfirmedBalance.js | 10 +- .../methods/getUnconfirmedBalance.spec.js | 35 ++ .../types/Account/methods/getUnusedAddress.js | 2 - .../Account/methods/getUnusedIdentityIndex.js | 2 +- .../Account/methods/importBlockHeader.js | 16 +- .../Account/methods/importTransactions.js | 3 - .../src/types/Account/methods/injectPlugin.js | 4 +- .../Account/methods/injectPlugin.spec.js | 10 +- .../methods/getIdentityHDKeyById.spec.js | 8 +- .../wallet-lib/src/types/Storage/Storage.js | 22 +- .../Storage/methods/calculateDuffBalance.js | 45 -- .../Storage/methods/createAccountStore.js | 29 - .../Storage/methods/createWalletStore.js | 5 +- .../types/Storage/methods/rehydrateState.js | 43 +- .../src/types/Storage/methods/saveState.js | 7 +- .../wallet-lib/src/types/Wallet/Wallet.js | 10 +- .../types/Wallet/methods/sweepWallet.spec.js | 12 +- .../src/utils/calculateDuffBalance.js | 28 + .../src/utils/categorizeTransactions.js | 95 +++- .../src/utils/categorizeTransactions.spec.js | 14 +- .../wallet-lib/src/utils/classifyAddresses.js | 63 ++- .../src/utils/classifyAddresses.spec.js | 526 +----------------- packages/wallet-lib/src/utils/index.js | 2 + 42 files changed, 728 insertions(+), 1593 deletions(-) delete mode 100644 packages/wallet-lib/src/types/Account/methods/getBalance.spec.js create mode 100644 packages/wallet-lib/src/types/Account/methods/getConfirmedBalance.spec.js create mode 100644 packages/wallet-lib/src/types/Account/methods/getTotalBalance.spec.js create mode 100644 packages/wallet-lib/src/types/Account/methods/getUnconfirmedBalance.spec.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/calculateDuffBalance.js delete mode 100644 packages/wallet-lib/src/types/Storage/methods/createAccountStore.js create mode 100644 packages/wallet-lib/src/utils/calculateDuffBalance.js diff --git a/packages/wallet-lib/src/types/Account/_initializeAccount.js b/packages/wallet-lib/src/types/Account/_initializeAccount.js index 858e748833e..9e418da3f32 100644 --- a/packages/wallet-lib/src/types/Account/_initializeAccount.js +++ b/packages/wallet-lib/src/types/Account/_initializeAccount.js @@ -1,31 +1,43 @@ const logger = require('../../logger'); const EVENTS = require('../../EVENTS'); -const { WALLET_TYPES } = require('../../CONSTANTS'); const preparePlugins = require('./_preparePlugins'); -const ensureAddressesToGapLimit = require('../../utils/bip44/ensureAddressesToGapLimit'); +const { UPDATED_ADDRESS } = require('../../EVENTS'); // eslint-disable-next-line no-underscore-dangle async function _initializeAccount(account, userUnsafePlugins) { const self = account; + + function markAddressAsUsed(props) { + const { address } = props.payload; + // This works if the TX cames from our main address, but not in all cases... + self.keyChainStore + .getMasterKeyChain() + .markAddressAsUsed(address); + } + + self.on(UPDATED_ADDRESS, markAddressAsUsed); + + const accountStore = account.storage + .getWalletStore(account.walletId) + .getPathState(account.accountPath); + + const chainStore = account.storage.getChainStore(account.network); + + const issuedPaths = account.keyChainStore + .getMasterKeyChain() + .getIssuedPaths(); + + issuedPaths.forEach((issuedPath) => { + accountStore.addresses[issuedPath.path] = issuedPath.address.toString(); + chainStore.importAddress(issuedPath.address.toString()); + }); + // We run faster in offlineMode to speed up the process when less happens. const readinessIntervalTime = (account.offlineMode) ? 50 : 200; // TODO: perform rejection with a timeout // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { try { - if (account.injectDefaultPlugins) { - if ([WALLET_TYPES.HDWALLET, WALLET_TYPES.HDPUBLIC].includes(account.walletType)) { - ensureAddressesToGapLimit( - account.store.wallets[account.walletId], - account.walletType, - account.index, - account.getAddress.bind(account), - ); - } else { - await account.getAddress('0'); // We force what is usually done by the BIP44Worker. - } - } - // Will sort and inject plugins. await preparePlugins(account, userUnsafePlugins); @@ -60,7 +72,7 @@ async function _initializeAccount(account, userUnsafePlugins) { }); logger.debug(`Initializing - ${readyPlugins}/${watchedPlugins.length} plugins`); if (readyPlugins === watchedPlugins.length) { - // At this stage, our worker are initialized + // At this stage, our worker are initialized sendInitialized(); // If both of the plugins are present @@ -68,35 +80,11 @@ async function _initializeAccount(account, userUnsafePlugins) { // while SyncWorker fetch'em on network clearInterval(self.readinessInterval); - switch (account.walletType) { - case WALLET_TYPES.PRIVATEKEY: - case WALLET_TYPES.SINGLE_ADDRESS: - account.generateAddress(0); - sendReady(); - return resolve(true); - case WALLET_TYPES.PUBLICKEY: - case WALLET_TYPES.ADDRESS: - account.generateAddress(0); - sendReady(); - return resolve(true); - default: - break; - } - if (!account.injectDefaultPlugins) { sendReady(); return resolve(true); } - if ([WALLET_TYPES.HDWALLET, WALLET_TYPES.HDPUBLIC].includes(account.walletType)) { - ensureAddressesToGapLimit( - account.store.wallets[account.walletId], - account.walletType, - account.index, - account.getAddress.bind(account), - ); - } - sendReady(); return resolve(true); } diff --git a/packages/wallet-lib/src/types/Account/_sortPlugins.spec.js b/packages/wallet-lib/src/types/Account/_sortPlugins.spec.js index 06f22b0827d..bd75e148714 100644 --- a/packages/wallet-lib/src/types/Account/_sortPlugins.spec.js +++ b/packages/wallet-lib/src/types/Account/_sortPlugins.spec.js @@ -274,8 +274,8 @@ describe('Account - _sortPlugins', () => { }); it('should handle userDefinedConflictingDependencies', function () { expect(() => sortPlugins(accountOnlineWithDefaultPlugins, userDefinedConflictingDependencies)) - .to - .throw('Conflicting dependency order for userDefinedConflictingDependenciesWorker'); + .to + .throw('Conflicting dependency order for userDefinedConflictingDependenciesWorker'); }); it('should handle userDefinedSimpleDependencyPluginDependenciesPlugins', async function () { const sortedPlugins = sortPlugins(accountOnlineWithDefaultPlugins, userDefinedSimpleDependencyPluginDependenciesPlugins); @@ -294,8 +294,8 @@ describe('Account - _sortPlugins', () => { // TODO: User specified wrongly sorted plugins with deps is not yet handled. // rejecting with error for now. expect(() => sortPlugins(accountOnlineWithDefaultPlugins, userDefinedComplexPluginDependenciesPlugins)) - .to - .throw('Dependency withSinglePluginDependenciesWorker not found'); + .to + .throw('Dependency withSinglePluginDependenciesWorker not found'); // const sortedPlugins = await sortPlugins(accountOnlineWithDefaultPlugins, userDefinedComplexPluginDependenciesPlugins); // expect(sortedPlugins).to.deep.equal([ // [ChainPlugin, true], diff --git a/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.js b/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.js index a4f9f7e6d3e..0d11eba63c9 100644 --- a/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.js +++ b/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.js @@ -6,40 +6,41 @@ const { InvalidDashcoreTransaction, } = require('../../../errors'); -function impactAffectedInputs({ transaction, txid }) { - const { inputs, changeIndex } = transaction.toObject(); - +function impactAffectedInputs({ transaction }) { const { - storage, walletId, network, + storage, network, } = this; + const { inputs, changeIndex } = transaction.toObject(); + const txid = transaction.hash; + + const addresses = storage.getChainStore(network).getAddresses(); // We iterate out input to substract their balance. inputs.forEach((input) => { - const potentiallySelectedAddresses = storage.searchAddressesWithTx(input.prevTxId); - // Fixme : If you want this check, you will need to modify fixtures of our tests. - // if (!potentiallySelectedAddresses.found) { - // throw new Error('Input is not part of that Wallet.'); - // } - potentiallySelectedAddresses.results.forEach((potentiallySelectedAddress) => { - const { type, path } = potentiallySelectedAddress; - if (potentiallySelectedAddress.utxos[`${input.prevTxId}-${input.outputIndex}`]) { - const inputUTXO = potentiallySelectedAddress.utxos[`${input.prevTxId}-${input.outputIndex}`]; - const address = storage.store.wallets[walletId].addresses[type][path]; + const potentiallySelectedAddresses = [...addresses] + .reduce((acc, [address, { transactions }]) => { + if (transactions.includes(input.prevTxId)) acc.push(address); + return acc; + }, []); + + potentiallySelectedAddresses.forEach((potentiallySelectedAddress) => { + // console.log(addresses.get(pot)); + const addressData = addresses.get(potentiallySelectedAddress); + if (addressData.utxos[`${input.prevTxId}-${input.outputIndex}`]) { + const inputUTXO = addressData.utxos[`${input.prevTxId}-${input.outputIndex}`]; + // const address = storage.store.wallets[walletId].addresses[type][path]; // Todo: This modify the balance of an address, we need a std method to do that instead. - address.balanceSat -= inputUTXO.satoshis; - delete address.utxos[`${input.prevTxId}-${input.outputIndex}`]; + addressData.balanceSat -= inputUTXO.satoshis; + delete addressData.utxos[`${input.prevTxId}-${input.outputIndex}`]; } }); }); const changeOutput = transaction.getChangeOutput(); if (changeOutput) { - const addressString = changeOutput.script.toAddress(network); + const addressString = changeOutput.script.toAddress(network).toString(); - const mapped = storage.mappedAddress[addressString]; - - const { type, path } = mapped; - const address = storage.store.wallets[walletId].addresses[type][path]; + const address = addresses.get(addressString); const utxoKey = `${txid}-${changeIndex}`; /** @@ -65,10 +66,15 @@ function impactAffectedInputs({ transaction, txid }) { return true; } -// eslint-disable-next-line no-underscore-dangle -async function _broadcastTransaction(transaction, options = {}) { +/** + * Broadcast a Transaction to the transport layer + * @param {Transaction|RawTransaction} transaction - A txobject or it's hexadecimal representation + * @param {Object} [options] + * @param {Boolean} [options.skipFeeValidation=false] - Allow to skip fee validation + * @return {Promise} + */ +async function broadcastTransaction(transaction, options = {}) { const { network, storage } = this; - const { chains } = storage.getStore(); if (!this.transport) throw new ValidTransportLayerRequired('broadcast'); // We still support having in rawtransaction, if this is the case @@ -76,7 +82,7 @@ async function _broadcastTransaction(transaction, options = {}) { if (is.string(transaction)) { const rawtx = transaction.toString(); if (!is.rawtx(rawtx)) throw new InvalidRawTransaction(rawtx); - return _broadcastTransaction.call(this, new Dashcore.Transaction(rawtx)); + return broadcastTransaction.call(this, new Dashcore.Transaction(rawtx)); } if (!is.dashcoreTransaction(transaction)) { @@ -87,7 +93,7 @@ async function _broadcastTransaction(transaction, options = {}) { throw new Error('Transaction not signed.'); } - const { minRelay: minRelayFeeRate } = chains[network.toString()].fees; + const { minRelay: minRelayFeeRate } = storage.getChainStore(network).state.fees; // eslint-disable-next-line no-underscore-dangle const estimateKbSize = transaction._estimateSize() / 1000; @@ -104,40 +110,8 @@ async function _broadcastTransaction(transaction, options = {}) { // so we clear them out from UTXOset. impactAffectedInputs.call(this, { transaction, - txid, }); - return txid; } -/** - * Broadcast a Transaction to the transport layer - * @param {Transaction|RawTransaction} transaction - A txobject or it's hexadecimal representation - * @param {Object} [options] - * @param {Boolean} [options.skipFeeValidation=false] - Allow to skip fee validation - * @return {Promise} - */ -async function broadcastTransaction(transaction, options = {}) { - if (!this.txISLockListener) { - const txId = await _broadcastTransaction.call(this, transaction, options); - - this.txISLockListener = new Promise((resolve) => { - this.subscribeToTransactionInstantLock.call(this, txId, () => { - this.txISLockListener = null; - resolve(); - }); - - // TODO: Also subscribe to FETCHED_CONFIRMED_TRANSACTION - // to use as a fallback to resolve the promise - // (blocked by https://github.com/dashevo/wallet-lib/pull/340) - }); - - return txId; - } - - await this.txISLockListener; - - return broadcastTransaction.call(this, transaction, options); -} - module.exports = broadcastTransaction; diff --git a/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.spec.js b/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.spec.js index 0613a8e3ad1..6ea735154c1 100644 --- a/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.spec.js +++ b/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.spec.js @@ -4,7 +4,7 @@ const broadcastTransaction = require('./broadcastTransaction'); const validRawTxs = require('../../../../fixtures/rawtx').valid; const invalidRawTxs = require('../../../../fixtures/rawtx').invalid; const expectThrowsAsync = require('../../../utils/expectThrowsAsync'); - +const ChainStore = require('../../ChainStore/ChainStore'); const { PrivateKey } = Dashcore; describe('Account - broadcastTransaction', function suite() { @@ -14,12 +14,12 @@ describe('Account - broadcastTransaction', function suite() { let keysToSign; let oneToOneTx; let fee; + + const chainStore = new ChainStore('testnet'); + chainStore.state.fees.minRelay = 888; + chainStore.importAddress('yTBXsrcGw74yMUsK34fBKAWJx3RNCq97Aq'); const storage = { - getStore: ()=>({ - chains:{ - "testnet": { fees: { minRelay: 888 }} - } - }) + getChainStore:()=> chainStore } beforeEach(() => { utxos = [ @@ -34,12 +34,12 @@ describe('Account - broadcastTransaction', function suite() { fee = 680; address = 'yTBXsrcGw74yMUsK34fBKAWJx3RNCq97Aq'; keysToSign = [ - new PrivateKey('26d6b24119d1a71de6372ea2d3dc22a014d37e4828b43db6936cb41ea461cce8') + new PrivateKey('26d6b24119d1a71de6372ea2d3dc22a014d37e4828b43db6936cb41ea461cce8') ]; oneToOneTx = new Dashcore.Transaction() - .from(utxos) - .to(address, 138) - .fee(fee); + .from(utxos) + .to(address, 138) + .fee(fee); oneToOneTx.sign(keysToSign); }); @@ -87,11 +87,7 @@ describe('Account - broadcastTransaction', function suite() { sendTransaction: () => sendCalled = +1, }, network: 'testnet', - storage: { - getStore: storage.getStore, - searchAddress: () => { searchCalled = +1; return { found: false }; }, - searchAddressesWithTx: () => { searchCalled = +1; return { results: [] }; }, - }, + storage }; const tx = oneToOneTx; @@ -109,11 +105,7 @@ describe('Account - broadcastTransaction', function suite() { sendTransaction: () => sendCalled = +1, }, network: 'testnet', - storage: { - getStore: storage.getStore, - searchAddress: () => { searchCalled = +1; return { found: false }; }, - searchAddressesWithTx: (affectedTxId) => { searchCalled = +1; return { results: [] }; }, - }, + storage }; return broadcastTransaction @@ -132,11 +124,7 @@ describe('Account - broadcastTransaction', function suite() { sendTransaction: () => sendCalled = +1, }, network: 'testnet', - storage: { - getStore: storage.getStore, - searchAddress: () => { searchCalled = +1; return { found: false }; }, - searchAddressesWithTx: () => { searchCalled = +1; return { results: [] }; }, - }, + storage }; const tx = oneToOneTx; @@ -151,20 +139,16 @@ describe('Account - broadcastTransaction', function suite() { sendTransaction: () => sendCalled = +1, }, network: 'testnet', - storage: { - getStore: storage.getStore, - searchAddress: () => { searchCalled = +1; return { found: false }; }, - searchAddressesWithTx: () => { searchCalled = +1; return { results: [] }; }, - }, + storage }; const tx = oneToOneTx; tx.fee(0); return broadcastTransaction - .call(self, tx, {skipFeeValidation: true}) - .then( - () => expect(sendCalled).to.equal(1) && expect(searchCalled).to.equal(1), - ); + .call(self, tx, {skipFeeValidation: true}) + .then( + () => expect(sendCalled).to.equal(1) && expect(searchCalled).to.equal(1), + ); }); }); diff --git a/packages/wallet-lib/src/types/Account/methods/getAddress.js b/packages/wallet-lib/src/types/Account/methods/getAddress.js index d4771dc7738..86efad07386 100644 --- a/packages/wallet-lib/src/types/Account/methods/getAddress.js +++ b/packages/wallet-lib/src/types/Account/methods/getAddress.js @@ -1,40 +1,26 @@ const { WALLET_TYPES } = require('../../../CONSTANTS'); -const getTypePathFromWalletType = (walletType, addressType = 'external', index, BIP44PATH) => { - let type; - let path; - - const addressTypeIndex = (addressType === 'external') ? 0 : 1; - switch (walletType) { - case WALLET_TYPES.HDWALLET: - type = addressType; - path = `${BIP44PATH}/${addressTypeIndex}/${index}`; - break; - case WALLET_TYPES.HDPUBLIC: - type = 'external'; - path = `${BIP44PATH}/${addressTypeIndex}/${index}`; - break; - case WALLET_TYPES.PUBLICKEY: - case WALLET_TYPES.ADDRESS: - case WALLET_TYPES.PRIVATEKEY: - case WALLET_TYPES.SINGLE_ADDRESS: - default: - type = 'misc'; - path = '0'; - } - return { type, path }; -}; /** * Get a specific addresss based on the index and type of address. * @param {number} index - The index on the type - * @param {AddressType} [_type="external"] - Type of the address (external, internal, misc) + * @param {AddressType} [addressType="external"] - Type of the address (external, internal, misc) * @return */ -function getAddress(index = 0, _type = 'external') { - const { type, path } = getTypePathFromWalletType(this.walletType, _type, index, this.BIP44PATH); +function getAddress(addressIndex = 0, addressType = 'external') { + const addressTypeIndex = (addressType === 'external') ? 0 : 1; + + const { addresses } = this.storage.getWalletStore(this.walletId).getPathState(this.accountPath); + const addressPath = ([WALLET_TYPES.HDPUBLIC, WALLET_TYPES.HDWALLET].includes(this.walletType)) + ? `m/${addressTypeIndex}/${addressIndex}` : '0'; + + const address = addresses[addressPath]; + if (!address) return this.generateAddress(addressPath); - const { wallets } = this.storage.getStore(); - const matchingTypeAddresses = wallets[this.walletId].addresses[type]; - return (matchingTypeAddresses[path]) ? matchingTypeAddresses[path] : this.generateAddress(path); + const chainStore = this.storage.getChainStore(this.network); + return { + index: addressIndex, + path: addressPath, + ...chainStore.getAddress(address), + }; } module.exports = getAddress; diff --git a/packages/wallet-lib/src/types/Account/methods/getAddresses.js b/packages/wallet-lib/src/types/Account/methods/getAddresses.js index 92a64e65368..6d957f63d61 100644 --- a/packages/wallet-lib/src/types/Account/methods/getAddresses.js +++ b/packages/wallet-lib/src/types/Account/methods/getAddresses.js @@ -2,20 +2,36 @@ const { WALLET_TYPES } = require('../../../CONSTANTS'); /** * Get all the addresses from the store from a given type - * @param {AddressType} [_type="external"] - Type of the address (external, internal, misc) + * @param {AddressType} [addressType="external"] - Type of the address (external, internal, misc) * @return {[AddressObj]} address - All address matching the type */ -function getAddresses(_type = 'external') { - const miscTypes = [ - WALLET_TYPES.SINGLE_ADDRESS, - WALLET_TYPES.PUBLICKEY, - WALLET_TYPES.PRIVATEKEY, - WALLET_TYPES.ADDRESS, - ]; - const walletType = (miscTypes.includes(this.walletType)) - ? 'misc' - : ((_type) || 'external'); - const store = this.storage.getStore(); - return store.wallets[this.walletId].addresses[walletType]; +function getAddresses(addressType = 'external') { + const addressTypeIndex = (addressType === 'external') ? 0 : 1; + + const { addresses } = this.storage + .getWalletStore(this.walletId) + .getPathState(this.accountPath); + + const chainStore = this.storage.getChainStore(this.network); + + const baseAddressPath = ([WALLET_TYPES.HDPUBLIC, WALLET_TYPES.HDWALLET].includes(this.walletType)) + ? `m/${addressTypeIndex}` : '0'; + + const typedAddresses = {}; + + Object + .entries(addresses) + .forEach(([path, address]) => { + if (path.startsWith(baseAddressPath)) { + const index = parseInt(path.split('/').slice(-1)[0], 10); + typedAddresses[path] = { + index, + path, + ...chainStore.getAddress(address), + }; + } + }); + + return typedAddresses; } module.exports = getAddresses; diff --git a/packages/wallet-lib/src/types/Account/methods/getBalance.spec.js b/packages/wallet-lib/src/types/Account/methods/getBalance.spec.js deleted file mode 100644 index 9ade6080a04..00000000000 --- a/packages/wallet-lib/src/types/Account/methods/getBalance.spec.js +++ /dev/null @@ -1,42 +0,0 @@ -const { expect } = require('chai'); -const mockedStore = require('../../../../fixtures/sirentonight-fullstore-snapshot-1562711703'); -const getTotalBalance = require('./getTotalBalance'); -const getConfirmedBalance = require('./getConfirmedBalance'); -const getUnconfirmedBalance = require('./getUnconfirmedBalance'); -const calculateDuffBalance = require('../../Storage/methods/calculateDuffBalance'); - -let mockedWallet; -describe('Account - getTotalBalance', function suite() { - this.timeout(10000); - before(() => { - const storageHDW = { - store: mockedStore, - calculateDuffBalance, - getStore: () => mockedStore, - mappedAddress: {}, - }; - const walletId = Object.keys(mockedStore.wallets)[0]; - mockedWallet = { - walletId, - index: 0, - storage: storageHDW, - }; - }); - it('should correctly get the balance', async () => { - const balance = await getTotalBalance.call(mockedWallet); - expect(balance).to.equal(184499999506); - }); - it('should correctly get the balance confirmed only', async () => { - const balance = await getConfirmedBalance.call(mockedWallet); - expect(balance).to.equal(184499999506); - }); - it('should correctly get the balance dash value instead of duff', async () => { - const balanceTotalDash = await getTotalBalance.call(mockedWallet, false); - const balanceUnconfDash = await getUnconfirmedBalance.call(mockedWallet, false); - const balanceConfDash = await getConfirmedBalance.call(mockedWallet, false); - - expect(balanceTotalDash).to.equal(1844.99999506); - expect(balanceUnconfDash).to.equal(0); - expect(balanceConfDash).to.equal(1844.99999506); - }); -}); diff --git a/packages/wallet-lib/src/types/Account/methods/getConfirmedBalance.js b/packages/wallet-lib/src/types/Account/methods/getConfirmedBalance.js index c84a71c48b1..158ca50f02e 100644 --- a/packages/wallet-lib/src/types/Account/methods/getConfirmedBalance.js +++ b/packages/wallet-lib/src/types/Account/methods/getConfirmedBalance.js @@ -1,4 +1,4 @@ -const { duffsToDash } = require('../../../utils'); +const { duffsToDash, calculateDuffBalance } = require('../../../utils'); /** * Return the confirmed balance of an account. @@ -7,10 +7,13 @@ const { duffsToDash } = require('../../../utils'); */ function getConfirmedBalance(displayDuffs = true) { const { - walletId, storage, + walletId, storage, accountPath, network, } = this; - const accountIndex = this.index; - const totalSat = storage.calculateDuffBalance(walletId, accountIndex, 'confirmed'); + + const { addresses } = storage.getWalletStore(walletId).getPathState(accountPath); + + const chainStore = storage.getChainStore(network); + const totalSat = (calculateDuffBalance(Object.values(addresses), chainStore, 'confirmed')); return (displayDuffs) ? totalSat : duffsToDash(totalSat); } diff --git a/packages/wallet-lib/src/types/Account/methods/getConfirmedBalance.spec.js b/packages/wallet-lib/src/types/Account/methods/getConfirmedBalance.spec.js new file mode 100644 index 00000000000..d8c6f4b6590 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getConfirmedBalance.spec.js @@ -0,0 +1,36 @@ +const { expect } = require('chai'); +const getTotalBalance = require('./getTotalBalance'); +const getConfirmedBalance = require('./getConfirmedBalance'); +const getUnconfirmedBalance = require('./getUnconfirmedBalance'); +const mockAccountWithStorage = require("../../../test/mocks/mockAccountWithStorage"); + + +let mockedAccount; +describe('Account - getTotalBalance', function suite() { + this.timeout(10000); + before(() => { + mockedAccount = mockAccountWithStorage() + }); + + it('should correctly get the balance', () => { + const balance = getTotalBalance.call(mockedAccount); + expect(balance).to.equal(224108673); + }); + + it('should correctly get the balance confirmed only', () => { + const balance = getConfirmedBalance.call(mockedAccount); + expect(balance).to.equal(224108673); + }); + + // TODO: file looks like a complete duplicate of the getTotalBalance.spec.js + // Should we actually mock and test confirmed balance? + it('should correctly get the balance dash value instead of duff', () => { + const balanceTotalDash = getTotalBalance.call(mockedAccount, false); + const balanceUnconfDash = getUnconfirmedBalance.call(mockedAccount, false); + const balanceConfDash = getConfirmedBalance.call(mockedAccount, false); + + expect(balanceTotalDash).to.equal(2.24108673); + expect(balanceUnconfDash).to.equal(0); + expect(balanceConfDash).to.equal(2.24108673); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/getTotalBalance.js b/packages/wallet-lib/src/types/Account/methods/getTotalBalance.js index 8b490ac8f6c..9c0161549d6 100644 --- a/packages/wallet-lib/src/types/Account/methods/getTotalBalance.js +++ b/packages/wallet-lib/src/types/Account/methods/getTotalBalance.js @@ -1,4 +1,4 @@ -const { duffsToDash } = require('../../../utils'); +const { duffsToDash, calculateDuffBalance } = require('../../../utils'); /** * Return the total balance of an account (confirmed + unconfirmed). @@ -7,9 +7,14 @@ const { duffsToDash } = require('../../../utils'); */ function getTotalBalance(displayDuffs = true) { const { - walletId, storage, index, + walletId, storage, accountPath, network, } = this; - const totalSat = storage.calculateDuffBalance(walletId, index, 'total'); + + const { addresses } = storage.getWalletStore(walletId).getPathState(accountPath); + + const chainStore = storage.getChainStore(network); + + const totalSat = (calculateDuffBalance(Object.values(addresses), chainStore, 'total')); return (displayDuffs) ? totalSat : duffsToDash(totalSat); } diff --git a/packages/wallet-lib/src/types/Account/methods/getTotalBalance.spec.js b/packages/wallet-lib/src/types/Account/methods/getTotalBalance.spec.js new file mode 100644 index 00000000000..0368595db06 --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getTotalBalance.spec.js @@ -0,0 +1,30 @@ +const { expect } = require('chai'); +const getTotalBalance = require('./getTotalBalance'); +const getConfirmedBalance = require('./getConfirmedBalance'); +const getUnconfirmedBalance = require('./getUnconfirmedBalance'); +const mockAccountWithStorage = require("../../../test/mocks/mockAccountWithStorage"); + +let mockedAccount; +describe('Account - getTotalBalance', function suite() { + this.timeout(10000); + before(() => { + mockedAccount = mockAccountWithStorage() + }); + it('should correctly get the balance',() => { + const balance = getTotalBalance.call(mockedAccount); + expect(balance).to.equal(224108673); + }); + it('should correctly get the balance confirmed only', () => { + const balance = getConfirmedBalance.call(mockedAccount); + expect(balance).to.equal(224108673); + }); + it('should correctly get the balance dash value instead of duff', () => { + const balanceTotalDash = getTotalBalance.call(mockedAccount, false); + const balanceUnconfDash = getUnconfirmedBalance.call(mockedAccount, false); + const balanceConfDash = getConfirmedBalance.call(mockedAccount, false); + + expect(balanceTotalDash).to.equal(2.24108673); + expect(balanceUnconfDash).to.equal(0); + expect(balanceConfDash).to.equal(2.24108673); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/getTransaction.js b/packages/wallet-lib/src/types/Account/methods/getTransaction.js index 4c7dce06a5e..da7a097052d 100644 --- a/packages/wallet-lib/src/types/Account/methods/getTransaction.js +++ b/packages/wallet-lib/src/types/Account/methods/getTransaction.js @@ -6,15 +6,14 @@ const EVENTS = require('../../../EVENTS'); * @return {Promise<{metadata: TransactionMetaData|null, transaction: Transaction}>} */ async function getTransaction(txid = null) { - const searchTransaction = await this.storage.searchTransaction(txid); - const searchTransactionMetadata = await this.storage.searchTransactionMetadata(txid); - if (searchTransaction.found) { - const searchResult = { transaction: searchTransaction.result, metadata: null }; - if (searchTransactionMetadata.found) { - searchResult.metadata = searchTransactionMetadata.result; - } - return searchResult; + const { storage, network } = this; + const chainStore = storage.getChainStore(network); + const searchedTransaction = chainStore.getTransaction(txid); + + if (searchedTransaction) { + return searchedTransaction; } + const getTransactionResponse = await this.transport.getTransaction(txid); if (!getTransactionResponse) return null; const { diff --git a/packages/wallet-lib/src/types/Account/methods/getTransaction.spec.js b/packages/wallet-lib/src/types/Account/methods/getTransaction.spec.js index 2a4f80cd037..ea65ce935f1 100644 --- a/packages/wallet-lib/src/types/Account/methods/getTransaction.spec.js +++ b/packages/wallet-lib/src/types/Account/methods/getTransaction.spec.js @@ -1,123 +1,64 @@ const { expect } = require('chai'); -const mockedStore = require('../../../../fixtures/sirentonight-fullstore-snapshot-1562711703'); const getTransaction = require('./getTransaction'); -const searchTransaction = require('../../Storage/methods/searchTransaction'); -const searchTransactionMetadata = require('../../Storage/methods/searchTransactionMetadata'); +const mockAccountWithStorage = require("../../../test/mocks/mockAccountWithStorage"); -let mockedWallet; +let mockedAccount; let fetchTransactionInfoCalledNb = 0; describe('Account - getTransaction', function suite() { this.timeout(10000); before(() => { - const storageHDW = { - store: mockedStore, - getStore: () => mockedStore, - mappedAddress: {}, - searchTransaction, - searchTransactionMetadata, - importTransactions: () => null, - }; - const walletId = Object.keys(mockedStore.wallets)[0]; - mockedWallet = { - walletId, - index: 0, - storage: storageHDW, + mockedAccount = mockAccountWithStorage({ transport: { - getTransaction: () => {fetchTransactionInfoCalledNb += 1; return null}, - }, - }; + getTransaction: () => { + fetchTransactionInfoCalledNb += 1; + return null + }, + } + }); }); it('should correctly get a existing transaction', async () => { - const tx = await getTransaction.call(mockedWallet, '92150f239013c961db15bc91d904404d2ae0520929969b59b69b17493569d0d5'); - expect(tx.transaction).to.deep.equal(expectedTx); + const tx = await getTransaction.call(mockedAccount, 'c8f0d780e6cedb9c37724f98cc3fecd6d5ad314db28e3cd439184bf25196ceb4'); + + expect(tx.transaction.toObject()).to.deep.equal(expectedTx); + expect(tx.metadata).to.deep.equal({ - hash: "92150f239013c961db15bc91d904404d2ae0520929969b59b69b17493569d0d5", - blockHash: '000000c5d6ca463ebbfddffe9a0a135312b6d8fc4eae2787b82b0fca9de7a554', - height: 29197, - instantLocked: false, - chainLocked: false + blockHash: '000001810cf3b49ed94ae033f007923d3c243077f5f9e24b559b536087e8b960', + height: 615751, + isInstantLocked: null, + isChainLocked: null }); }); + it('should correctly try to fetch un unexisting transaction', async () => { expect(fetchTransactionInfoCalledNb).to.equal(0); - const tx = await getTransaction.call(mockedWallet, '92151f239013c961db15bc91d904404d2ae0520929969b59b69b17493569d0d5'); + const tx = await getTransaction.call(mockedAccount, '92151f239013c961db15bc91d904404d2ae0520929969b59b69b17493569d0d5'); expect(fetchTransactionInfoCalledNb).to.equal(1); expect(tx).to.equal(null); }); }); const expectedTx = { - hash: '92150f239013c961db15bc91d904404d2ae0520929969b59b69b17493569d0d5', - blockhash: '000000c5d6ca463ebbfddffe9a0a135312b6d8fc4eae2787b82b0fca9de7a554', - blockheight: 29197, - blocktime: 1562060795, - fees: 522, - size: 521, - vout: [{ - value: '0.99990990', - n: 0, - scriptPubKey: { - hex: '76a914ba84943e63925288d2972cd5d0c2e1e06873c7c688ac', - asm: 'OP_DUP OP_HASH160 ba84943e63925288d2972cd5d0c2e1e06873c7c6 OP_EQUALVERIFY OP_CHECKSIG', - addresses: ['ydKfMe2n4vWsrzvgfSieQsFFxM9XMoWBff'], - type: 'pubkeyhash', - }, - spentTxId: 'eabe39ada39b58d70c03e0e79b7d2c767ed1239dda436bbc5a58954285421acc', - spentIndex: 1, - spentHeight: 30969, - }, { - value: '1000.00000000', - n: 1, - scriptPubKey: { - hex: '76a91485ada58442067249829d52ddd6c99c97a112749188ac', - asm: 'OP_DUP OP_HASH160 85ada58442067249829d52ddd6c99c97a1127491 OP_EQUALVERIFY OP_CHECKSIG', - addresses: ['yYWGjtb7XJqbXsUPfkaTWQKzcPYfmMp1Co'], - type: 'pubkeyhash', + hash: 'c8f0d780e6cedb9c37724f98cc3fecd6d5ad314db28e3cd439184bf25196ceb4', + version: 3, + inputs: [ + { + prevTxId: 'a43f20bcb46fef22926745a27ca38f63c773f2c7c4cbb55aaf184b58b3755965', + outputIndex: 0, + sequenceNumber: 4294967295, + script: '48304502210080185b616b2f8cb013e264b66146954cdc1597053ea8238467b896413b836039022071e71147dc45fc2dbde666c2774912c5bfd00b761268a3a4f8951375eb895c310121029671ae86f7eeb5c127568fddc977a1cce1f76ad64efa83c8ec9fcaef08ea9738', + scriptString: '72 0x304502210080185b616b2f8cb013e264b66146954cdc1597053ea8238467b896413b836039022071e71147dc45fc2dbde666c2774912c5bfd00b761268a3a4f8951375eb895c3101 33 0x029671ae86f7eeb5c127568fddc977a1cce1f76ad64efa83c8ec9fcaef08ea9738' + } + ], + outputs: [ + { + satoshis: 10000, + script: '76a9141ec5c66e9789c655ae068d35088b4073345fe0b088ac' }, - spentTxId: '5a5626c59f3830d5d9e7261bed5ced2694a343100c18d4a730b26639f5832944', - spentIndex: 0, - spentHeight: 29197, - }], - vin: [{ - hash: '0c25c534aeef8a151e8ce325882f80af647621b9f0a54f995f75c0d2994966ad', - vout: 0, - sequence: 4294967294, - n: 0, - scriptSig: { - hex: '483045022100b24c95914f666ecb3ac41048110d7732b890b0d3fac9a9ff05560913e530430a022006660d72df91158f4d4710b75b9b05502a1332b5afca1ab5f98d012119f62553012103a6592040a30bf9254306a9d1086803cd450ae817ed5b4ba34e3e1b43d48bb783', - asm: '3045022100b24c95914f666ecb3ac41048110d7732b890b0d3fac9a9ff05560913e530430a022006660d72df91158f4d4710b75b9b05502a1332b5afca1ab5f98d012119f62553[ALL] 03a6592040a30bf9254306a9d1086803cd450ae817ed5b4ba34e3e1b43d48bb783', - }, - addr: 'yXzZsVfpPxjewfVd7oa2D6tBMHW7JbonBr', - valueSat: 99991512, - value: 0.99991512, - doubleSpentTxID: null, - }, { - hash: '92056b727a3e37f5946dc18aa4f497ba9c0e3a328105e743175629bf7c8f3d37', - vout: 0, - sequence: 4294967294, - n: 1, - scriptSig: { - hex: '483045022100fe69fdb70c0550b900960e9fbfd7254726a237c8b5688e5c9a7fba15947638fa02206ba0463b51922b56d0064c06b55c21328a39aa81312cf25ec982fa5c1eed9214012103353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844', - asm: '3045022100fe69fdb70c0550b900960e9fbfd7254726a237c8b5688e5c9a7fba15947638fa02206ba0463b51922b56d0064c06b55c21328a39aa81312cf25ec982fa5c1eed9214[ALL] 03353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844', - }, - addr: 'yhvXpqQjfN9S4j5mBKbxeGxiETJrrLETg5', - valueSat: 50000000000, - value: 500, - doubleSpentTxID: null, - }, { - hash: 'be27a3dae2742aaca103fea0967edd9a6d0ef5cf90159af39f80ad5a7a50b7d6', - vout: 0, - sequence: 4294967294, - n: 2, - scriptSig: { - hex: '47304402205d30afd97e5efbec984faae5be922a487d7adce1518a3214966198a5423c150d02204ea0e15a5fcf5b8034294c9d2b9d6fc638f75506fcac3530c91b960eaf2e6859012103353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844', - asm: '304402205d30afd97e5efbec984faae5be922a487d7adce1518a3214966198a5423c150d02204ea0e15a5fcf5b8034294c9d2b9d6fc638f75506fcac3530c91b960eaf2e6859[ALL] 03353b4deb77923b026278d116e2007d6f97a058e42d35f1fd39efd5314705f844', - }, - addr: 'yhvXpqQjfN9S4j5mBKbxeGxiETJrrLETg5', - valueSat: 50000000000, - value: 500, - doubleSpentTxID: null, - }], - txlock: false, - spendable: false, + { + satoshis: 224508306, + script: '76a9147f6f4280f91e00126d927466cd48629439b763fa88ac' + } + ], + nLockTime: 0 }; + diff --git a/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.js b/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.js index 3118f363a65..6425f667e06 100644 --- a/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.js +++ b/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.js @@ -1,8 +1,6 @@ const { each } = require('lodash'); const { - filterTransactions, categorizeTransactions, - extendTransactionsWithMetadata, // calculateTransactionFees, } = require('../../../utils'); @@ -24,36 +22,18 @@ function getTransactionHistory() { network, } = this; - const transactions = this.getTransactions(); - const store = storage.getStore(); - - const chainStore = store.chains[network.toString()]; - const { blockHeaders } = chainStore; - - const { wallets: walletStore, transactionsMetadata } = store; - - const accountStore = walletStore[walletId]; - - // In store, not all transaction are specific to this account, we filter our transactions. - const filteredTransactions = filterTransactions( - accountStore, - walletType, - accountIndex, - transactions, - ); - const filteredTransactionsWithMetadata = extendTransactionsWithMetadata( - filteredTransactions, - transactionsMetadata, - ); + const transactionsWithMetadata = this.getTransactions(); + const walletStore = storage.getWalletStore(walletId); + const chainStore = storage.getChainStore(network); + const { blockHeaders } = chainStore.state; const categorizedTransactions = categorizeTransactions( - filteredTransactionsWithMetadata, - accountStore, + transactionsWithMetadata, + walletStore, accountIndex, walletType, network, ); - const sortedCategorizedTransactions = categorizedTransactions.sort(sortByHeightDescending); each(sortedCategorizedTransactions, (categorizedTransaction) => { @@ -65,19 +45,18 @@ function getTransactionHistory() { isChainLocked, isInstantLocked, } = categorizedTransaction; - - const blockHash = categorizedTransaction.blockHash !== '' ? categorizedTransaction.blockHash : null; - + const blockHash = categorizedTransaction.blockHash !== '' + ? categorizedTransaction.blockHash + : null; // To get time of block, let's find the blockheader. - const blockHeader = blockHeaders[blockHash]; - + const blockHeader = blockHeaders.get(blockHash); // If it's unconfirmed, we won't have a blockHeader nor it's time. const time = blockHeader ? blockHeader.time : -1; const normalizedTransactionHistory = { // Would require knowing the vout of this vin to determinate inputAmount. // This information could be fetched, but the necessity vs the cost is questionable. - // fees: calculateTransactionFees(categorizedTransaction.transaction), + // fees: calculateTransactionFees(categorizedTransaction.transaction), from, to, type, @@ -90,6 +69,7 @@ function getTransactionHistory() { transactionHistory.push(normalizedTransactionHistory); }); + // Sort by decreasing time. return transactionHistory.sort(sortbyTimeDescending); } diff --git a/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.spec.js b/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.spec.js index 6dd869b2a33..04e72c5dac6 100644 --- a/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.spec.js +++ b/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.spec.js @@ -3,15 +3,15 @@ const {Transaction, BlockHeader} = require('@dashevo/dashcore-lib'); const {WALLET_TYPES} = require('../../../CONSTANTS'); const getTransactions = require('./getTransactions'); const getTransactionHistory = require('./getTransactionHistory'); -const searchTransaction = require('../../Storage/methods/searchTransaction'); -const getTransaction = require('../../Storage/methods/getTransaction'); -const getBlockHeader = require('../../Storage/methods/getBlockHeader'); -const searchBlockHeader = require('../../Storage/methods/searchBlockHeader'); -const searchAddress = require('../../Storage/methods/searchAddress'); const mockedStoreHDWallet = require('../../../../fixtures/duringdevelop-fullstore-snapshot-1548538361'); const mockedStoreSingleAddress = require('../../../../fixtures/da07-fullstore-snapshot-1548533266'); +const getFixtureHDAccountWithStorage = require('../../../../fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage'); +const getFixturePrivateAccountWithStorage = require('../../../../fixtures/wallets/2a331817b9d6bf85100ef0/getFixtureAccountWithStorage'); + + + const normalizedHDStoreFixtures = require('../../../../fixtures/wallets/apart-trip-dignity/store.json'); const normalizedPKStoreFixtures = require('../../../../fixtures/wallets/2a331817b9d6bf85100ef0/store.json'); const CONSTANTS = require("../../../CONSTANTS"); @@ -39,7 +39,7 @@ const normalizedStoreToStore = (normalizedStore) => { } return store; } -const mockedSelf = { +const mockedEmptySelf = { getTransactions, network: 'testnet', walletId: 'd6143ef4e6', @@ -72,20 +72,35 @@ const mockedSelf = { } } } + +const mockedHDAccount = getFixtureHDAccountWithStorage(); +mockedHDAccount.getTransactions = getTransactions; + +const mockedPKAccount = getFixturePrivateAccountWithStorage(); +mockedPKAccount.getTransactions = getTransactions; + describe('Account - getTransactionHistory', () => { it('should return empty array on no transaction history', async function () { const mockedHDSelf = { - ...mockedSelf + ...getFixtureHDAccountWithStorage(), } + mockedHDSelf.getTransactions = getTransactions; + const chainStore = mockedHDSelf.storage.getChainStore('testnet') + chainStore.state.blockHeaders = new Map(); + chainStore.state.transactions = new Map(); + chainStore.state.addresses.forEach((address)=>{ + address.transactions = []; + address.utxos = {}; + address.balanceSat = 0; + }) const transactionHistoryHD = await getTransactionHistory.call(mockedHDSelf); const expectedTransactionHistoryHD = []; expect(transactionHistoryHD).to.deep.equal(expectedTransactionHistoryHD); }); it('should return valid transaction for HDWallet', async function () { const mockedHDSelf = { - ...mockedSelf + ...mockedHDAccount } - mockedHDSelf.storage.store = normalizedStoreToStore(normalizedHDStoreFixtures) const timestartTs = +new Date(); const transactionHistoryHD = await getTransactionHistory.call(mockedHDSelf); const timeendTs = +new Date(); @@ -176,6 +191,7 @@ describe('Account - getTransactionHistory', () => { isChainLocked: true, isInstantLocked: true }, + { from: [ {address: 'ygHAVkMtYSqoTWHebDv7qkhMV6dHyuRsp2'}, @@ -219,16 +235,77 @@ describe('Account - getTransactionHistory', () => { blockHash: '00000c1e4556add15119392ed36ec6af2640569409abfa23a9972bc3be1b3717', isChainLocked: true, isInstantLocked: true + }, + { + from: [{address: 'yXxUiAnB31voBDPqnwxkffcPnUvwJz6a2k'},{address: 'yNh6Xzw4rs1kenAo8VWCswdyUnkdYXDZsg'}], + to: [ + {"address": "yXiTNo71QQAqiw2u1i6vkEEj3m6y4sEGae","satoshis": 1768694}, + {"address": "yMLhEsiP2ajSh8STmXnNmkWXtoHsmawZxd","satoshis": 840010000} + ], + time: 1629126597, + txId: "eb1a7fc8e3b43d3021653b1176f8f9b41e9667d05b65ee225d14c149a5b14f77", + blockHash: "00000221952c2a60adcb929de837f659308cb5c6bb7783016479381fb550fbad", + type: "received", + isChainLocked: true, + isInstantLocked: true, + }, + { + from: [{address: 'yTcjWB7v7opDzpfYKpFdFEtEvSKFsh3bW3'}], + to: [ + {"address": "ygk3GCSba2J3L9G665Snozhj9HSkh5ByVE","satoshis": 10000000}, + {"address": "yiDVYtUZ2mKV4teSJzKBArqY4BRsZoFLYs","satoshis": 522649259} + ], + time: 1628846998, + txId: "7d1b78157f9f2238669f260d95af03aeefc99577ff0cddb91b3e518ee557a2fd", + blockHash: "0000012cf6377c6cf2b317a4deed46573c09f04f6880dca731cc9ccea6691e19", + type: "received", + isChainLocked: true, + isInstantLocked: true, + }, + { + from: [{address: 'yaLhoAZ4iex2zKmfvS9rvEmxXmRiPrjHdD'}], + to: [ + {"address": "yercyhdN9oEkZcB9BsW5ktFaDxFEuK6qXN","satoshis": 10000000}, + {"address": "yTcjWB7v7opDzpfYKpFdFEtEvSKFsh3bW3","satoshis": 532649506} + ], + type: 'received', + time: 1628846768, + txId: 'd37b6c7dd449d605bea9997af8bbeed2f3fbbcb23a4068b1f1ad694db801912d', + blockHash: '000000b6006c758eda23ec7e2a640a0bf2c6a0c44827be216faff6bf4fd388e8', + isChainLocked: true, + isInstantLocked: true + }, + { + from: [ + {address: 'ygrRyPRf9vSHnP1ieoRRvY9THtFbTMc66e'}, + {address: 'yhDaDMNRUAB93S2ZcprNLuEGHPG4VT8kYL'}, + {address: 'ygZ5fgrtGQDtwsN8K7sftSNPXN4Srhz99s'}, + {address: 'yb39TanhfUKeqaBtzqDvAE3ad9UsDuj3Fd'}, + {address: 'yToX9gDE6tn2Sv1zhq88WNfJSomeHee3rR'}, + {address: 'yViAv63brJ5kB7Gyc7yX2c7rJ9NuykCzRh'}, + {address: 'yfnJMvdE32izNQP68PhMPiHAeJKYo2PBdH'}, + ], + to: [ + {"address": "ySE2UYPf7PWMJ5oYikSscVifzQEoGiGRmd","satoshis": 1823313}, + {"address": "yTwEca67QSkZ6axGdpNFzWPaCj8zqYybY7","satoshis": 187980000} + ], + type: 'received', + time: 1628846677, + txId: 'a43845e580ad01f31bc06ce47ab39674e40316c4c6b765b6e54d6d35777ef456', + blockHash: '000001deee9f99e8219a9abcaaea135dbaae8a9b0f1ea214e6b6a37a5c5b115d', + isChainLocked: true, + isInstantLocked: true } + ] expect(transactionHistoryHD).to.deep.equal(expectedTransactionHistoryHD); }); - it('should correctly deal with HDWallet accounts', async function () { + it('should correctly deal with multiple HDWallet accounts', async function () { const mockedHDSelf = { - ...mockedSelf + ...mockedHDAccount } - mockedHDSelf.storage.store = normalizedStoreToStore(normalizedHDStoreFixtures) mockedHDSelf.index = 1; + mockedHDSelf.accountPath = `m/44'/1'/1'`; const transactionHistoryHD = await getTransactionHistory.call(mockedHDSelf); const expectedTransactionHistoryHD = [ { @@ -250,25 +327,6 @@ describe('Account - getTransactionHistory', () => { isChainLocked: true, isInstantLocked: true }, - { - from: [ { address: 'yYJmzWey5kNecAThet5BFxAga1F4b4DKQ2' } ], - to: [ - { - address: 'yNCqctyQaq51WU1hN5aNwsgMsZ5fRiB7GY', - satoshis: 1200000000 - }, - { - address: 'yXMrw79LPgu78EJsfGGYpm6fXKc1EMnQ49', - satoshis: 59999753 - } - ], - type: 'address_transfer', - time: 1629235557, - txId: '9cd3d44a87a7f99a33aebc6957105d5fb41698ef642189a36bac59ec0b5cd840', - blockHash: '0000016fb685b4b1efed743d2263de34a9f8323ed75e732654b1b951c5cb4dde', - isChainLocked: true, - isInstantLocked: true - }, { from: [ { address: 'yj8rRKATAUHcAgXvNZekob58xKm2oNyvhv' } ], to: [ @@ -289,17 +347,18 @@ describe('Account - getTransactionHistory', () => { isInstantLocked: true } ] - expect(transactionHistoryHD).to.deep.equal(expectedTransactionHistoryHD); + expect(transactionHistoryHD.slice(0,5)).to.deep.equal(expectedTransactionHistoryHD); }); it('should correctly compute transaction history for private key based wallet', async function (){ const mockedPKSelf = { - ...mockedSelf + ...mockedPKAccount } - mockedPKSelf.storage.store = normalizedStoreToStore(normalizedPKStoreFixtures) - mockedPKSelf.walletType = 'single_address'; - mockedPKSelf.walletId = '6101b44d50'; + // mockedPKSelf.storage.store = normalizedStoreToStore(normalizedPKStoreFixtures) + // mockedPKSelf.walletType = 'single_address'; + // mockedPKSelf.walletId = '6101b44d50'; const transactionHistoryPK = await getTransactionHistory.call(mockedPKSelf); + const expectedTransactionHistoryPK = [ { from: [ { address: 'ycDeuTfs4U77bTb5cq17dame28zdWHVYfk' } ], @@ -313,7 +372,7 @@ describe('Account - getTransactionHistory', () => { satoshis: 8999753 } ], - type: 'address_transfer', + type: 'sent', time: 1629510092, txId: '47d13f7f713f4258953292c2298c1d91e2d6dee309d689f3c8b44ccf457bab52', blockHash: '0000007b7356e715b43ed7d5b7135fb9a2bf403e079bbcf7faec0f0da5c40117', diff --git a/packages/wallet-lib/src/types/Account/methods/getTransactions.js b/packages/wallet-lib/src/types/Account/methods/getTransactions.js index 453fe3884b8..880502aeca7 100644 --- a/packages/wallet-lib/src/types/Account/methods/getTransactions.js +++ b/packages/wallet-lib/src/types/Account/methods/getTransactions.js @@ -3,6 +3,29 @@ * @return {[Transaction]} transactions - All transaction in the store */ module.exports = function getTransactions() { - const store = this.storage.getStore(); - return store.transactions; + const chainStore = this.storage.getChainStore(this.network); + const walletStore = this.storage.getWalletStore(this.walletId); + const transactions = []; + const transactionsArray = []; + const { addresses } = walletStore.getPathState(this.accountPath); + + Object + .values(addresses) + .forEach((address) => { + const addressData = chainStore.getAddress(address); + if (addressData) { + const transactionIds = addressData.transactions; + transactionIds.forEach((transactionId) => { + const tx = chainStore.getTransaction(transactionId); + transactions[tx.transaction.hash] = [tx.transaction, tx.metadata]; + }); + } + }); + + Object.entries(transactions) + .forEach(([, transactionWithMeta]) => { + transactionsArray.push(transactionWithMeta); + }); + + return transactionsArray; }; diff --git a/packages/wallet-lib/src/types/Account/methods/getTransactions.spec.js b/packages/wallet-lib/src/types/Account/methods/getTransactions.spec.js index 2c9aefa41b8..fa4ac069218 100644 --- a/packages/wallet-lib/src/types/Account/methods/getTransactions.spec.js +++ b/packages/wallet-lib/src/types/Account/methods/getTransactions.spec.js @@ -1,280 +1,33 @@ const { expect } = require('chai'); -const Dashcore = require('@dashevo/dashcore-lib'); const getTransactions = require('./getTransactions'); +const getFixtureHDAccountWithStorage = require('../../../../fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage'); + +const mockedHDSelf = { + ...getFixtureHDAccountWithStorage(), +} +mockedHDSelf.getTransactions = getTransactions; describe('Account - getTransactions', function suite() { this.timeout(10000); it('should get the transactions', () => { - const getStore = () => mockedStore; - const transactions = getTransactions.call({ - storage: { getStore }, - walletId: '123456789', - }, 'internal'); - expect(transactions).to.equal(mockedStore.transactions); + const transactionsWithMetadata = getTransactions.call(mockedHDSelf); + const transactionsHash = transactionsWithMetadata.reduce((acc, el)=>{ + acc.push(el[0].hash); + return acc; + },[]); + + expect(transactionsHash).to.deep.equal([ + 'a43845e580ad01f31bc06ce47ab39674e40316c4c6b765b6e54d6d35777ef456', + 'f230a9414bf577d93d6f7f2515d9b549ede78cfba4168920892970fa8aa1eef8', + 'd37b6c7dd449d605bea9997af8bbeed2f3fbbcb23a4068b1f1ad694db801912d', + '7d1b78157f9f2238669f260d95af03aeefc99577ff0cddb91b3e518ee557a2fd', + '1cbb35edc105918b956838570f122d6f3a1fba2b67467e643e901d09f5f8ac1b', + 'eb1a7fc8e3b43d3021653b1176f8f9b41e9667d05b65ee225d14c149a5b14f77', + 'c3fb3620ebd1c7678879b40df1495cc86a179b5a6f9e48ce0b687a5c6f5a1db5', + '6f37b0d6284aab627c31c50e1c9d7cce39912dd4f2393f91734f794bc6408533', + '6f76ca8038c6cb1b373bbbf80698afdc0d638e4a223be12a4feb5fd8e1801135', + 'e6b6f85a18d77974f376f05d6c96d0fdde990e733664248b1a00391565af6841' + ]); }); }); -const mockedStore = { - wallets: { - 123456789: { - addresses: { - external: { - "m/44'/1'/0'/0/0": { - address: 'yizmJb63ygipuJaRgYtpWCV2erQodmaZt8', - balanceSat: 100000000, - fetchedLast: 0, - path: "m/44'/1'/0'/0/0", - transactions: [ - 'dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc', - '1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35', - '7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6', - ], - index: 0, - unconfirmedBalanceSat: 0, - used: true, - utxos: [ - { - address: 'yizmJb63ygipuJaRgYtpWCV2erQodmaZt8', - txid: 'dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc', - outputIndex: 0, - scriptPubKey: '76a914f8c2652847720ab6d401291e5a48e2c8fe5d3c9f88ac', - satoshis: 100000000, - }, - ], - }, - "m/44'/1'/0'/0/1": { - address: 'yhLGmtf5Jmdb3DUvsaNJUHyCjjxTcBJEry', - balanceSat: 0, - fetchedLast: 1534867406705, - path: "m/44'/1'/0'/0/1", - transactions: [], - unconfirmedBalanceSat: 0, - utxos: [], - used: false, - }, - "m/44'/1'/0'/0/2": { - address: 'yfX4zBhV6syJTbRJLwxhCNdXkDnE7Mj1N7', - balanceSat: 0, - fetchedLast: 1534867406711, - path: "m/44'/1'/0'/0/2", - transactions: [], - unconfirmedBalanceSat: 0, - utxos: [], - used: false, - }, - }, - internal: { - "m/44'/1'/0'/1/0": { - address: 'yjSivd8eWH1vVywaeePiHBLXqMbHFXxxXE', - balanceSat: 1220560, - fetchedLast: 1534867407092, - path: "m/44'/1'/0'/1/0", - transactions: [ - '688dd18dea2b6f3c2d3892d13b41922fde7be01cd6040be9f3568dafbf9b1a23', - ], - unconfirmedBalanceSat: 0, - utxos: [ - '688dd18dea2b6f3c2d3892d13b41922fde7be01cd6040be9f3568dafbf9b1a23', - ], - used: true, - }, - "m/44'/1'/0'/1/1": { - address: 'yTM7nPiekjMBkMCU6cPmFD2KReeFUeVwCp', - balanceSat: 99890000, - fetchedLast: 1534867407080, - path: "m/44'/1'/0'/1/1", - transactions: [ - '1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35', - ], - unconfirmedBalanceSat: 0, - utxos: [], - used: true, - }, - "m/44'/1'/0'/1/2": { - address: 'yXy7rfGH5tZJs9GmFbdk6hPg2mzb8MRdfC', - balanceSat: 0, - fetchedLast: 1534867407080, - path: "m/44'/1'/0'/1/2", - transactions: [], - unconfirmedBalanceSat: 0, - utxos: [], - used: false, - }, - }, - }, - }, - }, - transactions: { - '9ab39713e9ce713d41ca6974db83e57bced02402e9516b8a662ed60d5c08f6d1': { - blockhash: '000000000a84c4703da7a69cfa65837251e4aac80e1621f2a2cc9504e0c149ba', - blockHeight: 201436, - blocktime: 1533525448, - fees: 1000, - size: 225, - txid: '9ab39713e9ce713d41ca6974db83e57bced02402e9516b8a662ed60d5c08f6d1', - txlock: true, - vin: [ - { - txid: 'e4524e918977b70ab47160d8e3b87a5fa9f88f22e43f0eec2abbee2cf364c93b', - vout: 1, - sequence: 4294967295, - n: 0, - scriptSig: { - hex: '4730440220154e37879e70784daff6cf04993cc88e8cf7e5357f82e98df9c117941cd5b3f702200d02f583085fbfd28c77d31c6b9a69f641a8fef5e99aaec8b8aedd4a3326e4100121025deea4fcd79eb876daa0f5829659c76f00f6b3fe6bf12e3ea83ecc763219bf88', - asm: '30440220154e37879e70784daff6cf04993cc88e8cf7e5357f82e98df9c117941cd5b3f702200d02f583085fbfd28c77d31c6b9a69f641a8fef5e99aaec8b8aedd4a3326e410[ALL] 025deea4fcd79eb876daa0f5829659c76f00f6b3fe6bf12e3ea83ecc763219bf88', - }, - addr: 'yhzoBe1aCTTganFBzFb3ErF4ufwMqonK5a', - valueSat: 81246619083, - value: 812.46619083, - doubleSpentTxID: null, - }, - ], - vout: [ - { - value: '2.00000000', - n: 0, - scriptPubKey: { - hex: '76a914df128447b46f9c81edbf13494d12aabca066b65688ac', - asm: 'OP_DUP OP_HASH160 df128447b46f9c81edbf13494d12aabca066b656 OP_EQUALVERIFY OP_CHECKSIG', - addresses: [ - 'ygewiYb7ZJxU4uuNGEVzbbA3wZEpEQJKhr', - ], - type: 'pubkeyhash', - }, - spentTxId: '6b90bf01b10a0c6cac018d376823f6b330edf2cbb783cc3d02004f8706bbc311', - spentIndex: 7, - spentHeight: 203517, - }, - { - value: '810.46609083', - n: 1, - scriptPubKey: { - hex: '76a914f67b2c4f47ea0a2bae829d3816a01cc486463d7988ac', - asm: 'OP_DUP OP_HASH160 f67b2c4f47ea0a2bae829d3816a01cc486463d79 OP_EQUALVERIFY OP_CHECKSIG', - addresses: [ - 'yinidcHwrfzb4bEJDSq3wtQyxRAgQxsQia', - ], - type: 'pubkeyhash', - }, - spentTxId: '22c368e09ad8b36553b383c6a4ae989f91d1f66622b2b685262580c8a45175a4', - spentIndex: 0, - spentHeight: 203155, - }, - ], - }, - '7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6': { - txid: '7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6', - blockhash: '000000000388f8a1de91702e25209aad802d28fcd2710ac2e2acd12e9738dbe2', - blockHeight: 203633, - blocktime: 1533827521, - fees: 1000, - size: 225, - txlock: true, - vout: [ - { - value: '1.00000000', - n: 0, - scriptPubKey: { - hex: '76a914f8c2652847720ab6d401291e5a48e2c8fe5d3c9f88ac', - asm: 'OP_DUP OP_HASH160 f8c2652847720ab6d401291e5a48e2c8fe5d3c9f OP_EQUALVERIFY OP_CHECKSIG', - addresses: [ - 'yizmJb63ygipuJaRgYtpWCV2erQodmaZt8', - ], - type: 'pubkeyhash', - }, - spentTxId: '1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35', - spentIndex: 0, - spentHeight: 204161, - }, - { - value: '18.99980000', - n: 1, - scriptPubKey: { - hex: '76a9147d39e5ff6ea7b82c6dc69994f1075f157bee9ef688ac', - asm: 'OP_DUP OP_HASH160 7d39e5ff6ea7b82c6dc69994f1075f157bee9ef6 OP_EQUALVERIFY OP_CHECKSIG', - addresses: [ - 'yXjag2zjTwA5Ya2sSV5KoFzTvE6uKdrnKa', - ], - type: 'pubkeyhash', - }, - spentTxId: '6ca8795f2534972e1371249c3d7b6c5095e1513bc8cc351eeaa2f364020dbc01', - spentIndex: 1, - spentHeight: 203674, - }, - ], - vin: [ - { - txid: '4ae8d1960c9a4ed83dbeaf1ad94b4a82f11c8574207144beda87113d94a31da1', - vout: 1, - sequence: 4294967295, - n: 0, - scriptSig: { - hex: '47304402205483f0b26a04876fe8eceb9a5d86b1da93012be27b5482a3d53443cda5ee7a13022027410cef1e19c2620ad0da1b800252b620e3e8dc576efb069e95eb1d2222ccfb01210221d2f6bd1b101dc4eb40f570af2a87221e3ffa166b6c5eac2faf496b0c53cbdf', - asm: '304402205483f0b26a04876fe8eceb9a5d86b1da93012be27b5482a3d53443cda5ee7a13022027410cef1e19c2620ad0da1b800252b620e3e8dc576efb069e95eb1d2222ccfb[ALL] 0221d2f6bd1b101dc4eb40f570af2a87221e3ffa166b6c5eac2faf496b0c53cbdf', - }, - addr: 'yUvr9AxuFx3ifp8HHFdYnVGbvK8Qqz25SQ', - valueSat: 1999990000, - value: 19.9999, - doubleSpentTxID: null, - }, - ], - }, - '1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35': { - txid: '1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35', - blockhash: '0000000002922d7d0a69ce3e29908dc26aba6565af760d208dac77a8b520fbf3', - blockHeight: 204161, - blocktime: 1533900199, - fees: 1000, - size: 226, - txlock: false, - vout: [ - { - value: '0.00100000', - n: 0, - scriptPubKey: { - hex: '76a914f3145d42d98196ca439eee48da05af56c2d7c4a688ac', - asm: 'OP_DUP OP_HASH160 f3145d42d98196ca439eee48da05af56c2d7c4a6 OP_EQUALVERIFY OP_CHECKSIG', - addresses: [ - 'yiUjSkhkAfaHfYYmTMhc27NCmogJ3iRBaS', - ], - type: 'pubkeyhash', - }, - spentTxId: null, - spentIndex: null, - spentHeight: null, - }, - { - value: '0.99890000', - n: 1, - scriptPubKey: { - hex: '76a9144d19cc13106cac13b386c89b003b02992ae8c5e688ac', - asm: 'OP_DUP OP_HASH160 4d19cc13106cac13b386c89b003b02992ae8c5e6 OP_EQUALVERIFY OP_CHECKSIG', - addresses: [ - 'yTM7nPiekjMBkMCU6cPmFD2KReeFUeVwCp', - ], - type: 'pubkeyhash', - }, - spentTxId: null, - spentIndex: null, - spentHeight: null, - }, - ], - vin: [ - { - txid: '7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6', - vout: 0, - sequence: 4294967295, - n: 0, - scriptSig: { - hex: '483045022100ad3c7a5eae5ccb491c0e2cc3521df2cc410bf403340d6587848dc5ceb2b6831f022016b622ee329a4ceb1364d3e7295832af30e4fbc63420257b5b4971185048ac410121022efe5f45f47813efa1a279296c0171823736ae90c617ef2bda52becc56611536', - asm: '3045022100ad3c7a5eae5ccb491c0e2cc3521df2cc410bf403340d6587848dc5ceb2b6831f022016b622ee329a4ceb1364d3e7295832af30e4fbc63420257b5b4971185048ac41[ALL] 022efe5f45f47813efa1a279296c0171823736ae90c617ef2bda52becc56611536', - }, - addr: 'yizmJb63ygipuJaRgYtpWCV2erQodmaZt8', - valueSat: 100000000, - value: 1, - doubleSpentTxID: null, - }, - ], - }, - }, -}; diff --git a/packages/wallet-lib/src/types/Account/methods/getUTXOS.js b/packages/wallet-lib/src/types/Account/methods/getUTXOS.js index d0dcfcd6b76..cd14fefb190 100644 --- a/packages/wallet-lib/src/types/Account/methods/getUTXOS.js +++ b/packages/wallet-lib/src/types/Account/methods/getUTXOS.js @@ -1,6 +1,6 @@ /* eslint-disable no-continue, no-restricted-syntax */ const { Address, Transaction } = require('@dashevo/dashcore-lib'); -const { WALLET_TYPES, COINBASE_MATURITY } = require('../../../CONSTANTS'); +const { COINBASE_MATURITY } = require('../../../CONSTANTS'); /** * Return all the utxos @@ -11,44 +11,32 @@ const { WALLET_TYPES, COINBASE_MATURITY } = require('../../../CONSTANTS'); function getUTXOS(options = { coinbaseMaturity: COINBASE_MATURITY, }) { - const self = this; const { walletId, network, - BIP44PATH, - walletType, } = this; const utxos = []; - const isHDWallet = [WALLET_TYPES.HDPUBLIC, WALLET_TYPES.HDWALLET].includes(walletType); - const currentBlockHeight = this.store.chains[network].blockHeight; + const chainStore = this.storage.getChainStore(network); + const accountState = this.storage.getWalletStore(walletId).getPathState(this.accountPath); + const currentBlockHeight = chainStore.blockHeight; - for (const addressType in this.store.wallets[walletId].addresses) { - if (!addressType || !['external', 'internal', 'misc'].includes(addressType)) { - continue; - } - for (const path in self.store.wallets[walletId].addresses[addressType]) { - if (!path) continue; - const address = self.store.wallets[walletId].addresses[addressType][path]; - - if (isHDWallet && !path.startsWith(BIP44PATH)) continue; - - for (const identifier in address.utxos) { - if (!identifier) continue; - const [txid, outputIndex] = identifier.split('-'); - const transaction = new Transaction(this.store.transactions[txid]); - - if (transaction.isCoinbase()) { - // If the transaction is not a special transaction, we can't check its - // maturity at the moment of writing this comment. - // The wallet library doesn't maintain the header chain and thus we can - // figure out the height only from the payload, but old coinbase transactions - // doesn't have a payload. - if (!transaction.isSpecialTransaction()) { - continue; - } + Object.values(accountState.addresses).forEach((address) => { + const addressData = chainStore.getAddress(address); + const utxosKeys = Object.keys(addressData.utxos); + utxosKeys.forEach((utxoIdentifier) => { + let skipUtxo = false; + const [txid, outputIndex] = utxoIdentifier.split('-'); + const { transaction } = chainStore.getTransaction(txid); + if (transaction.isCoinbase()) { + // If the transaction is not a special transaction, we can't check its + // maturity at the moment of writing this comment. + // The wallet library doesn't maintain the header chain and thus we can + // figure out the height only from the payload, but old coinbase transactions + // doesn't have a payload. + if (transaction.isSpecialTransaction()) { const transactionHeight = (this.store.transactionsMetadata[txid]) ? this.store.transactionsMetadata[txid].height : transaction.extraPayload.height; @@ -56,22 +44,24 @@ function getUTXOS(options = { // We check maturity is at least 100 blocks. // another way is to just read _scriptBuffer height value. if (transactionHeight + options.coinbaseMaturity > currentBlockHeight) { - continue; + skipUtxo = true; } } + } + if (!skipUtxo) { utxos.push(new Transaction.UnspentOutput( { txId: txid, vout: parseInt(outputIndex, 10), - script: address.utxos[identifier].script, - satoshis: address.utxos[identifier].satoshis, - address: new Address(address.address, network), + script: addressData.utxos[utxoIdentifier].script, + satoshis: addressData.utxos[utxoIdentifier].satoshis, + address: new Address(addressData.address, network), }, )); } - } - } + }); + }); return utxos.sort((a, b) => b.satoshis - a.satoshis); } diff --git a/packages/wallet-lib/src/types/Account/methods/getUTXOS.spec.js b/packages/wallet-lib/src/types/Account/methods/getUTXOS.spec.js index eafae7efce1..0e2d8fbb77f 100644 --- a/packages/wallet-lib/src/types/Account/methods/getUTXOS.spec.js +++ b/packages/wallet-lib/src/types/Account/methods/getUTXOS.spec.js @@ -1,167 +1,38 @@ const { expect } = require('chai'); const Dashcore = require('@dashevo/dashcore-lib'); const getUTXOS = require('./getUTXOS'); -const { Transaction } = Dashcore; +const mockAccountWithStorage = require("../../../test/mocks/mockAccountWithStorage"); -const mockedStoreEmpty = { - wallets: { - 123456789: { - addresses: {}, - }, - }, - chains: { - testnet: { - blockHeight: 10000 - } - } -}; +describe('Account - getUTXOS', function suite() { + this.timeout(10000); -const mockedStore2 = { - wallets: { - 123456789: { - addresses: { - external: { - "m/44'/1'/0'/0/0": { - address: 'yizmJb63ygipuJaRgYtpWCV2erQodmaZt8', - balanceSat: 100000000, - fetchedLast: 0, - path: "m/44'/1'/0'/0/0", - transactions: [ - 'dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc', - '1d8f924bef2e24d945d7de2ac66e98c8625e4cefeee4e07db2ea334ce17f9c35', - '7ae825f4ecccd1e04e6c123e0c55d236c79cd04c6ab64e839aed2ae0af3003e6', - ], - index: 0, - unconfirmedBalanceSat: 0, - used: true, - utxos: { - "dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc-0": - { - address: 'yizmJb63ygipuJaRgYtpWCV2erQodmaZt8', - txId: 'dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc', - outputIndex: 0, - script: '76a914f8c2652847720ab6d401291e5a48e2c8fe5d3c9f88ac', - satoshis: 100000000, - }, - } - }, - "m/44'/1'/1'/0/0":{ - address: 'yQ5TfKcj3NHM4V4K5VBgoFJj9Q4LKX13gn', - balanceSat: 14419880000, - fetchedLast: 0, - path: "m/44'/1'/1'/0/0", - transactions: [ - 'b8838022a663ae486192cf2499f9ae657e8c3a7e823a447b8b7e3d348d3916ba', - ], - index: 0, - unconfirmedBalanceSat: 0, - used: true, - utxos: { - "b8838022a663ae486192cf2499f9ae657e8c3a7e823a447b8b7e3d348d3916ba-0": - { - address: 'yQ5TfKcj3NHM4V4K5VBgoFJj9Q4LKX13gn', - txId: 'b8838022a663ae486192cf2499f9ae657e8c3a7e823a447b8b7e3d348d3916ba', - outputIndex: 0, - script: '76a914293b5b9a2154a0e4543027d694276cd5fdcb74cd88ac', - satoshis: 14419880000, - }, - } - } - }, - }, - }, - }, - chains: { - testnet: { - blockHeight: 10000 - } - }, - transactions: { - dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc: new Transaction({ - "hash": "f13a95fc3a9b6146590b12fbe48749738a1b3ffe30e42de5b1898f8f9d76b879", - "version": 3, - "inputs": [ - { - "prevTxId": "0000000000000000000000000000000000000000000000000000000000000000", - "outputIndex": 4294967295, - "sequenceNumber": 4294967295, - "script": "0264080101" - } - ], - "outputs": [ - { - "satoshis": 7972544484, - "script": "76a9144c1b05387342497e3c8fbe0b80754ae4b33134c488ac" - }, - { - "satoshis": 7972544480, - "script": "76a914214035c10a2d2cef9992ca715a0115366edd229e88ac" - } - ], - "nLockTime": 0, - "type": 5, - "extraPayload": "020064080000aa254fcb634bf1962b67bb64ce178a954353c71d0b6119361390a9fd1a71bd2c0000000000000000000000000000000000000000000000000000000000000000" - }), - b8838022a663ae486192cf2499f9ae657e8c3a7e823a447b8b7e3d348d3916ba: new Transaction({ - "hash": "f13a95fc3a9b6146590b12fbe48749738a1b3ffe30e42de5b1898f8f9d76b879", - "version": 3, - "inputs": [ - { - "prevTxId": "0000000000000000000000000000000000000000000000000000000000000000", - "outputIndex": 4294967295, - "sequenceNumber": 4294967295, - "script": "0264080101" - } - ], - "outputs": [ - { - "satoshis": 7972544484, - "script": "76a9144c1b05387342497e3c8fbe0b80754ae4b33134c488ac" - }, - { - "satoshis": 7972544480, - "script": "76a914214035c10a2d2cef9992ca715a0115366edd229e88ac" - } - ], - "nLockTime": 0, - "type": 5, - "extraPayload": "020064080000aa254fcb634bf1962b67bb64ce178a954353c71d0b6119361390a9fd1a71bd2c0000000000000000000000000000000000000000000000000000000000000000" - }), - }, - transactionsMetadata:{ + it('should return empty UTXOs list for new account', () => { + const mockedAccount = mockAccountWithStorage(); + const { walletId, accountPath, network } = mockedAccount; - } -}; + // Wipe transactions and addresses from the storage to simulate empty UTXOs + mockedAccount.storage.getWalletStore(walletId).state.paths.get(accountPath).addresses = {} + const chainStore = mockedAccount.storage.getChainStore(network); + chainStore.state.blockHeaders = {}; + chainStore.state.transactions = {}; + chainStore.state.addresses = {}; + + const utxos = getUTXOS.call(mockedAccount); -describe('Account - getUTXOS', function suite() { - this.timeout(10000); - it('should get the proper UTXOS list', () => { - const utxos = getUTXOS.call({ - store: mockedStoreEmpty, - getStore: mockedStoreEmpty, - walletId: '123456789', - network: 'testnet', - walletType: 'hdwallet', - BIP44PATH: "m/44'/1'/0'" - }); expect(utxos).to.be.deep.equal([]); + }) - const utxos2 = getUTXOS.call({ - store: mockedStore2, - getStore: mockedStore2, - walletId: '123456789', - network: 'testnet', - walletType: 'hdwallet', - BIP44PATH: "m/44'/1'/0'" - }); + it('should get the proper UTXOS list', () => { + const mockedAccount = mockAccountWithStorage(); + const utxos = getUTXOS.call(mockedAccount); - expect(utxos2).to.be.deep.equal([new Dashcore.Transaction.UnspentOutput( + expect(utxos).to.be.deep.equal([new Dashcore.Transaction.UnspentOutput( { - address: new Dashcore.Address('yizmJb63ygipuJaRgYtpWCV2erQodmaZt8'), - txId: 'dd7afaadedb5f022cec6e33f1c8520aac897df152bd9f876842f3723ab9614bc', - outputIndex: 0, - script: new Dashcore.Script('76a914f8c2652847720ab6d401291e5a48e2c8fe5d3c9f88ac'), - satoshis: 100000000, + address: new Dashcore.Address('yMEnFG5TBqEZXYXTg3PhENtZgGbwhw6qbX'), + txId: '33b14c6bc960c5717d734d5a15dc86b2060bf6e746cc509863344204d356cee4', + outputIndex: 1, + script: new Dashcore.Script('76a9140a163cfcba43b87e58b1996f61376d7bd8d9805288ac'), + satoshis: 224108673, }, )]); }); diff --git a/packages/wallet-lib/src/types/Account/methods/getUnconfirmedBalance.js b/packages/wallet-lib/src/types/Account/methods/getUnconfirmedBalance.js index 6b320a0ff34..9833525b4b1 100644 --- a/packages/wallet-lib/src/types/Account/methods/getUnconfirmedBalance.js +++ b/packages/wallet-lib/src/types/Account/methods/getUnconfirmedBalance.js @@ -1,4 +1,4 @@ -const { duffsToDash } = require('../../../utils'); +const { duffsToDash, calculateDuffBalance } = require('../../../utils'); /** * Return the total balance of unconfirmed utxo @@ -7,11 +7,13 @@ const { duffsToDash } = require('../../../utils'); */ function getUnconfirmedBalance(displayDuffs = true) { const { - walletId, storage, + walletId, storage, accountPath, network, } = this; - const accountIndex = this.index; - const totalSat = storage.calculateDuffBalance(walletId, accountIndex, 'unconfirmed'); + const { addresses } = storage.getWalletStore(walletId).getPathState(accountPath); + + const chainStore = storage.getChainStore(network); + const totalSat = (calculateDuffBalance(Object.values(addresses), chainStore, 'unconfirmed')); return (displayDuffs) ? totalSat : duffsToDash(totalSat); } diff --git a/packages/wallet-lib/src/types/Account/methods/getUnconfirmedBalance.spec.js b/packages/wallet-lib/src/types/Account/methods/getUnconfirmedBalance.spec.js new file mode 100644 index 00000000000..ad562c6bb2a --- /dev/null +++ b/packages/wallet-lib/src/types/Account/methods/getUnconfirmedBalance.spec.js @@ -0,0 +1,35 @@ +const { expect } = require('chai'); +const getTotalBalance = require('./getTotalBalance'); +const getConfirmedBalance = require('./getConfirmedBalance'); +const getUnconfirmedBalance = require('./getUnconfirmedBalance'); +const mockAccountWithStorage = require("../../../test/mocks/mockAccountWithStorage"); + +let mockedAccount; +describe('Account - getUnconfirmedBalance', function suite() { + this.timeout(10000); + before(() => { + mockedAccount = mockAccountWithStorage() + }); + + it('should correctly get the balance', () => { + const balance = getTotalBalance.call(mockedAccount); + expect(balance).to.equal(224108673); + }); + + it('should correctly get the balance confirmed only', () => { + const balance = getConfirmedBalance.call(mockedAccount); + expect(balance).to.equal(224108673); + }); + + // TODO: file looks like a complete duplicate of the getTotalBalance.spec.js + // Should we actually mock and test unconfirmed balance? + it('should correctly get the balance dash value instead of duff', () => { + const balanceTotalDash = getTotalBalance.call(mockedAccount, false); + const balanceUnconfDash = getUnconfirmedBalance.call(mockedAccount, false); + const balanceConfDash = getConfirmedBalance.call(mockedAccount, false); + + expect(balanceTotalDash).to.equal(2.24108673); + expect(balanceUnconfDash).to.equal(0); + expect(balanceConfDash).to.equal(2.24108673); + }); +}); diff --git a/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.js b/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.js index 9a544719938..f07c81d4b4b 100644 --- a/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.js +++ b/packages/wallet-lib/src/types/Account/methods/getUnusedAddress.js @@ -1,5 +1,3 @@ -const logger = require('../../../logger'); - /** * Get an unused address from the store * @param {AddressType} [type="external"] - Type of the requested usused address diff --git a/packages/wallet-lib/src/types/Account/methods/getUnusedIdentityIndex.js b/packages/wallet-lib/src/types/Account/methods/getUnusedIdentityIndex.js index 7cf353d075c..0e32b274e18 100644 --- a/packages/wallet-lib/src/types/Account/methods/getUnusedIdentityIndex.js +++ b/packages/wallet-lib/src/types/Account/methods/getUnusedIdentityIndex.js @@ -6,7 +6,7 @@ async function getUnusedIdentityIndex() { // Force identities sync before return unused index await this.getWorker('IdentitySyncWorker').execWorker(); - const identityIds = this.storage.getIndexedIdentityIds(this.walletId); + const identityIds = this.storage.getWalletStore(this.walletId).getIndexedIdentityIds(); const firstMissingIndex = identityIds.findIndex((identityId) => !identityId); diff --git a/packages/wallet-lib/src/types/Account/methods/importBlockHeader.js b/packages/wallet-lib/src/types/Account/methods/importBlockHeader.js index 01e4517a42b..9200695a187 100644 --- a/packages/wallet-lib/src/types/Account/methods/importBlockHeader.js +++ b/packages/wallet-lib/src/types/Account/methods/importBlockHeader.js @@ -1,5 +1,5 @@ const logger = require('../../../logger'); -const { WALLET_TYPES } = require('../../../CONSTANTS'); +const EVENTS = require('../../../EVENTS'); /** * Import transactions and always keep a number of unused addresses up to gap * @@ -15,16 +15,14 @@ module.exports = async function importBlockHeader(blockHeader) { // knowing the following blockHeight blockheader's prevHash value // const previousHash = blockHeader.prevHash.reverse().toString('hex'); const { - walletId, BIP44PATH, index, store, storage, walletType, + storage, network, } = this; - const localWalletStore = store.wallets[walletId]; - const localAccountStore = ([WALLET_TYPES.HDPUBLIC, WALLET_TYPES.HDWALLET].includes(walletType)) - ? localWalletStore.accounts[BIP44PATH.toString()] - : localWalletStore.accounts[index.toString()]; + const applicationStore = storage.application; + const chainStore = storage.getChainStore(network); + applicationStore.blockHash = blockHeader.id; - localAccountStore.blockHash = blockHeader.id; - - storage.importBlockHeader(blockHeader); + chainStore.importBlockHeader(blockHeader); + this.emit(EVENTS.BLOCKHEADER, { type: EVENTS.BLOCKHEADER, payload: blockHeader }); logger.silly(`Account.importBlockHeader(${blockHeader.id})`); }; diff --git a/packages/wallet-lib/src/types/Account/methods/importTransactions.js b/packages/wallet-lib/src/types/Account/methods/importTransactions.js index e657a1b642c..9af3b9681c5 100644 --- a/packages/wallet-lib/src/types/Account/methods/importTransactions.js +++ b/packages/wallet-lib/src/types/Account/methods/importTransactions.js @@ -1,7 +1,4 @@ -const { chain } = require('lodash/seq'); const logger = require('../../../logger'); -const { WALLET_TYPES } = require('../../../CONSTANTS'); -const ensureAddressesToGapLimit = require('../../../utils/bip44/ensureAddressesToGapLimit'); /** * Import transactions and always keep a number of unused addresses up to gap diff --git a/packages/wallet-lib/src/types/Account/methods/injectPlugin.js b/packages/wallet-lib/src/types/Account/methods/injectPlugin.js index 202e5b61261..4405e29b49e 100644 --- a/packages/wallet-lib/src/types/Account/methods/injectPlugin.js +++ b/packages/wallet-lib/src/types/Account/methods/injectPlugin.js @@ -90,9 +90,9 @@ module.exports = async function injectPlugin( const setReadyWatch = (_watcher) => _watcher.ready = true; const onStartedEvent = () => startWatcher(watcher) - && logger.silly(`WORKER/${pluginName.toUpperCase()}/STARTED`); + && logger.silly(`WORKER/${pluginName.toUpperCase()}/STARTED`); const onExecuteEvent = () => setReadyWatch(watcher) - && logger.silly(`WORKER/${pluginName.toUpperCase()}/EXECUTED`); + && logger.silly(`WORKER/${pluginName.toUpperCase()}/EXECUTED`); self.on(`WORKER/${pluginName.toUpperCase()}/STARTED`, onStartedEvent); self.on(`WORKER/${pluginName.toUpperCase()}/EXECUTED`, onExecuteEvent); diff --git a/packages/wallet-lib/src/types/Account/methods/injectPlugin.spec.js b/packages/wallet-lib/src/types/Account/methods/injectPlugin.spec.js index 68bd7c9b885..161c1f76cc1 100644 --- a/packages/wallet-lib/src/types/Account/methods/injectPlugin.spec.js +++ b/packages/wallet-lib/src/types/Account/methods/injectPlugin.spec.js @@ -22,7 +22,7 @@ describe('Account - injectPlugin', function suite() { emit: emitter.emit, } it('should prevent sensible access', async function () { - const expectedException1 = 'Injection of plugin : storage Unallowed'; + const expectedException1 = 'Injection of plugin : storage into WorkingWorker not allowed'; await expectThrowsAsync(async () => await injectPlugin.call(mockedSelf, WorkingWorker), expectedException1); }); it('should work', function (done) { @@ -42,9 +42,9 @@ describe('Account - injectPlugin', function suite() { it('should handle faulty worker', async function () { const expectedException1 = 'Some reason.'; await expectThrowsAsync(async () => await injectPlugin.call(mockedSelf, FaultyWorker, true), expectedException1); - expect(mockedSelf.plugins.workers['faultyworker']).to.exist; - expect(mockedSelf.plugins.workers['faultyworker'].worker).to.equal(null); - expect(mockedSelf.plugins.workers['faultyworker'].isWorkerRunning).to.equal(false); - expect(mockedSelf.plugins.workers['faultyworker'].state.started).to.equal(false); + expect(mockedSelf.plugins.workers['faultyworker']).to.exist; + expect(mockedSelf.plugins.workers['faultyworker'].worker).to.equal(null); + expect(mockedSelf.plugins.workers['faultyworker'].isWorkerRunning).to.equal(false); + expect(mockedSelf.plugins.workers['faultyworker'].state.started).to.equal(false); }); }); diff --git a/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.spec.js b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.spec.js index e13dc087569..87e60e4d58d 100644 --- a/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.spec.js +++ b/packages/wallet-lib/src/types/Identities/methods/getIdentityHDKeyById.spec.js @@ -1,7 +1,6 @@ const { expect } = require('chai'); const mockedStore = require('../../../../fixtures/sirentonight-fullstore-snapshot-1562711703'); const getIdentityHDKeyById = require('./getIdentityHDKeyById'); -const searchTransaction = require('../../Storage/methods/searchTransaction'); let walletMock; let fetchTransactionInfoCalledNb = 0; @@ -10,12 +9,15 @@ describe('Wallet#getIdentityHDKeyById', function suite() { this.timeout(10000); before(() => { expectedKeyMock = "123"; + const walletStoreMock = { + getIndexedIdentityIds: () => mockedStore.wallets[Object.keys(mockedStore.wallets)].identityIds + } + const storageMock = { store: mockedStore, getStore: () => mockedStore, mappedAddress: {}, - searchTransaction, - getIndexedIdentityIds: () => mockedStore.wallets[Object.keys(mockedStore.wallets)].identityIds, + getWalletStore: () => walletStoreMock, }; const walletId = Object.keys(mockedStore.wallets)[0]; walletMock = { diff --git a/packages/wallet-lib/src/types/Storage/Storage.js b/packages/wallet-lib/src/types/Storage/Storage.js index 10ecd29ce3c..731f0d14d2a 100644 --- a/packages/wallet-lib/src/types/Storage/Storage.js +++ b/packages/wallet-lib/src/types/Storage/Storage.js @@ -1,4 +1,13 @@ const EventEmitter = require('events'); +const { has } = require('lodash'); +const CONSTANTS = require('../../CONSTANTS'); + +const defaultOpts = { + rehydrate: true, + autosave: true, + autosaveIntervalTime: CONSTANTS.STORAGE.autosaveIntervalTime, + network: 'testnet', +}; /** * Handle all the storage logic, it's a wrapper around the adapters @@ -6,18 +15,27 @@ const EventEmitter = require('events'); * should be limited. * */ class Storage extends EventEmitter { - constructor() { + constructor(opts = {}) { super(); this.wallets = new Map(); this.chains = new Map(); this.application = { blockHeight: 0, }; + + this.rehydrate = has(opts, 'rehydrate') ? opts.rehydrate : defaultOpts.rehydrate; + this.autosave = has(opts, 'autosave') ? opts.autosave : defaultOpts.autosave; + this.autosaveIntervalTime = has(opts, 'autosaveIntervalTime') + ? opts.autosaveIntervalTime + : defaultOpts.autosaveIntervalTime; + + this.lastRehydrate = null; + this.lastSave = null; + this.lastModified = null; } } Storage.prototype.configure = require('./methods/configure'); -Storage.prototype.createAccountStore = require('./methods/createAccountStore'); Storage.prototype.createChainStore = require('./methods/createChainStore'); Storage.prototype.createWalletStore = require('./methods/createWalletStore'); Storage.prototype.getChainStore = require('./methods/getChainStore'); diff --git a/packages/wallet-lib/src/types/Storage/methods/calculateDuffBalance.js b/packages/wallet-lib/src/types/Storage/methods/calculateDuffBalance.js deleted file mode 100644 index 8f8e75841b1..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/calculateDuffBalance.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * - * @param walletId - The wallet Id where to perform the calculation - * @param accountIndex - The account Index where to perform the calculation - * @param type {{'confirmed','unconfirmed','total'}} Default: total. Calculate balance by utxo type. - * @return {number} Balance in duff - */ -module.exports = function calculateDuffBalance(walletId, accountIndex, type = 'total') { - let totalSat = 0; - if (walletId === undefined || accountIndex === undefined) { - throw new Error('Cannot calculate without walletId and accountIndex params'); - } - - const { addresses } = this.getStore().wallets[walletId]; - const subwallets = Object.keys(addresses); - subwallets.forEach((subwallet) => { - const paths = Object.keys(addresses[subwallet]) - // We filter out other potential account - .filter((el) => { - const splitted = el.split('/'); - const index = parseInt((splitted.length === 1) ? splitted[0] : splitted[3], 10); - return index === accountIndex; - }); - - paths.forEach((path) => { - const address = addresses[subwallet][path]; - const { balanceSat, unconfirmedBalanceSat } = address; - switch (type) { - case 'total': - totalSat += balanceSat + unconfirmedBalanceSat; - break; - case 'confirmed': - totalSat += balanceSat; - break; - case 'unconfirmed': - totalSat += unconfirmedBalanceSat; - break; - default: - throw new Error(`Unexpected balance type. Got ${type}`); - } - }); - }); - - return totalSat; -}; diff --git a/packages/wallet-lib/src/types/Storage/methods/createAccountStore.js b/packages/wallet-lib/src/types/Storage/methods/createAccountStore.js deleted file mode 100644 index 3069a3b0d25..00000000000 --- a/packages/wallet-lib/src/types/Storage/methods/createAccountStore.js +++ /dev/null @@ -1,29 +0,0 @@ -const { hasProp } = require('../../../utils'); - -/** - * Create a new account store into a wallet - * @param {string} walletId - * @param {string} path - * @param {string} network - * @param {string|null} [label] - * @return {boolean} - */ -module.exports = function createAccount(walletId, path, network, label = null) { - const walletStore = this.getWalletStore(walletId); - // if (!hasProp(this.store.wallets, walletId.toString())) { - // if (!this.searchWallet(walletId).found) { - // this.createWallet(walletId, network); - // } - // } - // if (!hasProp(this.store.wallets[walletId].accounts, path.toString())) { - // this.store.wallets[walletId].accounts[path.toString()] = { - // label, - // path, - // network, - // blockHeight: 0, // Used to keep track of local state sync of the account - // }; - // - // return true; - // } - // return false; -}; diff --git a/packages/wallet-lib/src/types/Storage/methods/createWalletStore.js b/packages/wallet-lib/src/types/Storage/methods/createWalletStore.js index d3053b1a889..b398746ac47 100644 --- a/packages/wallet-lib/src/types/Storage/methods/createWalletStore.js +++ b/packages/wallet-lib/src/types/Storage/methods/createWalletStore.js @@ -1,9 +1,6 @@ -const Dashcore = require('@dashevo/dashcore-lib'); const WalletStore = require('../../WalletStore/WalletStore'); -const { testnet } = Dashcore.Networks; - -const createWalletStore = function createWallet(walletId = 'squawk7700', network = testnet.toString(), mnemonic = null, type = null) { +const createWalletStore = function createWallet(walletId = 'squawk7700') { if (!this.wallets.has(walletId)) { this.wallets.set(walletId, new WalletStore(walletId)); return true; diff --git a/packages/wallet-lib/src/types/Storage/methods/rehydrateState.js b/packages/wallet-lib/src/types/Storage/methods/rehydrateState.js index 4381086714a..96b8fda6897 100644 --- a/packages/wallet-lib/src/types/Storage/methods/rehydrateState.js +++ b/packages/wallet-lib/src/types/Storage/methods/rehydrateState.js @@ -1,9 +1,10 @@ const { merge } = require('lodash'); -const { InstantLock } = require('@dashevo/dashcore-lib'); const { hasMethod } = require('../../../utils'); const mergeHelper = (initial = {}, additional = {}) => merge(initial, additional); const { REHYDRATE_STATE_FAILED, REHYDRATE_STATE_SUCCESS } = require('../../../EVENTS'); +const WalletStore = require('../../WalletStore/WalletStore'); +const ChainStore = require('../../ChainStore/ChainStore'); /** * Fetch the state from the persistence adapter @@ -12,31 +13,27 @@ const { REHYDRATE_STATE_FAILED, REHYDRATE_STATE_SUCCESS } = require('../../../EV const rehydrateState = async function rehydrateState() { if (this.rehydrate && this.lastRehydrate === null) { try { - const transactions = (this.adapter && hasMethod(this.adapter, 'getItem')) - ? (await this.adapter.getItem('transactions') || this.store.transactions) - : this.store.transactions; - const wallets = (this.adapter && hasMethod(this.adapter, 'getItem')) - ? (await this.adapter.getItem('wallets') || this.store.wallets) - : this.store.wallets; - const chains = (this.adapter && hasMethod(this.adapter, 'getItem')) - ? (await this.adapter.getItem('chains') || this.store.chains) - : this.store.chains; - const instantLocks = (this.adapter && hasMethod(this.adapter, 'getItem')) - ? (await this.adapter.getItem('instantLocks') || this.store.instantLocks) - : this.store.instantLocks; + if (this.adapter && hasMethod(this.adapter, 'getItem')) { + const wallets = await this.adapter.getItem('wallets'); + if (wallets) { + wallets.forEach((walletState) => { + const walletStore = new WalletStore(); + walletStore.importState(walletState); + this.wallets.add(walletStore); + }); + } - // We need to keep deserialized instant locks - Object.keys(instantLocks).forEach((transactionHash) => { - const instantLock = instantLocks[transactionHash]; - if (!(instantLock instanceof InstantLock)) { - instantLocks[transactionHash] = new InstantLock(instantLock); + const chains = await this.adapter.getItem('chains'); + if (chains) { + chains.forEach((chainState) => { + const chainStore = new ChainStore(); + chainStore.importState(chainState); + this.chains.add(chainStore); + }); } - }); + this.application = mergeHelper(this.application, await this.adapter.getItem('application')); + } - this.store.transactions = mergeHelper(this.store.transactions, transactions); - this.store.wallets = mergeHelper(this.store.wallets, wallets); - this.store.chains = mergeHelper(this.store.chains, chains); - this.store.instantLocks = mergeHelper(this.store.instantLocks, instantLocks); this.lastRehydrate = +new Date(); this.emit(REHYDRATE_STATE_SUCCESS, { type: REHYDRATE_STATE_SUCCESS, payload: null }); } catch (e) { diff --git a/packages/wallet-lib/src/types/Storage/methods/saveState.js b/packages/wallet-lib/src/types/Storage/methods/saveState.js index 2eb95eab9bd..7b6562a61bc 100644 --- a/packages/wallet-lib/src/types/Storage/methods/saveState.js +++ b/packages/wallet-lib/src/types/Storage/methods/saveState.js @@ -8,10 +8,9 @@ const saveState = async function saveState() { if (this.autosave && this.adapter && this.adapter.setItem) { const self = this; try { - await this.adapter.setItem('transactions', { ...self.store.transactions }); - await this.adapter.setItem('wallets', { ...self.store.wallets }); - await this.adapter.setItem('chains', { ...self.store.chains }); - await this.adapter.setItem('instantLocks', { ...self.store.instantLocks }); + await this.adapter.setItem('wallets', [...self.wallets].map(([, walletStore]) => walletStore.exportState())); + await this.adapter.setItem('chains', [...self.chains].map(([, chainStore]) => chainStore.exportState())); + await this.adapter.setItem('application', { ...self.application }); this.lastSave = +new Date(); this.emit(SAVE_STATE_SUCCESS, { type: SAVE_STATE_SUCCESS, payload: this.lastSave }); return true; diff --git a/packages/wallet-lib/src/types/Wallet/Wallet.js b/packages/wallet-lib/src/types/Wallet/Wallet.js index 32738df9f71..04089436199 100644 --- a/packages/wallet-lib/src/types/Wallet/Wallet.js +++ b/packages/wallet-lib/src/types/Wallet/Wallet.js @@ -112,21 +112,22 @@ class Wallet extends EventEmitter { this.storage = new Storage({ rehydrate: true, autosave: true, - network, }); this.storage.configure({ adapter: opts.adapter, }); - this.store = this.storage.store; + this.storage.application.network = this.network; + this.storage.createWalletStore(this.walletId); + this.storage.createChainStore(this.network); if (createdFromNewMnemonic) { // As it is pretty complicated to pass any of wallet options // to a specific plugin, using `store` as an options mediator // is easier. - this.store.syncOptions = { + this.storage.application.syncOptions = { skipSynchronization: true, }; @@ -135,7 +136,7 @@ class Wallet extends EventEmitter { + ' created from the new mnemonic'); } } else if (this.unsafeOptions.skipSynchronizationBeforeHeight) { - this.store.syncOptions = { + this.storage.application.syncOptions = { skipSynchronizationBeforeHeight: this.unsafeOptions.skipSynchronizationBeforeHeight, }; } @@ -173,7 +174,6 @@ class Wallet extends EventEmitter { this.accounts = []; this.interface = opts.interface; - // Suppressed global require to avoid cyclic dependencies // eslint-disable-next-line global-require const Identities = require('../Identities/Identities'); diff --git a/packages/wallet-lib/src/types/Wallet/methods/sweepWallet.spec.js b/packages/wallet-lib/src/types/Wallet/methods/sweepWallet.spec.js index 77935054505..21af353bc23 100644 --- a/packages/wallet-lib/src/types/Wallet/methods/sweepWallet.spec.js +++ b/packages/wallet-lib/src/types/Wallet/methods/sweepWallet.spec.js @@ -4,7 +4,7 @@ const sweepWallet = require('./sweepWallet'); const paperWallet = { publicKey: 'ybvbBPisVjiemj4qSg1mzZAzTSAPk64Ppf', - privateKey: 'XE6ZTNwkjyuryGho75fAfCBBtL8rMy9ttLq1ANLF1TmMo2zwZXHq', + privateKey: '53d0f7df9103127f159f939438254011f6fa11df18a843d3962313e38938f020', }; describe('Wallet - sweepWallet', function suite() { @@ -12,11 +12,11 @@ describe('Wallet - sweepWallet', function suite() { let emptyWallet; let emptyAccount; const transportOpts = (process.env.DAPI_SEED) - ? { - seeds: process.env.DAPI_SEED - .split(',') - } - : {} + ? { + seeds: process.env.DAPI_SEED + .split(',') + } + : {} before(async () => { emptyWallet = new Wallet({ privateKey: paperWallet.privateKey, diff --git a/packages/wallet-lib/src/utils/calculateDuffBalance.js b/packages/wallet-lib/src/utils/calculateDuffBalance.js new file mode 100644 index 00000000000..82be58fe1e9 --- /dev/null +++ b/packages/wallet-lib/src/utils/calculateDuffBalance.js @@ -0,0 +1,28 @@ +/** + * + * @param walletId - The wallet Id where to perform the calculation + * @param accountIndex - The account Index where to perform the calculation + * @param type {{'confirmed','unconfirmed','total'}} Default: total. Calculate balance by utxo type. + * @return {number} Balance in duff + */ +module.exports = function calculateDuffBalance(addresses, chainStore, type = 'total') { + let totalSat = 0; + + addresses.forEach((address) => { + const addressData = chainStore.getAddress(address); + switch (type) { + case 'total': + totalSat += addressData.balanceSat + addressData.unconfirmedBalanceSat; + break; + case 'confirmed': + totalSat += addressData.balanceSat; + break; + case 'unconfirmed': + totalSat += addressData.unconfirmedBalanceSat; + break; + default: + throw new Error(`Unexpected balance type. Got ${type}`); + } + }); + return totalSat; +}; diff --git a/packages/wallet-lib/src/utils/categorizeTransactions.js b/packages/wallet-lib/src/utils/categorizeTransactions.js index b13bbfaf483..1b84ac88f41 100644 --- a/packages/wallet-lib/src/utils/categorizeTransactions.js +++ b/packages/wallet-lib/src/utils/categorizeTransactions.js @@ -7,37 +7,52 @@ const { TRANSACTION_HISTORY_TYPES } = require('../CONSTANTS'); // and a sent transaction where our own address is a change... const determineType = (inputsDetection, outputsDetection) => { let type = TRANSACTION_HISTORY_TYPES.UNKNOWN; - if (inputsDetection.hasExternalAddress) { - type = TRANSACTION_HISTORY_TYPES.RECEIVED; - } - if (outputsDetection.hasExternalAddress) { - type = TRANSACTION_HISTORY_TYPES.RECEIVED; - } - if ( - !outputsDetection.hasExternalAddress - && (inputsDetection.hasChangeAddress || inputsDetection.hasExternalAddress) - ) { - type = TRANSACTION_HISTORY_TYPES.SENT; - } - if (inputsDetection.hasExternalAddress && outputsDetection.hasExternalAddress) { - type = TRANSACTION_HISTORY_TYPES.ADDRESS_TRANSFER; - } else if ( - (inputsDetection.hasExternalAddress && outputsDetection.hasOtherAccountAddress) - || (inputsDetection.hasOtherAccountAddress && outputsDetection.hasExternalAddress) + + // We first discriminate with account transfer from or to another account + if (inputsDetection.hasOtherAccountAddress + && !inputsDetection.hasOwnAddress) { + type = TRANSACTION_HISTORY_TYPES.ACCOUNT_TRANSFER; + } else if (inputsDetection.hasOwnAddress + && outputsDetection.hasOtherAccountAddress ) { type = TRANSACTION_HISTORY_TYPES.ACCOUNT_TRANSFER; + } else if (inputsDetection.hasOwnAddress + && !outputsDetection.hasUnknownAddress + && !outputsDetection.hasOtherAccountAddress) { + // Detecting an address transfer is the second element we need to discriminate + type = TRANSACTION_HISTORY_TYPES.ADDRESS_TRANSFER; + } else { + if (inputsDetection.hasExternalAddress) { + type = TRANSACTION_HISTORY_TYPES.RECEIVED; + } + if (outputsDetection.hasExternalAddress && !inputsDetection.hasExternalAddress) { + type = TRANSACTION_HISTORY_TYPES.RECEIVED; + } + if ( + outputsDetection.hasUnknownAddress + && (inputsDetection.hasOwnAddress) + ) { + type = TRANSACTION_HISTORY_TYPES.SENT; + } } + return type; }; -function categorizeTransactions(transactionsWithMetadata, accountStore, accountIndex, walletType, network = 'testnet') { +function categorizeTransactions( + transactionsWithMetadata, + walletStore, + accountIndex, + walletType, + network = 'testnet', +) { const categorizedTransactions = []; const { - externalAddressList, - internalAddressList, - otherAccountAddressList, - } = classifyAddresses(accountStore.addresses, accountIndex, walletType); + externalAddressesList, + internalAddressesList, + otherAccountAddressesList, + } = classifyAddresses(walletStore, accountIndex, walletType, network); each(transactionsWithMetadata, (transactionWithMetadata) => { const [transaction, metadata] = transactionWithMetadata; @@ -47,19 +62,29 @@ function categorizeTransactions(transactionsWithMetadata, accountStore, accountI let outputsHasChangeAddress = false; let outputsHasExternalAddress = false; let outputsHasOtherAccountAddress = false; + let outputsHasOwnAddress = false; + let outputsHasUnknownAddress = false; let inputsHasChangeAddress = false; let inputsHasExternalAddress = false; let inputsHasOtherAccountAddress = false; + let inputsHasOwnAddress = false; + let inputsHasUnknownAddress = false; // For each vout, we will look at matching known addresses transaction.outputs.forEach((vout) => { const { satoshis, script } = vout; const address = script.toAddress(network).toString(); if (address) { - if (internalAddressList.includes(address)) outputsHasChangeAddress = true; - if (externalAddressList.includes(address)) outputsHasExternalAddress = true; - if (otherAccountAddressList.includes(address)) outputsHasOtherAccountAddress = true; + if (internalAddressesList.includes(address)) { + outputsHasChangeAddress = true; + outputsHasOwnAddress = true; + } else if (externalAddressesList.includes(address)) { + outputsHasExternalAddress = true; + outputsHasOwnAddress = true; + } else if (otherAccountAddressesList.includes(address)) { + outputsHasOtherAccountAddress = true; + } else outputsHasUnknownAddress = true; to.push({ address, satoshis, @@ -72,9 +97,15 @@ function categorizeTransactions(transactionsWithMetadata, accountStore, accountI const { script } = vin; const address = script.toAddress(network).toString(); if (address) { - if (internalAddressList.includes(address)) inputsHasChangeAddress = true; - if (externalAddressList.includes(address)) inputsHasExternalAddress = true; - if (otherAccountAddressList.includes(address)) inputsHasOtherAccountAddress = true; + if (internalAddressesList.includes(address)) { + inputsHasChangeAddress = true; + inputsHasOwnAddress = true; + } else if (externalAddressesList.includes(address)) { + inputsHasExternalAddress = true; + inputsHasOwnAddress = true; + } else if (otherAccountAddressesList.includes(address)) { + inputsHasOtherAccountAddress = true; + } else inputsHasUnknownAddress = true; from.push({ address, }); @@ -85,10 +116,14 @@ function categorizeTransactions(transactionsWithMetadata, accountStore, accountI hasChangeAddress: inputsHasChangeAddress, hasExternalAddress: inputsHasExternalAddress, hasOtherAccountAddress: inputsHasOtherAccountAddress, + hasOwnAddress: inputsHasOwnAddress, + hasUnknownAddress: inputsHasUnknownAddress, }, { hasChangeAddress: outputsHasChangeAddress, hasExternalAddress: outputsHasExternalAddress, hasOtherAccountAddress: outputsHasOtherAccountAddress, + hasOwnAddress: outputsHasOwnAddress, + hasUnknownAddress: outputsHasUnknownAddress, }); const categorizedTransaction = { @@ -98,8 +133,8 @@ function categorizeTransactions(transactionsWithMetadata, accountStore, accountI type, blockHash: metadata.blockHash, height: metadata.height, - isInstantLocked: metadata.instantLocked, - isChainLocked: metadata.chainLocked, + isInstantLocked: metadata.isInstantLocked, + isChainLocked: metadata.isChainLocked, }; categorizedTransactions.push(categorizedTransaction); }); diff --git a/packages/wallet-lib/src/utils/categorizeTransactions.spec.js b/packages/wallet-lib/src/utils/categorizeTransactions.spec.js index 1d48b25a849..4d9e71c22a4 100644 --- a/packages/wallet-lib/src/utils/categorizeTransactions.spec.js +++ b/packages/wallet-lib/src/utils/categorizeTransactions.spec.js @@ -5,9 +5,10 @@ const {WALLET_TYPES} = require('../CONSTANTS'); const categorizeTransactions = require('./categorizeTransactions'); const transactionsWithMetadataFixtures = require('../../fixtures/wallets/apart-trip-dignity/transactions-with-metadata.json'); -const addressesFixtures = require('../../fixtures/wallets/apart-trip-dignity/addresses.json'); -const walletFixtures = require('../../fixtures/wallets/apart-trip-dignity/wallet.json'); const expectedResults = require('../../fixtures/wallets/apart-trip-dignity/categorizeTransactions.expectedResults'); +const getFixtureHDAccountWithStorage = require("../../fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage"); + +const mockedHDAccount = getFixtureHDAccountWithStorage(); const prepareTransactionsWithMetadata = () => { const transactionsWithMetadata = []; @@ -46,14 +47,7 @@ const normalizeResults = (results) =>{ describe('Utils - categorizeTransactions', function suite() { const transactionsWithMetadata = prepareTransactionsWithMetadata(); - const accountStore = { - accounts: walletFixtures.store.accounts, - network: walletFixtures.network, - mnemonic: null, - type: walletFixtures.type, - identityIds: walletFixtures.identityIds, - addresses: addressesFixtures - }; + const accountStore = mockedHDAccount.storage.getWalletStore(mockedHDAccount.walletId); const accountIndex = 0; const walletType = WALLET_TYPES.HDWALLET; diff --git a/packages/wallet-lib/src/utils/classifyAddresses.js b/packages/wallet-lib/src/utils/classifyAddresses.js index 52dfcff9b33..c6f6f55d297 100644 --- a/packages/wallet-lib/src/utils/classifyAddresses.js +++ b/packages/wallet-lib/src/utils/classifyAddresses.js @@ -1,34 +1,49 @@ -const { map, filter, difference } = require('lodash'); -const { WALLET_TYPES } = require('../CONSTANTS'); +const { WALLET_TYPES, BIP44_LIVENET_ROOT_PATH, BIP44_TESTNET_ROOT_PATH } = require('../CONSTANTS'); -function classifyAddresses(addressStore, accountIndex, walletType) { - const { external, internal, misc } = addressStore; +function classifyAddresses(walletStore, accountIndex, walletType, network = 'testnet') { + const externalAddressesList = []; + const internalAddressesList = []; + const otherAccountAddressesList = []; + const miscAddressesList = []; - // This will filter addresses to return only the one that are directly one the account manage. - // TODO: Computational improvement can be made by having accountIndex - // member of the address info format and thus comparing only 2 numbers - const filterPathByAccount = (address) => (parseInt(address.path.split('/')[3], 10) === accountIndex); - const addressMappingPredicate = (addressInfo) => (addressInfo.address); + const rootPath = (network.toString() === 'testnet') + ? BIP44_TESTNET_ROOT_PATH + : BIP44_LIVENET_ROOT_PATH; - const externalAddressList = (walletType === WALLET_TYPES.HDWALLET) - ? map(filter(external, filterPathByAccount), addressMappingPredicate) - : map(misc, addressMappingPredicate); + const accountsPaths = [...walletStore.state.paths.keys()]; - const internalAddressList = (walletType === WALLET_TYPES.HDWALLET) - ? map(filter(internal, filterPathByAccount), addressMappingPredicate) - : []; + const isHDWallet = [ + WALLET_TYPES.HDWALLET, + WALLET_TYPES.HDPRIVATE, + WALLET_TYPES.HDPUBLIC].includes(walletType); - const otherAccountAddressList = (walletType === WALLET_TYPES.HDWALLET) - ? difference( - [...map(external, addressMappingPredicate), ...map(internal, addressMappingPredicate)], - [...externalAddressList, ...internalAddressList], - ) - : []; + const currentAccountPath = (isHDWallet) ? `${rootPath}/${accountIndex}'` : `m/${accountIndex}`; + + accountsPaths.forEach((accountPath) => { + const isCurrentAccountPath = accountPath === currentAccountPath; + const accountPaths = walletStore.getPathState(accountPath); + + Object.entries(accountPaths.addresses) + .forEach(([path, address]) => { + if (isCurrentAccountPath) { + if (isHDWallet) { + if (path.startsWith('m/0')) externalAddressesList.push(address); + else if (path.startsWith('m/1')) internalAddressesList.push(address); + else miscAddressesList.push(address); + } else { + externalAddressesList.push(address); + } + } else { + otherAccountAddressesList.push(address); + } + }); + }); return { - externalAddressList, - internalAddressList, - otherAccountAddressList, + externalAddressesList, + internalAddressesList, + otherAccountAddressesList, + miscAddressesList, }; } module.exports = classifyAddresses; diff --git a/packages/wallet-lib/src/utils/classifyAddresses.spec.js b/packages/wallet-lib/src/utils/classifyAddresses.spec.js index a0f55d39ee0..d3a1969e8b5 100644 --- a/packages/wallet-lib/src/utils/classifyAddresses.spec.js +++ b/packages/wallet-lib/src/utils/classifyAddresses.spec.js @@ -1,526 +1,22 @@ -const { expect } = require('chai'); -const { WALLET_TYPES } = require('../CONSTANTS'); +const {expect} = require('chai'); +const {WALLET_TYPES} = require('../CONSTANTS'); const classifyAddresses = require('./classifyAddresses'); +const getFixtureHDAccountWithStorage = require("../../fixtures/wallets/apart-trip-dignity/getFixtureAccountWithStorage"); + +const mockedHDAccount = getFixtureHDAccountWithStorage(); describe('Utils - classifyAddresses', function suite() { it('should correctly classify address for HDWallet', function () { const walletType = WALLET_TYPES.HDWALLET; const accountIndex = 0; - const accountStore = { - accounts: { - "m/44'/1'/0'": { - label: null, - path: "m/44'/1'/0'", - network: 'testnet', - blockHeight: 554643, - blockHash: '0000007a84abfe1d2b4201f4844bb1e59f24daf965c928281589269f281abc01' - } - }, - network: 'testnet', - mnemonic: null, - type: null, - identityIds: [], - addresses: { - external:{ - "m/44'/1'/0'/0/0": { - path: "m/44'/1'/0'/0/0", - index: 0, - address: 'yd1ohc12LgCYp56CDuckTEHwoa6LbPghMd', - transactions: ["1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6"], - balanceSat: 642650000, - unconfirmedBalanceSat: 0, - utxos: { - "1a74dc225b3336c4edb1f94c9ec2ed88fd0ef136866fda26f8a734924407b4d6-1" : {} - }, - fetchedLast: 0, - used: true - }, - "m/44'/1'/0'/0/1": { - path: "m/44'/1'/0'/0/1", - index: 1, - address: 'yMX3ycrLVF2k6YxWQbMoYgs39aeTfY4wrB', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/2": { - path: "m/44'/1'/0'/0/2", - index: 2, - address: 'ydJGUUmNxdmvyskoZXqtJRqWyqsaFPitGQ', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/3": { - path: "m/44'/1'/0'/0/3", - index: 3, - address: 'yhugNjDRVJUL7PK9MqQP9M6M1HJm8zuWJL', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/4": { - path: "m/44'/1'/0'/0/4", - index: 4, - address: 'ySgckdRYxpa7Uda8yUNRqjYeuusqLy3AY3', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/5": { - path: "m/44'/1'/0'/0/5", - index: 5, - address: 'yS4DeSU3MTBisgL6p8PDRzSxTPN2PUt3vE', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/6": { - path: "m/44'/1'/0'/0/6", - index: 6, - address: 'yRZef5UGotGgLMaLYTzhvfknogqMRBkUiX', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/7": { - path: "m/44'/1'/0'/0/7", - index: 7, - address: 'yTUw2bGzi9rYs41XH1dxbRqiJoDtwuUcv2', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/8": { - path: "m/44'/1'/0'/0/8", - index: 8, - address: 'yNQHAGhNP7UbhxnkZH4muP2oKkuayGEuwX', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/9": { - path: "m/44'/1'/0'/0/9", - index: 9, - address: 'yPPDLBDjHctWpMxLiMTJXLngcYPke7YNaY', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/10": { - path: "m/44'/1'/0'/0/10", - index: 10, - address: 'ya2vVJAJdZN2We7MYiSjGf9wkdWF6A1RLr', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/11": { - path: "m/44'/1'/0'/0/11", - index: 11, - address: 'ya5k2YMjfyfxZoidq4UdQ65jYUXvtVomEv', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/12": { - path: "m/44'/1'/0'/0/12", - index: 12, - address: 'yhXZja3Apyp9S32zEVsPqLssNJZLrczxJC', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/13": { - path: "m/44'/1'/0'/0/13", - index: 13, - address: 'yhWqJXsp25aZNQHEprebQrqPoANj6A13Aa', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/14": { - path: "m/44'/1'/0'/0/14", - index: 14, - address: 'yjV3sKAGsuJHDGyf6HDMNuLfMgGp5pBxRy', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/15": { - path: "m/44'/1'/0'/0/15", - index: 15, - address: 'yjPeTiRatdvotxUuPFEPDJc2aF774uMB9J', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/16": { - path: "m/44'/1'/0'/0/16", - index: 16, - address: 'yRrhuVw6Vd3NzgYrfqb1oTvdhyxzDT9PGz', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/17": { - path: "m/44'/1'/0'/0/17", - index: 17, - address: 'yYpL5JJLVGfJXPE15ZMQzNvUGkD4JY6ETF', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/18": { - path: "m/44'/1'/0'/0/18", - index: 18, - address: 'ygcW1365Hs2LSLY5LXnkJAUB94pS4HouNu', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/0/19": { - path: "m/44'/1'/0'/0/19", - index: 19, - address: 'yW8RA7zTUz14sNiGjvFQaNupugwwmE1aQi', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - } - }, - internal: { - "m/44'/1'/0'/1/0": { - path: "m/44'/1'/0'/1/0", - index: 0, - address: 'yaLhoAZ4iex2zKmfvS9rvEmxXmRiPrjHdD', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/1": { - path: "m/44'/1'/0'/1/1", - index: 1, - address: 'yTcjWB7v7opDzpfYKpFdFEtEvSKFsh3bW3', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/2": { - path: "m/44'/1'/0'/1/2", - index: 2, - address: 'yiDVYtUZ2mKV4teSJzKBArqY4BRsZoFLYs', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/3": { - path: "m/44'/1'/0'/1/3", - index: 3, - address: 'ya7Me5KMoSz5x4GGZ1pJGrJjC3yMkDDWDa', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/4": { - path: "m/44'/1'/0'/1/4", - index: 4, - address: 'yXwS8mRrrxF3pt1GfG7yGKNpPnD6pdwX3a', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/5": { - path: "m/44'/1'/0'/1/5", - index: 5, - address: 'yXd4eqycSaJRhRZxXT3iK5H34af4TV5REE', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/6": { - path: "m/44'/1'/0'/1/6", - index: 6, - address: 'yWaDfpToRxHc3qtcd8P1agW4Fvj1ueWgwH', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/7": { - path: "m/44'/1'/0'/1/7", - index: 7, - address: 'yPYdu2jDrD3Bai83AdvHTYwpAgAkzMaCcM', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/8": { - path: "m/44'/1'/0'/1/8", - index: 8, - address: 'yMEkuZ67vZ6kUgDHVVDSTzwU3GbHoTFwqR', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/9": { - path: "m/44'/1'/0'/1/9", - index: 9, - address: 'yYSSMwkEqU4hNF2z5kbVBTDYtgt8dQQYd7', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/10": { - path: "m/44'/1'/0'/1/10", - index: 10, - address: 'yfzAa63gQ6arpyBzuqQtZmSnU8HLnJnEan', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/11": { - path: "m/44'/1'/0'/1/11", - index: 11, - address: 'ySjTorgG6VVfPiY7TJ2tdU2hkohfFAtzJf', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/12": { - path: "m/44'/1'/0'/1/12", - index: 12, - address: 'yMUJsy5HEiQTeDgqxY2zBGEeTCDr4g5V8c', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/13": { - path: "m/44'/1'/0'/1/13", - index: 13, - address: 'yWDkvzhKk7BKEU6Ybz1Kyarejzt8zhSqxy', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/14": { - path: "m/44'/1'/0'/1/14", - index: 14, - address: 'yTJQE4vYXRdEcPt3nADF36S6FKLkLRG2Ty', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/15": { - path: "m/44'/1'/0'/1/15", - index: 15, - address: 'yTjku3TMxJN3uiiSHYB2tQ4wp1rJJ92M2A', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/16": { - path: "m/44'/1'/0'/1/16", - index: 16, - address: 'yZ8JgZrpuEr1srdcgLgTAsomtMBgSYwTrW', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/17": { - path: "m/44'/1'/0'/1/17", - index: 17, - address: 'yUG2YZrss5JfZrN4AG4RKkkbfYyd4H6TSx', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/18": { - path: "m/44'/1'/0'/1/18", - index: 18, - address: 'yiLMqyvjBV3CqkCL8H44bRSUBap7tPCmvo', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - }, - "m/44'/1'/0'/1/19": { - path: "m/44'/1'/0'/1/19", - index: 19, - address: 'yfrCYcuD7ezYnpNAmbPMjrTETjipsNGsEe', - transactions: [], - balanceSat: 0, - unconfirmedBalanceSat: 0, - utxos: {}, - fetchedLast: 0, - used: false - } - }, - misc: {} + const result = classifyAddresses(mockedHDAccount.storage.getWalletStore(mockedHDAccount.walletId), accountIndex, walletType); + const expectedResult = { + "externalAddressesList": ["yTwEca67QSkZ6axGdpNFzWPaCj8zqYybY7", "yercyhdN9oEkZcB9BsW5ktFaDxFEuK6qXN", "ygk3GCSba2J3L9G665Snozhj9HSkh5ByVE", "ybuL6rM6dgrKzCg8s99f3jxGuv5oz5JcDA", "ygHAVkMtYSqoTWHebDv7qkhMV6dHyuRsp2", "yMLhEsiP2ajSh8STmXnNmkWXtoHsmawZxd", "yj8rRKATAUHcAgXvNZekob58xKm2oNyvhv", "yhaAB6e8m3F8zmGX7WAVYa6eEfmSrrnY8x", "yiXh4Yo5djG6QH8WzXkKm5EFzqLRJWakXz", "yQYv3Um6DsdtANo1ZPTUte75wAGMstLRex", "yiYPJmu7eEm1cXUNumQRdjv1fvPhsfgMS4", "yii4aUZhNfL6EWN9KAgAFrJzGJmqHnF4wx", "yLpTquSct2SGz2Ka45uTPDd81Kzro2Jt2k", "yMiJtpzb1Qthy9TGnavsf5NZ6EZZa4j9q3", "yacgSfW7RkwWakEZPg8USAVdzCypiG3vxS", "yVvrmoRPFLy6nUpCQBT8ZExxF5wF3DhiGU", "yaJf2aG6cFUtfv4o6TuEKsh5kr4xq5iAY4", "yfardJQ4ucgWLKQPaRHGMRMbSGm5H4ExJR", "yLSCqx7dcM5JKR2fG7vHbF2axMvuYqomaw", "yVij8XpJ78LM5hepSV1KF7T8vRpUEXCpK5", "ydJpjuJGossAZR7S5oS7cWvjygEwoj8Xwp", "yW3TmWnmhvpxRbgFcQ8oXqDRkn3RhRH6jj", "yRegVX85DThKRkH8C61TtRacfzrkiBfNy5", "yPtDCqDFRe1JuDp8pvdiEMQMz2erGwS3VG", "yM9pSw3L4oBfG7uQL5o522Hu3WTvy9awgZ", "yNC6qYJYungzuk5XUynDFKCn54Dy8ngox4", "yR5KcLr1bceLT4teTk2qoJx6pFLik1zyzL", "yRrKLGJa9JmdjBWvrHtedKjHTao6CRDTKf", "yP5dShZBydpbEzgGoXL6kcjv2KzervRrYB"], + "internalAddressesList": ["yNDpPsJqXKM36zHSNEW7c1zSvNnrZ699FY", "yLk4Hw3w4zDudrDVP6W8J9TggkY57zQUki", "yirJaK8KCE5YAmwvLadizqFw3TCXqBuZXL", "yhdRfg5gNr587dtEC4YYMcSHmLVEGqqtHc", "yYwKP1FQae5kbjXkmuirGx6Xzf8NzHpLqW", "yX9gmsm8aSxZZjYhq4w35aidT7qbhcpNjU", "ybgXCTGMHEBbQeUib8c3xAjtGAc12XtWiU", "yS31WpdMT2b34uL9C37fbUoACHhiupHCyP", "yTSpFqRoX3vyN286AUtKKhgmX5Xb41YKQe", "yQU5YsqN7psTTASuYbcMi7N5nNZGaxXb2X", "yVGGFj9BLgEab5rucSGLC6UGVLQKB4U1wJ", "yQCh5yYCHEbJzgSJE9rdHiqXHidKm3kwr5", "yX7T3Ac3yaLk5CTC5UaR93Fc7SjYkeT5hn", "yXx3WXq8kYNPbYEg5U6bL8Xfih4g5LCYVo", "yYnLMTz3jCi2KKKNuo3TVkEAGyUFg8tgkJ", "yiKa1dA6B4tSTNJqJP9Y5pQfQEffnQQDTL", "yf7vcuDnE9DVhXdMfBMQQTEi43otYQzkWE", "yTmSmocwERCeRHqNNG5SbpYKUra1HTmj8m", "yivUe5NeJsGsREwPQZUGYaTSwWB3E1oLcz", "ygfsZojdfW9UjCRU4ra95Aq6YgCC7UqZFx", "yU9fdXaUVtefwDZvxjJAr9xj1z2MtYi34A", "yXgMN6FgrgZCnTN1vhoZMh8afKMBmi3JC4", "yiqaCbXscvR8y3VFYMzdaKCaAGuDuZxMzt", "ydcgWDxheSxrLAqDBP4JXBndMCzUNf77gq"], + "otherAccountAddressesList": ["yYJmzWey5kNecAThet5BFxAga1F4b4DKQ2", "yNCqctyQaq51WU1hN5aNwsgMsZ5fRiB7GY", "yNPbYz5cZKw2EwxtkL3VSVzPi2FYp9VKjQ", "ybsGWzsnSCAZufgSeUjScVxqEdved99UM2", "yfNHuPojk8XKWP5nuueDptX4nM7qToudgx", "yXxLnDkk6s8h1PSnYaFM6MAyRarc1Kc1rY", "yipResSzN2zUvL7UYkmptKKmQTv7sNssRn", "yZPtNwimHdRiKYbNQW49qezw1Kc1YwUJeT", "yPMjYYfQbga2nBiuqqfUyX41U1vwRZ8fG8", "yLueLWWcLQsaXQ8D5o9tcyo8tfTxMWXvG4", "yN8gzgsc1RVjXThMQT5qZH2jjpnMymz6zP", "yPQLWBNwMdLxUW2oUwHGwQtfyYxD41BARJ", "yg5g2AfWFdwWexWGfbSXYbUHf1y5WWrFPs", "yWyABu4naV1Jzw7w9sn1gqhebPRSkCndsS", "ycuUPzUBjhKyUjezQR1LNot79a6C4aRLaR", "yQ7YjvAXgDAUCekveHVjr6NBveXrUemVno", "yi8bghcw627cMGpuH4bJqH6bqR5ywv1NLH", "yizHu8i2rfwzwBgnJ62s2WUe6wLoDjne6N", "yW1u3tySeUKAKJsz7sjZFyjUiTyKLB6xBv", "yNaSkdy1Q8JNubUdbLMGsGf7sTRofEJYZq", "yXMrw79LPgu78EJsfGGYpm6fXKc1EMnQ49", "yh6Hcyipdvp6WJpQxjNbaXP4kzPQUJpY3n", "yNphpXuaTZRpU9FBh2W7NkUYcr3kBDE8me", "yXFppDT59xYD41mT2pmAdnvr7aZEFdgdrN", "yeKGAiiEHBGRujvLoYewA77jDDpeDamxvF", "yaxTG66CVzKgHhHZXojRHC9ztLTvz3fwdT", "yYw6qU7dwGoELZkSTj3oSKRpM4U8qTMc1U", "yQE2MksEnSfbeNre19oja9Jj8tvpj64C5a", "yaRnvHo8oLvVmv46vMj5XPbDJouQSnmcLT", "yj5ofWf2uYQQkSavYm2WXgu1QkaZCyP3Cm", "yUCjGmEwrHJwNDrE1o2rMre6MkSbiE6yz7", "yfJzd1nE2rEqz5XEurD6vs4ykizwmw9xTv", "yUk8U3jRZMHKVTa1eFDEtZpa1G4E13FP4d", "yMr59YWQFCADq4FbWrtxDUtMwwshSrmAyK", "yetSehBupzGS9yps5ogqARUGmTMAs2xVcQ", "yNcESKLwriNrhM6EyoSpZEXrzdY3uht92T", "yN2FihGU7KdaEspp39bKrhsHypeyeYzoM2", "yirpWLxHuhwFzA6LfUPKUh1Ke9RB9BUjit", "yVDN66vvdshWdNzhUaQNB6xExAHkzs1zj8", "yPzofnEhRVfDisL2nCUJtAoSHkuyMirHZS", "yjG4jiCMNbx3MyFCAsaNaU29CBVmgPf8hS", "yWrEYTtydaZCyJoKE1vzX4e4RXDNPGdnw9", "yM7Smr8HEhbRW4CXga898brATrJSsk1QUh", "yUprvaE5KrKVPgABmGtxba9MmZqDzKQHom"], + "miscAddressesList": [] } - - }; - - const result = classifyAddresses(accountStore.addresses, accountIndex, walletType); - const expectedResult = { - externalAddressList: [ - 'yd1ohc12LgCYp56CDuckTEHwoa6LbPghMd', - 'yMX3ycrLVF2k6YxWQbMoYgs39aeTfY4wrB', - 'ydJGUUmNxdmvyskoZXqtJRqWyqsaFPitGQ', - 'yhugNjDRVJUL7PK9MqQP9M6M1HJm8zuWJL', - 'ySgckdRYxpa7Uda8yUNRqjYeuusqLy3AY3', - 'yS4DeSU3MTBisgL6p8PDRzSxTPN2PUt3vE', - 'yRZef5UGotGgLMaLYTzhvfknogqMRBkUiX', - 'yTUw2bGzi9rYs41XH1dxbRqiJoDtwuUcv2', - 'yNQHAGhNP7UbhxnkZH4muP2oKkuayGEuwX', - 'yPPDLBDjHctWpMxLiMTJXLngcYPke7YNaY', - 'ya2vVJAJdZN2We7MYiSjGf9wkdWF6A1RLr', - 'ya5k2YMjfyfxZoidq4UdQ65jYUXvtVomEv', - 'yhXZja3Apyp9S32zEVsPqLssNJZLrczxJC', - 'yhWqJXsp25aZNQHEprebQrqPoANj6A13Aa', - 'yjV3sKAGsuJHDGyf6HDMNuLfMgGp5pBxRy', - 'yjPeTiRatdvotxUuPFEPDJc2aF774uMB9J', - 'yRrhuVw6Vd3NzgYrfqb1oTvdhyxzDT9PGz', - 'yYpL5JJLVGfJXPE15ZMQzNvUGkD4JY6ETF', - 'ygcW1365Hs2LSLY5LXnkJAUB94pS4HouNu', - 'yW8RA7zTUz14sNiGjvFQaNupugwwmE1aQi' - ], - internalAddressList: [ - 'yaLhoAZ4iex2zKmfvS9rvEmxXmRiPrjHdD', - 'yTcjWB7v7opDzpfYKpFdFEtEvSKFsh3bW3', - 'yiDVYtUZ2mKV4teSJzKBArqY4BRsZoFLYs', - 'ya7Me5KMoSz5x4GGZ1pJGrJjC3yMkDDWDa', - 'yXwS8mRrrxF3pt1GfG7yGKNpPnD6pdwX3a', - 'yXd4eqycSaJRhRZxXT3iK5H34af4TV5REE', - 'yWaDfpToRxHc3qtcd8P1agW4Fvj1ueWgwH', - 'yPYdu2jDrD3Bai83AdvHTYwpAgAkzMaCcM', - 'yMEkuZ67vZ6kUgDHVVDSTzwU3GbHoTFwqR', - 'yYSSMwkEqU4hNF2z5kbVBTDYtgt8dQQYd7', - 'yfzAa63gQ6arpyBzuqQtZmSnU8HLnJnEan', - 'ySjTorgG6VVfPiY7TJ2tdU2hkohfFAtzJf', - 'yMUJsy5HEiQTeDgqxY2zBGEeTCDr4g5V8c', - 'yWDkvzhKk7BKEU6Ybz1Kyarejzt8zhSqxy', - 'yTJQE4vYXRdEcPt3nADF36S6FKLkLRG2Ty', - 'yTjku3TMxJN3uiiSHYB2tQ4wp1rJJ92M2A', - 'yZ8JgZrpuEr1srdcgLgTAsomtMBgSYwTrW', - 'yUG2YZrss5JfZrN4AG4RKkkbfYyd4H6TSx', - 'yiLMqyvjBV3CqkCL8H44bRSUBap7tPCmvo', - 'yfrCYcuD7ezYnpNAmbPMjrTETjipsNGsEe' - ], - otherAccountAddressList: [] - }; expect(result).to.deep.equal(expectedResult); }); }); diff --git a/packages/wallet-lib/src/utils/index.js b/packages/wallet-lib/src/utils/index.js index ac209d5d53d..c23bb6a6e58 100644 --- a/packages/wallet-lib/src/utils/index.js +++ b/packages/wallet-lib/src/utils/index.js @@ -1,6 +1,7 @@ const extendTransactionsWithMetadata = require('./extendTransactionsWithMetadata'); const calculateTransactionFees = require('./calculateTransactionFees'); const categorizeTransactions = require('./categorizeTransactions'); +const calculateDuffBalance = require('./calculateDuffBalance'); const filterTransactions = require('./filterTransactions'); const { hash, doubleSha256, sha256 } = require('./crypto'); const { varIntSizeBytesFromLength } = require('./varInt'); @@ -29,6 +30,7 @@ module.exports = { calculateTransactionFees, categorizeTransactions, mnemonicToHDPrivateKey, + calculateDuffBalance, generateNewMnemonic, seedToHDPrivateKey, mnemonicToWalletId, From e933ac058827300680e05cd79e478546569fe33b Mon Sep 17 00:00:00 2001 From: Igor Markin Date: Fri, 4 Feb 2022 15:12:41 +0300 Subject: [PATCH 09/27] Fixes KeyChain import in SDK --- packages/js-dash-sdk/src/SDK/SDK.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/js-dash-sdk/src/SDK/SDK.ts b/packages/js-dash-sdk/src/SDK/SDK.ts index 9d7a4957b10..82ae6401c4d 100644 --- a/packages/js-dash-sdk/src/SDK/SDK.ts +++ b/packages/js-dash-sdk/src/SDK/SDK.ts @@ -6,7 +6,7 @@ import { default as _DAPIClient } from '@dashevo/dapi-client'; import { Wallet as _Wallet, Account as _Account, - KeyChain as _KeyChain, + DerivableKeyChain as _KeyChain, CONSTANTS as _WalletLibCONSTANTS, EVENTS as _WalletLibEVENTS, utils as _WalletLibUtils, From dea2a3e7728c4e7bd18d95dd1bb157bbb371d07f Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Mon, 14 Feb 2022 21:24:25 +0100 Subject: [PATCH 10/27] fix: missing integration of Storage --- .../src/plugins/Plugins/ChainPlugin.js | 18 +++++----- .../src/plugins/Workers/IdentitySyncWorker.js | 14 ++++---- .../TransactionSyncStreamWorker.js | 28 +++++---------- .../handlers/onStreamError.js | 1 + .../methods/getAddressesToSync.js | 16 ++------- .../methods/getAddressesToSync.spec.js | 34 +++++++++++++++---- .../methods/getLastSyncedBlockHeight.js | 8 +---- .../methods/handleTransactionFromStream.js | 4 +-- .../methods/processChunks.js | 20 +++++++---- .../methods/setLastSyncedBlockHeight.js | 14 ++------ 10 files changed, 76 insertions(+), 81 deletions(-) diff --git a/packages/wallet-lib/src/plugins/Plugins/ChainPlugin.js b/packages/wallet-lib/src/plugins/Plugins/ChainPlugin.js index af0d1c45d43..eccbfd61873 100644 --- a/packages/wallet-lib/src/plugins/Plugins/ChainPlugin.js +++ b/packages/wallet-lib/src/plugins/Plugins/ChainPlugin.js @@ -33,15 +33,15 @@ class ChainPlugin extends StandardPlugin { */ async execBlockListener() { const self = this; - const { network } = this.storage.store.wallets[this.walletId]; + const { network } = this.storage.application; + const chainStore = this.storage.getChainStore(network); if (!this.isSubscribedToBlocks) { self.transport.on(EVENTS.BLOCK, async (ev) => { - // const { network } = self.storage.store.wallets[self.walletId]; const { payload: block } = ev; this.parentEvents.emit(EVENTS.BLOCK, { type: EVENTS.BLOCK, payload: block }); // We do not announce BLOCKHEADER as this is done by Storage - await self.storage.importBlockHeader(block.header); + await chainStore.importBlockHeader(block.header); }); self.transport.on(EVENTS.BLOCKHEIGHT_CHANGED, async (ev) => { const { payload: blockheight } = ev; @@ -50,7 +50,7 @@ class ChainPlugin extends StandardPlugin { type: EVENTS.BLOCKHEIGHT_CHANGED, payload: blockheight, }); - this.storage.store.chains[network.toString()].blockHeight = blockheight; + chainStore.state.blockHeight = blockheight; logger.debug(`ChainPlugin - setting chain blockheight ${blockheight}`); }); await self.transport.subscribeToBlocks(); @@ -69,20 +69,20 @@ class ChainPlugin extends StandardPlugin { return false; } + const { network } = this.storage.application; + const chainStore = this.storage.getChainStore(network); const { chain: { blocksCount: blocks }, network: { fee: { relay } } } = res; - const { network } = this.storage.store.wallets[this.walletId]; - logger.debug('ChainPlugin - Setting up starting blockHeight', blocks); - this.storage.store.chains[network.toString()].blockHeight = blocks; + chainStore.state.blockHeight = blocks; if (relay) { - this.storage.store.chains[network.toString()].fees.minRelay = dashToDuffs(relay); + chainStore.state.fees.minRelay = dashToDuffs(relay); } const bestBlock = await this.transport.getBlockHeaderByHeight(blocks); - await this.storage.importBlockHeader(bestBlock); + await chainStore.importBlockHeader(bestBlock); return true; } diff --git a/packages/wallet-lib/src/plugins/Workers/IdentitySyncWorker.js b/packages/wallet-lib/src/plugins/Workers/IdentitySyncWorker.js index d44d101d9de..920a847adb4 100644 --- a/packages/wallet-lib/src/plugins/Workers/IdentitySyncWorker.js +++ b/packages/wallet-lib/src/plugins/Workers/IdentitySyncWorker.js @@ -26,7 +26,8 @@ class IdentitySyncWorker extends Worker { } async execute() { - const indexedIds = await this.storage.getIndexedIdentityIds(this.walletId); + const walletStore = this.storage.getWalletStore(this.walletId); + const indexedIds = await walletStore.getIndexedIdentityIds(); // Add gaps to empty indices const unusedIndices = []; @@ -100,11 +101,12 @@ class IdentitySyncWorker extends Worker { logger.silly(`IdentitySyncWorker - got ${Identifier.from(fetchedId)} at ${index}`); // eslint-disable-next-line no-await-in-loop - await this.storage.insertIdentityIdAtIndex( - this.walletId, - Identifier.from(fetchedId).toString(), - index, - ); + await this.storage + .getWalletStore(this.walletId) + .insertIdentityIdAtIndex( + Identifier.from(fetchedId).toString(), + index, + ); } logger.silly('IdentitySyncWorker - sync finished'); diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/TransactionSyncStreamWorker.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/TransactionSyncStreamWorker.js index 73d4bfda672..07a9b8cbadb 100644 --- a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/TransactionSyncStreamWorker.js +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/TransactionSyncStreamWorker.js @@ -3,7 +3,6 @@ const { } = require('@dashevo/dashcore-lib'); const GrpcError = require('@dashevo/grpc-common/lib/server/error/GrpcError'); const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes'); -const { WALLET_TYPES } = require('../../../CONSTANTS'); const sleep = require('../../../utils/sleep'); const Worker = require('../../Worker'); @@ -25,6 +24,7 @@ class TransactionSyncStreamWorker extends Worker { 'importBlockHeader', 'importInstantLock', 'storage', + 'keyChainStore', 'transport', 'walletId', 'getAddress', @@ -49,7 +49,7 @@ class TransactionSyncStreamWorker extends Worker { * @param {string[]} addressList * @param {string} network */ - static filterWalletTransactions(transactions, addressList, network) { + static filterAddressesTransactions(transactions, addressList, network) { const spentOutputs = []; const unspentOutputs = []; const filteredTransactions = transactions.filter((tx) => { @@ -135,11 +135,11 @@ class TransactionSyncStreamWorker extends Worker { const { skipSynchronizationBeforeHeight, skipSynchronization, - } = (this.storage.store.syncOptions || {}); + } = (this.storage.application.syncOptions || {}); if (skipSynchronization) { logger.debug('TransactionSyncStreamWorker - Wallet created from a new mnemonic. Sync from the best block height.'); - const bestBlockHeight = this.storage.store.chains[this.network.toString()].blockHeight; + const bestBlockHeight = this.storage.getChainStore(this.network.toString()).blockHeight; this.setLastSyncedBlockHeight(bestBlockHeight); return; } @@ -238,25 +238,13 @@ class TransactionSyncStreamWorker extends Worker { } setLastSyncedBlockHash(hash) { - const { walletId } = this; - const accountsStore = this.storage.store.wallets[walletId].accounts; - - const accountStore = ([WALLET_TYPES.HDWALLET, WALLET_TYPES.HDPUBLIC].includes(this.walletType)) - ? accountsStore[this.BIP44PATH.toString()] - : accountsStore[this.index.toString()]; - - accountStore.blockHash = hash; - - return accountStore.blockHash; + const applicationStore = this.storage.application; + applicationStore.blockHash = hash; + return applicationStore.blockHash; } getLastSyncedBlockHash() { - const { walletId } = this; - const accountsStore = this.storage.store.wallets[walletId].accounts; - - const { blockHash } = ([WALLET_TYPES.HDWALLET, WALLET_TYPES.HDPUBLIC].includes(this.walletType)) - ? accountsStore[this.BIP44PATH.toString()] - : accountsStore[this.index.toString()]; + const { blockHash } = this.storage.application; return blockHash; } diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/handlers/onStreamError.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/handlers/onStreamError.js index fb3577c98f9..624c5821010 100644 --- a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/handlers/onStreamError.js +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/handlers/onStreamError.js @@ -2,6 +2,7 @@ const logger = require('../../../../logger'); function onStreamError(error, reject) { logger.silly('TransactionSyncStreamWorker - end stream on error'); + logger.silly(error.message); reject(error); } module.exports = onStreamError; diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.js index 7e3f57e5301..e87d86e14aa 100644 --- a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.js +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.js @@ -1,15 +1,5 @@ -const { WALLET_TYPES } = require('../../../../CONSTANTS'); - module.exports = function getAddressesToSync() { - const { BIP44PATH, walletId, walletType } = this; - const { addresses } = this.storage.getStore().wallets[walletId]; - - const isHDWallet = [WALLET_TYPES.HDPUBLIC, WALLET_TYPES.HDWALLET].includes(walletType); - // We have two cases, for privateKey based wallet, we return all address in store. - // But for HDWallet, addresses in store can be of another account, that we filter out - return Object.keys(addresses) - .map((addressType) => Object.values(addresses[addressType])) - .flatMap((addressList) => addressList) - .filter((accountAddress) => !isHDWallet || accountAddress.path.startsWith(BIP44PATH)) - .map((filteredAccountAddress) => filteredAccountAddress.address); + return this.keyChainStore.getKeyChains() + .map((keychain) => keychain.getWatchedAddresses()) + .reduce((pre, cur) => pre.concat(cur)); }; diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.spec.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.spec.js index 16597f841cb..0ba57ad286a 100644 --- a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.spec.js +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.spec.js @@ -1,6 +1,26 @@ const { expect } = require('chai'); const getAddressesToSync = require('./getAddressesToSync'); +const KeyChainStore = require("../../../../types/KeyChainStore/KeyChainStore"); +const KeyChain = require("../../../../types/KeyChain/KeyChain"); +const { HDPrivateKey, HDPublicKey, PrivateKey } = require("@dashevo/dashcore-lib"); + +const privateKey = new PrivateKey('ee56be968a42e58fda23b83da17f90e002cafbe35a702c2f5598b13fdaa238db', 'testnet') +const hdprivateKey1 = new HDPrivateKey("xprv9s21ZrQH143K39R9Ux28kCBUHcQFdBeVE2CXFVz6GnA2a6pqTsPhHR5QHtMP5ZTRpYkKqc9ifjkJ2V1h318qWsYgyxCBUurRdTNthjgwKMw", 'testnet'); +const hdpublicKey1 = new HDPublicKey("xpub661MyMwAqRbcFhaucFQun3ivEyA5gy5NKnjr1xMUVkyqdF3VNNy3TLinwnYMSUye5FF5pDSrn2SPX3zvKRQGrpZ44VVUBeuxuzov7enWpkf",'testnet'); + +const keychainPrivate1 = new KeyChain({privateKey}); +const keychainHDPrivate1 = new KeyChain({HDPrivateKey: hdprivateKey1}); +const keychainHDPublic1 = new KeyChain({HDPublicKey: hdpublicKey1}); + +const keychainStorePrivateKeyWallet = new KeyChainStore(); +keychainStorePrivateKeyWallet.addKeyChain(keychainPrivate1, { isMasterKeyChain: true}); +keychainPrivate1.getForPath(0, { isWatched: true }) + +const keychainStoreHDPrivateKeyWallet = new KeyChainStore(); +keychainStoreHDPrivateKeyWallet.addKeyChain(keychainHDPrivate1, { isMasterKeyChain: true}); +keychainHDPrivate1.getForPath(`m/0/0`, { isWatched: true }) +keychainHDPrivate1.getForPath(`m/0/1`, { isWatched: true }) const mockedStore1 = { wallets: { 123456789: { @@ -107,11 +127,13 @@ const mockedStore2 = { const mockSelfPrivateKeyType = { storage: { getStore:()=>mockedStore1 }, + keyChainStore: keychainStorePrivateKeyWallet, walletId: '123456789', walletType: 'privateKey', } const mockSelfIndex0 = { storage: { getStore:()=>mockedStore2 }, + keyChainStore: keychainStorePrivateKeyWallet, walletId: '123456789', walletType: 'hdwallet', BIP44PATH: `m/44'/1'/0'` @@ -125,13 +147,13 @@ const mockSelfIndex1 = { describe('TransactionSyncStreamWorker#getAddressesToSync', function suite() { it('should correctly fetch addresses to sync', async () => { - const addressesIndex0 = getAddressesToSync.call(mockSelfIndex0 ); - expect(addressesIndex0).to.deep.equal(['yizmJb63ygipuJaRgYtpWCV2erQodmaZt8', 'yizmJb63ygipuJaRgYtpWCV2erQodmaZt9']) - - const addressesIndex1 = getAddressesToSync.call(mockSelfIndex1 ); - expect(addressesIndex1).to.deep.equal(['yQ5TfKcj3NHM4V4K5VBgoFJj9Q4LKX13gn']) + const addressesIndex0 = getAddressesToSync.call(mockSelfIndex0); + expect(addressesIndex0).to.deep.equal([ + 'Xpkr9M3DP8RgcWw4SHUW75PYtmU1Lh5Ss2', + 'Xp1kwhXoUVHKRKmoXt3dB4i4KhryHSYjtW' + ]) const addressesIndex2 = getAddressesToSync.call(mockSelfPrivateKeyType ); - expect(addressesIndex2).to.deep.equal(['yizmJb63ygipuJaRgYtpWCV2erQodmaZt1']) + expect(addressesIndex2).to.deep.equal(['yZprpQkn7FYUHjqm3dY4sCs9SorMCi4oyR']) }); }); diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getLastSyncedBlockHeight.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getLastSyncedBlockHeight.js index ef9ca277d76..85e0a4aea13 100644 --- a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getLastSyncedBlockHeight.js +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getLastSyncedBlockHeight.js @@ -1,15 +1,9 @@ -const { WALLET_TYPES } = require('../../../../CONSTANTS'); /** * Return last synced block height * @return {number} */ module.exports = function getLastSyncedBlockHeight() { - const { walletId } = this; - const accountsStore = this.storage.store.wallets[walletId].accounts; - - let { blockHeight } = ([WALLET_TYPES.HDWALLET, WALLET_TYPES.HDPUBLIC].includes(this.walletType)) - ? accountsStore[this.BIP44PATH.toString()] - : accountsStore[this.index.toString()]; + let { blockHeight } = this.storage.application; // Fix Genesis issue on DCore if (blockHeight === 0) blockHeight = 1; diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/handleTransactionFromStream.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/handleTransactionFromStream.js index 99326c9d495..29104fa3450 100644 --- a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/handleTransactionFromStream.js +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/handleTransactionFromStream.js @@ -77,8 +77,8 @@ async function handleTransactionFromStream(transaction) { const metadata = { blockHash: getTransactionResponse.blockHash, height: getTransactionResponse.height, - instantLocked: getTransactionResponse.instantLocked, - chainLocked: getTransactionResponse.chainLocked, + instantLocked: getTransactionResponse.isInstantLocked, + chainLocked: getTransactionResponse.isChainLocked, }; delete this.pendingRequest[transactionHash]; diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/processChunks.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/processChunks.js index 19e51bff8f1..f0b5975942b 100644 --- a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/processChunks.js +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/processChunks.js @@ -23,20 +23,23 @@ async function processChunks(dataChunk) { const transactionsFromResponse = this.constructor .getTransactionListFromStreamResponse(dataChunk); - const walletTransactions = this.constructor - .filterWalletTransactions(transactionsFromResponse, addresses, network); + const addressesTransaction = this.constructor + .filterAddressesTransactions(transactionsFromResponse, addresses, network); + + if (addressesTransaction.transactions.length) { + // Normalizing format of transaction for account.importTransactions + const addressesTransactionsWithoutMetadata = addressesTransaction.transactions + .map((tx) => [tx]); - if (walletTransactions.transactions.length) { // When a transaction exist, there is multiple things we need to do : // 1) The transaction itself needs to be imported const addressesGeneratedCount = await self - .importTransactions(walletTransactions.transactions); - + .importTransactions(addressesTransactionsWithoutMetadata); // 2) Transaction metadata need to be fetched and imported as well. // as such event might happen in the future // As we require height information, we fetch transaction using client - const awaitingMetadataPromises = walletTransactions.transactions + const awaitingMetadataPromises = addressesTransaction.transactions .map((transaction) => self.handleTransactionFromStream(transaction) .then(({ transactionResponse, @@ -46,6 +49,7 @@ async function processChunks(dataChunk) { Promise .all(awaitingMetadataPromises) .then(async (transactionsWithMetadata) => { + // Import into account await self.importTransactions(transactionsWithMetadata); }); @@ -95,7 +99,9 @@ async function processChunks(dataChunk) { if (merkleBlockFromResponse) { // Reverse hashes, as they're little endian in the header const transactionsInHeader = merkleBlockFromResponse.hashes.map((hashHex) => Buffer.from(hashHex, 'hex').reverse().toString('hex')); - const transactionsInWallet = Object.keys(self.storage.getStore().transactions); + const transactionsInWallet = [ + ...self.storage.getChainStore(self.network).state.transactions.keys(), + ]; const isTruePositive = isAnyIntersection(transactionsInHeader, transactionsInWallet); if (isTruePositive) { self.importBlockHeader(merkleBlockFromResponse.header); diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/setLastSyncedBlockHeight.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/setLastSyncedBlockHeight.js index 19bd654ac85..c54075b44e4 100644 --- a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/setLastSyncedBlockHeight.js +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/setLastSyncedBlockHeight.js @@ -1,5 +1,3 @@ -const { WALLET_TYPES } = require('../../../../CONSTANTS'); - /** * Set last synced block height * @@ -7,14 +5,8 @@ const { WALLET_TYPES } = require('../../../../CONSTANTS'); * @return {number} */ module.exports = function setLastSyncedBlockHeight(blockHeight) { - const { walletId } = this; - const accountsStore = this.storage.store.wallets[walletId].accounts; - - const accountStore = ([WALLET_TYPES.HDWALLET, WALLET_TYPES.HDPUBLIC].includes(this.walletType)) - ? accountsStore[this.BIP44PATH.toString()] - : accountsStore[this.index.toString()]; - - accountStore.blockHeight = blockHeight; + const applicationStore = this.storage.application; + applicationStore.blockHeight = blockHeight; - return accountStore.blockHeight; + return applicationStore.blockHeight; }; From 7d015eaca3e93d8a8d1c195ea43b9bda2f42daf5 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Mon, 14 Feb 2022 21:26:03 +0100 Subject: [PATCH 11/27] fix: remove file from merge conflict --- .../src/types/KeyChain/KeyChain.d.ts | 48 ------- .../src/types/KeyChain/KeyChain.spec.js | 125 ------------------ .../KeyChain/methods/getDIP15ExtendedKey.js | 26 ---- .../KeyChain/methods/getHardenedBIP44HDKey.js | 12 -- .../methods/getHardenedDIP15AccountKey.js | 14 -- 5 files changed, 225 deletions(-) delete mode 100644 packages/wallet-lib/src/types/KeyChain/KeyChain.d.ts delete mode 100644 packages/wallet-lib/src/types/KeyChain/KeyChain.spec.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getDIP15ExtendedKey.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getHardenedBIP44HDKey.js delete mode 100644 packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP15AccountKey.js diff --git a/packages/wallet-lib/src/types/KeyChain/KeyChain.d.ts b/packages/wallet-lib/src/types/KeyChain/KeyChain.d.ts deleted file mode 100644 index 0d774d60b6f..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/KeyChain.d.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {PrivateKey, Network,} from "../types"; -import {HDPrivateKey, HDPublicKey} from "@dashevo/dashcore-lib"; -import {Transaction} from "@dashevo/dashcore-lib/typings/transaction/Transaction"; - -export declare namespace KeyChain { - interface IKeyChainOptions { - network?: Network; - keys?: [Keys] - } -} - -export declare class KeyChain { - constructor(options?: KeyChain.IKeyChainOptions); - network: Network; - keys: [Keys]; - - type: HDKeyTypesParam|PrivateKeyTypeParam; - HDPrivateKey?: HDPrivateKey; - privateKey?: PrivateKey; - - generateKeyForChild(index: number, type?: HDKeyTypesParam): HDPrivateKey|HDPublicKey; - generateKeyForPath(path: string, type?: HDKeyTypesParam): HDPrivateKey|HDPublicKey; - - getDIP15ExtendedKey(userUniqueId: string, contactUniqueId: string, index?: number, accountIndex?: number, type?: HDKeyTypesParam): HDKeyTypes; - getHardenedDIP15AccountKey(index?: number, type?: HDKeyTypesParam): HDKeyTypes; - getHardenedBIP44HDKey(type?: HDKeyTypesParam): HDKeyTypes; - getHardenedDIP9FeatureHDKey(type?: HDKeyTypesParam): HDKeyTypes; - getKeyForChild(index: number, type?: HDKeyTypesParam): HDKeyTypes; - getKeyForPath(path: string, type?: HDKeyTypesParam): HDKeyTypes; - getPrivateKey(): PrivateKey; - - sign(object: Transaction|any, privateKeys:[PrivateKey], sigType: number): any; -} - -type HDKeyTypes = HDPublicKey | HDPrivateKey; - -export declare enum HDKeyTypesParam { - HDPrivateKey="HDPrivateKey", - HDPublicKey="HDPrivateKey", -} -export declare enum PrivateKeyTypeParam { - privateKey='privateKey' -} -export declare interface Keys { - [path: string]: { - path: string - }; -} diff --git a/packages/wallet-lib/src/types/KeyChain/KeyChain.spec.js b/packages/wallet-lib/src/types/KeyChain/KeyChain.spec.js deleted file mode 100644 index 3cf9bb0b5a1..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/KeyChain.spec.js +++ /dev/null @@ -1,125 +0,0 @@ -const Dashcore = require('@dashevo/dashcore-lib'); -const { expect } = require('chai'); -const KeyChain = require('./KeyChain'); -const { mnemonicToHDPrivateKey } = require('../../utils/mnemonic'); - -let keychain; -let keychain2; -const mnemonic = 'during develop before curtain hazard rare job language become verb message travel'; -const mnemonic2 = 'birth kingdom trash renew flavor utility donkey gasp regular alert pave layer'; -const pk = '4226d5e2fe8cbfe6f5beb7adf5a5b08b310f6c4a67fc27826779073be6f5699e'; - -const expectedRootDIP15AccountKey_0 = 'tprv8hRzmheQujhJN5XP2dj955nAFCKeEoSifJRWuutdbwWRtusdDQ426jbp75EqErUSuTxmPyxYmP1TpcF5qdxGhXLNXRLMGsRLG6NFCv1WnaQ'; -const expectedRootDIP15AccountKey_1 = 'tprv8hRzmheQujhJQyCtFTuUFHxB3Ag5VLB994zhH4CfxbA41cq73HT2mpYq5M33V54oJyn6g514saxxVJB886G55eYX56J6D6x87UNNT6iQHkR'; -const expectedKeyForChild_0 = 'tprv8d4podc2Tg459CH2bwLHXj3vdJFBT2rdsk5Nr1djH7hzHdt5LRdvN6QyFwMiDy7ffRdik7fEVRKKgsHB4F18sh8xF6jFXpKq4sUgGBoSbKw'; -describe('Keychain', function suite() { - this.timeout(10000); - it('should create a keychain', () => { - const expectedException1 = 'Expect privateKey, publicKey, HDPublicKey, HDPrivateKey or Address'; - expect(() => new KeyChain()).to.throw(expectedException1); - expect(() => new KeyChain(mnemonic)).to.throw(expectedException1); - - keychain = new KeyChain({ HDPrivateKey: mnemonicToHDPrivateKey(mnemonic, 'testnet') }); - expect(keychain.type).to.equal('HDPrivateKey'); - expect(keychain.network.toString()).to.equal('testnet'); - expect(keychain.keys).to.deep.equal({}); - - keychain2 = new KeyChain({ HDPrivateKey: mnemonicToHDPrivateKey(mnemonic2, 'mainnet') }); - }); - it('should get private key', () => { - expect(keychain.getPrivateKey().toString()).to.equal(pk); - }); - it('should generate key for full path', () => { - const path = 'm/44\'/1\'/0\'/0/0'; - const pk2 = keychain.getKeyForPath(path); - const address = new Dashcore.Address(pk2.publicKey.toAddress()).toString(); - expect(address).to.equal('yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT'); - }); - it('should get hardened feature path', () => { - const hardenedPk = keychain.getHardenedBIP44HDKey(); - const pk2 = keychain.getKeyForPath('m/44\'/1\''); - expect(pk2.toString()).to.equal(hardenedPk.toString()); - }); - it('should get DIP15 account key', function () { - const rootDIP15AccountKey_0 = keychain.getHardenedDIP15AccountKey(0); - expect(rootDIP15AccountKey_0.toString()).to.deep.equal(expectedRootDIP15AccountKey_0); - const rootDIP15AccountKey_1 = keychain.getHardenedDIP15AccountKey(1); - expect(rootDIP15AccountKey_1.toString()).to.deep.equal(expectedRootDIP15AccountKey_1); - }); - it('should get DIP15 extended key', function () { - const userUniqueId = '0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'; - const contactUniqueId = '0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'; - - // m/9'/5'/15'/0'/0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'/0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'/0 - const DIP15ExtPubKey_0 = keychain2.getDIP15ExtendedKey(userUniqueId, contactUniqueId, 0, 0, type='HDPublicKey'); - expect(DIP15ExtPubKey_0.toString()).to.equal('xpub6LTkTQFSb8KMgMSz4B6sMZLpkQAY6wSTDprDkHDmLwWLpnjxazuxZn13FrSLKUafitsxuaaffM5a49P6aswhpppWUuYW6eFnwBXshR2W2eY'); - expect(DIP15ExtPubKey_0.publicKey.toString()).to.equal('038030c88ab0106e1f4af3b939db2bafc56f892554106f08da1ce1f9ef10f807bd') - - const DIP15ExtPrivKey_0 = keychain2.getDIP15ExtendedKey(userUniqueId, contactUniqueId, 0, 0); - expect(DIP15ExtPrivKey_0.toString()).to.equal('xprvA7UQ3tiYkkm4TsNWx9ZrzRQ6CNL3hUibrbvcwtp9nbyMwzQp3Tbi1ygZQaPoigDhCf8XUjMmGK2NbnB2kLXPYg99Lp6e3iki318sdWcFN3q'); - expect(DIP15ExtPrivKey_0.privateKey.toString()).to.equal('fac40790776d171ee1db90899b5eb2df2f7d2aaf35ad56f07ffb8ed2c57f8e60') - expect(DIP15ExtPrivKey_0.publicKey.toString()).to.equal('038030c88ab0106e1f4af3b939db2bafc56f892554106f08da1ce1f9ef10f807bd') - - const userAhash = "0xa11ce14f698b32e9bb306dba7bbbee831263dcf658abeebb39930460ead117e5"; - const userBhash = "0xb0b052ff075c5ca3c16c3e20e9ac8223834475cc1324ab07889cb24ce6a62793"; - const DIP15ExtKey_1 = keychain.getDIP15ExtendedKey(userAhash, userBhash, 0, 0); - expect(DIP15ExtKey_1.privateKey.toString()).to.equal('60581b6dca8244d3fb3cfe619b5a22277e5423b01e5285f356981f247e0f4a60') - expect(DIP15ExtKey_1.publicKey.toString()).to.equal('03deaac00f721151307fbc7bf80d7b8afab98c1f026d67e5f56b21e2013f551ce6') - - }); - it('should derive from hardened feature path', () => { - const hardenedHDKey = keychain.getHardenedBIP44HDKey(); - const pk2 = keychain.getKeyForPath(`m/44'/1'`); - expect(pk2.toString()).to.equal(hardenedHDKey.toString()); - expect(hardenedHDKey.toString()).to.deep.equal('tprv8dtrJNytYHRiZY585hmHGbguS6VjGpK49puSB7oXZjLHcQfrAzQkF4ZCxM2DkEbyY85J4EYcZ8EjT5ZCU8ozB727TDdodbfXet5GkGau2RQ'); - const derivedPk = hardenedHDKey.deriveChild(0, true).deriveChild(0).deriveChild(0); - const address = new Dashcore.Address(derivedPk.publicKey.toAddress()).toString(); - expect(address).to.equal('yNfUebksUc5HoSfg8gv98ruC3jUNJUM8pT'); - }); - it('should get hardened DIP9FeatureHDKey', function () { - const hardenedHDKey = keychain.getHardenedDIP9FeatureHDKey(); - const pk2 = keychain.getKeyForPath(`m/9'/1'`); - expect(pk2.toString()).to.equal(hardenedHDKey.toString()); - expect(hardenedHDKey.toString()).to.deep.equal('tprv8fBJjWoGgCpGRCbyzE9RUA59rmoN1RUijhLnXGL4VHnLxvSe523yVg4GrGzbR6TyXtdynAEh5z8UX55EXt2Cb3xjvrsx2PgTY9BHxzFVkWn'); - }); - it('should generate key for child', () => { - const keychain2 = new KeyChain({ HDPrivateKey: mnemonicToHDPrivateKey(mnemonic, 'testnet') }); - const keyForChild = keychain2.generateKeyForChild(0); - expect(keyForChild.toString()).to.equal(expectedKeyForChild_0); - }); - - - it('should sign', () => { - - }); -}); -describe('Keychain - clone', function suite() { - this.timeout(10000); - it('should clone', () => { - const keychain2 = new KeyChain(keychain); - expect(keychain2).to.deep.equal(keychain); - expect(keychain2.keys).to.deep.equal(keychain.keys); - }); -}); -describe('Keychain - single privateKey', function suite() { - this.timeout(10000); - it('should correctly errors out when not a HDPublicKey (privateKey)', () => { - const privateKey = Dashcore.PrivateKey().toString(); - const network = 'livenet'; - const pkKeyChain = new KeyChain({ privateKey, network }); - expect(pkKeyChain.network).to.equal(network); - expect(pkKeyChain.keys).to.deep.equal({}); - expect(pkKeyChain.type).to.equal('privateKey'); - expect(pkKeyChain.privateKey).to.equal(privateKey); - - const expectedException1 = 'Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate keys'; - const expectedException2 = 'Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate child'; - expect(() => pkKeyChain.generateKeyForPath()).to.throw(expectedException1); - expect(() => pkKeyChain.generateKeyForChild()).to.throw(expectedException2); - }); - it('should get private key', () => { - const privateKey = Dashcore.PrivateKey().toString(); - const pkKeyChain = new KeyChain({ privateKey, network: 'livenet' }); - expect(pkKeyChain.getPrivateKey().toString()).to.equal(privateKey); - }); -}); diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getDIP15ExtendedKey.js b/packages/wallet-lib/src/types/KeyChain/methods/getDIP15ExtendedKey.js deleted file mode 100644 index a7eed1547dc..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/getDIP15ExtendedKey.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Return the extended key of the relationship between two dashpay contacts. - * @param userUniqueId - Current userID - * @param contactUniqueId - Contact userID - * @param index - the key index. - * @param accountIndex[=0] - the internal wallet account from which derivation is done - * @param type {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys - * @return {HDPrivateKey|HDPublicKey} - */ -function getDIP15ExtendedKey(userUniqueId, contactUniqueId, index = 0, accountIndex = 0, type = 'HDPrivateKey') { - if (!['HDPrivateKey', 'HDPublicKey'].includes(this.type)) { - throw new Error('Wallet is not loaded from a mnemonic or a HDPubKey, impossible to derivate keys'); - } - if (!userUniqueId || !contactUniqueId) throw new Error('Required userUniqueId and contactUniqueId to be defined'); - - // Require a HDPrivateKey for hardened derivation - const extendedPrivateKey = this - .getHardenedDIP15AccountKey(accountIndex, 'HDPrivateKey') - .deriveChild((userUniqueId), true) - .deriveChild((contactUniqueId), true) - .deriveChild(index, false); - - return (type === 'HDPublicKey' ? extendedPrivateKey.hdPublicKey : extendedPrivateKey); -} - -module.exports = getDIP15ExtendedKey; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedBIP44HDKey.js b/packages/wallet-lib/src/types/KeyChain/methods/getHardenedBIP44HDKey.js deleted file mode 100644 index 75a6628475f..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedBIP44HDKey.js +++ /dev/null @@ -1,12 +0,0 @@ -const { BIP44_TESTNET_ROOT_PATH, BIP44_LIVENET_ROOT_PATH } = require('../../../CONSTANTS'); - -/** - * Return a safier root keys to derivate from - * @param {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys - * @return {HDPrivateKey|HDPublicKey} - */ -function getHardenedBIP44HDKey(type = 'HDPrivateKey') { - const pathRoot = (this.network.toString() === 'testnet') ? BIP44_TESTNET_ROOT_PATH : BIP44_LIVENET_ROOT_PATH; - return this.generateKeyForPath(pathRoot, type); -} -module.exports = getHardenedBIP44HDKey; diff --git a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP15AccountKey.js b/packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP15AccountKey.js deleted file mode 100644 index c01f240fc85..00000000000 --- a/packages/wallet-lib/src/types/KeyChain/methods/getHardenedDIP15AccountKey.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Return a safier root path to derivate from - * @param {number} [accountIndex=0] - set the account index - * @param {HDPrivateKey|HDPublicKey} [type=HDPrivateKey] - set the type of returned keys - * @return {HDPrivateKey|HDPublicKey} - */ -function getHardenedDIP15AccountKey(accountIndex = 0, type = 'HDPrivateKey') { - const hardenedFeatureRootKey = this.getHardenedDIP9FeatureHDKey(type); - - // Feature is set to 15' for all DashPay Incoming Funds derivation paths (see DIP15). - const featureKey = hardenedFeatureRootKey.deriveChild(15, true); - return featureKey.deriveChild(accountIndex, true); -} -module.exports = getHardenedDIP15AccountKey; From 8b73e48a06a86db778ff300396af23a69614b716 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Tue, 15 Feb 2022 09:59:35 +0100 Subject: [PATCH 12/27] docs: added typings --- .../src/types/ChainStore/ChainStore.d.ts | 38 +++++++++++++++++++ .../DerivableKeyChain/DerivableKeyChain.d.ts | 24 ++++++++---- .../types/KeyChainStore/KeyChainStore.d.ts | 20 ++++++++++ .../src/types/WalletStore/WalletStore.d.ts | 26 +++++++++++++ 4 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 packages/wallet-lib/src/types/ChainStore/ChainStore.d.ts create mode 100644 packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.d.ts create mode 100644 packages/wallet-lib/src/types/WalletStore/WalletStore.d.ts diff --git a/packages/wallet-lib/src/types/ChainStore/ChainStore.d.ts b/packages/wallet-lib/src/types/ChainStore/ChainStore.d.ts new file mode 100644 index 00000000000..1d63afc46d9 --- /dev/null +++ b/packages/wallet-lib/src/types/ChainStore/ChainStore.d.ts @@ -0,0 +1,38 @@ +export declare interface feeState { + minRelay: number +} + +export declare interface ChainStoreState { + fees: feeState; + blockHeight: number; + blockHeaders: Map + transactions: Map + instantLocks: Map + addresses: Map +} + +type networkIdentifier = string; +type exportedState = any; + +export declare class ChainStore { + constructor(networkIdentifier: networkIdentifier); + network: networkIdentifier; + + state: ChainStoreState; + + considerTransaction(transactionHash: string): any; + exportState(): exportedState; + importState(exportedState): void; + + getAddress(address: string): any; + getAddresses(address: string): Map + + getBlockHeader(blockHeaderHash: string): any; + getInstantLock(transactionHash: string): any; + getTransaction(transactionHash: string): any; + + importAddress(address: any): void; + importAddress(blockHeader: any): void; + importInstantLock(instantLock: any): void; + importTransaction(transaction: any, metadata: any): any; +} diff --git a/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.d.ts b/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.d.ts index 406d2baf5ff..1989fd6784c 100644 --- a/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.d.ts +++ b/packages/wallet-lib/src/types/DerivableKeyChain/DerivableKeyChain.d.ts @@ -9,6 +9,14 @@ export declare namespace DerivableKeyChain { } } +type keyChainId = string; +type rootKey = any; +type firstUnusedAddress = { + path: string; + address: string +} + + export declare class DerivableKeyChain { constructor(options?: DerivableKeyChain.IDerivableKeyChainOptions); network: Network; @@ -18,17 +26,19 @@ export declare class DerivableKeyChain { HDPrivateKey?: HDPrivateKey; privateKey?: PrivateKey; - generateKeyForChild(index: number, type?: HDKeyTypesParam): HDPrivateKey|HDPublicKey; - generateKeyForPath(path: string, type?: HDKeyTypesParam): HDPrivateKey|HDPublicKey; + getForPath(path: string, opts: any): any; + getForAddress(address): any; getDIP15ExtendedKey(userUniqueId: string, contactUniqueId: string, index?: number, accountIndex?: number, type?: HDKeyTypesParam): HDKeyTypes; - getHardenedDIP15AccountKey(index?: number, type?: HDKeyTypesParam): HDKeyTypes; + getFirstUnusedAddress(): firstUnusedAddress; getHardenedBIP44HDKey(type?: HDKeyTypesParam): HDKeyTypes; getHardenedDIP9FeatureHDKey(type?: HDKeyTypesParam): HDKeyTypes; - getKeyForChild(index: number, type?: HDKeyTypesParam): HDKeyTypes; - getKeyForPath(path: string, type?: HDKeyTypesParam): HDKeyTypes; - getPrivateKey(): PrivateKey; - + getHardenedDIP15AccountKey(index?: number, type?: HDKeyTypesParam): HDKeyTypes; + getRootKey(): rootKey; + getWatchedAddresses(): Array; + getIssuedPaths(): Array; + maybeLookAhead(): any; + markAddressAsUsed(address: string): any; sign(object: Transaction|any, privateKeys:[PrivateKey], sigType: number): any; } diff --git a/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.d.ts b/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.d.ts new file mode 100644 index 00000000000..7d65d7ba31d --- /dev/null +++ b/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.d.ts @@ -0,0 +1,20 @@ +import {keyChainId, DerivableKeyChain} from "../DerivableKeyChain/DerivableKeyChain"; + +export declare class KeyChainStore { + constructor(); + + keyChains: Map + masterKeyChainId: keyChainId | null; + + addKeyChain(keychain: DerivableKeyChain, opts?: addKeyChainParam): void; + getKeyChain(keychainId: keyChainId): DerivableKeyChain; + getKeyChains(): Array; + makeChildKeyChainStore(path: string, opts: DerivableKeyChain.IDerivableKeyChainOptions): KeyChainStore; + getMasterKeyChain(): DerivableKeyChain; +} + +export declare interface addKeyChainParam { + isMasterKeyChain?: boolean; +} + + diff --git a/packages/wallet-lib/src/types/WalletStore/WalletStore.d.ts b/packages/wallet-lib/src/types/WalletStore/WalletStore.d.ts new file mode 100644 index 00000000000..91697ef6a8f --- /dev/null +++ b/packages/wallet-lib/src/types/WalletStore/WalletStore.d.ts @@ -0,0 +1,26 @@ +export declare interface WalletStoreState { + mnemonic: string; + paths: Map + identities: Map +} + +type walletId = string; +type exportedState = any; + +export declare class WalletStore { + constructor(walletId: walletId); + + walletId: walletId; + state: WalletStoreState; + + createPathState(path: string): void; + exportState(): exportedState; + getIdentityIdByIndex(identityIndex: number): string; + getIndexedIdentityIds(identityIndex: number): Array; + getPathState(path: string): any; + + importState(exportedState): void; + insertIdentityIdAtIndex(identityId: string, identityIndex: number): void; +} + + From 8bb4206da0737e0e16562fc950fd2bf4048f71f3 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Tue, 15 Feb 2022 09:59:54 +0100 Subject: [PATCH 13/27] fix: getAddressesToSync tests --- .../methods/getAddressesToSync.spec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.spec.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.spec.js index 0ba57ad286a..159b4d47e27 100644 --- a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.spec.js +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.spec.js @@ -1,7 +1,7 @@ const { expect } = require('chai'); const getAddressesToSync = require('./getAddressesToSync'); const KeyChainStore = require("../../../../types/KeyChainStore/KeyChainStore"); -const KeyChain = require("../../../../types/KeyChain/KeyChain"); +const DerivableKeyChain = require("../../../../types/DerivableKeyChain/DerivableKeyChain"); const { HDPrivateKey, HDPublicKey, PrivateKey } = require("@dashevo/dashcore-lib"); @@ -9,9 +9,9 @@ const privateKey = new PrivateKey('ee56be968a42e58fda23b83da17f90e002cafbe35a702 const hdprivateKey1 = new HDPrivateKey("xprv9s21ZrQH143K39R9Ux28kCBUHcQFdBeVE2CXFVz6GnA2a6pqTsPhHR5QHtMP5ZTRpYkKqc9ifjkJ2V1h318qWsYgyxCBUurRdTNthjgwKMw", 'testnet'); const hdpublicKey1 = new HDPublicKey("xpub661MyMwAqRbcFhaucFQun3ivEyA5gy5NKnjr1xMUVkyqdF3VNNy3TLinwnYMSUye5FF5pDSrn2SPX3zvKRQGrpZ44VVUBeuxuzov7enWpkf",'testnet'); -const keychainPrivate1 = new KeyChain({privateKey}); -const keychainHDPrivate1 = new KeyChain({HDPrivateKey: hdprivateKey1}); -const keychainHDPublic1 = new KeyChain({HDPublicKey: hdpublicKey1}); +const keychainPrivate1 = new DerivableKeyChain({privateKey}); +const keychainHDPrivate1 = new DerivableKeyChain({HDPrivateKey: hdprivateKey1}); +const keychainHDPublic1 = new DerivableKeyChain({HDPublicKey: hdpublicKey1}); const keychainStorePrivateKeyWallet = new KeyChainStore(); keychainStorePrivateKeyWallet.addKeyChain(keychainPrivate1, { isMasterKeyChain: true}); From 03e4f6e8b7b0d435a72c20cdd6052c91cecd4a39 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Tue, 15 Feb 2022 10:00:03 +0100 Subject: [PATCH 14/27] fix: removed unused props --- packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.js b/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.js index fd5c24dfacb..c81c2c0ac2a 100644 --- a/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.js +++ b/packages/wallet-lib/src/types/KeyChainStore/KeyChainStore.js @@ -1,9 +1,7 @@ class KeyChainStore { constructor() { this.keyChains = new Map(); - this.walletKeyChainId = null; this.masterKeyChainId = null; - this.accountKeyChains = new Map(); } } From 578a1c4a779b9b3b03db25498a3c6428750dd03b Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Tue, 15 Feb 2022 15:06:59 +0100 Subject: [PATCH 15/27] fix: tests for HDPrivateKey on store mock --- .../methods/getAddressesToSync.spec.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.spec.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.spec.js index 159b4d47e27..b78d93b64dd 100644 --- a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.spec.js +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/methods/getAddressesToSync.spec.js @@ -6,8 +6,8 @@ const { HDPrivateKey, HDPublicKey, PrivateKey } = require("@dashevo/dashcore-lib const privateKey = new PrivateKey('ee56be968a42e58fda23b83da17f90e002cafbe35a702c2f5598b13fdaa238db', 'testnet') -const hdprivateKey1 = new HDPrivateKey("xprv9s21ZrQH143K39R9Ux28kCBUHcQFdBeVE2CXFVz6GnA2a6pqTsPhHR5QHtMP5ZTRpYkKqc9ifjkJ2V1h318qWsYgyxCBUurRdTNthjgwKMw", 'testnet'); -const hdpublicKey1 = new HDPublicKey("xpub661MyMwAqRbcFhaucFQun3ivEyA5gy5NKnjr1xMUVkyqdF3VNNy3TLinwnYMSUye5FF5pDSrn2SPX3zvKRQGrpZ44VVUBeuxuzov7enWpkf",'testnet'); +const hdprivateKey1 = new HDPrivateKey("xprv9s21ZrQH143K39R9Ux28kCBUHcQFdBeVE2CXFVz6GnA2a6pqTsPhHR5QHtMP5ZTRpYkKqc9ifjkJ2V1h318qWsYgyxCBUurRdTNthjgwKMw", 'mainnet'); +const hdpublicKey1 = new HDPublicKey("xpub661MyMwAqRbcFhaucFQun3ivEyA5gy5NKnjr1xMUVkyqdF3VNNy3TLinwnYMSUye5FF5pDSrn2SPX3zvKRQGrpZ44VVUBeuxuzov7enWpkf",'mainnet'); const keychainPrivate1 = new DerivableKeyChain({privateKey}); const keychainHDPrivate1 = new DerivableKeyChain({HDPrivateKey: hdprivateKey1}); @@ -133,7 +133,7 @@ const mockSelfPrivateKeyType = { } const mockSelfIndex0 = { storage: { getStore:()=>mockedStore2 }, - keyChainStore: keychainStorePrivateKeyWallet, + keyChainStore: keychainStoreHDPrivateKeyWallet, walletId: '123456789', walletType: 'hdwallet', BIP44PATH: `m/44'/1'/0'` @@ -149,8 +149,8 @@ describe('TransactionSyncStreamWorker#getAddressesToSync', function suite() { const addressesIndex0 = getAddressesToSync.call(mockSelfIndex0); expect(addressesIndex0).to.deep.equal([ - 'Xpkr9M3DP8RgcWw4SHUW75PYtmU1Lh5Ss2', - 'Xp1kwhXoUVHKRKmoXt3dB4i4KhryHSYjtW' + "Xpkr9M3DP8RgcWw4SHUW75PYtmU1Lh5Ss2", + "Xp1kwhXoUVHKRKmoXt3dB4i4KhryHSYjtW" ]) const addressesIndex2 = getAddressesToSync.call(mockSelfPrivateKeyType ); From d377e61fefd11d473ae564cb6f30cb0c40b3bd65 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Wed, 16 Feb 2022 19:43:59 +0100 Subject: [PATCH 16/27] test: update integration tests --- .../createIdentityFixtureInAccount.ts | 5 +- .../test/mocks/createTransactionInAccount.js | 4 +- .../types/Storage/methods/rehydrateState.js | 4 +- .../TransactionSyncStreamWorker.spec.js | 106 +++++++++++------- .../tests/integration/types/Account.spec.js | 12 +- 5 files changed, 79 insertions(+), 52 deletions(-) diff --git a/packages/js-dash-sdk/src/test/fixtures/createIdentityFixtureInAccount.ts b/packages/js-dash-sdk/src/test/fixtures/createIdentityFixtureInAccount.ts index 1f19dc70964..537483b198e 100644 --- a/packages/js-dash-sdk/src/test/fixtures/createIdentityFixtureInAccount.ts +++ b/packages/js-dash-sdk/src/test/fixtures/createIdentityFixtureInAccount.ts @@ -15,8 +15,9 @@ export function createIdentityFixtureInAccount(account) { securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER }); - account.storage.insertIdentityIdAtIndex( - account.walletId, + account.storage + .getWalletStore(account.walletId) + .insertIdentityIdAtIndex( identityFixture.getId().toString(), identityFixtureIndex, ); diff --git a/packages/wallet-lib/src/test/mocks/createTransactionInAccount.js b/packages/wallet-lib/src/test/mocks/createTransactionInAccount.js index 4df5b64bee0..52496b44e31 100644 --- a/packages/wallet-lib/src/test/mocks/createTransactionInAccount.js +++ b/packages/wallet-lib/src/test/mocks/createTransactionInAccount.js @@ -16,8 +16,8 @@ async function createTransactionInAccount(account) { }]) .to(account.getAddress(10).address, 100000); - await account.importTransactions([walletTransaction.serialize(true)]); - + await account.importTransactions([[walletTransaction.serialize(true)]]); + // console.log(account.storage.wallets.get('361032c8a0').state.paths) return walletTransaction; } diff --git a/packages/wallet-lib/src/types/Storage/methods/rehydrateState.js b/packages/wallet-lib/src/types/Storage/methods/rehydrateState.js index 96b8fda6897..a391c9e6bb9 100644 --- a/packages/wallet-lib/src/types/Storage/methods/rehydrateState.js +++ b/packages/wallet-lib/src/types/Storage/methods/rehydrateState.js @@ -19,7 +19,7 @@ const rehydrateState = async function rehydrateState() { wallets.forEach((walletState) => { const walletStore = new WalletStore(); walletStore.importState(walletState); - this.wallets.add(walletStore); + this.wallets.set(walletStore.walletId, walletStore); }); } @@ -28,7 +28,7 @@ const rehydrateState = async function rehydrateState() { chains.forEach((chainState) => { const chainStore = new ChainStore(); chainStore.importState(chainState); - this.chains.add(chainStore); + this.chains.set(chainStore.network, chainStore); }); } this.application = mergeHelper(this.application, await this.adapter.getItem('application')); diff --git a/packages/wallet-lib/tests/integration/plugins/Workers/TransactionSyncStreamWorker.spec.js b/packages/wallet-lib/tests/integration/plugins/Workers/TransactionSyncStreamWorker.spec.js index c638e1efcd5..053cdbd4e07 100644 --- a/packages/wallet-lib/tests/integration/plugins/Workers/TransactionSyncStreamWorker.spec.js +++ b/packages/wallet-lib/tests/integration/plugins/Workers/TransactionSyncStreamWorker.spec.js @@ -55,6 +55,7 @@ describe('TransactionSyncStreamWorker', function suite() { plugins: [worker], allowSensitiveOperations: true, HDPrivateKey: new HDPrivateKey(testHDKey), + network: 'mainnet' }); ({ txStreamMock, transportMock } = await createAndAttachTransportMocksToWallet(wallet, this.sinonSandbox)); @@ -65,12 +66,11 @@ describe('TransactionSyncStreamWorker', function suite() { account = await wallet.getAccount(); storage = account.storage; - walletId = Object.keys(storage.store.wallets)[0]; + walletId = account.walletId; address = account.getAddress(0).address; addressAtIndex19 = account.getAddress(19).address; }); - afterEach(() => { worker.stopWorker(); }) @@ -117,9 +117,8 @@ describe('TransactionSyncStreamWorker', function suite() { await worker.onStart(); - const transactionsInStorage = Object - .values(storage.getStore().transactions) - .map((t) => t.toJSON()); + const transactionsInStorage = Array.from(storage.getChainStore('livenet').state.transactions) + .map(([,t]) => t.transaction.toJSON()); const expectedTransactions = transactionsSent .map((t) => t.toJSON()); @@ -128,7 +127,6 @@ describe('TransactionSyncStreamWorker', function suite() { expect(transactionsInStorage.length).to.be.equal(3); expect(transactionsInStorage).to.have.deep.members(expectedTransactions); }); - it('should reconnect to the historical stream when gap limit is filled', async function () { const lastSavedBlockHeight = 40; const bestBlockHeight = 42; @@ -193,31 +191,34 @@ describe('TransactionSyncStreamWorker', function suite() { await worker.onStart(); - const transactionsInStorage = Object - .values(storage.getStore().transactions) - .map((t) => t.toJSON()); + const transactionsInStorage = Array.from(storage.getChainStore('livenet').state.transactions) + .map(([,t]) => t.transaction.toJSON()); const expectedTransactions = transactionsSent .map((t) => t.toJSON()); + const {addresses} = storage.getWalletStore(walletId).state.paths.get(`m/44'/5'/0'`); - const addressesInStorage = storage.store.wallets[walletId].addresses.external; + const addressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/0')) + .map(([path, address])=> address); // We send transaction to index 19, so wallet should generate additional 20 addresses to keep the gap between // the last used address expect(Object.keys(addressesInStorage).length).to.be.equal(40); // It should reconnect after the gap limit is reached - expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(2); + expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(1); // 20 external and 20 internal expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[0].length).to.be.equal(40); expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[1]).to.be.deep.equal({ fromBlockHeight: 40, count: 2}); // 20 more of external, since the last address is used. - expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(60); - expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHash: '0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9', count: 2}); + // expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(60); + // expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHash: '0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9', count: 2}); expect(worker.stream).to.be.null; expect(transactionsInStorage.length).to.be.equal(2); expect(transactionsInStorage).to.have.deep.members(expectedTransactions); }); + it('should reconnect to the historical stream if stream is closed due to operational GRPC error', async function () { const lastSavedBlockHeight = 40; const bestBlockHeight = 42; @@ -242,7 +243,11 @@ describe('TransactionSyncStreamWorker', function suite() { await worker.onStart(); - const addressesInStorage = storage.store.wallets[walletId].addresses.external; + const {addresses} = storage.getWalletStore(walletId).state.paths.get(`m/44'/5'/0'`); + + const addressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/0')) + .map(([path, address])=> address); expect(Object.keys(addressesInStorage).length).to.be.equal(20); // It should reconnect after because of the operational error @@ -275,7 +280,11 @@ describe('TransactionSyncStreamWorker', function suite() { await expect(worker.onStart()).to.be.rejectedWith('Some random error'); - const addressesInStorage = storage.store.wallets[walletId].addresses.external; + const {addresses} = storage.getWalletStore(walletId).state.paths.get(`m/44'/5'/0'`); + + const addressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/0')) + .map(([path, address])=> address); expect(Object.keys(addressesInStorage).length).to.be.equal(20); // Shouldn't try to reconnect @@ -287,7 +296,6 @@ describe('TransactionSyncStreamWorker', function suite() { expect(worker.stream).to.be.null; }); }); - describe("#execute", () => { it('should sync incoming transactions and save it to the storage', async function () { const lastSavedBlockHeight = 40; @@ -330,9 +338,8 @@ describe('TransactionSyncStreamWorker', function suite() { await worker.onStop(); - const transactionsInStorage = Object - .values(storage.getStore().transactions) - .map((t) => t.toJSON()); + const transactionsInStorage = Array.from(storage.getChainStore('livenet').state.transactions) + .map(([,t]) => t.transaction.toJSON()); const expectedTransactions = transactionsSent .map((t) => t.toJSON()); @@ -405,25 +412,28 @@ describe('TransactionSyncStreamWorker', function suite() { await worker.onStop(); - const transactionsInStorage = Object - .values(storage.getStore().transactions) - .map((t) => t.toJSON()); + const transactionsInStorage = Array.from(storage.getChainStore('livenet').state.transactions) + .map(([,t]) => t.transaction.toJSON()); const expectedTransactions = transactionsSent .map((t) => t.toJSON()); - const addressesInStorage = storage.store.wallets[walletId].addresses.external; + const {addresses} = storage.getWalletStore(walletId).state.paths.get(`m/44'/5'/0'`); + + const addressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/0')) + .map(([path, address])=> address); // We send transaction to index 19, so wallet should generate additional 20 addresses to keep the gap between // the last used address expect(Object.keys(addressesInStorage).length).to.be.equal(40); // It should reconnect after the gap limit is reached - expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(2); + expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(1); // 20 external and 20 internal expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[0].length).to.be.equal(40); expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[1]).to.be.deep.equal({ fromBlockHeight: 40, count: 0}); // 20 more of external, since the last address is used. - expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(60); - expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHash: '0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9', count: 0}); + // expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(60); + // expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHash: '0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9', count: 0}); expect(worker.stream).to.be.null; expect(transactionsInStorage.length).to.be.equal(2); @@ -453,7 +463,11 @@ describe('TransactionSyncStreamWorker', function suite() { await worker.onStop(); - const addressesInStorage = storage.store.wallets[walletId].addresses.external; + const {addresses} = storage.getWalletStore(walletId).state.paths.get(`m/44'/5'/0'`); + + const addressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/0')) + .map(([path, address])=> address); expect(Object.keys(addressesInStorage).length).to.be.equal(20); // It should reconnect after the gap limit is reached @@ -486,7 +500,11 @@ describe('TransactionSyncStreamWorker', function suite() { await worker.onStop(); - const addressesInStorage = storage.store.wallets[walletId].addresses.external; + const {addresses} = storage.getWalletStore(walletId).state.paths.get(`m/44'/5'/0'`); + + const addressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/0')) + .map(([path, address])=> address); expect(Object.keys(addressesInStorage).length).to.be.equal(20); // It should reconnect if the server closes the stream @@ -520,7 +538,11 @@ describe('TransactionSyncStreamWorker', function suite() { await expect(worker.incomingSyncPromise).to.be.rejectedWith('Some random error'); - const addressesInStorage = storage.store.wallets[walletId].addresses.external; + const {addresses} = storage.getWalletStore(walletId).state.paths.get(`m/44'/5'/0'`); + + const addressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/0')) + .map(([path, address])=> address); expect(Object.keys(addressesInStorage).length).to.be.equal(20); // Shouldn't try to reconnect @@ -648,30 +670,34 @@ describe('TransactionSyncStreamWorker', function suite() { await worker.onStop(); - const transactionsInStorage = Object - .values(storage.getStore().transactions) - .map((t) => t.toJSON()); + const transactionsInStorage = Array.from(storage.getChainStore('livenet').state.transactions) + .map(([,t]) => t.transaction.toJSON()); const expectedTransactions = transactionsSent .map((t) => t.toJSON()); - const { - external: externalAddressesInStorage, - internal: internalAddressesInStorage - } = storage.store.wallets[walletId].addresses + const {addresses} = storage.getWalletStore(walletId).state.paths.get(`m/44'/5'/0'`); + + const externalAddressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/0')) + .map(([path, address])=> address); + + const internalAddressesInStorage = Object.entries(addresses) + .filter(([path, address])=> path.includes('m/1')) + .map(([path, address])=> address); // We send transaction to index 19, so wallet should generate additional 20 addresses to keep the gap between // the last used address expect(Object.keys(externalAddressesInStorage).length).to.be.equal(40); expect(Object.keys(internalAddressesInStorage).length).to.be.equal(20); // It should reconnect after the gap limit is reached - expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(2); + expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(1); // 20 external and 20 internal expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[1]).to.be.deep.equal({ fromBlockHeight: 40, count: 0}); expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[0].length).to.be.equal(40); // 20 more of external, since the last address is used, Merkle Block received - expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(60); - expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHash: '0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9', count: 0}); + // expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(60); + // expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHash: '0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9', count: 0}); expect(worker.stream).to.be.null; expect(transactionsInStorage.length).to.be.equal(2); expect(transactionsInStorage).to.have.deep.members(expectedTransactions); @@ -702,7 +728,7 @@ describe('TransactionSyncStreamWorker', function suite() { // Check that wait method throws if timeout has passed await expect(account.waitForInstantLock(transactions[2].hash, 1000)).to.eventually - .be.rejectedWith('InstantLock waiting period for transaction 256d5b3bf6d8869f5cc882ae070af9b648fa0f512bfa2b6f07b35d55e160a16c timed out'); + .be.rejectedWith('InstantLock waiting period for transaction 823c272fc1694b571805d2bc2f8936597ee52de638a0ca5323233c239fd3e8c4 timed out'); }); it('should start from the height specified in `skipSynchronizationBeforeHeight` options', async function () { const bestBlockHeight = 42; diff --git a/packages/wallet-lib/tests/integration/types/Account.spec.js b/packages/wallet-lib/tests/integration/types/Account.spec.js index 620cce2f413..6aface918c3 100644 --- a/packages/wallet-lib/tests/integration/types/Account.spec.js +++ b/packages/wallet-lib/tests/integration/types/Account.spec.js @@ -50,7 +50,8 @@ describe('Account', function suite() { plugins: [worker], allowSensitiveOperations: true, HDPrivateKey: new HDPrivateKey(testHDKey), - adapter: storageAdapterMock + adapter: storageAdapterMock, + network: 'livenet' }); ({txStreamMock, transportMock} = await createAndAttachTransportMocksToWallet(wallet, this.sinonSandbox)); @@ -58,7 +59,7 @@ describe('Account', function suite() { account = await wallet.getAccount(); storage = account.storage; - walletId = Object.keys(storage.store.wallets)[0]; + walletId = account.walletId; address = account.getAddress(0).address; addressAtIndex19 = account.getAddress(19).address; @@ -74,20 +75,19 @@ describe('Account', function suite() { // Saving state to restore it later await account.storage.saveState(); - // Restoring wallet from the saved state const restoredWallet = new Wallet({ offlineMode: true, plugins: [worker], allowSensitiveOperations: true, HDPrivateKey: new HDPrivateKey(testHDKey), - adapter: storageAdapterMock + adapter: storageAdapterMock, + network: 'livenet' }); const restoredAccount = await restoredWallet.getAccount(); - const utxos = await restoredAccount.getUTXOS(); expect(utxos.length).to.be.equal(1); }); }); -}); \ No newline at end of file +}); From 6752a48aec2e790111a0e58a4e09da794743fb8c Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Thu, 17 Feb 2022 10:27:51 +0100 Subject: [PATCH 17/27] tests: update dashjs import transactions test --- .../src/test/fixtures/createTransactionFixtureInAccount.ts | 4 ++-- .../src/types/Identities/methods/getIdentityIds.js | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/js-dash-sdk/src/test/fixtures/createTransactionFixtureInAccount.ts b/packages/js-dash-sdk/src/test/fixtures/createTransactionFixtureInAccount.ts index a5f95a24139..6e88f507f0c 100644 --- a/packages/js-dash-sdk/src/test/fixtures/createTransactionFixtureInAccount.ts +++ b/packages/js-dash-sdk/src/test/fixtures/createTransactionFixtureInAccount.ts @@ -11,7 +11,7 @@ export async function createTransactionInAccount(account) { }]) .to(account.getAddress(10).address, 100000); - await account.importTransactions([walletTransaction.serialize(true)]); + await account.importTransactions([[walletTransaction.serialize(true)]]); return walletTransaction; -} \ No newline at end of file +} diff --git a/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.js b/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.js index dba62110f7f..e72711adb42 100644 --- a/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.js +++ b/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.js @@ -3,7 +3,10 @@ * @return {string[]} */ function getIdentityIds() { - return this.storage.getIndexedIdentityIds(this.walletId).filter(Boolean); + return this.storage + .getWalletStore(this.walletId) + .getIndexedIdentityIds() + .filter(Boolean); } module.exports = getIdentityIds; From 335701d9c2e6944891d3446d4a4f2df84486b22d Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Thu, 17 Feb 2022 10:33:56 +0100 Subject: [PATCH 18/27] fix: update insertion of identity id for js-dash-sdk --- .../SDK/Client/Platform/methods/identities/register.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/register.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/register.ts index 69d89ba16eb..fc38f769a68 100644 --- a/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/register.ts +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/methods/identities/register.ts @@ -36,10 +36,11 @@ export default async function register( await broadcastStateTransition(this, identityCreateTransition); // If state transition was broadcast without any errors, import identity to the account - account.storage.insertIdentityIdAtIndex( - account.walletId, - identity.getId().toString(), - identityIndex, + account.storage + .getWalletStore(account.walletId) + .insertIdentityIdAtIndex( + identity.getId().toString(), + identityIndex, ); return identity; From bfa30d5b625c03e1d943b4fb8624ecf4e00673b3 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Thu, 17 Feb 2022 10:36:10 +0100 Subject: [PATCH 19/27] docs: update typings definition --- packages/wallet-lib/src/index.d.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/wallet-lib/src/index.d.ts b/packages/wallet-lib/src/index.d.ts index 0b4606bec8a..ab1ab579cc8 100644 --- a/packages/wallet-lib/src/index.d.ts +++ b/packages/wallet-lib/src/index.d.ts @@ -4,7 +4,9 @@ import { Account } from "./types/Account/Account"; import { Wallet } from "./types/Wallet/Wallet"; import { Identities } from "./types/Identities/Identities"; +import { ChainStore } from "./types/ChainStore/ChainStore"; import { DerivableKeyChain } from "./types/DerivableKeyChain/DerivableKeyChain"; +import { KeyChainStore } from "./types/KeyChainStore/KeyChainStore"; import CONSTANTS from "./CONSTANTS"; import EVENTS from "./EVENTS"; import utils from "./utils"; @@ -17,7 +19,6 @@ export { DerivableKeyChain, KeyChainStore, Identities, - IdentitiesStore, EVENTS, CONSTANTS, utils, From ce6511445205d7f71b39ca8bc412d3c2a3178998 Mon Sep 17 00:00:00 2001 From: "markin.io" Date: Sun, 20 Feb 2022 11:13:49 +0700 Subject: [PATCH 20/27] Fixes getIdentityIds.spec.js --- .../Identities/methods/getIdentityIds.spec.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.spec.js b/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.spec.js index df2e887176b..6d0ee52d568 100644 --- a/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.spec.js +++ b/packages/wallet-lib/src/types/Identities/methods/getIdentityIds.spec.js @@ -1,19 +1,24 @@ const { expect } = require('chai'); const mockedStore = require('../../../../fixtures/sirentonight-fullstore-snapshot-1562711703'); const getIdentityIds = require('./getIdentityIds'); +const WalletStore = require("../../WalletStore/WalletStore"); let mockedWallet; let fetchTransactionInfoCalledNb = 0; describe('Wallet#getIdentityIds', function suite() { this.timeout(10000); before(() => { + const walletId = Object.keys(mockedStore.wallets)[0]; + const walletStore = new WalletStore(walletId) + const identityIds = mockedStore.wallets[walletId].identityIds; + identityIds.forEach((id, i) => { + walletStore.insertIdentityIdAtIndex(id, i) + }) + const storageHDW = { - store: mockedStore, - getStore: () => mockedStore, - mappedAddress: {}, - getIndexedIdentityIds: () => mockedStore.wallets[Object.keys(mockedStore.wallets)].identityIds, + getWalletStore: () => walletStore }; - const walletId = Object.keys(mockedStore.wallets)[0]; + mockedWallet = { walletId, index: 0, From 7d5c276c28d6116fc7b7f5ed36c5d4eb7ffee408 Mon Sep 17 00:00:00 2001 From: "markin.io" Date: Sun, 20 Feb 2022 19:40:09 +0300 Subject: [PATCH 21/27] Fixes test with incorrect order of identity ids --- packages/js-dash-sdk/src/SDK/Client/Client.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/js-dash-sdk/src/SDK/Client/Client.spec.ts b/packages/js-dash-sdk/src/SDK/Client/Client.spec.ts index 46a657e74d4..5a2e271a7da 100644 --- a/packages/js-dash-sdk/src/SDK/Client/Client.spec.ts +++ b/packages/js-dash-sdk/src/SDK/Client/Client.spec.ts @@ -37,7 +37,7 @@ describe('Dash - Client', function suite() { beforeEach(async function beforeEach() { testMnemonic = 'agree country attract master mimic ball load beauty join gentle turtle hover'; - testHDKey = "xprv9s21ZrQH143K4PgfRZPuYjYUWRZkGfEPuWTEUESMoEZLC274ntC4G49qxgZJEPgmujsmY52eVggtwZgJPrWTMXmbYgqDVySWg46XzbGXrSZ"; + testHDKey = "tprv8ZgxMBicQKsPeGi4CikhacVPz6UmErenu1PoD3S4XcEDSPP8auRaS8hG3DQtsQ2i9HACgohHwF5sgMVJNksoKqYoZbis8o75Pp1koCme2Yo"; client = new Client({ wallet: { @@ -145,7 +145,7 @@ describe('Dash - Client', function suite() { const importedIdentityIds = account.identities.getIdentityIds(); // Check that we've imported identities properly expect(importedIdentityIds.length).to.be.equal(accountIdentitiesCountBeforeTest + 1); - expect(importedIdentityIds[0]).to.be.equal(interceptedIdentityStateTransition.getIdentityId().toString()); + expect(importedIdentityIds[1]).to.be.equal(interceptedIdentityStateTransition.getIdentityId().toString()); }); it('should throw TransitionBroadcastError when transport resolves error', async () => { From 3f8c683133891aecb3a7977d109c3120e0ec0f1f Mon Sep 17 00:00:00 2001 From: "markin.io" Date: Sun, 20 Feb 2022 20:30:59 +0300 Subject: [PATCH 22/27] Fixes identity fixture index --- .../src/test/fixtures/createIdentityFixtureInAccount.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/js-dash-sdk/src/test/fixtures/createIdentityFixtureInAccount.ts b/packages/js-dash-sdk/src/test/fixtures/createIdentityFixtureInAccount.ts index 537483b198e..c631b97a100 100644 --- a/packages/js-dash-sdk/src/test/fixtures/createIdentityFixtureInAccount.ts +++ b/packages/js-dash-sdk/src/test/fixtures/createIdentityFixtureInAccount.ts @@ -4,7 +4,7 @@ const getIdentityFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityFi export function createIdentityFixtureInAccount(account) { const identityFixture = getIdentityFixture(); - const identityFixtureIndex = 10000; + const identityFixtureIndex = 0; const { privateKey: identityPrivateKey } = account.identities.getIdentityHDKeyByIndex(identityFixtureIndex, 0); identityFixture.publicKeys[0] = new IdentityPublicKey({ From 5c81461020624a90b1dd43773e84c791fd418a53 Mon Sep 17 00:00:00 2001 From: "markin.io" Date: Mon, 28 Feb 2022 21:43:07 +0300 Subject: [PATCH 23/27] Fixes a broadcast of chain of transactions --- .../wallet-lib/src/types/Account/Account.js | 10 +++++ .../Account/methods/broadcastTransaction.js | 42 +++++++++++++++---- .../src/types/Account/methods/getUTXOS.js | 11 +++-- .../src/types/ChainStore/ChainStore.js | 5 ++- .../ChainStore/methods/considerTransaction.js | 14 +++++++ .../types/Storage/methods/createChainStore.js | 15 ++++++- .../wallet-lib/tests/functional/wallet.js | 6 ++- 7 files changed, 86 insertions(+), 17 deletions(-) diff --git a/packages/wallet-lib/src/types/Account/Account.js b/packages/wallet-lib/src/types/Account/Account.js index 4f963a79880..1949da45c31 100644 --- a/packages/wallet-lib/src/types/Account/Account.js +++ b/packages/wallet-lib/src/types/Account/Account.js @@ -216,6 +216,16 @@ class Account extends EventEmitter { // } // } this.emit(EVENTS.CREATED, { type: EVENTS.CREATED, payload: null }); + + /** + * Stores promise that waits for the IS lock of the particular transaction + * @type {Promise} + */ + this.txISLockListener = null; + + // Increases a limit of max listeners for transactions related events + // 25 - mempool limit + this.setMaxListeners(25); } static getInstantLockTopicName(transactionHash) { diff --git a/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.js b/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.js index 0d11eba63c9..f537977d0f4 100644 --- a/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.js +++ b/packages/wallet-lib/src/types/Account/methods/broadcastTransaction.js @@ -66,14 +66,8 @@ function impactAffectedInputs({ transaction }) { return true; } -/** - * Broadcast a Transaction to the transport layer - * @param {Transaction|RawTransaction} transaction - A txobject or it's hexadecimal representation - * @param {Object} [options] - * @param {Boolean} [options.skipFeeValidation=false] - Allow to skip fee validation - * @return {Promise} - */ -async function broadcastTransaction(transaction, options = {}) { +// eslint-disable-next-line no-underscore-dangle +async function _broadcastTransaction(transaction, options = {}) { const { network, storage } = this; if (!this.transport) throw new ValidTransportLayerRequired('broadcast'); @@ -82,7 +76,7 @@ async function broadcastTransaction(transaction, options = {}) { if (is.string(transaction)) { const rawtx = transaction.toString(); if (!is.rawtx(rawtx)) throw new InvalidRawTransaction(rawtx); - return broadcastTransaction.call(this, new Dashcore.Transaction(rawtx)); + return _broadcastTransaction.call(this, new Dashcore.Transaction(rawtx)); } if (!is.dashcoreTransaction(transaction)) { @@ -114,4 +108,34 @@ async function broadcastTransaction(transaction, options = {}) { return txid; } +/** + * Broadcast a Transaction to the transport layer + * @param {Transaction|RawTransaction} transaction - A txobject or it's hexadecimal representation + * @param {Object} [options] + * @param {Boolean} [options.skipFeeValidation=false] - Allow to skip fee validation + * @return {Promise} + */ +async function broadcastTransaction(transaction, options = {}) { + if (!this.txISLockListener) { + const txId = await _broadcastTransaction.call(this, transaction, options); + + this.txISLockListener = new Promise((resolve) => { + this.subscribeToTransactionInstantLock.call(this, txId, () => { + this.txISLockListener = null; + resolve(); + }); + + // TODO: Also subscribe to FETCHED_CONFIRMED_TRANSACTION + // to use as a fallback to resolve the promise + // (blocked by https://github.com/dashevo/wallet-lib/pull/340) + }); + + return txId; + } + + await this.txISLockListener; + + return broadcastTransaction.call(this, transaction, options); +} + module.exports = broadcastTransaction; diff --git a/packages/wallet-lib/src/types/Account/methods/getUTXOS.js b/packages/wallet-lib/src/types/Account/methods/getUTXOS.js index cd14fefb190..292798cc173 100644 --- a/packages/wallet-lib/src/types/Account/methods/getUTXOS.js +++ b/packages/wallet-lib/src/types/Account/methods/getUTXOS.js @@ -29,16 +29,19 @@ function getUTXOS(options = { utxosKeys.forEach((utxoIdentifier) => { let skipUtxo = false; const [txid, outputIndex] = utxoIdentifier.split('-'); - const { transaction } = chainStore.getTransaction(txid); - if (transaction.isCoinbase()) { + + const txInStore = chainStore.getTransaction(txid); + + if (txInStore && txInStore.transaction.isCoinbase()) { + const { transaction, metadata } = txInStore; // If the transaction is not a special transaction, we can't check its // maturity at the moment of writing this comment. // The wallet library doesn't maintain the header chain and thus we can // figure out the height only from the payload, but old coinbase transactions // doesn't have a payload. if (transaction.isSpecialTransaction()) { - const transactionHeight = (this.store.transactionsMetadata[txid]) - ? this.store.transactionsMetadata[txid].height + const transactionHeight = metadata + ? metadata.height : transaction.extraPayload.height; // We check maturity is at least 100 blocks. diff --git a/packages/wallet-lib/src/types/ChainStore/ChainStore.js b/packages/wallet-lib/src/types/ChainStore/ChainStore.js index 0c9e4df6b11..e1ebed074d5 100644 --- a/packages/wallet-lib/src/types/ChainStore/ChainStore.js +++ b/packages/wallet-lib/src/types/ChainStore/ChainStore.js @@ -1,10 +1,13 @@ +const EventEmitter = require('events'); + /** * ChainStore holds any information that is relatives to a specific network. * Information such as blockHeaders, transactions, instantLocks. * Also holds the state of addresses based on the transactions imported (e.g: balances and utxos). */ -class ChainStore { +class ChainStore extends EventEmitter { constructor(networkIdentifier = 'testnet') { + super(); this.network = networkIdentifier; this.state = { diff --git a/packages/wallet-lib/src/types/ChainStore/methods/considerTransaction.js b/packages/wallet-lib/src/types/ChainStore/methods/considerTransaction.js index b8c991a4954..bbfd5e895a8 100644 --- a/packages/wallet-lib/src/types/ChainStore/methods/considerTransaction.js +++ b/packages/wallet-lib/src/types/ChainStore/methods/considerTransaction.js @@ -1,5 +1,6 @@ const { Transaction } = require('@dashevo/dashcore-lib'); const logger = require('../../../logger'); +const EVENTS = require('../../../EVENTS'); const { Output } = Transaction; @@ -12,6 +13,8 @@ function considerTransaction(transactionHash) { const processedAddressesForTx = {}; + let broadcastTxEvent = false; + [...inputs, ...outputs].forEach((element) => { const isOutput = (element instanceof Output); if (isOutput) outputIndex += 1; @@ -39,6 +42,7 @@ function considerTransaction(transactionHash) { const previousOutput = watchedAddress.utxos[utxoKey]; watchedAddress.balanceSat -= previousOutput.satoshis; delete watchedAddress.utxos[utxoKey]; + broadcastTxEvent = true; } } else { const vout = element; @@ -47,6 +51,11 @@ function considerTransaction(transactionHash) { if (!watchedAddress.utxos[utxoKey]) { watchedAddress.utxos[utxoKey] = vout.toJSON(); watchedAddress.balanceSat += vout.satoshis; + broadcastTxEvent = true; + } else if (watchedAddress.unconfirmedBalanceSat >= vout.satoshis) { + watchedAddress.unconfirmedBalanceSat -= vout.satoshis; + watchedAddress.balanceSat += vout.satoshis; + broadcastTxEvent = true; } } } @@ -77,6 +86,11 @@ function considerTransaction(transactionHash) { }); } + // TODO: restore EVENTS.FETCHED_UNCONFIRMED_TRANSACTION + if (broadcastTxEvent) { + this.emit(EVENTS.FETCHED_CONFIRMED_TRANSACTION, { transaction }); + } + return processedAddressesForTx; } diff --git a/packages/wallet-lib/src/types/Storage/methods/createChainStore.js b/packages/wallet-lib/src/types/Storage/methods/createChainStore.js index 47ae5c2bde6..f978e5b9413 100644 --- a/packages/wallet-lib/src/types/Storage/methods/createChainStore.js +++ b/packages/wallet-lib/src/types/Storage/methods/createChainStore.js @@ -1,4 +1,9 @@ const ChainStore = require('../../ChainStore/ChainStore'); +const EVENTS = require('../../../EVENTS'); + +const EVENTS_TO_FORWARD = [ + EVENTS.FETCHED_CONFIRMED_TRANSACTION, +]; /** * Create when does not yet exist a chainStore @@ -7,7 +12,15 @@ const ChainStore = require('../../ChainStore/ChainStore'); */ const createChainStore = function createChain(network) { if (!this.chains.has(network.toString())) { - this.chains.set(network.toString(), new ChainStore(network.toString())); + const chainStore = new ChainStore(network.toString()); + this.chains.set(network.toString(), chainStore); + + EVENTS_TO_FORWARD.forEach((event) => { + chainStore.on(event, (data) => { + this.emit(event, { type: event, payload: data }); + }); + }); + return true; } return false; diff --git a/packages/wallet-lib/tests/functional/wallet.js b/packages/wallet-lib/tests/functional/wallet.js index 0d2f1f81027..e78856ab4f5 100644 --- a/packages/wallet-lib/tests/functional/wallet.js +++ b/packages/wallet-lib/tests/functional/wallet.js @@ -46,7 +46,8 @@ describe('Wallet-lib - functional', function suite() { expect(newWallet.walletType).to.be.equal('hdwallet'); expect(newWallet.plugins).to.be.deep.equal({}); expect(newWallet.accounts).to.be.deep.equal([]); - expect(newWallet.keyChain.type).to.be.deep.equal('HDPrivateKey'); + expect(newWallet.keyChainStore.getMasterKeyChain().rootKeyType) + .to.be.deep.equal('HDPrivateKey'); expect(newWallet.passphrase).to.be.deep.equal(null); expect(newWallet.allowSensitiveOperations).to.be.deep.equal(false); expect(newWallet.injectDefaultPlugins).to.be.deep.equal(true); @@ -71,7 +72,8 @@ describe('Wallet-lib - functional', function suite() { expect(wallet.walletType).to.be.equal('hdwallet'); expect(wallet.plugins).to.be.deep.equal({}); expect(wallet.accounts).to.be.deep.equal([]); - expect(wallet.keyChain.type).to.be.deep.equal('HDPrivateKey'); + expect(newWallet.keyChainStore.getMasterKeyChain().rootKeyType) + .to.be.deep.equal('HDPrivateKey'); expect(wallet.passphrase).to.be.deep.equal(null); expect(wallet.allowSensitiveOperations).to.be.deep.equal(false); expect(wallet.injectDefaultPlugins).to.be.deep.equal(true); From eb6ee75111041f5414b8c1dd9a5d98c5c3114ead Mon Sep 17 00:00:00 2001 From: "markin.io" Date: Tue, 1 Mar 2022 14:20:56 +0300 Subject: [PATCH 24/27] Fixes undefined bestBlockHeight and reverts breaking getTransactions API change --- .../platform-test-suite/test/e2e/wallet.spec.js | 14 +++++++++----- .../TransactionSyncStreamWorker.js | 2 +- .../src/types/Account/methods/getTransactions.js | 11 +++-------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/platform-test-suite/test/e2e/wallet.spec.js b/packages/platform-test-suite/test/e2e/wallet.spec.js index 3e1ba322db5..e9d9369f592 100644 --- a/packages/platform-test-suite/test/e2e/wallet.spec.js +++ b/packages/platform-test-suite/test/e2e/wallet.spec.js @@ -105,13 +105,17 @@ describe('e2e', () => { restoredAccount = await restoredWallet.getWalletAccount(); - // Due to the limitations of DAPI, we need to wait for a block to be mined if we connected - // in the moment when transaction already entered the mempool, but haven't been mined yet - await new Promise((resolve) => restoredAccount.once(EVENTS.BLOCKHEADER, resolve)); - + let transactions = restoredAccount.getTransactions(); + + if (Object.keys(transactions).length === 0) { + // Due to the limitations of DAPI, we need to wait for a block to be mined if we connected + // in the moment when transaction already entered the mempool, but haven't been mined yet + await new Promise((resolve) => restoredAccount.once(EVENTS.BLOCKHEADER, resolve)); + transactions = restoredAccount.getTransactions(); + } await waitForBalanceToChange(restoredAccount); - const transactionIds = Object.keys(restoredAccount.getTransactions()); + const transactionIds = Object.keys(transactions); expect(transactionIds).to.have.lengthOf(1); diff --git a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/TransactionSyncStreamWorker.js b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/TransactionSyncStreamWorker.js index 07a9b8cbadb..f623a080e7d 100644 --- a/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/TransactionSyncStreamWorker.js +++ b/packages/wallet-lib/src/plugins/Workers/TransactionSyncStreamWorker/TransactionSyncStreamWorker.js @@ -139,7 +139,7 @@ class TransactionSyncStreamWorker extends Worker { if (skipSynchronization) { logger.debug('TransactionSyncStreamWorker - Wallet created from a new mnemonic. Sync from the best block height.'); - const bestBlockHeight = this.storage.getChainStore(this.network.toString()).blockHeight; + const bestBlockHeight = this.storage.getChainStore(this.network.toString()).state.blockHeight; this.setLastSyncedBlockHeight(bestBlockHeight); return; } diff --git a/packages/wallet-lib/src/types/Account/methods/getTransactions.js b/packages/wallet-lib/src/types/Account/methods/getTransactions.js index 880502aeca7..8ef9ec7ee9f 100644 --- a/packages/wallet-lib/src/types/Account/methods/getTransactions.js +++ b/packages/wallet-lib/src/types/Account/methods/getTransactions.js @@ -6,7 +6,7 @@ module.exports = function getTransactions() { const chainStore = this.storage.getChainStore(this.network); const walletStore = this.storage.getWalletStore(this.walletId); const transactions = []; - const transactionsArray = []; + const { addresses } = walletStore.getPathState(this.accountPath); Object @@ -17,15 +17,10 @@ module.exports = function getTransactions() { const transactionIds = addressData.transactions; transactionIds.forEach((transactionId) => { const tx = chainStore.getTransaction(transactionId); - transactions[tx.transaction.hash] = [tx.transaction, tx.metadata]; + transactions[tx.transaction.hash] = tx.transaction; }); } }); - Object.entries(transactions) - .forEach(([, transactionWithMeta]) => { - transactionsArray.push(transactionWithMeta); - }); - - return transactionsArray; + return transactions; }; From 3ba72133813ce270d927c48fa815bb95bacf47f0 Mon Sep 17 00:00:00 2001 From: "markin.io" Date: Tue, 1 Mar 2022 18:16:43 +0300 Subject: [PATCH 25/27] Fixes getTransactionHistory.js and Identity.spec.js --- .../test/functional/platform/Identity.spec.js | 13 +++++++------ .../types/Account/methods/getTransactionHistory.js | 10 +++++++++- .../types/Account/methods/getTransactions.spec.js | 7 ++----- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/platform-test-suite/test/functional/platform/Identity.spec.js b/packages/platform-test-suite/test/functional/platform/Identity.spec.js index 450a176797f..30ddd81656c 100644 --- a/packages/platform-test-suite/test/functional/platform/Identity.spec.js +++ b/packages/platform-test-suite/test/functional/platform/Identity.spec.js @@ -15,10 +15,10 @@ const BalanceIsNotEnoughError = require('@dashevo/dpp/lib/errors/consensus/fee/B const DAPIClient = require('@dashevo/dapi-client/lib/DAPIClient'); const { hash } = require('@dashevo/dpp/lib/util/hash'); const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); +const Transaction = require('@dashevo/dashcore-lib/lib/transaction'); const createClientWithFundedWallet = require('../../../lib/test/createClientWithFundedWallet'); const wait = require('../../../lib/wait'); const getDAPISeeds = require('../../../lib/test/getDAPISeeds'); -const Transaction = require('@dashevo/dashcore-lib/lib/transaction'); describe('Platform', () => { describe('Identity', () => { @@ -114,11 +114,12 @@ describe('Platform', () => { await wait(5000); } - walletAccount.storage.insertIdentityIdAtIndex( - walletAccount.walletId, - identityOne.getId().toString(), - identityOneIndex, - ); + walletAccount.storage + .getWalletStore(walletAccount.walletId) + .insertIdentityIdAtIndex( + identityOne.getId().toString(), + identityOneIndex, + ); // Creating transition that tries to spend the same transaction const { diff --git a/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.js b/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.js index 3dce856d09f..f2cbdca2c6a 100644 --- a/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.js +++ b/packages/wallet-lib/src/types/Account/methods/getTransactionHistory.js @@ -22,9 +22,17 @@ function getTransactionHistory() { network, } = this; - const transactionsWithMetadata = this.getTransactions(); + const transactions = this.getTransactions(); + const walletStore = storage.getWalletStore(walletId); const chainStore = storage.getChainStore(network); + + const transactionsWithMetadata = Object.keys(transactions).reduce((acc, hash) => { + const { metadata } = chainStore.getTransaction(hash); + acc.push([transactions[hash], metadata]); + return acc; + }, []); + const { blockHeaders } = chainStore.state; const categorizedTransactions = categorizeTransactions( diff --git a/packages/wallet-lib/src/types/Account/methods/getTransactions.spec.js b/packages/wallet-lib/src/types/Account/methods/getTransactions.spec.js index fa4ac069218..defa1d59220 100644 --- a/packages/wallet-lib/src/types/Account/methods/getTransactions.spec.js +++ b/packages/wallet-lib/src/types/Account/methods/getTransactions.spec.js @@ -11,11 +11,8 @@ mockedHDSelf.getTransactions = getTransactions; describe('Account - getTransactions', function suite() { this.timeout(10000); it('should get the transactions', () => { - const transactionsWithMetadata = getTransactions.call(mockedHDSelf); - const transactionsHash = transactionsWithMetadata.reduce((acc, el)=>{ - acc.push(el[0].hash); - return acc; - },[]); + const transactions = getTransactions.call(mockedHDSelf); + const transactionsHash = Object.keys(transactions); expect(transactionsHash).to.deep.equal([ 'a43845e580ad01f31bc06ce47ab39674e40316c4c6b765b6e54d6d35777ef456', From 9f528c57959fc779b5c3040a41e3cfce0f8074eb Mon Sep 17 00:00:00 2001 From: "markin.io" Date: Tue, 8 Mar 2022 12:54:39 +0300 Subject: [PATCH 26/27] Fixes auto-reconnection in case new addresses have been generated --- packages/wallet-lib/src/EVENTS.js | 1 - .../wallet-lib/src/types/Account/Account.js | 37 ------------------- .../src/types/Account/_initializeAccount.js | 11 ------ .../Account/methods/importTransactions.js | 5 ++- .../TransactionSyncStreamWorker.spec.js | 18 ++++----- 5 files changed, 13 insertions(+), 59 deletions(-) diff --git a/packages/wallet-lib/src/EVENTS.js b/packages/wallet-lib/src/EVENTS.js index 1f6448ffc82..7be5a5edd6d 100644 --- a/packages/wallet-lib/src/EVENTS.js +++ b/packages/wallet-lib/src/EVENTS.js @@ -10,7 +10,6 @@ module.exports = { TRANSACTION: 'transaction', BLOCKHEADER: 'blockheader', FETCHED_ADDRESS: 'FETCHED/ADDRESS', - UPDATED_ADDRESS: 'UPDATED/ADDRESS', ERROR_UPDATE_ADDRESS: 'ERROR/UPDATE_ADDRESS', FETCHED_TRANSACTION: 'FETCHED/TRANSACTION', FETCHED_UNCONFIRMED_TRANSACTION: 'FETCHED/UNCONFIRMED_TRANSACTION', diff --git a/packages/wallet-lib/src/types/Account/Account.js b/packages/wallet-lib/src/types/Account/Account.js index 1949da45c31..e9f9d293f83 100644 --- a/packages/wallet-lib/src/types/Account/Account.js +++ b/packages/wallet-lib/src/types/Account/Account.js @@ -93,7 +93,6 @@ class Account extends EventEmitter { this.storage = wallet.storage; // Forward all storage event - this.storage.on(EVENTS.UPDATED_ADDRESS, (ev) => this.emit(ev.type, ev)); this.storage.on(EVENTS.CONFIGURED, (ev) => this.emit(ev.type, ev)); this.storage.on(EVENTS.REHYDRATE_STATE_FAILED, (ev) => this.emit(ev.type, ev)); this.storage.on(EVENTS.REHYDRATE_STATE_SUCCESS, (ev) => this.emit(ev.type, ev)); @@ -115,15 +114,6 @@ class Account extends EventEmitter { switch (this.walletType) { case WALLET_TYPES.HDWALLET: this.accountPath = getBIP44Path(this.network, this.index); - // this.storage - // .getWalletStore(this.walletId) - // .createPathState(this.BIP44PATH); - // this.storage.createAccount( - // this.walletId, - // this.BIP44PATH, - // this.network, - // this.label, - // ); break; case WALLET_TYPES.HDPUBLIC: case WALLET_TYPES.PRIVATEKEY: @@ -131,14 +121,6 @@ class Account extends EventEmitter { case WALLET_TYPES.ADDRESS: case WALLET_TYPES.SINGLE_ADDRESS: this.accountPath = 'm/0'; - // this.storage - // .getWalletStore(this.walletId) - // .createPathState(this); - // this.storage.createSingleAddress( - // this.walletId, - // this.network, - // this.label, - // ); break; default: throw new Error(`Invalid wallet type ${this.walletType}`); @@ -196,25 +178,6 @@ class Account extends EventEmitter { watchers: {}, }; - // // Handle import of cache - // if (opts.cache) { - // if (opts.cache.addresses) { - // try { - // this.storage.importAddresses(opts.cache.addresses, this.walletId); - // } catch (e) { - // this.disconnect(); - // throw e; - // } - // } - // if (opts.cache.transactions) { - // try { - // this.storage.importTransactions(opts.cache.transactions); - // } catch (e) { - // this.disconnect(); - // throw e; - // } - // } - // } this.emit(EVENTS.CREATED, { type: EVENTS.CREATED, payload: null }); /** diff --git a/packages/wallet-lib/src/types/Account/_initializeAccount.js b/packages/wallet-lib/src/types/Account/_initializeAccount.js index 9e418da3f32..adee1ea8a00 100644 --- a/packages/wallet-lib/src/types/Account/_initializeAccount.js +++ b/packages/wallet-lib/src/types/Account/_initializeAccount.js @@ -1,22 +1,11 @@ const logger = require('../../logger'); const EVENTS = require('../../EVENTS'); const preparePlugins = require('./_preparePlugins'); -const { UPDATED_ADDRESS } = require('../../EVENTS'); // eslint-disable-next-line no-underscore-dangle async function _initializeAccount(account, userUnsafePlugins) { const self = account; - function markAddressAsUsed(props) { - const { address } = props.payload; - // This works if the TX cames from our main address, but not in all cases... - self.keyChainStore - .getMasterKeyChain() - .markAddressAsUsed(address); - } - - self.on(UPDATED_ADDRESS, markAddressAsUsed); - const accountStore = account.storage .getWalletStore(account.walletId) .getPathState(account.accountPath); diff --git a/packages/wallet-lib/src/types/Account/methods/importTransactions.js b/packages/wallet-lib/src/types/Account/methods/importTransactions.js index 9af3b9681c5..42c5f167884 100644 --- a/packages/wallet-lib/src/types/Account/methods/importTransactions.js +++ b/packages/wallet-lib/src/types/Account/methods/importTransactions.js @@ -15,6 +15,8 @@ module.exports = async function importTransactions(transactionsWithMayBeMetadata keyChainStore, } = this; + let addressesGenerated = 0; + const chainStore = storage.getChainStore(network); const accountStore = storage .getWalletStore(walletId) @@ -36,6 +38,7 @@ module.exports = async function importTransactions(transactionsWithMayBeMetadata keyChains.forEach((keyChain) => { const issuedPaths = keyChain.markAddressAsUsed(address); if (issuedPaths) { + addressesGenerated += issuedPaths.length; issuedPaths.forEach((issuedPath) => { if (keyChain.keyChainId === masterKeyChain.keyChainId) { logger.silly(`Account.importTransactions - newly issued paths ${issuedPath.length}`); @@ -48,5 +51,5 @@ module.exports = async function importTransactions(transactionsWithMayBeMetadata }); }); logger.silly(`Account.importTransactions(len: ${transactionsWithMayBeMetadata.length})`); - return 0; + return addressesGenerated; }; diff --git a/packages/wallet-lib/tests/integration/plugins/Workers/TransactionSyncStreamWorker.spec.js b/packages/wallet-lib/tests/integration/plugins/Workers/TransactionSyncStreamWorker.spec.js index 053cdbd4e07..109d93ba870 100644 --- a/packages/wallet-lib/tests/integration/plugins/Workers/TransactionSyncStreamWorker.spec.js +++ b/packages/wallet-lib/tests/integration/plugins/Workers/TransactionSyncStreamWorker.spec.js @@ -206,13 +206,13 @@ describe('TransactionSyncStreamWorker', function suite() { // the last used address expect(Object.keys(addressesInStorage).length).to.be.equal(40); // It should reconnect after the gap limit is reached - expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(1); + expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(2); // 20 external and 20 internal expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[0].length).to.be.equal(40); expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[1]).to.be.deep.equal({ fromBlockHeight: 40, count: 2}); // 20 more of external, since the last address is used. - // expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(60); - // expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHash: '0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9', count: 2}); + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(60); + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHash: '0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9', count: 2}); expect(worker.stream).to.be.null; expect(transactionsInStorage.length).to.be.equal(2); @@ -427,13 +427,13 @@ describe('TransactionSyncStreamWorker', function suite() { // the last used address expect(Object.keys(addressesInStorage).length).to.be.equal(40); // It should reconnect after the gap limit is reached - expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(1); + expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(2); // 20 external and 20 internal expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[0].length).to.be.equal(40); expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[1]).to.be.deep.equal({ fromBlockHeight: 40, count: 0}); // 20 more of external, since the last address is used. - // expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(60); - // expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHash: '0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9', count: 0}); + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(60); + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHash: '0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9', count: 0}); expect(worker.stream).to.be.null; expect(transactionsInStorage.length).to.be.equal(2); @@ -691,13 +691,13 @@ describe('TransactionSyncStreamWorker', function suite() { expect(Object.keys(externalAddressesInStorage).length).to.be.equal(40); expect(Object.keys(internalAddressesInStorage).length).to.be.equal(20); // It should reconnect after the gap limit is reached - expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(1); + expect(account.transport.subscribeToTransactionsWithProofs.callCount).to.be.equal(2); // 20 external and 20 internal expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[1]).to.be.deep.equal({ fromBlockHeight: 40, count: 0}); expect(account.transport.subscribeToTransactionsWithProofs.firstCall.args[0].length).to.be.equal(40); // 20 more of external, since the last address is used, Merkle Block received - // expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(60); - // expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHash: '0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9', count: 0}); + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[0].length).to.be.equal(60); + expect(account.transport.subscribeToTransactionsWithProofs.secondCall.args[1]).to.be.deep.equal({ fromBlockHash: '0000025d24ebe65454bd51a61bab94095a6ad1df996be387e31495f764d8e2d9', count: 0}); expect(worker.stream).to.be.null; expect(transactionsInStorage.length).to.be.equal(2); expect(transactionsInStorage).to.have.deep.members(expectedTransactions); From cc6773bdb90dd1a9c0d32108a9a66d7e4204e243 Mon Sep 17 00:00:00 2001 From: "markin.io" Date: Tue, 8 Mar 2022 17:53:25 +0300 Subject: [PATCH 27/27] Fix importInstantLock --- packages/wallet-lib/src/types/ChainStore/methods/importState.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wallet-lib/src/types/ChainStore/methods/importState.js b/packages/wallet-lib/src/types/ChainStore/methods/importState.js index ab0feb9a2c6..64460e0954f 100644 --- a/packages/wallet-lib/src/types/ChainStore/methods/importState.js +++ b/packages/wallet-lib/src/types/ChainStore/methods/importState.js @@ -36,7 +36,7 @@ function importState(state) { this.importTransaction(new Transaction(Buffer.from(serializedTransaction, 'hex')), metadata); }); Object.values(instantLocks).forEach((serializedInstantLock) => { - this.importTransaction(new InstantLock(Buffer.from(serializedInstantLock, 'hex'))); + this.importInstantLock(new InstantLock(Buffer.from(serializedInstantLock, 'hex'))); }); } module.exports = importState;