Home Reference Source Test

test/specs/generic/wallet/MultiSigWallet.spec.js

describe('MultiSigWallet', () => {
    const recipient = Address.unserialize(BufferUtils.fromBase64(Dummy.address1));
    const value = 8888888;
    const fee = 888;
    const deepLockRounds = KeyPair.EXPORT_KDF_ROUNDS;

    beforeAll(() => {
        // Temporarily reduce deep lock rounds.
        KeyPair.EXPORT_KDF_ROUNDS = KeyPair.LOCK_KDF_ROUNDS;
    });

    afterAll(() => {
        KeyPair.EXPORT_KDF_ROUNDS = deepLockRounds;
    });

    it('rejects if own public key is missing', () => {
        const keyPair1 = KeyPair.generate();
        const keyPair2 = KeyPair.generate();
        const keyPair3 = KeyPair.generate();

        expect(() => MultiSigWallet.fromPublicKeys(keyPair3, 2, [keyPair1.publicKey, keyPair2.publicKey])).toThrow();
    });

    it('order of public keys does not matter', () => {
        const keyPair1 = KeyPair.generate();
        const keyPair2 = KeyPair.generate();

        const wallet1 = MultiSigWallet.fromPublicKeys(keyPair1, 2, [keyPair1.publicKey, keyPair2.publicKey]);
        const wallet2 = MultiSigWallet.fromPublicKeys(keyPair2, 2, [keyPair2.publicKey, keyPair1.publicKey]);

        expect(wallet1.address.equals(wallet2.address)).toBe(true);
        for (let i = 0; i < wallet1.publicKeys.length; ++i) {
            expect(wallet1.publicKeys[i].equals(wallet2.publicKeys[i])).toBe(true);
        }
    });

    it('can create a signed transaction', () => {
        const keyPair1 = KeyPair.generate();
        const keyPair2 = KeyPair.generate();

        const wallet1 = MultiSigWallet.fromPublicKeys(keyPair1, 2, [keyPair1.publicKey, keyPair2.publicKey]);
        const wallet2 = MultiSigWallet.fromPublicKeys(keyPair2, 2, [keyPair2.publicKey, keyPair1.publicKey]);

        const commitmentPair1 = wallet1.createCommitment();
        const commitmentPair2 = wallet2.createCommitment();
        const aggregatedCommitment = Commitment.sum([commitmentPair1.commitment, commitmentPair2.commitment]);
        const aggregatedPublicKey = PublicKey.sum([keyPair1.publicKey, keyPair2.publicKey]);

        let transaction = wallet1.createTransaction(recipient, value, fee, 1);

        const partialSignature1 = wallet1.partiallySignTransaction(transaction, [keyPair2.publicKey, keyPair1.publicKey],
            aggregatedCommitment, commitmentPair1.secret);
        const partialSignature2 = wallet2.partiallySignTransaction(transaction, [keyPair1.publicKey, keyPair2.publicKey],
            aggregatedCommitment, commitmentPair2.secret);

        transaction = wallet1.completeTransaction(transaction, aggregatedPublicKey, aggregatedCommitment,
            [partialSignature1, partialSignature2]);
        const isValid = transaction.verify();
        expect(isValid).toBe(true);
    });

    it('can reject invalid wallet seed', () => {
        expect(() => {
            MultiSigWallet.loadPlain('');
        }).toThrowError('Invalid wallet seed');

        expect(() => {
            MultiSigWallet.loadPlain('i am not a valid base64 seed :(');
        }).toThrowError('Invalid wallet seed');

        expect(() => {
            MultiSigWallet.loadPlain('527ec2efe780dc38a5561348b928bf0225a6986c0b56796ba9af81f91b10c16ffdaa8cab1175bfbf7de576bb0b0009737ecb5c59e60bd0c86fae0f9fa457706b8fca286eaa4030fcd6d2b4d55d24f243f08c9c8bf03d5c1e11ab3860f759607');
        }).toThrowError('Invalid wallet seed');
    });

    it('can export & import a plaintext wallet', () => {
        const keyPair1 = KeyPair.generate();
        const keyPair2 = KeyPair.generate();
        const wallet = MultiSigWallet.fromPublicKeys(keyPair1, 1, [keyPair1.publicKey, keyPair2.publicKey]);
        const wallet2 = MultiSigWallet.loadPlain(wallet.exportPlain());

        expect(wallet.keyPair.equals(wallet2.keyPair)).toBeTruthy();
        expect(wallet.address.equals(wallet2.address)).toBeTruthy();
        expect(wallet2.minSignatures).toBe(wallet.minSignatures);
        expect(wallet2.publicKeys.length).toBe(wallet.publicKeys.length);
        for (let i = 0; i < wallet2.publicKeys.length; ++i) {
            expect(wallet2.publicKeys[i].equals(wallet.publicKeys[i])).toBeTruthy();
        }
    });

    it('can lock, unlock and relock itself', (done) => {
        (async () => {
            const keyPair1 = KeyPair.generate();
            const keyPair2 = KeyPair.generate();
            const wallet = MultiSigWallet.fromPublicKeys(keyPair1, 1, [keyPair1.publicKey, keyPair2.publicKey]);
            const key = 'password';

            expect(wallet.isLocked).toBeFalsy();
            await wallet.lock(key);
            expect(wallet.isLocked).toBeTruthy();
            await wallet.unlock(key);
            expect(wallet.isLocked).toBeFalsy();
            wallet.relock();
            expect(wallet.isLocked).toBeTruthy();
        })().then(done, done.fail);
    });

    it('can export an encrypted wallet and import it', (done) => {
        (async () => {
            const keyPair1 = KeyPair.generate();
            const keyPair2 = KeyPair.generate();
            const wallet = MultiSigWallet.fromPublicKeys(keyPair1, 1, [keyPair1.publicKey, keyPair2.publicKey]);
            const key = 'password';

            const encryptedWallet = await wallet.exportEncrypted(key);
            const unlockedWallet = await MultiSigWallet.loadEncrypted(encryptedWallet, key);
            expect(wallet.keyPair.equals(unlockedWallet.keyPair)).toBeTruthy();
            expect(wallet.address.equals(unlockedWallet.address)).toBeTruthy();
            expect(wallet.minSignatures).toBe(unlockedWallet.minSignatures);
            expect(unlockedWallet.publicKeys.length).toBe(wallet.publicKeys.length);
            for (let i = 0; i < unlockedWallet.publicKeys.length; ++i) {
                expect(unlockedWallet.publicKeys[i].equals(wallet.publicKeys[i])).toBeTruthy();
            }
        })().then(done, done.fail);
    });

    it('can detect wrong key when exporting an encrypted wallet from locked wallet', (done) => {
        (async () => {
            const keyPair1 = KeyPair.generate();
            const keyPair2 = KeyPair.generate();
            const wallet = MultiSigWallet.fromPublicKeys(keyPair1, 1, [keyPair1.publicKey, keyPair2.publicKey]);
            const key = 'password';
            const key2 = '123456';

            await wallet.lock(key);
            let err = false;
            await wallet.exportEncrypted(key2).catch(() => err = true);
            expect(err).toBeTruthy();

            err = false;
            await wallet.exportEncrypted(key, key2).catch(() => err = true);
            expect(err).toBeTruthy();
        })().then(done, done.fail);
    });

    it('can detect wrong key on an encrypted wallet', (done) => {
        (async () => {
            const keyPair1 = KeyPair.generate();
            const keyPair2 = KeyPair.generate();
            const wallet = MultiSigWallet.fromPublicKeys(keyPair1, 1, [keyPair1.publicKey, keyPair2.publicKey]);
            const key = 'password';
            const key2 = '123456';

            const encryptedWallet = await wallet.exportEncrypted(key);
            let err = false;
            await Wallet.loadEncrypted(encryptedWallet, key2).catch(() => err = true);
            expect(err).toBeTruthy();
        })().then(done, done.fail);
    });
});