Files
grin-web-wallet/scripts/wallet.js
2024-12-20 18:08:44 -08:00

4227 lines
108 KiB
JavaScript
Executable File

// 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;