diff --git a/CHANGELOG.md b/CHANGELOG.md index f1cf0f5..098a97d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -534,4 +534,5 @@ * Added validation for pin parameter in export private key, restore keyring state & current pin parameter in change pin * Added validation for encryption key in add account, sign message, delete account, get vault details & update label * Sync the pin validation steps with other methods in import wallet -* Updated tests wrt changes in vault generation and parameter validations \ No newline at end of file +* Updated tests wrt changes in vault generation and parameter validations +* Implemented vault recovery using logs and updated tests \ No newline at end of file diff --git a/src/constants/responses/index.js b/src/constants/responses/index.js index 8a28bc5..96cf01f 100644 --- a/src/constants/responses/index.js +++ b/src/constants/responses/index.js @@ -15,5 +15,6 @@ module.exports = { INCORRECT_CURRENT_PIN: 'The current pin passed is incorrect', INCORRECT_LABEL_TYPE: 'Label should be a valid string', REQUEST_LIMIT_EXCEEDED: 'Request limit exceeded. Please retry later', - REQUEST_BLOCKED: 'Requests to method blocked' + REQUEST_BLOCKED: 'Requests to method blocked', + INVALID_API_KEY: 'Invalid API key passed' }; diff --git a/src/lib/test/vault.test.js b/src/lib/test/vault.test.js index 9a89324..74549e6 100644 --- a/src/lib/test/vault.test.js +++ b/src/lib/test/vault.test.js @@ -8,6 +8,70 @@ let phrase="fun rough treat scan glimpse region century purpose expire video rem let pin=696969 let vault =new Vault({}) +const logs = [ + { + "action": "add-account", + "timestamp": 1000000000001, + "platform": "web", + "address": "0xF8919220F674a553F0F0F6e86481612A2bEd44EB", + "storage": [ + "mobile" + ], + "_id": "64e881b05b04774ca85aee51" + }, + { + "action": "add-account", + "timestamp": 1000000000002, + "platform": "web", + "address": "0x627437E29e7363C0F53896e84467EF6F8f9D0247", + "storage": [ + "mobile" + ], + "_id": "64e881e3bae0e048dfaefc46" + }, + { + "action": "add-account", + "timestamp": 1000000000003, + "platform": "web", + "address": "0xa1F77e4D8306000639D1d44a6013ad53b992182E", + "storage": [ + "mobile" + ], + "_id": "64ec3339a58abcbf66a9b34a" + }, + { + "action": "add-account", + "timestamp": 1000000000004, + "platform": "web", + "address": "0x9e6627384a3E6453b9EC061e4DaeD4cE0223bbdc", + "storage": [ + "mobile" + ], + "_id": "64ec333ca58abcbf66a9b354" + }, + { + "action": "add-account", + "timestamp": 1000000000005, + "platform": "mobile", + "address": "0xCccbD31ea19acE5688731148a4f63907F273BEe0", + "storage": [ + "mobile" + ], + "_id": "64e87e9e72e00ccf96bce1fc" + }, + { + "action": "delete-account", + "timestamp": 1000000000006, + "platform": "web", + "address": "0x9e6627384a3E6453b9EC061e4DaeD4cE0223bbdc", + "storage": [ + "mobile" + ], + "_id": "64ec3339a58abcbf66a9b34a" + }, + ] + + describe('getSupportedChains' , ()=>{ test('getSupportedChains' , async()=>{ @@ -105,11 +169,24 @@ describe("generateVault",()=>{ describe("recoverVault",()=>{ - test('recoverVault/valid' , async()=>{ + test('recoverVault/transaction valid' , async()=>{ let result = await vault.recoverVault(phrase,bufView,pin,'BgoGMHvB5R7iMNhZ2BoJd470aSZNEz9t2N8PBOWD') expect(result).toHaveProperty('response') }) + + test('recoverVault/logs valid' , async()=>{ + + let result = await vault.recoverVault(phrase,bufView,pin,null,'logs', logs) + expect(result).toHaveProperty('response') + }) + + test('recoverVault/logs empty logs valid' , async()=>{ + + let result = await vault.recoverVault(phrase,bufView,pin,null,'logs') + expect(result).toHaveProperty('response') + }) + test('recoverVault/empty phrase' , async()=>{ try{ let result = await vault.recoverVault(null,bufView,pin,'BgoGMHvB5R7iMNhZ2BoJd470aSZNEz9t2N8PBOWD') @@ -140,7 +217,8 @@ describe("recoverVault",()=>{ }) test('recoverVault/empty encryption key' , async()=>{ let result = await vault.recoverVault(phrase,null,pin,'BgoGMHvB5R7iMNhZ2BoJd470aSZNEz9t2N8PBOWD') - expect(result).toHaveProperty('response') + expect(result.error).toBe("Please enter both encryptionKey and pin") + }) test('recoverVault/invalid encryption key' , async()=>{ @@ -164,14 +242,10 @@ describe("recoverVault",()=>{ }) test('recoverVault/empty marshal key' , async()=>{ - try{ - let result = await vault.recoverVault(phrase,bufView,pin,null) - } - catch(e){ - expect(e.message).toBe("Cannot destructure property 'transactions' of 'response' as it is undefined.") + let result = await vault.recoverVault(phrase,bufView,pin,null) + expect(result.error).toBe("Invalid API key passed") - } }) test('recoverVault/invalid marshal key' , async()=>{ diff --git a/src/lib/vault.js b/src/lib/vault.js index 0e33e57..924c85b 100644 --- a/src/lib/vault.js +++ b/src/lib/vault.js @@ -98,14 +98,23 @@ class Vault extends Keyring { return { response: vault }; } - async recoverVault(mnemonic, encryptionKey, pin, unmarshalApiKey) { + async recoverVault(mnemonic, encryptionKey, pin, unmarshalApiKey, recoverMechanism = 'transactions', logs = {}) { + if (!Number.isInteger(pin) || pin < 0 || pin.toString().length !=6) { return { error: ERROR_MESSAGE.INCORRECT_PIN_TYPE }; } + + if (!encryptionKey) { + return { error : ERROR_MESSAGE.ENTER_CREDS } + } + + if(recoverMechanism === 'transactions' && !unmarshalApiKey) { + return { error: ERROR_MESSAGE.INVALID_API_KEY }; + } const vaultState = await this.keyringInstance.createNewVaultAndRestore(JSON.stringify(encryptionKey), mnemonic); - const accountsArray = await helper.removeEmptyAccounts(vaultState.keyrings[0].accounts[0], this.keyringInstance, vaultState, unmarshalApiKey); + const accountsArray = await helper.removeEmptyAccounts(vaultState.keyrings[0].accounts[0], this.keyringInstance, vaultState, unmarshalApiKey, recoverMechanism, logs); const privData = await helper.generatePrivData(mnemonic, pin); @@ -113,6 +122,8 @@ class Vault extends Keyring { const rawVault = { eth: { public: accountsArray, private: privData, numberOfAccounts } } + this.decryptedVault = rawVault + const vault = await helper.cryptography(JSON.stringify(rawVault), JSON.stringify(encryptionKey), 'encryption'); this.vault = vault; diff --git a/src/utils/helper.js b/src/utils/helper.js index 5649635..70825da 100644 --- a/src/utils/helper.js +++ b/src/utils/helper.js @@ -27,35 +27,54 @@ async function generatePrivData(mnemonic, pin) { return priv; } -async function removeEmptyAccounts(indexAddress, keyringInstance, vaultState, unmarshalApiKey) { + +async function removeEmptyAccounts(indexAddress, keyringInstance, vaultState, unmarshalApiKey, recoverMechanism, logs) { const keyring = keyringInstance.getKeyringsByType(vaultState.keyrings[0].type); let zeroCounter = 0; let accountsArray = []; accountsArray.push({ address: indexAddress, isDeleted: false, isImported: false, label: 'Wallet 1' }); - do { - zeroCounter = 0; - for(let i=0; i < 5; i++) { - const vaultState = await keyringInstance.addNewAccount(keyring[0]); - - const ethActivity = await getETHTransactions(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], 'ethereum', unmarshalApiKey); - const polygonActivity = await getPolygonTransactions(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], 'polygon', unmarshalApiKey); - const bscActivity = await getBSCTransactions(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], 'bsc', unmarshalApiKey); - const label = this.createWalletLabels('all', i+2); - - if (!ethActivity && !polygonActivity && !bscActivity) { - accountsArray.push({ address: vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], isDeleted: true, isImported: false, label, isExported: false }); - zeroCounter++; - } else { - accountsArray.push({ address: vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], isDeleted: false, isImported: false, label, isExported: false }); - zeroCounter = 0; + if( recoverMechanism === 'logs'){ + for(let i=0; i < logs.length; i++){ + if (logs[i].action === 'add-account'){ + const vaultState = await keyringInstance.addNewAccount(keyring[0]); + const newAccountAddr = Web3.utils.toChecksumAddress(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1]) + const label = this.createWalletLabels('all', i+2); + if (Web3.utils.toChecksumAddress(logs[i].address) === newAccountAddr) { + accountsArray.push({ address: newAccountAddr, isDeleted: false, isImported: false, label, isExported: false }); + } + + } + if(logs[i].action === 'delete-account') { + let ind = accountsArray.findIndex((acc) => acc.address === Web3.utils.toChecksumAddress(logs[i].address)) + ind >= 0 ? accountsArray[ind].isDeleted = true : false; } } - } - - while (zeroCounter < 5 ) + } else if( recoverMechanism === 'transactions'){ + do { + zeroCounter = 0; + for(let i=0; i < 5; i++) { + const vaultState = await keyringInstance.addNewAccount(keyring[0]); + + const ethActivity = await getETHTransactions(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], 'ethereum', unmarshalApiKey); + const polygonActivity = await getPolygonTransactions(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], 'polygon', unmarshalApiKey); + const bscActivity = await getBSCTransactions(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], 'bsc', unmarshalApiKey); + const label = this.createWalletLabels('all', i+2); + + if (!ethActivity && !polygonActivity && !bscActivity) { + accountsArray.push({ address: vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], isDeleted: true, isImported: false, label, isExported: false }); + zeroCounter++; + } else { + accountsArray.push({ address: vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], isDeleted: false, isImported: false, label, isExported: false }); + zeroCounter = 0; + } + } + } + + while (zeroCounter < 5 ) + } return accountsArray; }