Skip to content
This repository has been archived by the owner on Apr 15, 2019. It is now read-only.

Create transaction register second passhrase - Closes #249 #303

Merged
merged 14 commits into from Oct 26, 2017
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
71 changes: 71 additions & 0 deletions src/commands/createTransactionRegisterSecondPassphrase.js
@@ -0,0 +1,71 @@
/*
* LiskHQ/lisky
* Copyright © 2017 Lisk Foundation
*
* See the LICENSE file at the top-level directory of this distribution
* for licensing information.
*
* Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation,
* no part of this software, including this file, may be copied, modified,
* propagated, or distributed except according to the terms contained in the
* LICENSE file.
*
* Removal or modification of this copyright notice is prohibited.
*
*/
import {
getStdIn,
getPassphrase,
getFirstLineFromString,
} from '../utils/input';
import { createCommand } from '../utils/helpers';
import commonOptions from '../utils/options';
import transactions from '../utils/transactions';

const description = `Creates a transaction which will register a second passphrase for an existing account if broadcast to the network.

Examples:
- create transaction register second passphrase
- create transaction 1
`;

export const createSignature = (
[passphrase, secondPassphrase],
) => transactions.createSignature(passphrase, secondPassphrase);

export const actionCreator = vorpal => async ({ options }) => {
const {
passphrase: passphraseSource,
'second-passphrase': secondPassphraseSource,
} = options;

return getStdIn({
passphraseIsRequired: passphraseSource === 'stdin',
dataIsRequired: secondPassphraseSource === 'stdin',
})
.then(stdIn => getPassphrase(vorpal, passphraseSource, stdIn.passphrase, { shouldRepeat: true })
.then(passphrase => getPassphrase(
vorpal,
secondPassphraseSource,
getFirstLineFromString(stdIn.data),
{ shouldRepeat: true, displayName: 'your second secret passphrase' },
)
.then(secondPassphrase => [passphrase, secondPassphrase]),
),
)
.then(createSignature);
};

const createTransactionRegisterSecondPassphrase = createCommand({
command: 'create transaction register second passphrase',
alias: 'create transaction 1',
description,
actionCreator,
options: [
commonOptions.passphrase,
commonOptions.secondPassphrase,
],
errorPrefix: 'Could not create register second passphrase transaction',
});

export default createTransactionRegisterSecondPassphrase;
4 changes: 2 additions & 2 deletions src/commands/set.js
Expand Up @@ -18,7 +18,7 @@ import { CONFIG_VARIABLES } from '../utils/constants';
import config from '../utils/env';
import { writeJsonSync } from '../utils/fs';
import { createCommand } from '../utils/helpers';
import liskInstance from '../utils/liskInstance';
import liskAPIInstance from '../utils/api';

const configFilePath = `${os.homedir()}/.lisky/config.json`;

Expand Down Expand Up @@ -78,7 +78,7 @@ const setBoolean = (variable, path) => (value) => {
path.reduce(setNestedConfigProperty(newValue), config);

if (variable === 'testnet') {
liskInstance.setTestnet(newValue);
liskAPIInstance.setTestnet(newValue);
}

return attemptWriteToFile(variable, value);
Expand Down
File renamed without changes.
3 changes: 3 additions & 0 deletions src/utils/helpers.js
Expand Up @@ -39,6 +39,7 @@ export const createCommand = ({
command,
autocomplete,
description,
alias,
actionCreator,
options = [],
errorPrefix,
Expand All @@ -50,6 +51,8 @@ export const createCommand = ({
.description(description)
.action(action);

if (alias) commandInstance.alias(alias);

[
...options,
commonOptions.json,
Expand Down
4 changes: 3 additions & 1 deletion src/utils/input.js
Expand Up @@ -144,7 +144,9 @@ export const getPassphraseFromSource = async (source, { displayName }) => {
}
};

export const getPassphrase = async (vorpal, passphraseSource, passphrase, options) => {
export const getPassphrase = async (
vorpal, passphraseSource, passphrase, options,
) => {
const optionsWithDefaults = Object.assign({ displayName: 'your secret passphrase' }, options);
if (passphrase) return passphrase;
if (!passphraseSource) return getPassphraseFromPrompt(vorpal, optionsWithDefaults);
Expand Down
12 changes: 11 additions & 1 deletion src/utils/options.js
Expand Up @@ -8,7 +8,16 @@ const passphraseDescription = `Specifies a source for your secret passphrase. Li
- --passphrase 'pass:my secret passphrase' (should only be used where security is not important)
- --passphrase env:SECRET_PASSPHRASE
- --passphrase file:/path/to/my/passphrase.txt (takes the first line only)
- --passphrase stdin (takes the first line only)
- --passphrase stdin (takes one line only)
`;

const secondPassphraseDescription = `Specifies a source for your second secret passphrase. Lisky will prompt you for input if this option is not set. Source must be one of \`env\`, \`file\` or \`stdin\`. Except for \`stdin\`, a corresponding identifier must also be provided.

Examples:
- --second-passphrase 'pass:my second secret passphrase' (should only be used where security is not important)
- --second-passphrase env:SECOND_SECRET_PASSPHRASE
- --second-passphrase file:/path/to/my/secondPassphrase.txt (takes the first line only)
- --second-passphrase stdin (takes one line only)
`;

const passwordDescription = `Specifies a source for your secret password. Lisky will prompt you for input if this option is not set. Source must be one of \`env\`, \`file\` or \`stdin\`. Except for \`stdin\`, a corresponding identifier must also be provided.
Expand All @@ -33,6 +42,7 @@ const options = {
json: ['-j, --json', jsonDescription],
noJson: ['-t, --no-json', noJsonDescription],
passphrase: ['-p, --passphrase <source>', passphraseDescription],
secondPassphrase: ['-s, --second-passphrase <source>', secondPassphraseDescription],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think this is a helpful alias?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes :)

password: ['-w, --password <source>', passwordDescription],
message: ['-m, --message <source>', messageDescription],
};
Expand Down
4 changes: 2 additions & 2 deletions src/utils/query.js
Expand Up @@ -13,11 +13,11 @@
* Removal or modification of this copyright notice is prohibited.
*
*/
import lisk from './liskInstance';
import liskAPIInstance from './api';

class Query {
constructor() {
this.client = lisk;
this.client = liskAPIInstance;
}

isBlockQuery(input) {
Expand Down
24 changes: 24 additions & 0 deletions src/utils/transactions.js
@@ -0,0 +1,24 @@
/*
* LiskHQ/lisky
* Copyright © 2017 Lisk Foundation
*
* See the LICENSE file at the top-level directory of this distribution
* for licensing information.
*
* Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation,
* no part of this software, including this file, may be copied, modified,
* propagated, or distributed except according to the terms contained in the
* LICENSE file.
*
* Removal or modification of this copyright notice is prohibited.
*
*/
import lisk from 'lisk-js';

export default Object.assign({},
lisk.transaction,
lisk.multisignature,
lisk.signature,
lisk.delegate,
lisk.vote,
);
135 changes: 135 additions & 0 deletions test/specs/commands/createTransactionRegisterSecondPassphrase.js
@@ -0,0 +1,135 @@
/*
* LiskHQ/lisky
* Copyright © 2017 Lisk Foundation
*
* See the LICENSE file at the top-level directory of this distribution
* for licensing information.
*
* Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation,
* no part of this software, including this file, may be copied, modified,
* propagated, or distributed except according to the terms contained in the
* LICENSE file.
*
* Removal or modification of this copyright notice is prohibited.
*
*/
import { setUpInputStubs } from '../../steps/utils';
import * as given from '../../steps/1_given';
import * as when from '../../steps/2_when';
import * as then from '../../steps/3_then';

describe('create transaction register second passphrase command', () => {
beforeEach(() => {
setUpInputStubs();
});
describe('Given a Vorpal instance with a UI and an active command that can prompt', () => {
beforeEach(given.aVorpalInstanceWithAUIAndAnActiveCommandThatCanPrompt);
describe('Given an action "create transaction register second passphrase"', () => {
beforeEach(given.anAction);
describe('Given a passphrase "minute omit local rare sword knee banner pair rib museum shadow juice"', () => {
beforeEach(given.aPassphrase);
describe('Given a second passphrase "fame spoil quiz garbage mirror envelope island rapid lend year bike adapt"', () => {
beforeEach(given.aSecondPassphrase);
describe('Given a Lisk object that can create transactions', () => {
beforeEach(given.aLiskObjectThatCanCreateTransactions);
describe('Given an options object with passphrase set to unknown source "xxx"', () => {
beforeEach(given.anOptionsObjectWithPassphraseSetToUnknownSource);
describe('When the action is called with the options', () => {
beforeEach(when.theActionIsCalledWithTheOptions);
it('Then it should reject with message "Unknown passphrase source type. Must be one of `file`, or `stdin`."', then.itShouldRejectWithMessage);
});
});
describe('Given an options object with second passphrase set to unknown source "xxx"', () => {
beforeEach(given.anOptionsObjectWithSecondPassphraseSetToUnknownSource);
describe('When the action is called with the options', () => {
beforeEach(when.theActionIsCalledWithTheOptions);
it('Then it should reject with message "Unknown second passphrase source type. Must be one of `file`, or `stdin`."', then.itShouldRejectWithMessage);
});
});
describe('Given an empty options object', () => {
beforeEach(given.anEmptyOptionsObject);
describe('Given the passphrase and the second passphrase are provided via the prompt', () => {
beforeEach(given.thePassphraseAndTheSecondPassphraseAreProvidedViaThePrompt);
describe('When the action is called with the options', () => {
beforeEach(when.theActionIsCalledWithTheOptions);
it('Then it should not get the passphrase from stdin', then.itShouldNotGetThePassphraseFromStdIn);
it('Then it should get the passphrase using the vorpal instance', then.itShouldGetThePassphraseUsingTheVorpalInstance);
it('Then it should get the passphrase with a repeated prompt', then.itShouldGetThePassphraseWithARepeatedPrompt);
it('Then it should not get the second passphrase from stdin', then.itShouldNotGetTheSecondPassphraseFromStdIn);
it('Then it should get the second passphrase using the vorpal instance', then.itShouldGetTheSecondPassphraseUsingTheVorpalInstance);
it('Then it should get the second passphrase with a repeated prompt', then.itShouldGetTheSecondPassphraseWithARepeatedPrompt);
it('Then it should create a register second passphrase transaction using the passphrase and the second passphrase', then.itShouldCreateARegisterSecondPassphraseTransactionUsingThePassphraseAndTheSecondPassphrase);
it('Then it should resolve to the created transaction', then.itShouldResolveToTheCreatedTransaction);
});
});
});
describe('Given an options object with passphrase set to "stdin"', () => {
beforeEach(given.anOptionsObjectWithPassphraseSetTo);
describe('Given the passphrase is provided via stdin', () => {
beforeEach(given.thePassphraseIsProvidedViaStdIn);
describe('Given the second passphrase is provided via the prompt', () => {
beforeEach(given.theSecondPassphraseIsProvidedViaThePrompt);
describe('When the action is called with the options', () => {
beforeEach(when.theActionIsCalledWithTheOptions);
it('Then it should get the passphrase from stdin', then.itShouldGetThePassphraseFromStdIn);
it('Then it should get the passphrase using the passphrase from stdin', then.itShouldGetThePassphraseUsingThePassphraseFromStdIn);
it('Then it should not get the second passphrase from stdin', then.itShouldNotGetTheSecondPassphraseFromStdIn);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a test for repeated prompt here too.

it('Then it should get the second passphrase using the vorpal instance', then.itShouldGetTheSecondPassphraseUsingTheVorpalInstance);
it('Then it should get the second passphrase with a repeated prompt', then.itShouldGetTheSecondPassphraseWithARepeatedPrompt);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also using the Vorpal instance

it('Then it should create a register second passphrase transaction using the passphrase and the second passphrase', then.itShouldCreateARegisterSecondPassphraseTransactionUsingThePassphraseAndTheSecondPassphrase);
it('Then it should resolve to the created transaction', then.itShouldResolveToTheCreatedTransaction);
});
});
});
});
describe('Given an options object with passphrase set to "stdin" and second passphrase set to "stdin"', () => {
beforeEach(given.anOptionsObjectWithPassphraseSetToAndSecondPassphraseSetTo);
describe('Given the passphrase and the second passphrase are provided via stdin', () => {
beforeEach(given.thePassphraseAndTheSecondPassphraseAreProvidedViaStdIn);
describe('When the action is called with the options', () => {
beforeEach(when.theActionIsCalledWithTheOptions);
it('Then it should get the passphrase from stdin', then.itShouldGetThePassphraseFromStdIn);
it('Then it should get the passphrase using the passphrase from stdin', then.itShouldGetThePassphraseUsingThePassphraseFromStdIn);
it('Then it should get the second passphrase from stdin', then.itShouldGetTheSecondPassphraseFromStdIn);
it('Then it should get the second passphrase using the second passphrase from stdin', then.itShouldGetTheSecondPassphraseUsingTheSecondPassphraseFromStdIn);
it('Then it should create a register second passphrase transaction using the passphrase and the second passphrase', then.itShouldCreateARegisterSecondPassphraseTransactionUsingThePassphraseAndTheSecondPassphrase);
it('Then it should resolve to the created transaction', then.itShouldResolveToTheCreatedTransaction);
});
});
});
describe('Given an options object with passphrase set to "stdin" and second passphrase set to "file:/path/to/my/password.txt"', () => {
beforeEach(given.anOptionsObjectWithPassphraseSetToAndSecondPassphraseSetTo);
describe('Given the passphrase is provided via stdin', () => {
beforeEach(given.thePassphraseIsProvidedViaStdIn);
describe('When the action is called with the options', () => {
beforeEach(when.theActionIsCalledWithTheOptions);
it('Then it should get the passphrase from stdin', then.itShouldGetThePassphraseFromStdIn);
it('Then it should get the passphrase using the passphrase from stdin', then.itShouldGetThePassphraseUsingThePassphraseFromStdIn);
it('Then it should not get the second passphrase from stdin', then.itShouldNotGetTheSecondPassphraseFromStdIn);
it('Then it should get the second passphrase using the second passphrase source', then.itShouldGetTheSecondPassphraseUsingTheSecondPassphraseSource);
it('Then it should create a register second passphrase transaction using the passphrase and the second passphrase', then.itShouldCreateARegisterSecondPassphraseTransactionUsingThePassphraseAndTheSecondPassphrase);
it('Then it should resolve to the created transaction', then.itShouldResolveToTheCreatedTransaction);
});
});
});
describe('Given an options object with passphrase set to "file:/path/to/my/password.txt" and second passphrase set to "stdin"', () => {
beforeEach(given.anOptionsObjectWithPassphraseSetToAndSecondPassphraseSetTo);
describe('Given the second passphrase is provided via stdin', () => {
beforeEach(given.theSecondPassphraseIsProvidedViaStdIn);
describe('When the action is called with the options', () => {
beforeEach(when.theActionIsCalledWithTheOptions);
it('Then it should not get the passphrase from stdin', then.itShouldNotGetThePassphraseFromStdIn);
it('Then it should get the passphrase from the passphrase source', then.itShouldGetThePassphraseUsingThePassphraseSource);
it('Then it should get the second passphrase from stdin', then.itShouldGetTheSecondPassphraseFromStdIn);
it('Then it should get the second passphrase using the second passphrase from stdin', then.itShouldGetTheSecondPassphraseUsingTheSecondPassphraseFromStdIn);
it('Then it should create a register second passphrase transaction using the passphrase and the second passphrase', then.itShouldCreateARegisterSecondPassphraseTransactionUsingThePassphraseAndTheSecondPassphrase);
it('Then it should resolve to the created transaction', then.itShouldResolveToTheCreatedTransaction);
});
});
});
});
});
});
});
});
});
2 changes: 1 addition & 1 deletion test/specs/commands/decryptMessage.js
Expand Up @@ -96,7 +96,7 @@ describe('decrypt message command', () => {
beforeEach(given.anOptionsObjectWithPassphraseSetToUnknownSource);
describe('When the action is called with the message, the nonce, the senderPublicKey and the options', () => {
beforeEach(when.theActionIsCalledWithTheMessageTheNonceTheSenderPublicKeyAndTheOptions);
it('Then it should reject with message "Unknown data source type. Must be one of `file`, or `stdin`."', then.itShouldRejectWithMessage);
it('Then it should reject with message "Unknown passphrase source type. Must be one of `file`, or `stdin`."', then.itShouldRejectWithMessage);
});
});
describe('Given an options object with passphrase set to "file:/path/to/my/message.txt"', () => {
Expand Down
2 changes: 1 addition & 1 deletion test/specs/commands/encryptMessage.js
Expand Up @@ -94,7 +94,7 @@ describe('encrypt message command', () => {
beforeEach(given.anOptionsObjectWithPassphraseSetToUnknownSource);
describe('When the action is called with the recipient, the message and the options', () => {
beforeEach(when.theActionIsCalledWithTheRecipientTheMessageAndTheOptions);
it('Then it should reject with message "Unknown data source type. Must be one of `file`, or `stdin`."', then.itShouldRejectWithMessage);
it('Then it should reject with message "Unknown passphrase source type. Must be one of `file`, or `stdin`."', then.itShouldRejectWithMessage);
});
});
describe('Given an options object with passphrase set to "file:/path/to/my/message.txt"', () => {
Expand Down
2 changes: 1 addition & 1 deletion test/specs/commands/encryptPassphrase.js
Expand Up @@ -110,7 +110,7 @@ describe('encrypt passphrase command', () => {
beforeEach(given.anOptionsObjectWithPassphraseSetToUnknownSource);
describe('When the action is called with the options', () => {
beforeEach(when.theActionIsCalledWithTheOptions);
it('Then it should reject with message "Unknown data source type. Must be one of `file`, or `stdin`."', then.itShouldRejectWithMessage);
it('Then it should reject with message "Unknown passphrase source type. Must be one of `file`, or `stdin`."', then.itShouldRejectWithMessage);
});
});
describe('Given an options object with passphrase set to "file:/path/to/my/message.txt"', () => {
Expand Down
8 changes: 4 additions & 4 deletions test/specs/utils/liskInstance.js → test/specs/utils/api.js
Expand Up @@ -16,9 +16,9 @@
import * as given from '../../steps/1_given';
import * as then from '../../steps/3_then';

describe('liskInstance util', () => {
describe('Given a lisk instance', () => {
beforeEach(given.aLiskInstance);
it('Then the lisk instance should be a lisk-js api instance', then.theLiskInstanceShouldBeALiskJSApiInstance);
describe('api util', () => {
describe('Given a lisk api instance', () => {
beforeEach(given.aliskAPIInstance);
it('Then the lisk instance should be a lisk-js api instance', then.theliskAPIInstanceShouldBeALiskJSApiInstance);
});
});