<commit_before>﻿namespace AntShares.Wallets
{
    export abstract class Wallet
    {
        public balanceChanged = new __event(this);

        private path: string;
        private iv: Uint8Array;
        private masterKey: Uint8Array;
        private accounts = new Map<string, Account>();
        private contracts = new Map<string, Contract>();
        private coins: IO.Caching.TrackableCollection<string, Coin>;
        private current_height: number;
        private isrunning = true;
        private isclosed = false;

        public static get CoinVersion() { return 0x17; }
        public get dbPath() { return this.path; }
        protected get walletHeight() { return this.current_height; }

        public addContract(contract: Contract): PromiseLike<void>
        {
            if (!this.accounts.has(contract.publicKeyHash.toString()))
                throw new RangeError();
            this.contracts.set(contract.scriptHash.toString(), contract);
            return Promise.resolve(null);
        }

        protected buildDatabase(): PromiseLike<void>
        {
            return Promise.resolve(void 0);
        }

        public changePassword(password_old: string, password_new: string): PromiseLike<boolean>
        {
            let passwordKey: Uint8Array;
            let passwordKeyHash: Uint8Array;
            return Promise.all([
                this.loadStoredData("PasswordHash"),
                password_old.toAesKey().then(result =>
                {
                    return window.crypto.subtle.digest({ name: "SHA-256" }, result);
                })
            ]).then(results =>
            {
                if (results[0].byteLength != results[1].byteLength)
                    throw new Error();
                let x = new Uint8Array(results[0]), y = new Uint8Array(results[1]);
                for (let i = 0; i < x.length; i++)
                    if (x[i] != y[i])
                        throw new Error();
                return password_new.toAesKey();
            }).then(result =>
            {
                passwordKey = new Uint8Array(result);
                return Promise.all<any>([
                    window.crypto.subtle.digest({ name: "SHA-256" }, passwordKey),
                    window.crypto.subtle.importKey("raw", <any>result, { name: "AES-CBC" }, false, ["encrypt"])
                ]);
            }).then(results =>
            {
                passwordKeyHash = results[0];
                return window.crypto.subtle.encrypt({ name: "AES-CBC", iv: this.iv }, results[1], this.masterKey);
            }).then(result =>
            {
                return Promise.all([
                    this.saveStoredData("MasterKey", result),
                    this.saveStoredData("PasswordHash", passwordKeyHash)]);
            }).then(() => true, () => false);
        }

        public close(): PromiseLike<void>
        {
            this.isrunning = false;
            return new Promise<void>((resolve, reject) =>
            {
                let f = () =>
                {
                    if (this.isclosed) resolve();
                    else setTimeout(f, 1000);
                };
                f();
            });
        }

        public containsAccount(publicKey: Cryptography.ECPoint): PromiseLike<boolean>;
        public containsAccount(publicKeyHash: Uint160): boolean;
        public containsAccount(): PromiseLike<boolean> | boolean
        {
            if (arguments[0] instanceof Cryptography.ECPoint)
            {
                return arguments[0].encodePoint(true).toScriptHash().then(result =>
                {
                    return this.containsAccount(result);
                });
            }
            else
            {
                return this.accounts.has(arguments[0].toString());
            }
        }

        public containsAddress(scriptHash: Uint160): boolean
        {
            return this.contracts.has(scriptHash.toString());
        }

        public createAccount(privateKey?: ArrayBuffer | Uint8Array): PromiseLike<Account>
        {
            return Account.create(privateKey).then(result =>
            {
                this.accounts.set(result.publicKeyHash.toString(), result);
                return result;
            });
        }

        protected decryptPrivateKey(encryptedPrivateKey: Uint8Array): PromiseLike<Uint8Array>
        {
            if (encryptedPrivateKey == null) throw new RangeError();
            if (encryptedPrivateKey.byteLength != 112) throw new RangeError();
            return window.crypto.subtle.importKey("raw", this.masterKey, "AES-CBC", false, ["decrypt"]).then(result =>
            {
                return window.crypto.subtle.decrypt({ name: "AES-CBC", iv: this.iv }, result, encryptedPrivateKey);
            }).then(result =>
            {
                return new Uint8Array(result);
            });
        }

        public deleteAccount(publicKeyHash: Uint160): PromiseLike<boolean>
        {
            let promises = new Array<PromiseLike<boolean>>();
            this.contracts.forEach(contract =>
            {
                if (contract.publicKeyHash.equals(publicKeyHash))
                    promises.push(this.deleteContract(contract.scriptHash));
            });
            return Promise.all(promises).then(() =>
            {
                return this.accounts.delete(publicKeyHash.toString());
            });
        }

        public deleteContract(scriptHash: Uint160): PromiseLike<boolean>
        {
            this.coins.forEach((coin, key) =>
            {
                if (coin.scriptHash.equals(scriptHash))
                    this.coins.remove(key);
            });
            this.coins.commit();
            return Promise.resolve(this.contracts.delete(scriptHash.toString()));
        }

        protected encryptPrivateKey(decryptedPrivateKey: Uint8Array): PromiseLike<Uint8Array>
        {
            return window.crypto.subtle.importKey("raw", this.masterKey, "AES-CBC", false, ["encrypt"]).then(result =>
            {
                return window.crypto.subtle.encrypt({ name: "AES-CBC", iv: this.iv }, result, decryptedPrivateKey);
            }).then(result =>
            {
                return new Uint8Array(result);
            });
        }

        public findCoins(): Coin[]
        {
            let array = new Array<Coin>();
            this.coins.forEach(coin =>
            {
                if (coin.state == CoinState.Unconfirmed || coin.state == CoinState.Unspent)
                    array.push(coin);
            });
            return array;
        }

        public findUnspentCoins(asset_id?: Uint256, amount?: Fixed8): Coin[]
        {
            let array = new Array<Coin>();
            this.coins.forEach(coin =>
            {
                if (coin.state == CoinState.Unspent)
                    array.push(coin);
            });
            if (arguments.length == 0) return array;
            return Wallet.findUnspentCoins(array, asset_id, amount);<commit_msg>Fixed bug: Failed to execute 'importKey' on 'SubtleCrypto': parameter 3 ('algorithm') is not an object.<commit_after>﻿namespace AntShares.Wallets
{
    export abstract class Wallet
    {
        public balanceChanged = new __event(this);

        private path: string;
        private iv: Uint8Array;
        private masterKey: Uint8Array;
        private accounts = new Map<string, Account>();
        private contracts = new Map<string, Contract>();
        private coins: IO.Caching.TrackableCollection<string, Coin>;
        private current_height: number;
        private isrunning = true;
        private isclosed = false;

        public static get CoinVersion() { return 0x17; }
        public get dbPath() { return this.path; }
        protected get walletHeight() { return this.current_height; }

        public addContract(contract: Contract): PromiseLike<void>
        {
            if (!this.accounts.has(contract.publicKeyHash.toString()))
                throw new RangeError();
            this.contracts.set(contract.scriptHash.toString(), contract);
            return Promise.resolve(null);
        }

        protected buildDatabase(): PromiseLike<void>
        {
            return Promise.resolve(void 0);
        }

        public changePassword(password_old: string, password_new: string): PromiseLike<boolean>
        {
            let passwordKey: Uint8Array;
            let passwordKeyHash: Uint8Array;
            return Promise.all([
                this.loadStoredData("PasswordHash"),
                password_old.toAesKey().then(result =>
                {
                    return window.crypto.subtle.digest({ name: "SHA-256" }, result);
                })
            ]).then(results =>
            {
                if (results[0].byteLength != results[1].byteLength)
                    throw new Error();
                let x = new Uint8Array(results[0]), y = new Uint8Array(results[1]);
                for (let i = 0; i < x.length; i++)
                    if (x[i] != y[i])
                        throw new Error();
                return password_new.toAesKey();
            }).then(result =>
            {
                passwordKey = new Uint8Array(result);
                return Promise.all<any>([
                    window.crypto.subtle.digest({ name: "SHA-256" }, passwordKey),
                    window.crypto.subtle.importKey("raw", <any>result, { name: "AES-CBC" }, false, ["encrypt"])
                ]);
            }).then(results =>
            {
                passwordKeyHash = results[0];
                return window.crypto.subtle.encrypt({ name: "AES-CBC", iv: this.iv }, results[1], this.masterKey);
            }).then(result =>
            {
                return Promise.all([
                    this.saveStoredData("MasterKey", result),
                    this.saveStoredData("PasswordHash", passwordKeyHash)]);
            }).then(() => true, () => false);
        }

        public close(): PromiseLike<void>
        {
            this.isrunning = false;
            return new Promise<void>((resolve, reject) =>
            {
                let f = () =>
                {
                    if (this.isclosed) resolve();
                    else setTimeout(f, 1000);
                };
                f();
            });
        }

        public containsAccount(publicKey: Cryptography.ECPoint): PromiseLike<boolean>;
        public containsAccount(publicKeyHash: Uint160): boolean;
        public containsAccount(): PromiseLike<boolean> | boolean
        {
            if (arguments[0] instanceof Cryptography.ECPoint)
            {
                return arguments[0].encodePoint(true).toScriptHash().then(result =>
                {
                    return this.containsAccount(result);
                });
            }
            else
            {
                return this.accounts.has(arguments[0].toString());
            }
        }

        public containsAddress(scriptHash: Uint160): boolean
        {
            return this.contracts.has(scriptHash.toString());
        }

        public createAccount(privateKey?: ArrayBuffer | Uint8Array): PromiseLike<Account>
        {
            return Account.create(privateKey).then(result =>
            {
                this.accounts.set(result.publicKeyHash.toString(), result);
                return result;
            });
        }

        protected decryptPrivateKey(encryptedPrivateKey: Uint8Array): PromiseLike<Uint8Array>
        {
            if (encryptedPrivateKey == null) throw new RangeError();
            if (encryptedPrivateKey.byteLength != 112) throw new RangeError();
            return window.crypto.subtle.importKey("raw", this.masterKey, { name: "AES-CBC" }, false, ["decrypt"]).then(result =>
            {
                return window.crypto.subtle.decrypt({ name: "AES-CBC", iv: this.iv }, result, encryptedPrivateKey);
            }).then(result =>
            {
                return new Uint8Array(result);
            });
        }

        public deleteAccount(publicKeyHash: Uint160): PromiseLike<boolean>
        {
            let promises = new Array<PromiseLike<boolean>>();
            this.contracts.forEach(contract =>
            {
                if (contract.publicKeyHash.equals(publicKeyHash))
                    promises.push(this.deleteContract(contract.scriptHash));
            });
            return Promise.all(promises).then(() =>
            {
                return this.accounts.delete(publicKeyHash.toString());
            });
        }

        public deleteContract(scriptHash: Uint160): PromiseLike<boolean>
        {
            this.coins.forEach((coin, key) =>
            {
                if (coin.scriptHash.equals(scriptHash))
                    this.coins.remove(key);
            });
            this.coins.commit();
            return Promise.resolve(this.contracts.delete(scriptHash.toString()));
        }

        protected encryptPrivateKey(decryptedPrivateKey: Uint8Array): PromiseLike<Uint8Array>
        {
            return window.crypto.subtle.importKey("raw", this.masterKey, { name: "AES-CBC" }, false, ["encrypt"]).then(result =>
            {
                return window.crypto.subtle.encrypt({ name: "AES-CBC", iv: this.iv }, result, decryptedPrivateKey);
            }).then(result =>
            {
                return new Uint8Array(result);
            });
        }

        public findCoins(): Coin[]
        {
            let array = new Array<Coin>();
            this.coins.forEach(coin =>
            {
                if (coin.state == CoinState.Unconfirmed || coin.state == CoinState.Unspent)
                    array.push(coin);
            });
            return array;
        }

        public findUnspentCoins(asset_id?: Uint256, amount?: Fixed8): Coin[]
        {
            let array = new Array<Coin>();
            this.coins.forEach(coin =>
            {
                if (coin.state == CoinState.Unspent)
                    array.push(coin);
            });
            if (arguments.length == 0) return array;
            return Wallet.findUnspentCoins(array, asset_id, amount);
{'fraction_non_alphanumeric': 0.11661499436302142, 'fraction_numerical': 0.008244081172491544, 'mean_word_length': 1.6400669642857142, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 85, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7145196', 'n_tokens_mistral': 3750, 'n_tokens_neox': 3503, 'n_words': 707}