// Use strict "use strict"; // Classes // Wallet class class Wallet { // Public // Initialize static initialize() { // Return promise return new Promise(function(resolve, reject) { // Get the saved password pepper var currentPasswordPepper = localStorage.getItem(Wallet.PASSWORD_PEPPER_LOCAL_STORAGE_NAME); // Check if password pepper doesn't exist or isn't valid if(currentPasswordPepper === Common.INVALID_LOCAL_STORAGE_ITEM || Common.isHexString(currentPasswordPepper) === false) { // Create password pepper var passwordPepper = new Uint8Array(Wallet.PASSWORD_PEPPER_LENGTH); // Fill password pepper with random values crypto.getRandomValues(passwordPepper); // Try try { // Save password pepper localStorage.setItem(Wallet.PASSWORD_PEPPER_LOCAL_STORAGE_NAME, Common.toHexString(passwordPepper)); } // Catch errors catch(error) { // Reject reject(); // Return return; } } // Resolve resolve(); }); } // Constructor constructor(name, color, salt, initializationVector, numberOfIterations, encryptedSeed, addressSuffix, order, syncedHeight, spentAmount, unspentAmount, unconfirmedAmount, lockedAmount, pendingAmount, expiredAmount, walletType, networkType, lastIdentifier, hardwareType, encryptedRootPublicKey, useBip39, encryptedBip39Salt, accountNumber, paymentProofIndex, keyPath) { // Set performing address suffix operation this.setPerformingAddressSuffixOperation(false); // Set address suffix verified this.setAddressSuffixVerified(false); // Set syncing status to syncing this.setSyncingStatus(Wallet.STATUS_SYNCING); // Set last sync index this.setLastSyncIndex(Wallet.NO_SYNC_INDEX); // Set starting sync height this.setStartingSyncHeight(Wallet.CURRENT_HEIGHT); // Set seed to no seed this.seed = Wallet.NO_SEED; // Set root public key to no root public key this.rootPublicKey = Wallet.NO_ROOT_PUBLIC_KEY; // Set extended private key to no extended private key this.extendedPrivateKey = Wallet.NO_EXTENDED_PRIVATE_KEY; // Set hardware wallet to no hardware wallet this.hardwareWallet = Wallet.NO_HARDWARE_WALLET; // Set BIP39 salt to no BIP39 salt this.bip39Salt = Wallet.NO_BIP39_SALT; // Set percent synced this.setPercentSynced(new BigNumber(Wallets.MINIMUM_PERCENT)); // Check if parameters are provided if(typeof name !== "undefined") { // Set name this.setName(name); // Set color this.setColor(color); // Set salt this.setSalt(salt); // Set initialization vector this.setInitializationVector(initializationVector); // Set number of iterations this.setNumberOfIterations(numberOfIterations); // Set encrypted seed this.setEncryptedSeed(encryptedSeed); // Set address sv this.setAddressSuffix(addressSuffix); // Set order this.setOrder(order); // Set synced height this.setSyncedHeight(syncedHeight); // Set spent amount this.setSpentAmount(spentAmount); // Set unspent amount this.setUnspentAmount(unspentAmount); // Set unconfirmed amount this.setUnconfirmedAmount(unconfirmedAmount); // Set locked amount this.setLockedAmount(lockedAmount); // Set pending amount this.setPendingAmount(pendingAmount); // Set expired amount this.setExpiredAmount(expiredAmount); // Set wallet type this.setWalletType(walletType); // Set network type this.setNetworkType(networkType); // Set last identifier this.setLastIdentifier(lastIdentifier); // Set hardware type this.setHardwareType(hardwareType); // Set encrypted root public key this.setEncryptedRootPublicKey(encryptedRootPublicKey); // Set use BIP39 this.setUseBip39(useBip39); // Set encrypted BIP39 salt this.setEncryptedBip39Salt(encryptedBip39Salt); // Set account number this.setAccountNumber(accountNumber); // Set payment proof index this.setPaymentProofIndex(paymentProofIndex); // Set key path this.setKeyPath(keyPath); // Set starting sync height this.setStartingSyncHeight(syncedHeight); } } // Initialize initialize(name, password, walletType, networkType, syncedHeight, syncingStatus, hardwareWallet, passphrase, useBip39, bip39Salt, existingWallets = []) { // Set name this.setName(name); // Set color to random color this.setColor(Wallet.getRandomColor()); // Set salt to a random salt this.setSalt(crypto.getRandomValues(new Uint8Array(Wallet.SALT_LENGTH))); // Set initialization vector to a random initialization vector this.setInitializationVector(crypto.getRandomValues(new Uint8Array(Wallet.INITIALIZATION_VECTOR_LENGTH))); // Set number of iterations this.setNumberOfIterations(Wallet.DEFAULT_NUMBER_OF_ITERATIONS); // Set address suffix this.setAddressSuffix(Wallet.NO_ADDRESS_SUFFIX); // Set order this.setOrder(Wallet.NO_ORDER); // Set synced height this.setSyncedHeight(syncedHeight); // Set spent amount this.setSpentAmount(new BigNumber(0)); // Set unspent amount this.setUnspentAmount(new BigNumber(0)); // Set unconfirmed amount this.setUnconfirmedAmount(new BigNumber(0)); // Set locked amount this.setLockedAmount(new BigNumber(0)); // Set pending amount this.setPendingAmount(new BigNumber(0)); // Set expired amount this.setExpiredAmount(new BigNumber(0)); // Set wallet type this.setWalletType(walletType); // Set network type this.setNetworkType(networkType); // Set last identifier this.setLastIdentifier(Wallet.NO_LAST_IDENTIFIER); // Set hardware type this.setHardwareType((hardwareWallet !== Wallet.NO_HARDWARE_WALLET) ? hardwareWallet.getHardwareType() : Wallet.NO_HARDWARE_TYPE); // Set account number this.setAccountNumber(0); // Set payment proof index this.setPaymentProofIndex(0); // Set key path to no key path this.setKeyPath(Wallet.NO_KEY_PATH); // Set starting sync height this.setStartingSyncHeight(syncedHeight); // Set syncing status this.setSyncingStatus(syncingStatus); // Set hardware wallet this.hardwareWallet = hardwareWallet; // Set use BIP39 this.setUseBip39(useBip39); // Check if hardware wallet exists if(hardwareWallet !== Wallet.NO_HARDWARE_WALLET) { // Set hardware wallet's wallet key path hardwareWallet.setWalletKeyPath(Wallet.NO_KEY_PATH); } // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't a hardware wallet if(self.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { // Set seed to a new seed self.seed = new Seed(); // Return initializing seed return self.seed.initialize((passphrase !== Wallet.NO_PASSPHRASE) ? passphrase : Seed.DEFAULT_SEED_LENGTH, (passphrase !== Wallet.NO_PASSPHRASE) ? [] : existingWallets.map(function(existingWallet) { // Check if existing wallet isn't a hardware wallet and doesn't use BIP39 if(existingWallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE && existingWallet.getUseBip39() === false) { // Return existing wallet's seed return existingWallet.seed; } // Otherwise else { // Return new seed return new Seed(); } })).then(function() { // Set BIP39 salt self.bip39Salt = bip39Salt; // Return encrypting seed and BIP39 salt return self.encryptSeedAndBip39Salt(password, self.getSalt(), self.getNumberOfIterations(), self.getInitializationVector()).then(function(encryptedSeedAndBip39Salt) { // Set encrypted seed to the encrypted seed self.setEncryptedSeed(encryptedSeedAndBip39Salt[Wallet.ENCRYPT_SEED_AND_BIP39_SALT_ENCRYPTED_SEED_INDEX]); // Set encrypted root public key to no encrypted root public key self.setEncryptedRootPublicKey(Wallet.NO_ENCRYPTED_ROOT_PUBLIC_KEY); // Set encrypted BIP39 salt to the encrypted BIP39 salt self.setEncryptedBip39Salt(encryptedSeedAndBip39Salt[Wallet.ENCRYPT_SEED_AND_BIP39_SALT_ENCRYPTED_BIP39_SALT_INDEX]); // Return getting extended private key from seed return self.seed.getExtendedPrivateKey(Wallet.SEED_KEY, self.getUseBip39(), self.bip39Salt).then(function(extendedPrivateKey) { // Set extended private key self.extendedPrivateKey = extendedPrivateKey; // Return getting root public key return self.getRootPublicKey().then(function(rootPublicKey) { // Initialize get existing root public keys var getExistingRootPublicKeys = []; // Initialize existing root public key var existingRootPublicKeys = []; // Go through all existing wallets for(var i = 0; i < existingWallets["length"]; ++i) { // Get existing wallet let existingWallet = existingWallets[i]; // Append getting existing wallet's root public key to list getExistingRootPublicKeys.push(new Promise(function(resolve, reject) { // Return getting existing wallet's root public key return existingWallet.getRootPublicKey().then(function(existingRootPublicKey) { // Append existing root public key to list existingRootPublicKeys.push(existingRootPublicKey); // Resolve resolve(); // Catch errors }).catch(function(error) { // Reject error reject(error); }); })); } // Return getting all existing root public keys return Promise.all(getExistingRootPublicKeys).then(function() { // Go through all existing root public keys for(var i = 0; i < existingRootPublicKeys["length"]; ++i) { // Check if root public key already exists if(Common.arraysAreEqualTimingSafe(rootPublicKey, existingRootPublicKeys[i]) === true) { // Go through all remaining existing root public keys for(var j = i; j < existingRootPublicKeys["length"]; ++j) { // Securely clear existing root public key existingRootPublicKeys[j].fill(0); } // Securely clear the root public key rootPublicKey.fill(0); // Close self.close(); // Reject error reject(Language.getDefaultTranslation('A wallet with the same passphrase already exists in your list of wallets.')); // Return return; } // Securely clear existing root public key existingRootPublicKeys[i].fill(0); } // Securely clear the root public key rootPublicKey.fill(0); // Resolve resolve(); // Catch errors }).catch(function(error) { // Go through all existing root public keys for(var i = 0; i < existingRootPublicKeys["length"]; ++i) { // Securely clear existing root public key existingRootPublicKeys[i].fill(0); } // Securely clear the root public key rootPublicKey.fill(0); // Close self.close(); // Reject error reject(Language.getDefaultTranslation('Creating wallet failed.')); }); // Catch errors }).catch(function(error) { // Close self.close(); // Reject error reject(Language.getDefaultTranslation('Creating wallet failed.')); }); // Catch errors }).catch(function(error) { // Close self.close(); // Reject error reject(Language.getDefaultTranslation('Creating wallet failed.')); }); // Catch errors }).catch(function(error) { // Close self.close(); // Reject error reject(Language.getDefaultTranslation('Creating wallet failed.')); }); // Catch errors }).catch(function(error) { // Close self.close(); // Check if a passphrase was used if(passphrase !== Wallet.NO_PASSPHRASE) { // Reject error reject(Language.getDefaultTranslation('Invalid passphrase.')); } // Otherwise else { // Reject error reject(Language.getDefaultTranslation('Creating wallet failed.')); } }); } // Otherwise else { // Check if hardware is connected if(self.isHardwareConnected() === true) { // Set that hardware wallet is in use self.getHardwareWallet().setInUse(true); // Hardware wallet on hardware disconnect event $(self.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { // Check if hardware wallet exists if(self.getHardwareWallet() !== Wallet.NO_HARDWARE_WALLET) { // Close hardware wallet self.getHardwareWallet().close(); // Set hardware wallet to no hardware wallet self.hardwareWallet = Wallet.NO_HARDWARE_WALLET; } // Check if key path exists if(self.getKeyPath() !== Wallet.NO_KEY_PATH) { // Trigger wallet disconnect event $(document).trigger(Wallet.DISCONNECT_EVENT, [ // Key path self.getKeyPath() ]); } }); } // Otherwise else { // Check if hardware wallet exists if(self.getHardwareWallet() !== Wallet.NO_HARDWARE_WALLET) { // Close hardware wallet self.getHardwareWallet().close(); // Set hardware wallet to no hardware wallet self.hardwareWallet = Wallet.NO_HARDWARE_WALLET; } } // Initialize root public key candidate var rootPublicKeyCandidate = hardwareWallet.getRootPublicKey(); // Initialize get existing root public keys var getExistingRootPublicKeys = []; // Initialize existing root public key var existingRootPublicKeys = []; // Go through all existing wallets for(var i = 0; i < existingWallets["length"]; ++i) { // Get existing wallet let existingWallet = existingWallets[i]; // Append getting existing wallet's root public key to list getExistingRootPublicKeys.push(new Promise(function(resolve, reject) { // Return getting existing wallet's root public key return existingWallet.getRootPublicKey().then(function(existingRootPublicKey) { // Append existing root public key to list existingRootPublicKeys.push(existingRootPublicKey); // Resolve resolve(); // Catch errors }).catch(function(error) { // Reject error reject(error); }); })); } // Return getting all existing root public keys return Promise.all(getExistingRootPublicKeys).then(function() { // Go through all existing root public keys for(var i = 0; i < existingRootPublicKeys["length"]; ++i) { // Check if root public key already exists if(Common.arraysAreEqualTimingSafe(rootPublicKeyCandidate, existingRootPublicKeys[i]) === true) { // Go through all remaining existing root public keys for(var j = i; j < existingRootPublicKeys["length"]; ++j) { // Securely clear existing root public key existingRootPublicKeys[j].fill(0); } // Securely clear the root public key candidate rootPublicKeyCandidate.fill(0); // Close self.close(); // Reject error reject(Language.getDefaultTranslation('A wallet with the same root public key already exists in your list of wallets.')); // Return return; } // Securely clear existing root public key existingRootPublicKeys[i].fill(0); } // Set root public key self.rootPublicKey = rootPublicKeyCandidate; // Return encrypting root public key return self.encryptRootPublicKey(password, self.getSalt(), self.getNumberOfIterations(), self.getInitializationVector()).then(function(encryptedRootPublicKey) { // Set encrypted seed to no encrypted seed self.setEncryptedSeed(Wallet.NO_ENCRYPTED_SEED); // Set encrypted root public key to encrypted root public key self.setEncryptedRootPublicKey(encryptedRootPublicKey); // Set encrypted BIP39 salt to no encrypted BIP39 salt self.setEncryptedBip39Salt(Wallet.NO_ENCRYPTED_BIP39_SALT); // Resolve resolve(); // Catch errors }).catch(function(error) { // Close self.close(); // Reject error reject(Language.getDefaultTranslation('Creating wallet failed.')); }); // Catch errors }).catch(function(error) { // Go through all existing root public keys for(var i = 0; i < existingRootPublicKeys["length"]; ++i) { // Securely clear existing root public key existingRootPublicKeys[i].fill(0); } // Securely clear the root public key candidate rootPublicKeyCandidate.fill(0); // Close self.close(); // Reject error reject(Language.getDefaultTranslation('Creating wallet failed.')); }); } }); } // Open open(password) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't a hardware wallet if(self.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { // Return decrypting seed and BIP39 salt return self.decryptSeedAndBip39Salt(password, self.getSalt(), self.getNumberOfIterations(), self.getInitializationVector()).then(function(decryptedSeedAndBip39Salt) { // Check if seed exists if(self.seed !== Wallet.NO_SEED) { // Uninitialize seed self.seed.uninitialize(); } // Create seed self.seed = new Seed(); // Check if BIP39 salt exists if(self.bip39Salt !== Wallet.NO_BIP39_SALT) { // Securely clear the BIP39 salt self.bip39Salt.fill(0); } // Set BIP39 salt self.bip39Salt = decryptedSeedAndBip39Salt[Wallet.DECRYPT_SEED_AND_BIP39_SALT_DECRYPTED_BIP39_SALT_INDEX]; // Return initializing seed return self.seed.initialize(decryptedSeedAndBip39Salt[Wallet.DECRYPT_SEED_AND_BIP39_SALT_DECRYPTED_SEED_INDEX]).then(function() { // Return getting extended private key from seed return self.seed.getExtendedPrivateKey(Wallet.SEED_KEY, self.getUseBip39(), self.bip39Salt).then(function(extendedPrivateKey) { // Check if extended private key exists if(self.extendedPrivateKey !== Wallet.NO_EXTENDED_PRIVATE_KEY) { // Securely clear the extended private key self.extendedPrivateKey.fill(0); } // Set extended private key self.extendedPrivateKey = extendedPrivateKey; // Resolve resolve(); // Catch errors }).catch(function(error) { // Close self.close(); // Reject error reject(Language.getDefaultTranslation('Invalid wallet.')); }); // Catch errors }).catch(function(error) { // Securely clear decrypted seed decryptedSeedAndBip39Salt[Wallet.DECRYPT_SEED_AND_BIP39_SALT_DECRYPTED_SEED_INDEX].fill(0); // Securely clear decrypted BIP39 salt decryptedSeedAndBip39Salt[Wallet.DECRYPT_SEED_AND_BIP39_SALT_DECRYPTED_BIP39_SALT_INDEX].fill(0); // Close self.close(); // Reject error reject(Language.getDefaultTranslation('Invalid wallet.')); }); // Catch errors }).catch(function(error) { // Reject error reject(Language.getDefaultTranslation('Incorrect password.')); }); } // Otherwise else { // Return decrypting root public key return self.decryptRootPublicKey(password, self.getSalt(), self.getNumberOfIterations(), self.getInitializationVector()).then(function(decryptedRootPublicKey) { // Check if root public key exists if(self.rootPublicKey !== Wallet.NO_ROOT_PUBLIC_KEY) { // Securely clear the root public key self.rootPublicKey.fill(0); } // Set root public key to decrypted root public key self.rootPublicKey = decryptedRootPublicKey; // Resolve resolve(); // Catch errors }).catch(function(error) { // Reject error reject(Language.getDefaultTranslation('Incorrect password.')); }); } }); } // Is open isOpen() { // Return if seed or root public key exist return this.seed !== Wallet.NO_SEED || this.rootPublicKey !== Wallet.NO_ROOT_PUBLIC_KEY; } // Close close() { // Check if seed exists if(this.seed !== Wallet.NO_SEED) { // Uninitialize seed this.seed.uninitialize(); // Set seed to no seed this.seed = Wallet.NO_SEED; } // Check if root public key exists if(this.rootPublicKey !== Wallet.NO_ROOT_PUBLIC_KEY) { // Securely clear the root public key this.rootPublicKey.fill(0); // Set root public key to no root public key this.rootPublicKey = Wallet.NO_ROOT_PUBLIC_KEY; } // Check if extended private key exists if(this.extendedPrivateKey !== Wallet.NO_EXTENDED_PRIVATE_KEY) { // Securely clear the extended private key this.extendedPrivateKey.fill(0); // Set extended private key to no extended private key this.extendedPrivateKey = Wallet.NO_EXTENDED_PRIVATE_KEY; } // Check if hardware wallet exists if(this.getHardwareWallet() !== Wallet.NO_HARDWARE_WALLET) { // Close the hardware wallet this.getHardwareWallet().close(); // Set hardware wallet to no hardware wallet this.hardwareWallet = Wallet.NO_HARDWARE_WALLET; } // Check if BIP39 salt exists if(this.bip39Salt !== Wallet.NO_BIP39_SALT) { // Securely clear the BIP39 salt this.bip39Salt.fill(0); // Set BIP39 salt to no BIP39 salt this.bip39Salt = Wallet.NO_BIP39_SALT; } } // Get passphrase getPassphrase() { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't open if(self.isOpen() === false) { // Reject error reject(Language.getDefaultTranslation('The wallet is closed.')); } // Otherwise check if wallet is a hardware wallet else if(self.getHardwareType() !== Wallet.NO_HARDWARE_TYPE) { // Reject error reject(Language.getDefaultTranslation('Passphrases can\'t be retrieved from hardware wallets.')); } // Otherwise else { // Return getting seed's mnemonic return self.seed.getMnemonic().then(function(mnemonic) { // Check if BIP39 salt exists if(self.bip39Salt !== Wallet.NO_BIP39_SALT) { // Try try { // Resolve mnemonic with BIP39 salt resolve(mnemonic + " " + (new TextDecoder("utf-8", {"fatal": true})).decode(self.bip39Salt)); } // Catch errors catch(error) { // Reject error reject(Language.getDefaultTranslation('Invalid wallet.')); } } // Otherwise else { // Resolve mnemonic resolve(mnemonic); } // Catch errors }).catch(function(error) { // Reject error reject(Language.getDefaultTranslation('Invalid wallet.')); }); } }); } // Get root public key getRootPublicKey() { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't open if(self.isOpen() === false) { // Reject error reject("Wallet closed."); } // Otherwise check if wallet isn't a hardware wallet else if(self.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { // Return getting root public key return Crypto.rootPublicKey(self.extendedPrivateKey).then(function(rootPublicKey) { // Resolve root public key resolve(rootPublicKey); // Catch errors }).catch(function(error) { // Reject error reject("Invalid wallet."); }); } // Otherwise else { // Resolve copy of root public key resolve(new Uint8Array(self.rootPublicKey)); } }); } // Get address key getAddressKey(index) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't open if(self.isOpen() === false) { // Reject error reject("Wallet closed."); } // Otherwise check if wallet is a hardware wallet else if(self.getHardwareType() !== Wallet.NO_HARDWARE_TYPE) { // Reject error reject("Address key can't be retrieved from a hardware wallet."); } // Otherwise else { // Return getting address key return Crypto.addressKey(self.extendedPrivateKey, index).then(function(addressKey) { // Resolve address key resolve(addressKey); // Catch errors }).catch(function(error) { // Reject error reject("Invalid wallet."); }); } }); } // Get Tor proof address getTorProofAddress(hardwareWalletLockedText = HardwareWallet.NO_TEXT, hardwareWalletLockedTextArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't open if(self.isOpen() === false) { // Reject error reject("Wallet closed."); } // Otherwise else { // Check if wallet isn't a hardware wallet if(self.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { // Return getting address private key at payment proof Tor address index return self.getAddressKey(Wallet.PAYMENT_PROOF_TOR_ADDRESS_KEY_INDEX).then(function(addressSecretKey) { // Check if getting address public key from the address private key failed var addressPublicKey = Ed25519.publicKeyFromSecretKey(addressSecretKey); if(addressPublicKey === Ed25519.OPERATION_FAILED) { // Securely clear address private key addressSecretKey.fill(0); // Reject error reject("Getting address public key from the address private key failed."); } // Otherwise else { // Securely clear address private key addressSecretKey.fill(0); // Set Tor proof address to the Tor address of the addres public key var torProofAddress = Tor.publicKeyToTorAddress(addressPublicKey); // Resolve Tor proof address resolve(torProofAddress); } // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Check if hardware wallet is connected if(self.isHardwareConnected() === true) { // Return getting Tor address with the hardware wallet return self.getHardwareWallet().getTorAddress(Wallet.PAYMENT_PROOF_TOR_ADDRESS_KEY_INDEX, hardwareWalletLockedText, hardwareWalletLockedTextArguments, allowUnlock, preventMessages, cancelOccurred).then(function(torProofAddress) { // Resolve Tor proof address resolve(torProofAddress); // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Reject hardware disconnected error reject(HardwareWallet.DISCONNECTED_ERROR); } } } }); } // Get MQS proof address getMqsProofAddress(hardwareWalletLockedText = HardwareWallet.NO_TEXT, hardwareWalletLockedTextArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't open if(self.isOpen() === false) { // Reject error reject("Wallet closed."); } // Otherwise else { // Check if wallet isn't a hardware wallet if(self.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { // Return getting address private key at payment proof MQS address index return self.getAddressKey(Wallet.PAYMENT_PROOF_MQS_ADDRESS_KEY_INDEX).then(function(addressSecretKey) { // Check if getting address public key from the address private key failed var addressPublicKey = Secp256k1Zkp.publicKeyFromSecretKey(addressSecretKey); if(addressPublicKey === Secp256k1Zkp.OPERATION_FAILED) { // Securely clear address private key addressSecretKey.fill(0); // Reject error reject("Getting address public key from the address private key failed."); } // Otherwise else { // Securely clear address private key addressSecretKey.fill(0); // Set MQS proof address to the MQS address of the address public key var mqsProofAddress = Mqs.publicKeyToMqsAddress(addressPublicKey, self.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE); // Resolve MQS proof address resolve(mqsProofAddress); } // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Check if hardware wallet is connected if(self.isHardwareConnected() === true) { // Return getting MQS address with the hardware wallet return self.getHardwareWallet().getMqsAddress(Wallet.PAYMENT_PROOF_MQS_ADDRESS_KEY_INDEX, hardwareWalletLockedText, hardwareWalletLockedTextArguments, allowUnlock, preventMessages, cancelOccurred).then(function(mqsProofAddress) { // Resolve MQS proof address resolve(mqsProofAddress); // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Reject hardware disconnected error reject(HardwareWallet.DISCONNECTED_ERROR); } } } }); } // Get Slatepack proof address getSlatepackProofAddress(hardwareWalletLockedText = HardwareWallet.NO_TEXT, hardwareWalletLockedTextArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't open if(self.isOpen() === false) { // Reject error reject("Wallet closed."); } // Otherwise else { // Check if wallet isn't a hardware wallet if(self.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { // Return getting address private key at payment proof Slatepack address index return self.getAddressKey(Wallet.PAYMENT_PROOF_SLATEPACK_ADDRESS_KEY_INDEX).then(function(addressSecretKey) { // Check if getting address public key from the address private key failed var addressPublicKey = Ed25519.publicKeyFromSecretKey(addressSecretKey); if(addressPublicKey === Ed25519.OPERATION_FAILED) { // Securely clear address private key addressSecretKey.fill(0); // Reject error reject("Getting address public key from the address private key failed."); } // Otherwise else { // Securely clear address private key addressSecretKey.fill(0); // Set Slatepack proof address to the Slatepack address of the addres public key var slatepackProofAddress = Slatepack.publicKeyToSlatepackAddress(addressPublicKey); // Resolve Slatepack proof address resolve(slatepackProofAddress); } // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Check if hardware wallet is connected if(self.isHardwareConnected() === true) { // Return getting Slatepack address with the hardware wallet return self.getHardwareWallet().getSlatepackAddress(Wallet.PAYMENT_PROOF_SLATEPACK_ADDRESS_KEY_INDEX, hardwareWalletLockedText, hardwareWalletLockedTextArguments, allowUnlock, preventMessages, cancelOccurred).then(function(slatepackProofAddress) { // Resolve Slatepack proof address resolve(slatepackProofAddress); // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Reject hardware disconnected error reject(HardwareWallet.DISCONNECTED_ERROR); } } } }); } // Get coinbase proof getCoinbaseProof(fees, height, identifier, hardwareWalletLockedText = HardwareWallet.NO_TEXT, hardwareWalletLockedTextArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't open if(self.isOpen() === false) // Reject error reject("Wallet closed."); // Otherwise else { // Check if no last identifier exists if(self.getLastIdentifier() === Wallet.NO_LAST_IDENTIFIER) { // Get default identifier var defaultIdentifier = new Identifier(); // Set the current identifier to the default identifier's child identifier var currentIdentifier = defaultIdentifier.getChild(); } // Otherwise check if an identifier is provided and it was previously used else if(identifier !== Identifier.NO_IDENTIFIER && self.getLastIdentifier().includesValue(identifier) === true) // Set current identifier to the identifier var currentIdentifier = identifier; // Otherwise else { // Set the current identifier to the last identifier's next identifier var currentIdentifier = self.getLastIdentifier().getNext(); } // Get reward from fees and height var reward = Consensus.getReward(self.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, fees, height); // Set switch type var switchType = Crypto.SWITCH_TYPE_REGULAR; // Check if wallet isn't a hardware wallet if(self.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { // Return initializing new proof builder var proofBuilder = new NewProofBuilder(); return proofBuilder.initialize(self.extendedPrivateKey).then(function() { // Return creating proof from extended private key, reward, current identifier, switch type, and proof builder return Crypto.proof(self.extendedPrivateKey, reward, currentIdentifier, switchType, proofBuilder).then(function(proof) { // Uninitialize proof builder proofBuilder.uninitialize(); // Resolve proof resolve(proof); // Catch errors }).catch(function(error) { // Uninitialize proof builder proofBuilder.uninitialize(); // Reject error reject(error); }); // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Return initializing view proof builder var proofBuilder = new ViewProofBuilder(); return proofBuilder.initialize(self.rootPublicKey).then(function() { // Check if hardware wallet is connected if(self.isHardwareConnected() === true) { // Return getting proof from reward, current identifier, switch type, and proof builder with the hardware wallet return self.getHardwareWallet().getProof(reward, currentIdentifier, switchType, HardwareWallet.CREATING_COINBASE_MESSAGE, proofBuilder, hardwareWalletLockedText, hardwareWalletLockedTextArguments, allowUnlock, preventMessages, cancelOccurred).then(function(proof) { // Uninitialize proof builder proofBuilder.uninitialize(); // Resolve proof resolve(proof); // Catch errors }).catch(function(error) { // Uninitialize proof builder proofBuilder.uninitialize(); // Reject error reject(error); }); } // Otherwise else { // Uninitialize proof builder proofBuilder.uninitialize(); // Reject hardware disconnected error reject(HardwareWallet.DISCONNECTED_ERROR); } // Catch errors }).catch(function(error) { // Reject error reject(error); }); } } }); } // Build coinbase buildCoinbase(fees, height, identifier, proof = Wallet.NO_PROOF, hardwareWalletLockedText = HardwareWallet.NO_TEXT, hardwareWalletLockedTextArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't open if(self.isOpen() === false) // Reject error reject("Wallet closed."); // Otherwise else { // Check if no last identifier exists if(self.getLastIdentifier() === Wallet.NO_LAST_IDENTIFIER) { // Get default identifier var defaultIdentifier = new Identifier(); // Set the current identifier to the default identifier's child identifier var currentIdentifier = defaultIdentifier.getChild(); // Set last identifier to the current identifier without the extras self.setLastIdentifier(currentIdentifier); } // Otherwise check if an identifier is provided and it was previously used else if(identifier !== Identifier.NO_IDENTIFIER && self.getLastIdentifier().includesValue(identifier) === true) // Set current identifier to the identifier var currentIdentifier = identifier; // Otherwise else { // Set the current identifier to the last identifier's next identifier var currentIdentifier = self.getLastIdentifier().getNext(); // Set last identifier to the current identifier without the extras self.setLastIdentifier(currentIdentifier); } // Get reward from fees and height var reward = Consensus.getReward(self.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, fees, height); // Set switch type var switchType = Crypto.SWITCH_TYPE_REGULAR; // Get commit var getCommit = function() { // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't a hardware wallet if(self.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { // Return creating commit from extended private key, reward, current identifier, and switch type return Crypto.commit(self.extendedPrivateKey, reward, currentIdentifier, switchType).then(function(commit) { // Resolve commit resolve(commit); // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Check if hardware wallet is connected if(self.isHardwareConnected() === true) { // Return getting commit from reward, current identifier, and switch type with the hardware wallet return self.getHardwareWallet().getCommit(reward, currentIdentifier, switchType, hardwareWalletLockedText, hardwareWalletLockedTextArguments, allowUnlock, preventMessages, cancelOccurred).then(function(commit) { // Resolve commit resolve(commit); // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Reject hardware disconnected error reject(HardwareWallet.DISCONNECTED_ERROR); } } }); }; // Return getting commit return getCommit().then(function(commit) { // Get proof var getProof = function() { // Return promise return new Promise(function(resolve, reject) { // Check if a proof is provided if(proof !== Wallet.NO_PROOF) { // Resolve proof resolve(proof); } // Otherwise check if wallet isn't a hardware wallet else if(self.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { // Return initializing new proof builder var proofBuilder = new NewProofBuilder(); return proofBuilder.initialize(self.extendedPrivateKey).then(function() { // Return creating proof from extended private key, reward, current identifier, switch type, and proof builder return Crypto.proof(self.extendedPrivateKey, reward, currentIdentifier, switchType, proofBuilder).then(function(proof) { // Uninitialize proof builder proofBuilder.uninitialize(); // Resolve proof resolve(proof); // Catch errors }).catch(function(error) { // Uninitialize proof builder proofBuilder.uninitialize(); // Reject error reject(error); }); // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Return initializing view proof builder var proofBuilder = new ViewProofBuilder(); return proofBuilder.initialize(self.rootPublicKey).then(function() { // Check if hardware wallet is connected if(self.isHardwareConnected() === true) { // Return getting proof from reward, current identifier, switch type, and proof builder with the hardware wallet return self.getHardwareWallet().getProof(reward, currentIdentifier, switchType, HardwareWallet.CREATING_COINBASE_MESSAGE, proofBuilder, hardwareWalletLockedText, hardwareWalletLockedTextArguments, allowUnlock, preventMessages, cancelOccurred).then(function(proof) { // Uninitialize proof builder proofBuilder.uninitialize(); // Resolve proof resolve(proof); // Catch errors }).catch(function(error) { // Uninitialize proof builder proofBuilder.uninitialize(); // Reject error reject(error); }); } // Otherwise else { // Uninitialize proof builder proofBuilder.uninitialize(); // Reject hardware disconnected error reject(HardwareWallet.DISCONNECTED_ERROR); } // Catch errors }).catch(function(error) { // Reject error reject(error); }); } }); }; // Return getting proof return getProof().then(function(proof) { // Try try { // Get over commit from reward var overCommit = Crypto.commitAmount(reward); } // Catch errors catch(error) { // Reject error reject(error); // Return return; } // Check if getting excess from commit and over commit was successful var excess = Secp256k1Zkp.pedersenCommitSum([ // Commit commit ], [ // Over commit overCommit ]); if(excess !== Secp256k1Zkp.OPERATION_FAILED) { // Check if creating public key from excess was successful var publicKey = Secp256k1Zkp.pedersenCommitToPublicKey(excess); if(publicKey !== Secp256k1Zkp.OPERATION_FAILED) { // Get excess signature var getExcessSignature = function() { // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't a hardware wallet if(self.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { // Try try { // Create kernel coinbase signature message var message = SlateKernel.signatureMessage(SlateKernel.COINBASE_FEATURES); } // Catch errors catch(error) { // Reject error reject(error); // Return return; } // Return getting secret key from coinbase output return self.getSum([ // Output [ // Amount reward, // Identifier currentIdentifier, // Switch type switchType ] ], []).then(function(secretKey) { // Check if creating single-signer signature from the message, secret key, and public key was successful var excessSignature = Secp256k1Zkp.createSingleSignerSignature(message, secretKey, Secp256k1Zkp.NO_SECRET_NONCE, publicKey, Secp256k1Zkp.NO_PUBLIC_NONCE, Secp256k1Zkp.NO_PUBLIC_NONCE_TOTAL); if(excessSignature !== Secp256k1Zkp.OPERATION_FAILED) { // Securely clear secret key secretKey.fill(0); // Resolve excess signature resolve(excessSignature); } // Otherwise else { // Securely clear secret key secretKey.fill(0); // Reject error reject("Creating single-signer signature failed."); } // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Check if hardware wallet is connected if(self.isHardwareConnected() === true) { // Return starting transaction for the reward amount with the hardware wallet return self.getHardwareWallet().startTransaction(Wallet.PAYMENT_PROOF_TOR_ADDRESS_KEY_INDEX, reward, new BigNumber(0), new BigNumber(0), HardwareWallet.NO_SECRET_NONCE_INDEX, HardwareWallet.NO_ADDRESS, hardwareWalletLockedText, hardwareWalletLockedTextArguments, allowUnlock, preventMessages, cancelOccurred).then(function() { // Check if hardware wallet is connected if(self.isHardwareConnected() === true) { // Return including output in the transaction with the hardware wallet return self.getHardwareWallet().includeOutputInTransaction(reward, currentIdentifier, switchType, hardwareWalletLockedText, hardwareWalletLockedTextArguments, allowUnlock, preventMessages, cancelOccurred).then(function() { // Check if hardware wallet is connected if(self.isHardwareConnected() === true) { // Return getting transaction public nonce with the hardware wallet return self.getHardwareWallet().getTransactionPublicNonce(hardwareWalletLockedText, hardwareWalletLockedTextArguments, allowUnlock, preventMessages, cancelOccurred).then(function(publicNonce) { // Check if hardware wallet is connected if(self.isHardwareConnected() === true) { // Return getting transaction information with the public nonce, public key, and kernel features with the hardware wallet return self.getHardwareWallet().getTransactionInformation(publicNonce, publicKey, SlateKernel.COINBASE_FEATURES, Slate.NO_LOCK_HEIGHT, Slate.NO_RELATIVE_HEIGHT, HardwareWallet.NO_KERNEL_COMMIT, HardwareWallet.NO_ADDRESS, Slate.NO_RECEIVER_SIGNATURE, hardwareWalletLockedText, hardwareWalletLockedTextArguments, allowUnlock, preventMessages, cancelOccurred).then(function(transactionInformation) { // Check if hardware wallet is connected if(self.isHardwareConnected() === true) { // Return completing transaction with the hardware wallet return self.getHardwareWallet().completeTransaction().then(function() { // Resolve excess signature resolve(transactionInformation[HardwareWallet.TRANSACTION_INFORMATION_SIGNATURE_INDEX]); // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Reject hardware disconnected error reject(HardwareWallet.DISCONNECTED_ERROR); } // Catch errors }).catch(function(error) { // Check if hardware wallet is connected if(self.isHardwareConnected() === true) { // Return canceling transaction with the hardware wallet and catch errors return self.getHardwareWallet().cancelTransaction().catch(function(error) { // Finally }).finally(function() { // Reject error reject(error); }); } // Otherwise else { // Reject hardware disconnected error reject(HardwareWallet.DISCONNECTED_ERROR); } }); } // Otherwise else { // Reject hardware disconnected error reject(HardwareWallet.DISCONNECTED_ERROR); } // Catch errors }).catch(function(error) { // Check if hardware wallet is connected if(self.isHardwareConnected() === true) { // Return canceling transaction with the hardware wallet and catch errors return self.getHardwareWallet().cancelTransaction().catch(function(error) { // Finally }).finally(function() { // Reject error reject(error); }); } // Otherwise else { // Reject hardware disconnected error reject(HardwareWallet.DISCONNECTED_ERROR); } }); } // Otherwise else { // Reject hardware disconnected error reject(HardwareWallet.DISCONNECTED_ERROR); } // Catch errors }).catch(function(error) { // Check if hardware wallet is connected if(self.isHardwareConnected() === true) { // Return canceling transaction with the hardware wallet and catch errors return self.getHardwareWallet().cancelTransaction().catch(function(error) { // Finally }).finally(function() { // Reject error reject(error); }); } // Otherwise else { // Reject hardware disconnected error reject(HardwareWallet.DISCONNECTED_ERROR); } }); } // Otherwise else { // Reject hardware disconnected error reject(HardwareWallet.DISCONNECTED_ERROR); } // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Reject hardware disconnected error reject(HardwareWallet.DISCONNECTED_ERROR); } } }); }; // Return getting excess signature return getExcessSignature().then(function(excessSignature) { // Resolve resolve([ // Identifier currentIdentifier, // Commit commit, // Proof proof, // Excess excess, // Excess signature excessSignature ]); // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Reject error reject("Getting public key from Pedersen commit failed."); } } // Otherwise else { // Reject error reject("Performing Pedersen commit sum failed."); } // Catch errors }).catch(function(error) { // Reject error reject(error); }); // Catch errors }).catch(function(error) { // Reject error reject(error); }); } }); } // Get sum getSum(outputs, inputs) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't open if(self.isOpen() === false) { // Reject error reject("Wallet closed."); } // Otherwise check if wallet is a hardware wallet else if(self.getHardwareType() !== Wallet.NO_HARDWARE_TYPE) { // Reject error reject("Sum can't be retrieved from a hardware wallet."); } // Otherwise else { // Go through all outputs var deriveOutputKeys = []; var outputKeys = []; for(var i = 0; i < outputs["length"]; ++i) { // Get output let output = outputs[i]; // Append to derive output keys deriveOutputKeys.push(new Promise(function(resolve, reject) { // Return deriving output key from extended private key and output return Crypto.deriveSecretKey(self.extendedPrivateKey, output[Wallet.OUTPUT_AMOUNT_INDEX], output[Wallet.OUTPUT_IDENTIFIER_INDEX], output[Wallet.OUTPUT_SWITCH_TYPE_INDEX]).then(function(outputKey) { // Append output key to list outputKeys.push(outputKey); // Resolve resolve(); // Catch errors }).catch(function(error) { // Reject error reject(error); }); })); } // Go through all inputs var deriveInputKeys = []; var inputKeys = []; for(var i = 0; i < inputs["length"]; ++i) { // Get input let input = inputs[i]; // Append to derive input keys deriveInputKeys.push(new Promise(function(resolve, reject) { // Return deriving input key from extended private key and input return Crypto.deriveSecretKey(self.extendedPrivateKey, input[Wallet.INPUT_AMOUNT_INDEX], input[Wallet.INPUT_IDENTIFIER_INDEX], input[Wallet.INPUT_SWITCH_TYPE_INDEX]).then(function(inputKey) { // Append input key to list inputKeys.push(inputKey); // Resolve resolve(); // Catch errors }).catch(function(error) { // Reject error reject(error); }); })); } // Wait for all output keys to be derived Promise.all(deriveOutputKeys).then(function() { // Wait for all input keys to be derived Promise.all(deriveInputKeys).then(function() { // Check if getting sum from derived keys failed var sum = Secp256k1Zkp.blindSum(outputKeys, inputKeys); if(sum === Secp256k1Zkp.OPERATION_FAILED) { // Go through all output keys for(var i = 0; i < outputKeys["length"]; ++i) { // Securely clear output key outputKeys[i].fill(0); } // Go through all input keys for(var i = 0; i < inputKeys["length"]; ++i) { // Securely clear input key inputKeys[i].fill(0); } // Reject error reject("Getting sum from derived keys failed."); } // Otherwise else { // Go through all output keys for(var i = 0; i < outputKeys["length"]; ++i) { // Securely clear output key outputKeys[i].fill(0); } // Go through all input keys for(var i = 0; i < inputKeys["length"]; ++i) { // Securely clear input key inputKeys[i].fill(0); } // Check if sum isn't a valid secret key if(Secp256k1Zkp.isValidSecretKey(sum) !== true) { // Securely clear sum sum.fill(0); // Reject error reject("Sum isn't a valid secret key."); } // Otherwise else { // Resolve sum resolve(sum); } } // Catch errors }).catch(function(error) { // Go through all output keys for(var i = 0; i < outputKeys["length"]; ++i) { // Securely clear output key outputKeys[i].fill(0); } // Go through all input keys for(var i = 0; i < inputKeys["length"]; ++i) { // Securely clear input key inputKeys[i].fill(0); } // Reject error reject(error); }); // Catch errors }).catch(function(error) { // Go through all output keys for(var i = 0; i < outputKeys["length"]; ++i) { // Securely clear output key outputKeys[i].fill(0); } // Go through all input keys for(var i = 0; i < inputKeys["length"]; ++i) { // Securely clear input key inputKeys[i].fill(0); } // Reject error reject(error); }); } }); } // Build output buildOutput(amount, height = Identifier.NO_HEIGHT, message = HardwareWallet.SENDING_TRANSACTION_MESSAGE, hardwareWalletLockedText = HardwareWallet.NO_TEXT, hardwareWalletLockedTextArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't open if(self.isOpen() === false) // Reject error reject("Wallet closed."); // Otherwise else { // Check if no last identifier exists if(self.getLastIdentifier() === Wallet.NO_LAST_IDENTIFIER) { // Get default identifier var defaultIdentifier = new Identifier(); // Set the current identifier to the default identifier's child identifier var currentIdentifier = defaultIdentifier.getChild(height); // Set last identifier to the current identifier without the extras self.setLastIdentifier(currentIdentifier.removeExtras()); } // Otherwise else { // Set the current identifier to the last identifier's next identifier var currentIdentifier = self.getLastIdentifier().getNext(height); // Set last identifier to the current identifier without the extras self.setLastIdentifier(currentIdentifier.removeExtras()); } // Set switch type var switchType = Crypto.SWITCH_TYPE_REGULAR; // Get commit var getCommit = function() { // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't a hardware wallet if(self.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { // Return creating commit from extended private key, amount, current identifier, and switch type return Crypto.commit(self.extendedPrivateKey, amount, currentIdentifier, switchType).then(function(commit) { // Resolve commit resolve(commit); // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Check if hardware wallet is connected if(self.isHardwareConnected() === true) { // Return getting commit from amount, current identifier, and switch type with the hardware wallet return self.getHardwareWallet().getCommit(amount, currentIdentifier, switchType, hardwareWalletLockedText, hardwareWalletLockedTextArguments, allowUnlock, preventMessages, cancelOccurred).then(function(commit) { // Resolve commit resolve(commit); // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Reject hardware disconnected error reject(HardwareWallet.DISCONNECTED_ERROR); } } }); }; // Return getting commit return getCommit().then(function(commit) { // Get proof var getProof = function() { // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't a hardware wallet if(self.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { // Return initializing new proof builder var proofBuilder = new NewProofBuilder(); return proofBuilder.initialize(self.extendedPrivateKey).then(function() { // Return creating proof from extended private key, amount, current identifier, switch type, and proof builder return Crypto.proof(self.extendedPrivateKey, amount, currentIdentifier, switchType, proofBuilder).then(function(proof) { // Uninitialize proof builder proofBuilder.uninitialize(); // Resolve proof resolve(proof); // Catch errors }).catch(function(error) { // Uninitialize proof builder proofBuilder.uninitialize(); // Reject error reject(error); }); // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Return initializing view proof builder var proofBuilder = new ViewProofBuilder(); return proofBuilder.initialize(self.rootPublicKey).then(function() { // Check if hardware wallet is connected if(self.isHardwareConnected() === true) { // Return getting proof from amount, current identifier, switch type, and proof builder with the hardware wallet return self.getHardwareWallet().getProof(amount, currentIdentifier, switchType, message, proofBuilder, hardwareWalletLockedText, hardwareWalletLockedTextArguments, allowUnlock, preventMessages, cancelOccurred).then(function(proof) { // Uninitialize proof builder proofBuilder.uninitialize(); // Resolve proof resolve(proof); // Catch errors }).catch(function(error) { // Uninitialize proof builder proofBuilder.uninitialize(); // Reject error reject(error); }); } // Otherwise else { // Uninitialize proof builder proofBuilder.uninitialize(); // Reject hardware disconnected error reject(HardwareWallet.DISCONNECTED_ERROR); } // Catch errors }).catch(function(error) { // Reject error reject(error); }); } }); }; // Return getting proof return getProof().then(function(proof) { // Resolve resolve([ // Amount amount, // Identifier currentIdentifier, // Switch type switchType, // Features SlateOutput.PLAIN_FEATURES, // Commit commit, // Proof proof ]); // Catch errors }).catch(function(error) { // Reject error reject(error); }); // Catch errors }).catch(function(error) { // Reject error reject(error); }); } }); } // Build Tor payment proof buildTorPaymentProof(amount, commit, senderAddress) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't open if(self.isOpen() === false) { // Reject error reject("Wallet closed."); } // Otherwise check if wallet is a hardware wallet else if(self.getHardwareType() !== Wallet.NO_HARDWARE_TYPE) { // Reject error reject("Payment proof can't be retrieved from a hardware wallet."); } // Otherwise else { // Try try { // Get message from amount, commit, and sender address var message = Slate.getPaymentProofMessage(amount, commit, senderAddress); } // Catch errors catch(error) { // Reject error reject(error); // Return return; } // Return getting address private key at payment proof Tor address index return self.getAddressKey(Wallet.PAYMENT_PROOF_TOR_ADDRESS_KEY_INDEX).then(function(addressSecretKey) { // Check if signing message with address private key failed var torPaymentProof = Ed25519.sign(message, addressSecretKey); if(torPaymentProof === Ed25519.OPERATION_FAILED) { // Securely clear address private key addressSecretKey.fill(0); // Reject error reject("Signing message with address key failed."); } // Otherwise else { // Securely clear address private key addressSecretKey.fill(0); // Resolve Tor payment proof resolve(torPaymentProof); } }).catch(function(error) { // Reject error reject(error); }); } }); } // Build MQS payment proof buildMqsPaymentProof(amount, commit, senderAddress) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't open if(self.isOpen() === false) { // Reject error reject("Wallet closed."); } // Otherwise check if wallet is a hardware wallet else if(self.getHardwareType() !== Wallet.NO_HARDWARE_TYPE) { // Reject error reject("Payment proof can't be retrieved from a hardware wallet."); } // Otherwise else { // Try try { // Get message from amount, commit, and sender address var message = Slate.getPaymentProofMessage(amount, commit, senderAddress); } // Catch errors catch(error) { // Reject error reject(error); // Return return; } // Return getting address private key at payment proof MQS address index return self.getAddressKey(Wallet.PAYMENT_PROOF_MQS_ADDRESS_KEY_INDEX).then(function(addressSecretKey) { // Get message hash var messageHash = new Uint8Array(sha256.arrayBuffer(message)); // Check if signing message with address private key failed var mqsPaymentProof = Secp256k1Zkp.createMessageHashSignature(messageHash, addressSecretKey); if(mqsPaymentProof === Secp256k1Zkp.OPERATION_FAILED) { // Securely clear address private key addressSecretKey.fill(0); // Reject error reject("Signing message with address key failed."); } // Otherwise else { // Securely clear address private key addressSecretKey.fill(0); // Resolve MQS payment proof resolve(mqsPaymentProof); } }).catch(function(error) { // Reject error reject(error); }); } }); } // Build Slatepack payment proof buildSlatepackPaymentProof(amount, commit, senderAddress) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't open if(self.isOpen() === false) { // Reject error reject("Wallet closed."); } // Otherwise check if wallet is a hardware wallet else if(self.getHardwareType() !== Wallet.NO_HARDWARE_TYPE) { // Reject error reject("Payment proof can't be retrieved from a hardware wallet."); } // Otherwise else { // Try try { // Get message from amount, commit, and sender address var message = Slate.getPaymentProofMessage(amount, commit, senderAddress); } // Catch errors catch(error) { // Reject error reject(error); // Return return; } // Return getting address private key at payment proof Slatepack address index return self.getAddressKey(Wallet.PAYMENT_PROOF_SLATEPACK_ADDRESS_KEY_INDEX).then(function(addressSecretKey) { // Check if signing message with address private key failed var slatepackPaymentProof = Ed25519.sign(message, addressSecretKey); if(slatepackPaymentProof === Ed25519.OPERATION_FAILED) { // Securely clear address private key addressSecretKey.fill(0); // Reject error reject("Signing message with address key failed."); } // Otherwise else { // Securely clear address private key addressSecretKey.fill(0); // Resolve Slatepack payment proof resolve(slatepackPaymentProof); } }).catch(function(error) { // Reject error reject(error); }); } }); } // Get name getName() { // Return name return this.name; } // Set name setName(name) { // Set name this.name = name; } // Get color getColor() { // Return color return this.color; } // Set color setColor(color) { // Check if color is valid if(Wallet.COLOR_PATTERN.test(color) === true) // Set color this.color = color; // Otherwise else // Set color to random color this.color = Wallet.getRandomColor(); } // Get salt getSalt() { // Return salt return this.salt; } // Set salt setSalt(salt) { // Set salt this.salt = salt; } // Get initialization vector getInitializationVector() { // Return initialization vector return this.initializationVector; } // Set initialization vector setInitializationVector(initializationVector) { // Set initialization vector this.initializationVector = initializationVector; } // Get number of iterations getNumberOfIterations() { // Return number of iterations return this.numberOfIterations; } // Set number of iterations setNumberOfIterations(numberOfIterations) { // Set number of iterations this.numberOfIterations = numberOfIterations; } // Get encrypted seed getEncryptedSeed() { // Return encrypted seed return this.encryptedSeed; } // Set encrypted seed setEncryptedSeed(encryptedSeed) { // Set encrypted seed this.encryptedSeed = encryptedSeed; } // Get address suffix getAddressSuffix() { // Return address suffix return this.addressSuffix; } // Set address suffix setAddressSuffix(addressSuffix) { // Set address suffix this.addressSuffix = addressSuffix; } // Get order getOrder() { // Return order return this.order; } // Set order setOrder(order) { // Set order this.order = order; } // Get synced height getSyncedHeight() { // Return synced height return this.syncedHeight; } // Set synced height setSyncedHeight(syncedHeight) { // Set synced height this.syncedHeight = syncedHeight; } // Get spent amount getSpentAmount() { // Return spent amount return this.spentAmount; } // Set spent amount setSpentAmount(spentAmount) { // Set spent amount this.spentAmount = spentAmount; } // Get unspent amount getUnspentAmount() { // Return unspent amount return this.unspentAmount; } // Set unspent amount setUnspentAmount(unspentAmount) { // Set unspent amount this.unspentAmount = unspentAmount; } // Get unconfirmed amount getUnconfirmedAmount() { // Return unconfirmed amount return this.unconfirmedAmount; } // Set unconfirmed amount setUnconfirmedAmount(unconfirmedAmount) { // Set unconfirmed amount this.unconfirmedAmount = unconfirmedAmount; } // Get locked amount getLockedAmount() { // Return locked amount return this.lockedAmount; } // Set locked amount setLockedAmount(lockedAmount) { // Set locked amount this.lockedAmount = lockedAmount; } // Get pending amount getPendingAmount() { // Return pending amount return this.pendingAmount; } // Set pending amount setPendingAmount(pendingAmount) { // Set pending amount this.pendingAmount = pendingAmount; } // Get expired amount getExpiredAmount() { // Return expired amount return this.expiredAmount; } // Set expired amount setExpiredAmount(expiredAmount) { // Set expired amount this.expiredAmount = expiredAmount; } // Get wallet type getWalletType() { // Return wallet type return this.walletType; } // Set wallet type setWalletType(walletType) { // Set wallet type this.walletType = walletType; } // Get network type getNetworkType() { // Return network type return this.networkType; } // Set network type setNetworkType(networkType) { // Check if network type is valid if(networkType === Consensus.MAINNET_NETWORK_TYPE || networkType === Consensus.TESTNET_NETWORK_TYPE) // Set network type this.networkType = networkType; // Otherwise else // Set network type this.networkType = Consensus.getNetworkType(); } // Get last identifier getLastIdentifier() { // Return last identifier return this.lastIdentifier; } // Set last identifier setLastIdentifier(lastIdentifier) { // Set last identifier this.lastIdentifier = lastIdentifier; } // Get hardware type getHardwareType() { // Return hardware type return this.hardwareType; } // Set hardware type setHardwareType(hardwareType) { // Set hardware type this.hardwareType = hardwareType; } // Get encrypted root public key getEncryptedRootPublicKey() { // Return encrypted root public key return this.encryptedRootPublicKey; } // Set encrypted root public key setEncryptedRootPublicKey(encryptedRootPublicKey) { // Set encrypted root public key this.encryptedRootPublicKey = encryptedRootPublicKey; } // Get use BIP39 getUseBip39() { // Return use BIP39 return this.useBip39; } // Set use BIP39 setUseBip39(useBip39) { // Set use BIP39 this.useBip39 = useBip39; } // Get encrypted BIP39 salt getEncryptedBip39Salt() { // Return encrypted BIP39 salt return this.encryptedBip39Salt; } // Set encrypted BIP39 salt setEncryptedBip39Salt(encryptedBip39Salt) { // Set encrypted BIP39 salt this.encryptedBip39Salt = encryptedBip39Salt; } // Get account number getAccountNumber() { // Return account number return this.accountNumber; } // Set account number setAccountNumber(accountNumber) { // Set account number this.accountNumber = accountNumber; } // Get payment proof index getPaymentProofIndex() { // Return payment proof index return this.paymentProofIndex; } // Set payment proof index setPaymentProofIndex(paymentProofIndex) { // Set payment proof index this.paymentProofIndex = paymentProofIndex; } // Get key path getKeyPath() { // Return key path return this.keyPath; } // Set key path setKeyPath(keyPath) { // Set keyPath this.keyPath = keyPath; // Check if hardware wallet exists if(this.getHardwareWallet() !== Wallet.NO_HARDWARE_WALLET) { // Set hardware wallet's wallet key path this.getHardwareWallet().setWalletKeyPath(keyPath); } } // Get starting sync height getStartingSyncHeight() { // Return starting sync height return this.startingSyncHeight; } // Set starting sync height setStartingSyncHeight(startingSyncHeight) { // Set starting sync height this.startingSyncHeight = startingSyncHeight; } // Get sync complete value getSyncCompleteValue(syncCompleteValue) { // Return sync complete value return this.syncCompleteValue; } // Set sync complete value setSyncCompleteValue(syncCompleteValue) { // Set sync complete value this.syncCompleteValue = syncCompleteValue; } // Get performing address suffix operation getPerformingAddressSuffixOperation() { // Return performing address suffix operation return this.performingAddressSuffixOperation; } // Set performing address suffix operation setPerformingAddressSuffixOperation(performingAddressSuffixOperation) { // Set performing address suffix operation this.performingAddressSuffixOperation = performingAddressSuffixOperation; } // Get address suffix verified getAddressSuffixVerified() { // Return address suffix verified return this.addressSuffixVerified; } // Set address suffix verified setAddressSuffixVerified(addressSuffixVerified) { // Set address suffix verified this.addressSuffixVerified = addressSuffixVerified; } // Get syncing status getSyncingStatus() { // Return syncing status return this.syncingStatus; } // Set syncing status setSyncingStatus(syncingStatus) { // Set syncing status this.syncingStatus = syncingStatus; } // Get last sync index getLastSyncIndex() { // Return last sync index return this.lastSyncIndex; } // Set last sync index setLastSyncIndex(lastSyncIndex) { // Set last sync index this.lastSyncIndex = lastSyncIndex; } // Get percent synced getPercentSynced() { // Return percent synced return this.percentSynced; } // Set percent synced setPercentSynced(percentSynced) { // Set percent synced this.percentSynced = percentSynced; } // Get address getAddress(type) { // Check if doesn't have an address suffix if(this.getAddressSuffix() === Wallet.NO_ADDRESS_SUFFIX) // Return empty string return ""; // Otherwise else { // Try try { // Parse address suffix as a URL new URL(this.getAddressSuffix()); // Return address suffix return this.getAddressSuffix(); } // Catch errors catch(error) { // Check type switch(type) { // HTTP address type case Wallet.HTTP_ADDRESS_TYPE: // Return HTTP address return this.getHttpAddress(); // Tor address type case Wallet.TOR_ADDRESS_TYPE: // Return Tor address return this.getTorAddress(); // Match connection address type case Wallet.MATCH_CONNECTION_ADDRESS_TYPE: // Return address that matches connection return (Tor.isOnionService() === true) ? this.getTorAddress() : this.getHttpAddress(); } } } } // Owns output ownsOutput(output) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Return getting output's information return output.getInformation((self.getHardwareType() === Wallet.NO_HARDWARE_TYPE) ? self.extendedPrivateKey : self.rootPublicKey, self.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE).then(function(outputInformation) { // Resolve output information resolve(outputInformation); // Catch errors }).catch(function(error) { // Reject error reject(error); }); }); } // Is syncing isSyncing() { // Return is syncing status is syncing or resyning return this.getSyncingStatus() === Wallet.STATUS_SYNCING || this.getSyncingStatus() === Wallet.STATUS_RESYNCING; } // Is synced isSynced() { // Return if syncing status is synced return this.getSyncingStatus() === Wallet.STATUS_SYNCED; } // Connect to applicable hardware connectToApplicableHardware(hardwareWallets) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if open, is a hardware wallet, and hardware isn't connected if(self.isOpen() === true && self.getHardwareType() !== Wallet.NO_HARDWARE_TYPE && self.isHardwareConnected() === false) { // Return getting the seed cookie from the root public key return crypto["subtle"].digest(Wallet.SEED_COOKIE_DIGEST_ALGORITHM, self.rootPublicKey).then(function(seedCookie) { // Get seed cookie in the correct format seedCookie = new Uint8Array(seedCookie); // Check if open, is a hardware wallet, and hardware isn't connected if(self.isOpen() === true && self.getHardwareType() !== Wallet.NO_HARDWARE_TYPE && self.isHardwareConnected() === false) { // Go through all hardware wallets for(var i = 0; i < hardwareWallets["length"]; ++i) { // Get hardware wallet var hardwareWallet = hardwareWallets[i]; // Check if hardware wallet isn't in use if(hardwareWallet.getInUse() === false) { // Check if the hardware wallet's seed cookie matches the wallet's if(Common.arraysAreEqual(seedCookie, hardwareWallet.getSeedCookie()) === true) { // Set hardware wallet to the hardware wallet self.hardwareWallet = hardwareWallet; // Check if hardware types differ if(self.getHardwareType() !== self.hardwareWallet.getHardwareType()) { // Trigger wallet hardware type change event $(document).trigger(Wallet.HARDWARE_TYPE_CHANGE_EVENT, [ // Key path self.getKeyPath(), // New hardware type self.hardwareWallet.getHardwareType() ]); } // Set hardware wallet's wallet key path hardwareWallet.setWalletKeyPath(self.getKeyPath()); // Break break; } } } // Check if hardware is connected if(self.isHardwareConnected() === true) { // Set that hardware wallet is in use self.getHardwareWallet().setInUse(true); // Trigger wallet connect event $(document).trigger(Wallet.CONNECT_EVENT, [ // Key path self.getKeyPath(), // Connection type self.getHardwareWallet().getConnectionType() ]); // Hardware wallet on hardware disconnect event $(self.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { // Check if hardware wallet exists if(self.getHardwareWallet() !== Wallet.NO_HARDWARE_WALLET) { // Close hardware wallet self.getHardwareWallet().close(); // Set hardware wallet to no hardware wallet self.hardwareWallet = Wallet.NO_HARDWARE_WALLET; } // Check if key path exists if(self.getKeyPath() !== Wallet.NO_KEY_PATH) { // Trigger wallet disconnect event $(document).trigger(Wallet.DISCONNECT_EVENT, [ // Key path self.getKeyPath() ]); } }); } // Otherwise else { // Check if hardware wallet exists if(self.getHardwareWallet() !== Wallet.NO_HARDWARE_WALLET) { // Close hardware wallet self.getHardwareWallet().close(); // Set hardware wallet to no hardware wallet self.hardwareWallet = Wallet.NO_HARDWARE_WALLET; } } } // Resolve resolve(); // Catch errors }).catch(function(error) { // Reject error reject(error); }); } // Otherwise else { // Resolve resolve(); } }); } // Is hardware connected isHardwareConnected() { // Return is hardware wallet is connected return this.getHardwareWallet() !== Wallet.NO_HARDWARE_WALLET && this.getHardwareWallet().isConnected() === true; } // Get hardware wallet getHardwareWallet() { // Return hardware wallet return this.hardwareWallet; } // No key path static get NO_KEY_PATH() { // Return no key path return null; } // No name static get NO_NAME() { // Return no name return null; } // No encrypted seed static get NO_ENCRYPTED_SEED() { // Return no encrypted seed return null; } // No address suffix static get NO_ADDRESS_SUFFIX() { // Return no address suffix return null; } // No order static get NO_ORDER() { // Return no order return null; } // Unknown order static get UNKNOWN_ORDER() { // Return unknown order return undefined; } // No last identifier static get NO_LAST_IDENTIFIER() { // Return no last identifier return null; } // No hardware type static get NO_HARDWARE_TYPE() { // Return no hardware type return null; } // No encrypted root public key static get NO_ENCRYPTED_ROOT_PUBLIC_KEY() { // Return no encrypted seed return null; } // No encrypted BIP39 salt static get NO_ENCRYPTED_BIP39_SALT() { // Return no encrypted seed return null; } // Current height static get CURRENT_HEIGHT() { // Return current height return null; } // Status syncing static get STATUS_SYNCING() { // Return status syncing return 0; } // Status resyncing static get STATUS_RESYNCING() { // Return status resyncing return Wallet.STATUS_SYNCING + 1; } // Status synced static get STATUS_SYNCED() { // Return status synced return Wallet.STATUS_RESYNCING + 1; } // Status error static get STATUS_ERROR() { // Return status error return Wallet.STATUS_SYNCED + 1; } // No sync index static get NO_SYNC_INDEX() { // Return no sync index return null; } // Last sync index start index index static get LAST_SYNC_INDEX_START_INDEX_INDEX() { // Return last sync index start index index return 0; } // Last sync index last retrieved index index static get LAST_SYNC_INDEX_LAST_RETRIEVED_INDEX_INDEX() { // Return last sync index last retrieved index index return Wallet.LAST_SYNC_INDEX_START_INDEX_INDEX + 1; } // Coinbase identifier index static get COINBASE_IDENTIFIER_INDEX() { // Return coinbase identifier index return 0; } // Coinbase commit index static get COINBASE_COMMIT_INDEX() { // Return coinbase commit index return Wallet.COINBASE_IDENTIFIER_INDEX + 1; } // Coinbase proof index static get COINBASE_PROOF_INDEX() { // Return coinbase proof index return Wallet.COINBASE_COMMIT_INDEX + 1; } // Coinbase excess index static get COINBASE_EXCESS_INDEX() { // Return coinbase excess index return Wallet.COINBASE_PROOF_INDEX + 1; } // Coinbase excess signature index static get COINBASE_EXCESS_SIGNATURE_INDEX() { // Return coinbase excess signature index return Wallet.COINBASE_EXCESS_INDEX + 1; } // Output amount index static get OUTPUT_AMOUNT_INDEX() { // Return output amount index return 0; } // Output identifier index static get OUTPUT_IDENTIFIER_INDEX() { // Return output identifier index return Wallet.OUTPUT_AMOUNT_INDEX + 1; } // Output switch type index static get OUTPUT_SWITCH_TYPE_INDEX() { // Return output switch type index return Wallet.OUTPUT_IDENTIFIER_INDEX + 1; } // Output features index static get OUTPUT_FEATURES_INDEX() { // Return output features index return Wallet.OUTPUT_SWITCH_TYPE_INDEX + 1; } // Output commit index static get OUTPUT_COMMIT_INDEX() { // Return output commit index return Wallet.OUTPUT_FEATURES_INDEX + 1; } // Output proof index static get OUTPUT_PROOF_INDEX() { // Return output proof index return Wallet.OUTPUT_COMMIT_INDEX + 1; } // Input amount index static get INPUT_AMOUNT_INDEX() { // Return input amount index return 0; } // Input identifier index static get INPUT_IDENTIFIER_INDEX() { // Return input identifier index return Wallet.INPUT_AMOUNT_INDEX + 1; } // Input switch type index static get INPUT_SWITCH_TYPE_INDEX() { // Return input switch type index return Wallet.INPUT_IDENTIFIER_INDEX + 1; } // Input features index static get INPUT_FEATURES_INDEX() { // Return input features index return Wallet.INPUT_SWITCH_TYPE_INDEX + 1; } // Input commit index static get INPUT_COMMIT_INDEX() { // Return input commit index return Wallet.INPUT_FEATURES_INDEX + 1; } // Input key path index static get INPUT_KEY_PATH_INDEX() { // Return input key path index return Wallet.INPUT_COMMIT_INDEX + 1; } // HTTP address type static get HTTP_ADDRESS_TYPE() { // Return HTTP address type return "HTTP"; } // Tor address type static get TOR_ADDRESS_TYPE() { // Return Tor address type return "Tor"; } // Match connection address type static get MATCH_CONNECTION_ADDRESS_TYPE() { // Return match connection address type return "Match Connection"; } // Disconnect event static get DISCONNECT_EVENT() { // Return disconnect event return "WalletDisconnectEvent"; } // Connect event static get CONNECT_EVENT() { // Return disconnect event return "WalletConnectEvent"; } // Hardware type change event static get HARDWARE_TYPE_CHANGE_EVENT() { // Return hardware type change event return "WalletHardwareTypeChangeEvent"; } // Encrypt seed and BIP39 salt encrypted seed index static get ENCRYPT_SEED_AND_BIP39_SALT_ENCRYPTED_SEED_INDEX() { // Return encrypt seed and BIP39 salt encrypted seed index return 0; } // Encrypt seed and BIP39 salt encrypted BIP39 salt index static get ENCRYPT_SEED_AND_BIP39_SALT_ENCRYPTED_BIP39_SALT_INDEX() { // Return encrypt seed and BIP39 salt encrypted BIP39 salt index return Wallet.ENCRYPT_SEED_AND_BIP39_SALT_ENCRYPTED_SEED_INDEX + 1; } // Decrypt seed and BIP39 salt decrypted seed index static get DECRYPT_SEED_AND_BIP39_SALT_DECRYPTED_SEED_INDEX() { // Return decrypt seed and BIP39 salt decrypted seed index return 0; } // Decrypt seed and BIP39 salt decrypted BIP39 salt index static get DECRYPT_SEED_AND_BIP39_SALT_DECRYPTED_BIP39_SALT_INDEX() { // Return decrypt seed and BIP39 salt decrypted BIP39 salt index return Wallet.DECRYPT_SEED_AND_BIP39_SALT_DECRYPTED_SEED_INDEX + 1; } // No proof static get NO_PROOF() { // Return no proof return null; } // Private // Get HTTP address getHttpAddress() { // Return HTTP address return HTTPS_SERVER_ADDRESS + "/wallet/" + this.getAddressSuffix(); } // Get Tor address getTorAddress() { // Return Tor address return TOR_SERVER_ADDRESS + "/wallet/" + this.getAddressSuffix(); } // Encrypt seed and BIP39 salt encryptSeedAndBip39Salt(password, salt, numberOfIterations, initializationVector) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't open if(self.isOpen() === false) { // Reject error reject("Wallet closed."); } // Otherwise else { // Return getting key return Wallet.getKey(password, salt, numberOfIterations).then(function(key) { // Return encrypting the seed using key and initialization vector return crypto["subtle"].encrypt({ // Name "name": Wallet.ENCRYPTION_ALGORITHM, // Initialization vector "iv": initializationVector }, key, self.seed.getSeed()).then(function(encryptedSeed) { // Get encrypted seed in the correct format encryptedSeed = new Uint8Array(encryptedSeed); // Check if BIP39 salt exists if(self.bip39Salt !== Wallet.NO_BIP39_SALT) { // Return encrypting the BIP39 salt using key and initialization vector return crypto["subtle"].encrypt({ // Name "name": Wallet.ENCRYPTION_ALGORITHM, // Initialization vector "iv": initializationVector }, key, self.bip39Salt).then(function(encryptedBip39Salt) { // TODO Securely clear key // Get encrypted BIP39 salt in the correct format encryptedBip39Salt = new Uint8Array(encryptedBip39Salt); // Resolve resolve([ // Encrypted seed encryptedSeed, // Encrypted BIP39 salt encryptedBip39Salt ]); // Catch errors }).catch(function(error) { // Securely clear encrypted seed encryptedSeed.fill(0); // TODO Securely clear key // Reject error reject(error); }); } // Otherwise else { // TODO Securely clear key // Resolve resolve([ // Encrypted seed encryptedSeed, // No encrypted BIP39 salt Wallet.NO_ENCRYPTED_BIP39_SALT ]); } // Catch errors }).catch(function(error) { // TODO Securely clear key // Reject error reject(error); }); // Catch errors }).catch(function(error) { // Reject error reject(error); }); } }); } // Decrypt seed and BIP39 salt decryptSeedAndBip39Salt(password, salt, numberOfIterations, initializationVector) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Return getting key return Wallet.getKey(password, salt, numberOfIterations).then(function(key) { // Return decrypting the encrypted seed using key and initialization vector return crypto["subtle"].decrypt({ // Name "name": Wallet.ENCRYPTION_ALGORITHM, // Initialization vector "iv": initializationVector }, key, self.getEncryptedSeed()).then(function(decryptedSeed) { // Get decrypted seed in correct format decryptedSeed = new Uint8Array(decryptedSeed); // Check if encrypted BIP39 salt exists if(self.getEncryptedBip39Salt() !== Wallet.NO_ENCRYPTED_BIP39_SALT) { // Return decrypting the encrypted BIP39 salt using key and initialization vector return crypto["subtle"].decrypt({ // Name "name": Wallet.ENCRYPTION_ALGORITHM, // Initialization vector "iv": initializationVector }, key, self.getEncryptedBip39Salt()).then(function(decryptedBip39Salt) { // Get decrypted BIP39 salt in correct format decryptedBip39Salt = new Uint8Array(decryptedBip39Salt); // TODO Securely clear key // Resolve resolve([ // Decrypted seed decryptedSeed, // Decrypted BIP39 salt decryptedBip39Salt ]); // Catch errors }).catch(function(error) { // Securely clear decrypted seed decryptedSeed.fill(0); // TODO Securely clear key // Reject error reject(error); }); } // Otherwise else { // TODO Securely clear key // Resolve resolve([ // Decrypted seed decryptedSeed, // No BIP39 salt Wallet.NO_BIP39_SALT ]); } // Catch errors }).catch(function(error) { // TODO Securely clear key // Reject error reject(error); }); // Catch errors }).catch(function(error) { // Reject error reject(error); }); }); } // Encrypt root public key encryptRootPublicKey(password, salt, numberOfIterations, initializationVector) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Check if wallet isn't open if(self.isOpen() === false) { // Reject error reject("Wallet closed."); } // Otherwise else { // Return getting key return Wallet.getKey(password, salt, numberOfIterations).then(function(key) { // Return encrypting the root public key using key and initialization vector return crypto["subtle"].encrypt({ // Name "name": Wallet.ENCRYPTION_ALGORITHM, // Initialization vector "iv": initializationVector }, key, self.rootPublicKey).then(function(encryptedRootPublicKey) { // TODO Securely clear key // Resolve encrypted root public key resolve(new Uint8Array(encryptedRootPublicKey)); // Catch errors }).catch(function(error) { // TODO Securely clear key // Reject error reject(error); }); // Catch errors }).catch(function(error) { // Reject error reject(error); }); } }); } // Decrypt root public key decryptRootPublicKey(password, salt, numberOfIterations, initializationVector) { // Set self var self = this; // Return promise return new Promise(function(resolve, reject) { // Return getting key return Wallet.getKey(password, salt, numberOfIterations).then(function(key) { // Return decrypting the encrypted root public key using key and initialization vector return crypto["subtle"].decrypt({ // Name "name": Wallet.ENCRYPTION_ALGORITHM, // Initialization vector "iv": initializationVector }, key, self.getEncryptedRootPublicKey()).then(function(decryptedRootPublicKey) { // TODO Securely clear key // Resolve decrypted root public key resolve(new Uint8Array(decryptedRootPublicKey)); // Catch errors }).catch(function(error) { // TODO Securely clear key // Reject error reject(error); }); // Catch errors }).catch(function(error) { // Reject error reject(error); }); }); } // Get key static getKey(password, salt, numberOfIterations) { // Return promise return new Promise(function(resolve, reject) { // Get the saved password pepper var passwordPepper = localStorage.getItem(Wallet.PASSWORD_PEPPER_LOCAL_STORAGE_NAME); // Check if password pepper exists and is valid if(passwordPepper !== Common.INVALID_LOCAL_STORAGE_ITEM && Common.isHexString(passwordPepper) === true) { // Initialize combined password var combinedPassword = Common.mergeArrays([ // Password password, // Password pepper Common.fromHexString(passwordPepper) ]); // Return creating base key from password and password pepper return crypto["subtle"].importKey("raw", combinedPassword, { // Name "name": Wallet.IMPORT_ALGORITHM }, false, [ // Derive key "deriveKey" ]).then(function(baseKey) { // Securely clear combined password combinedPassword.fill(0); // Return deriving key from base key, salt, and number of iterations return crypto["subtle"].deriveKey({ // Name "name": Wallet.IMPORT_ALGORITHM, // Salt "salt": salt, // Iterations "iterations": numberOfIterations, // Hash "hash": Wallet.DIGEST_ALGORITHM }, baseKey, { // Name "name": Wallet.ENCRYPTION_ALGORITHM, // Length "length": Wallet.ENCRYPTION_KEY_LENGTH }, false, [ // Encrypt "encrypt", // Decrypt "decrypt" ]).then(function(derivedKey) { // TODO Securely clear baseKey // Resolve derived key resolve(derivedKey); // Catch errors }).catch(function(error) { // TODO Securely clear baseKey // Reject error reject(error); }); // Catch errors }).catch(function(error) { // Securely clear combined password combinedPassword.fill(0); // Reject error reject(error); }); } // Otherwise else { // Reject reject(); } }); } // To linear color space static toLinearColorSpace(color) { // Normalize color var normalizedColor = color / Common.BYTE_MAX_VALUE; // Return color in linear color space return (normalizedColor <= 0.03928) ? normalizedColor / 12.92 : Math.pow(((normalizedColor + 0.055) / 1.055), 2.4); } // Get luminance static getLuminance(red, green, blue) { // Return luminance of colors in linear color space return 0.2126 * Wallet.toLinearColorSpace(red) + 0.7152 * Wallet.toLinearColorSpace(green) + 0.0722 * Wallet.toLinearColorSpace(blue); } // Get random color static getRandomColor() { // Loop forever while(true) { // Get random red, green, and blue values var red = Common.randomNumber(0, Common.BYTE_MAX_VALUE); var green = Common.randomNumber(0, Common.BYTE_MAX_VALUE); var blue = Common.randomNumber(0, Common.BYTE_MAX_VALUE); // Get luminance of combined colors var luminance = Wallet.getLuminance(red, green, blue); // Check if luminance isn't too bright or too dark if((luminance + 0.05) / (0 + 0.05) <= (1 + 0.05) / (luminance + 0.05) && luminance > 0.02) // Return combined color return "#" + Math.floor(red).toString(Common.HEX_NUMBER_BASE).padStart(Common.HEX_NUMBER_LENGTH, Common.HEX_NUMBER_PADDING) + Math.floor(green).toString(Common.HEX_NUMBER_BASE).padStart(Common.HEX_NUMBER_LENGTH, Common.HEX_NUMBER_PADDING) + Math.floor(blue).toString(Common.HEX_NUMBER_BASE).padStart(Common.HEX_NUMBER_LENGTH, Common.HEX_NUMBER_PADDING); } } // No seed static get NO_SEED() { // Return no seed return null; } // No root public key static get NO_ROOT_PUBLIC_KEY() { // Return no root public key return null; } // No extended private key static get NO_EXTENDED_PRIVATE_KEY() { // Return no extended private key return null; } // No hardware wallet static get NO_HARDWARE_WALLET() { // Return no hardware wallet return null; } // No passphrase static get NO_PASSPHRASE() { // Return no passphrase return null; } // No BIP39 salt static get NO_BIP39_SALT() { // Return no BIP39 salt return null; } // Salt length static get SALT_LENGTH() { // Return salt length return 32; } // Initialization vector length static get INITIALIZATION_VECTOR_LENGTH() { // Return initialization vector length return 32; } // Default number of iterations static get DEFAULT_NUMBER_OF_ITERATIONS() { // Return default number of iterations return 200000; } // Import algorithm static get IMPORT_ALGORITHM() { // Return import algorithm return "PBKDF2"; } // Encryption algorithm static get ENCRYPTION_ALGORITHM() { // Return encryption algorithm return "AES-GCM"; } // Encryption key length static get ENCRYPTION_KEY_LENGTH() { // Return encryption key length return 256; } // Digest algorithm static get DIGEST_ALGORITHM() { // Return digest algorithm return "SHA-512"; } // Color pattern static get COLOR_PATTERN() { // Return whitespace pattern return /^#[0-9A-F]{6}$/iu; } // Seed key static get SEED_KEY() { // Return seed key return "IamVoldemort"; } // Password pepper local storage name static get PASSWORD_PEPPER_LOCAL_STORAGE_NAME() { // Return password pepper local storage name return "Password Pepper"; } // Password pepper length static get PASSWORD_PEPPER_LENGTH() { // Return password peper length return 32; } // Payment proof address Tor key index static get PAYMENT_PROOF_TOR_ADDRESS_KEY_INDEX() { // Return payment proof Tor address key index return 0; } // Payment proof MQS address key index static get PAYMENT_PROOF_MQS_ADDRESS_KEY_INDEX() { // Return payment proof MQS address key index return 0; } // Payment proof address Slatepack key index static get PAYMENT_PROOF_SLATEPACK_ADDRESS_KEY_INDEX() { // Return payment proof Slatepack address key index return 0; } // Seed cookie digest algorithm static get SEED_COOKIE_DIGEST_ALGORITHM() { // Return seed cookie digest algorithm return "SHA-512"; } } // Main function // Set global object's wallet globalThis["Wallet"] = Wallet;