Home Reference Source Test

src/main/generic/utils/crypto/CryptoWorkerImpl.js

class CryptoWorkerImpl extends IWorker.Stub(CryptoWorker) {
    constructor() {
        super();
        // FIXME: This is needed for Babel to work correctly. Can be removed as soon as we updated to Babel v7.
        this._superInit = super.init;
    }

    async init(name) {
        await this._superInit.call(this, name);
        await WasmHelper.doImportBrowser();
        CryptoWorker._workerAsync = this;
    }

    /**
     * @param {Uint8Array} input
     * @returns {Uint8Array}
     */
    computeArgon2d(input) {
        if (PlatformUtils.isNodeJs()) {
            const out = new Uint8Array(Hash.getSize(Hash.Algorithm.ARGON2D));
            const res = NodeNative.node_argon2(out, new Uint8Array(input), 512);
            if (res !== 0) {
                throw res;
            }
            return out;
        } else {
            let stackPtr;
            try {
                stackPtr = Module.stackSave();
                const hashSize = Hash.getSize(Hash.Algorithm.ARGON2D);
                const wasmOut = Module.stackAlloc(hashSize);
                const wasmIn = Module.stackAlloc(input.length);
                new Uint8Array(Module.HEAPU8.buffer, wasmIn, input.length).set(input);
                const res = Module._nimiq_argon2(wasmOut, wasmIn, input.length, 512);
                if (res !== 0) {
                    throw res;
                }
                const hash = new Uint8Array(hashSize);
                hash.set(new Uint8Array(Module.HEAPU8.buffer, wasmOut, hashSize));
                return hash;
            } catch (e) {
                Log.w(CryptoWorkerImpl, e);
                throw e;
            } finally {
                if (stackPtr !== undefined) Module.stackRestore(stackPtr);
            }
        }
    }

    /**
     * @param {Array.<Uint8Array>} inputs
     * @returns {Array.<Uint8Array>}
     */
    computeArgon2dBatch(inputs) {
        const hashes = [];
        if (PlatformUtils.isNodeJs()) {
            for(const input of inputs) {
                const out = new Uint8Array(Hash.getSize(Hash.Algorithm.ARGON2D));
                const res = NodeNative.node_argon2(out, new Uint8Array(input), 512);
                if (res !== 0) {
                    throw res;
                }
                hashes.push(out);
            }
            return hashes;
        } else {
            let stackPtr;
            try {
                stackPtr = Module.stackSave();
                const hashSize = Hash.getSize(Hash.Algorithm.ARGON2D);
                const wasmOut = Module.stackAlloc(hashSize);
                const stackTmp = Module.stackSave();
                for (const input of inputs) {
                    Module.stackRestore(stackTmp);
                    const wasmIn = Module.stackAlloc(input.length);
                    new Uint8Array(Module.HEAPU8.buffer, wasmIn, input.length).set(input);
                    const res = Module._nimiq_argon2(wasmOut, wasmIn, input.length, 512);
                    if (res !== 0) {
                        throw res;
                    }
                    const hash = new Uint8Array(hashSize);
                    hash.set(new Uint8Array(Module.HEAPU8.buffer, wasmOut, hashSize));
                    hashes.push(hash);
                }
                return hashes;
            } catch (e) {
                Log.w(CryptoWorkerImpl, e);
                throw e;
            } finally {
                if (stackPtr !== undefined) Module.stackRestore(stackPtr);
            }
        }
    }

    /**
     * @param {Uint8Array} key
     * @param {Uint8Array} salt
     * @param {number} iterations
     * @returns {Uint8Array}
     */
    kdf(key, salt, iterations) {
        if (PlatformUtils.isNodeJs()) {
            const out = new Uint8Array(Hash.getSize(Hash.Algorithm.ARGON2D));
            const res = NodeNative.node_kdf(out, new Uint8Array(key), new Uint8Array(salt), 512, iterations);
            if (res !== 0) {
                throw res;
            }
            return out;
        } else {
            let stackPtr;
            try {
                stackPtr = Module.stackSave();
                const hashSize = Hash.getSize(Hash.Algorithm.ARGON2D);
                const wasmOut = Module.stackAlloc(hashSize);
                const wasmIn = Module.stackAlloc(key.length);
                new Uint8Array(Module.HEAPU8.buffer, wasmIn, key.length).set(key);
                const wasmSalt = Module.stackAlloc(salt.length);
                new Uint8Array(Module.HEAPU8.buffer, wasmSalt, salt.length).set(salt);
                const res = Module._nimiq_kdf(wasmOut, wasmIn, key.length, wasmSalt, salt.length, 512, iterations);
                if (res !== 0) {
                    throw res;
                }
                const hash = new Uint8Array(hashSize);
                hash.set(new Uint8Array(Module.HEAPU8.buffer, wasmOut, hashSize));
                return hash;
            } catch (e) {
                Log.w(CryptoWorkerImpl, e);
                throw e;
            } finally {
                if (stackPtr !== undefined) Module.stackRestore(stackPtr);
            }
        }
    }

    /**
     * @param {Uint8Array} blockSerialized
     * @param {Array.<boolean|undefined>} transactionValid
     * @param {number} timeNow
     * @param {Uint8Array} genesisHash
     * @param {number} networkId
     * @returns {Promise.<{valid: boolean, pow: SerialBuffer, interlinkHash: SerialBuffer, bodyHash: SerialBuffer}>}
     */
    async blockVerify(blockSerialized, transactionValid, timeNow, genesisHash, networkId) {
        // The worker only uses a stub genesis config.
        GenesisConfig = {
            GENESIS_HASH: Hash.unserialize(new SerialBuffer(genesisHash)),
            NETWORK_ID: networkId
        };

        const block = Block.unserialize(new SerialBuffer(blockSerialized));
        for (let i = 0; i < transactionValid.length; i++) {
            block.body.transactions[i]._valid = transactionValid[i];
        }

        const valid = await block._verify(timeNow);
        const pow = await block.header.pow();
        const interlinkHash = block.interlink.hash();
        const bodyHash = block.body.hash();
        return { valid: valid, pow: pow.serialize(), interlinkHash: interlinkHash.serialize(), bodyHash: bodyHash.serialize() };
    }
}

IWorker.prepareForWorkerUse(CryptoWorker, new CryptoWorkerImpl());