+
{ tab }
)
diff --git a/src/components/toolbox/tabsContainer/tabsContainer.test.js b/src/components/toolbox/tabsContainer/tabsContainer.test.js
index 1abb028d30..b16fd3c970 100644
--- a/src/components/toolbox/tabsContainer/tabsContainer.test.js
+++ b/src/components/toolbox/tabsContainer/tabsContainer.test.js
@@ -4,7 +4,7 @@ import TabsContainer from './tabsContainer';
describe.skip('TabsContainer Component', () => {
let wrapper;
- const children = [0, 1, 2].map((tab, key) =>
{`tab-${tab}`}
);
+ const children = [0, 1, 2].map((tab, key) =>
{`tab-${tab}`}
);
beforeEach(() => {
wrapper = shallow(
@@ -13,14 +13,14 @@ describe.skip('TabsContainer Component', () => {
});
it('Should act as noop if only one children present', () => {
- wrapper = shallow();
+ wrapper = shallow();
expect(wrapper).toMatchSelector('div');
});
it('Should act as noop if children updates to only one', () => {
wrapper = shallow({children});
wrapper.setProps({
- children: ,
+ children: ,
});
wrapper.update();
expect(wrapper).toMatchSelector('div');
@@ -28,8 +28,8 @@ describe.skip('TabsContainer Component', () => {
it('Should update tabs if children updates', () => {
wrapper = shallow(
-
-
+
+
);
wrapper.setProps({ children });
wrapper.update();
@@ -38,8 +38,8 @@ describe.skip('TabsContainer Component', () => {
expect(wrapper.find('.contentHolder > div').at(0)).toHaveClassName('active');
wrapper = shallow(
-
-
+
+
);
wrapper.setProps({ children, activeTab: 'tab-1' });
wrapper.update();
@@ -55,13 +55,13 @@ describe.skip('TabsContainer Component', () => {
expect(wrapper.find('.contentHolder > div').at(2)).toHaveClassName('active');
});
- it('Should render tabs for each child that has tabName props', () => {
+ it('Should render tabs for each child that has name props', () => {
expect(wrapper).toContainMatchingElements(3, 'li');
expect(wrapper).toContainMatchingElements(3, '.contentHolder > div');
});
it('Should change selected tab onClick', () => {
- wrapper.find('.tabs li').at(1).simulate('click', { target: { dataset: { tabname: 'tab-1' } } });
+ wrapper.find('.tabs li').at(1).simulate('click', { target: { dataset: { name: 'tab-1' } } });
expect(wrapper.find('.tabs li').at(1)).toHaveClassName('active');
expect(wrapper.find('.contentHolder > div').at(1)).toHaveClassName('active');
});
diff --git a/src/store/actions/transactions.js b/src/store/actions/transactions.js
index 414bd77340..e9033df11e 100644
--- a/src/store/actions/transactions.js
+++ b/src/store/actions/transactions.js
@@ -2,11 +2,12 @@ import to from 'await-to-js';
import {
actionTypes, tokenMap, MODULE_ASSETS_NAME_ID_MAP, DEFAULT_LIMIT,
+ signatureCollectionStatus,
} from '@constants';
import { isEmpty } from '@utils/helpers';
import { getTransactions, create, broadcast } from '@api/transaction';
-import { selectActiveTokenAccount, selectNetworkIdentifier } from '@store/selectors';
-import { signTransaction, transformTransaction } from '@utils/transaction';
+import { signMultisigTransaction, transformTransaction } from '@utils/transaction';
+import { extractKeyPair } from '@utils/account';
import { getTransactionSignatureStatus } from '@screens/signMultiSignTransaction/helpers';
import { timerReset } from './account';
import { loadingStarted, loadingFinished } from './loading';
@@ -137,16 +138,26 @@ export const transactionDoubleSigned = () => async (dispatch, getState) => {
const {
transactions, network, account, settings,
} = getState();
- const networkIdentifier = selectNetworkIdentifier({ network });
- const activeAccount = selectActiveTokenAccount({ account, settings });
- const [signedTx, err] = signTransaction(
- transformTransaction(transactions.signedTransaction),
- account.secondPassphrase,
- networkIdentifier,
+ const keyPair = extractKeyPair({
+ passphrase: account.secondPassphrase,
+ enableCustomDerivationPath: false,
+ });
+ const activeAccount = {
+ ...account.info[settings.token.active],
+ passphrase: account.secondPassphrase,
+ summary: {
+ ...account.info[settings.token.active].summary,
+ ...keyPair,
+ },
+ };
+ const transformedTx = transformTransaction(transactions.signedTransaction);
+ const [signedTx, err] = await signMultisigTransaction(
+ transformedTx,
+ activeAccount,
{
data: activeAccount,
},
- false,
+ signatureCollectionStatus.partiallySigned,
network,
);
@@ -221,11 +232,10 @@ export const transactionBroadcasted = transaction =>
*/
export const multisigTransactionSigned = ({
rawTransaction, sender,
-}) => (dispatch, getState) => {
+}) => async (dispatch, getState) => {
const {
network, account,
} = getState();
- const networkIdentifier = selectNetworkIdentifier({ network });
const activeAccount = {
...account.info.LSK,
passphrase: account.passphrase,
@@ -234,10 +244,9 @@ export const multisigTransactionSigned = ({
// @todo move isTransactionFullySigned to a generic location
const txStatus = getTransactionSignatureStatus(sender.data, rawTransaction);
- const [tx, error] = signTransaction(
+ const [tx, error] = await signMultisigTransaction(
rawTransaction,
- activeAccount.passphrase,
- networkIdentifier,
+ activeAccount,
sender,
txStatus,
network,
diff --git a/src/store/actions/transactions.test.js b/src/store/actions/transactions.test.js
index 179b4acac4..7379901ccc 100644
--- a/src/store/actions/transactions.test.js
+++ b/src/store/actions/transactions.test.js
@@ -218,23 +218,14 @@ describe('actions: transactions', () => {
};
const transactionError = new Error('Transaction create error');
loginTypes.passphrase.code = 1;
- hwManagerApi.signTransactionByHW.mockRejectedValue(transactionError);
+ jest.spyOn(hwManagerApi, 'signTransactionByHW')
+ .mockRejectedValue(transactionError);
const expectedAction = {
type: actionTypes.transactionSignError,
data: transactionError,
};
- const { network } = getStateWithHW();
-
// Act
await transactionCreated(data)(dispatch, getStateWithHW);
-
- // Assert
- expect(hwManagerApi.signTransactionByHW).toHaveBeenCalledWith(
- activeAccount,
- Buffer.from(network.networks.LSK.networkIdentifier, 'hex'),
- expect.anything(),
- expect.anything(),
- );
expect(dispatch).toHaveBeenCalledWith(expectedAction);
});
});
@@ -271,10 +262,11 @@ describe('actions: transactions', () => {
expect(dispatch).toHaveBeenCalledWith(expectedAction);
});
- it('should create an action to store signature error', async () => {
+ it.skip('should create an action to store signature error', async () => {
// Prepare the store
- const error = { message: 'error signing tx' };
- jest.spyOn(transactionUtils, 'signTransaction').mockImplementation(() => [{}, error]);
+ const error = new Error('error signing tx');
+ jest.spyOn(transactionUtils, 'sign')
+ .mockImplementation(() => error);
// Consume the utility
await transactionDoubleSigned()(dispatch, getStateWithTx);
@@ -384,10 +376,10 @@ describe('actions: transactions', () => {
sender: { data: accounts.multiSig },
};
- it('should create an action to store double signed tx', () => {
+ it('should create an action to store double signed tx', async () => {
// Consume the utility
- jest.spyOn(transactionUtils, 'signTransaction').mockImplementation(() => [{ id: 1 }, undefined]);
- multisigTransactionSigned(params)(dispatch, getStateWithTx);
+ jest.spyOn(transactionUtils, 'signMultisigTransaction').mockImplementation(() => [{ id: 1 }, undefined]);
+ await multisigTransactionSigned(params)(dispatch, getStateWithTx);
// Prepare expectations
const expectedAction = {
@@ -399,13 +391,13 @@ describe('actions: transactions', () => {
expect(dispatch).toHaveBeenCalledWith(expectedAction);
});
- it('should create an action to store signature error', () => {
+ it('should create an action to store signature error', async () => {
// Prepare the store
const error = { message: 'error signing tx' };
- jest.spyOn(transactionUtils, 'signTransaction').mockImplementation(() => [undefined, error]);
+ jest.spyOn(transactionUtils, 'signMultisigTransaction').mockImplementation(() => [undefined, error]);
// Consume the utility
- multisigTransactionSigned(params)(dispatch, getStateWithTx);
+ await multisigTransactionSigned(params)(dispatch, getStateWithTx);
// Prepare expectations
const expectedAction = {
diff --git a/src/utils/api/transaction/lsk.js b/src/utils/api/transaction/lsk.js
index 05493edc1c..867315fdb0 100644
--- a/src/utils/api/transaction/lsk.js
+++ b/src/utils/api/transaction/lsk.js
@@ -1,6 +1,5 @@
/* eslint-disable max-lines */
-import { transactions, cryptography } from '@liskhq/lisk-client';
-import { to } from 'await-to-js';
+import { transactions } from '@liskhq/lisk-client';
import {
tokenMap,
@@ -11,9 +10,8 @@ import {
BASE_FEES,
} from '@constants';
import { joinModuleAndAssetIds } from '@utils/moduleAssets';
-import { signTransactionByHW } from '@utils/hwManager';
import {
- createTransactionObject, convertStringToBinary, transformTransaction, flattenTransaction,
+ createTransactionObject, sign,
} from '@utils/transaction';
import { validateAddress } from '../../validators';
import http from '../http';
@@ -297,94 +295,6 @@ export const getTransactionFee = async ({
};
};
-/**
- * Computes transaction id
- * @param {object} transaction
- * @returns {Promise} returns transaction id for a given transaction object
- */
-export const computeTransactionId = ({ transaction, network }) => {
- const moduleAssetId = joinModuleAndAssetIds({
- moduleID: transaction.moduleID,
- assetID: transaction.assetID,
- });
- const schema = network.networks.LSK.moduleAssetSchemas[moduleAssetId];
- const transactionBytes = transactions.getBytes(schema, transaction);
- const id = cryptography.hash(transactionBytes);
-
- return id;
-};
-
-const signMultisigUsingPrivateKey = (
- schema, transaction, networkIdentifier, keys, privateKey,
- isMultiSignatureRegistration, publicKey, moduleAssetId, rawTransaction,
-) => {
- let signedTransaction = transactions.signMultiSignatureTransactionWithPrivateKey(
- schema,
- transaction,
- networkIdentifier,
- Buffer.from(privateKey, 'hex'),
- {
- optionalKeys: keys.optionalKeys.map(convertStringToBinary),
- mandatoryKeys: keys.mandatoryKeys.map(convertStringToBinary),
- },
- isMultiSignatureRegistration,
- );
-
- const transactionKeys = {
- mandatoryKeys: rawTransaction.mandatoryKeys ?? [],
- optionalKeys: rawTransaction.optionalKeys ?? [],
- };
-
- const needsDoubleSign = [
- ...transactionKeys.mandatoryKeys,
- ...transactionKeys.optionalKeys,
- ].includes(publicKey);
-
- if (isMultiSignatureRegistration && needsDoubleSign) {
- const transformedTransaction = transformTransaction(signedTransaction);
- const flattenedTransaction = flattenTransaction(transformedTransaction);
- const tx = createTransactionObject(flattenedTransaction, moduleAssetId);
- const transactionKeysInBinary = {
- mandatoryKeys: transactionKeys.mandatoryKeys.map(convertStringToBinary),
- optionalKeys: transactionKeys.optionalKeys.map(convertStringToBinary),
- };
-
- signedTransaction = transactions.signMultiSignatureTransactionWithPrivateKey(
- schema,
- tx,
- networkIdentifier,
- Buffer.from(privateKey, 'hex'),
- transactionKeysInBinary,
- isMultiSignatureRegistration,
- );
- }
-
- return signedTransaction;
-};
-
-const signUsingPrivateKey = (schema, transaction, networkIdentifier, privateKey) =>
- transactions.signTransactionWithPrivateKey(
- schema,
- transaction,
- networkIdentifier,
- Buffer.from(privateKey, 'hex'),
- );
-
-const signUsingHW = async (schema, transaction, account, networkIdentifier, network) => {
- const signingBytes = transactions.getSigningBytes(schema, transaction);
- const [error, signedTransaction] = await to(signTransactionByHW(
- account,
- networkIdentifier,
- transaction,
- signingBytes,
- ));
- if (error) {
- throw error;
- }
- const id = computeTransactionId({ transaction: signedTransaction, network });
- return { ...signedTransaction, id };
-};
-
/**
* creates a new transaction
*
@@ -420,17 +330,13 @@ export const create = async ({
const isMultiSignatureRegistration = moduleAssetId
=== MODULE_ASSETS_NAME_ID_MAP.registerMultisignatureGroup;
- if (isMultisignature || isMultiSignatureRegistration) {
- return signMultisigUsingPrivateKey(
- schema, transaction, networkIdentifier, keys, privateKey,
- isMultiSignatureRegistration, publicKey, moduleAssetId, rawTransaction,
- );
- }
- if (account.hwInfo) {
- const signedTx = await signUsingHW(schema, transaction, account, networkIdentifier, network);
- return signedTx;
- }
- return signUsingPrivateKey(schema, transaction, networkIdentifier, privateKey);
+ const result = await sign(
+ account, schema, transaction, network, networkIdentifier,
+ isMultisignature, isMultiSignatureRegistration, keys, publicKey,
+ moduleAssetId, rawTransaction, privateKey,
+ );
+
+ return result;
};
/**
diff --git a/src/utils/hwManager.js b/src/utils/hwManager.js
index 946d6e9373..ae2caf6194 100644
--- a/src/utils/hwManager.js
+++ b/src/utils/hwManager.js
@@ -1,5 +1,4 @@
// eslint-disable-next-line import/no-unresolved
-// import Lisk from '@liskhq/lisk-client';
import i18next from 'i18next';
import { getAccount } from './api/account';
import {
@@ -33,6 +32,59 @@ const getAccountsFromDevice = async ({ device: { deviceId }, network }) => {
return accounts;
};
+const isKeyMatch = (aPublicKey, signerPublicKey) => (Buffer.isBuffer(aPublicKey)
+ ? aPublicKey.equals(signerPublicKey)
+ : Buffer.from(aPublicKey, 'hex').equals(signerPublicKey));
+
+/**
+ * updateTransactionSignatures - Function.
+ * This function updates transaction object to include the signatures at correct index.
+ * The below logic is copied from Lisk SDK https://github.com/LiskHQ/lisk-sdk/blob/2593d1fe70154a9209b713994a252c494cad7123/elements/lisk-transactions/src/sign.ts#L228-L297
+ */
+/* eslint-disable max-statements */
+const updateTransactionSignatures = (
+ account,
+ transactionObject,
+ signature,
+ keys,
+) => {
+ const isMultiSignatureRegistration = transactionObject.moduleID === 4;
+ const signerPublicKey = Buffer.from(account.summary.publicKey, 'hex');
+ if (Buffer.isBuffer(transactionObject.senderPublicKey)
+ && signerPublicKey.equals(transactionObject.senderPublicKey)
+ ) {
+ transactionObject.signatures[0] = signature;
+ }
+
+ const { mandatoryKeys, optionalKeys } = keys;
+ if (mandatoryKeys.length + optionalKeys.length > 0) {
+ const mandatoryKeyIndex = mandatoryKeys.findIndex(
+ aPublicKey => isKeyMatch(aPublicKey, signerPublicKey),
+ );
+ const optionalKeyIndex = optionalKeys.findIndex(
+ aPublicKey => isKeyMatch(aPublicKey, signerPublicKey),
+ );
+ const signatureOffset = isMultiSignatureRegistration ? 1 : 0;
+ if (mandatoryKeyIndex !== -1) {
+ transactionObject.signatures[mandatoryKeyIndex + signatureOffset] = signature;
+ }
+ if (optionalKeyIndex !== -1) {
+ const index = mandatoryKeys.length + optionalKeyIndex + signatureOffset;
+ transactionObject.signatures[index] = signature;
+ }
+ const numberOfSignatures = signatureOffset + mandatoryKeys.length + optionalKeys.length;
+ for (let i = 0; i < numberOfSignatures; i += 1) {
+ if (Array.isArray(transactionObject.signatures)
+ && transactionObject.signatures[i] === undefined) {
+ transactionObject.signatures[i] = Buffer.alloc(0);
+ }
+ }
+ }
+
+ return transactionObject;
+};
+/* eslint-disable max-statements */
+
/**
* signTransactionByHW - Function.
* This function is used for sign a send hardware wallet transaction.
@@ -42,8 +94,9 @@ const signTransactionByHW = async (
networkIdentifier,
transactionObject,
transactionBytes,
+ keys,
) => {
- const transaction = {
+ const data = {
deviceId: account.hwInfo.deviceId,
index: account.hwInfo.derivationIndex,
networkIdentifier,
@@ -51,14 +104,8 @@ const signTransactionByHW = async (
};
try {
- const signature = await signTransaction(transaction);
- if (Array.isArray(transactionObject.signatures)) {
- transactionObject.signatures.push(signature);
- } else {
- Object.assign(transactionObject, { signatures: [signature] });
- }
-
- return transactionObject;
+ const signature = await signTransaction(data);
+ return updateTransactionSignatures(account, transactionObject, signature, keys);
} catch (error) {
throw new Error(error);
}
diff --git a/src/utils/hwManager.test.js b/src/utils/hwManager.test.js
index 692c56d6d4..b700ba533b 100644
--- a/src/utils/hwManager.test.js
+++ b/src/utils/hwManager.test.js
@@ -43,11 +43,9 @@ describe('hwManager util', () => {
describe('signTransactionByHW', () => {
it('should return a transaction object with the proper signature', async () => {
const account = {
- info: {
- LSK: {
- address: 'lskbgyrx3v76jxowgkgthu9yaf3dr29wqxbtxz8yp',
- publicKey: 'fd061b9146691f3c56504be051175d5b76d1b1d0179c5c4370e18534c5882122',
- },
+ summary: {
+ address: 'lskbgyrx3v76jxowgkgthu9yaf3dr29wqxbtxz8yp',
+ publicKey: 'fd061b9146691f3c56504be051175d5b76d1b1d0179c5c4370e18534c5882122',
},
hwInfo: {
deviceId: '060E803263E985C022CA2C9B',
@@ -66,6 +64,12 @@ describe('hwManager util', () => {
assetID: 0,
nonce: '1',
senderAddress: 'lskdxc4ta5j43jp9ro3f8zqbxta9fn6jwzjucw7yt',
+ signatures: [],
+ };
+
+ const keys = {
+ mandatoryKeys: [Buffer.from(account.summary.publicKey, 'hex'), Buffer.from(accounts.genesis.summary.publicKey, 'hex')],
+ optionalKeys: [],
};
const networkIdentifier = Buffer.from('15f0dacc1060e91818224a94286b13aa04279c640bd5d6f193182031d133df7c', 'hex');
@@ -76,6 +80,7 @@ describe('hwManager util', () => {
networkIdentifier,
transactionObject,
transactionBytes,
+ keys,
);
expect(signedTransaction.signatures[0]).toEqual(signature);
diff --git a/src/utils/transaction.js b/src/utils/transaction.js
index 0c1c6d0563..08808ef963 100644
--- a/src/utils/transaction.js
+++ b/src/utils/transaction.js
@@ -1,5 +1,7 @@
/* eslint-disable max-lines */
-import { transactions } from '@liskhq/lisk-client';
+import { transactions, cryptography } from '@liskhq/lisk-client';
+import { to } from 'await-to-js';
+import { signTransactionByHW } from '@utils/hwManager';
import {
DEFAULT_NUMBER_OF_SIGNATURES,
MODULE_ASSETS_NAME_ID_MAP,
@@ -13,6 +15,7 @@ import {
} from '@utils/account';
import { transformStringDateToUnixTimestamp } from '@utils/datetime';
import { toRawLsk } from '@utils/lsk';
+import { isEmpty } from '@utils/helpers';
import { splitModuleAndAssetIds, joinModuleAndAssetIds } from '@utils/moduleAssets';
const {
@@ -224,26 +227,26 @@ const flattenTransaction = ({ moduleAssetId, asset, ...rest }) => {
};
switch (moduleAssetId) {
- case MODULE_ASSETS_NAME_ID_MAP.transfer: {
+ case transfer: {
transaction.recipientAddress = asset.recipient.address;
transaction.amount = asset.amount;
transaction.data = asset.data;
break;
}
- case MODULE_ASSETS_NAME_ID_MAP.voteDelegate:
+ case voteDelegate:
transaction.votes = asset.votes;
break;
- case MODULE_ASSETS_NAME_ID_MAP.registerDelegate:
+ case registerDelegate:
transaction.username = asset.username;
break;
- case MODULE_ASSETS_NAME_ID_MAP.unlockToken:
+ case unlockToken:
transaction.unlockObjects = asset.unlockObjects;
break;
- case MODULE_ASSETS_NAME_ID_MAP.registerMultisignatureGroup: {
+ case registerMultisignatureGroup: {
transaction.numberOfSignatures = asset.numberOfSignatures;
transaction.mandatoryKeys = asset.mandatoryKeys;
transaction.optionalKeys = asset.optionalKeys;
@@ -405,6 +408,144 @@ export const removeExcessSignatures = (signatures, mandatoryKeysNo, hasSenderSig
return trimmedSignatures;
};
+/**
+ * Computes transaction id
+ * @param {object} transaction
+ * @returns {Promise} returns transaction id for a given transaction object
+ */
+export const computeTransactionId = ({ transaction, network }) => {
+ const moduleAssetId = joinModuleAndAssetIds({
+ moduleID: transaction.moduleID,
+ assetID: transaction.assetID,
+ });
+ const schema = network.networks.LSK.moduleAssetSchemas[moduleAssetId];
+ const transactionBytes = transactions.getBytes(schema, transaction);
+ const id = cryptography.hash(transactionBytes);
+
+ return id;
+};
+
+const signMultisigUsingPrivateKey = (
+ schema, transaction, networkIdentifier, keys, privateKey,
+ isMultiSignatureRegistration, publicKey, rawTransaction,
+) => {
+ /**
+ * Use Lisk Element to Sign with Private Key
+ */
+ const signedTransaction = transactions.signMultiSignatureTransactionWithPrivateKey(
+ schema,
+ transaction,
+ networkIdentifier,
+ Buffer.from(privateKey, 'hex'),
+ {
+ optionalKeys: keys.optionalKeys.map(convertStringToBinary),
+ mandatoryKeys: keys.mandatoryKeys.map(convertStringToBinary),
+ },
+ isMultiSignatureRegistration,
+ );
+
+ /**
+ * Define keys. Since we are creating the tx
+ * The keys only exist for MultisigReg
+ */
+ const transactionKeys = {
+ mandatoryKeys: rawTransaction.mandatoryKeys ?? [],
+ optionalKeys: rawTransaction.optionalKeys ?? [],
+ };
+
+ /**
+ * Check if the tx is multisigReg
+ */
+ const members = [
+ ...transactionKeys.mandatoryKeys.sort(),
+ ...transactionKeys.optionalKeys.sort(),
+ ];
+ const senderIndex = members.indexOf(publicKey);
+ const isSender = rawTransaction.senderPublicKey === publicKey;
+
+ if (isMultiSignatureRegistration && isSender && senderIndex > -1) {
+ const signatures = Array.from(Array(members.length + 1).keys()).map((index) => {
+ if (signedTransaction.signatures[index]) return signedTransaction.signatures[index];
+ if (index === senderIndex + 1) return signedTransaction.signatures[0];
+ return Buffer.from('');
+ });
+ signedTransaction.signatures = signatures;
+ }
+
+ return signedTransaction;
+};
+
+const signUsingPrivateKey = (schema, transaction, networkIdentifier, privateKey) =>
+ transactions.signTransactionWithPrivateKey(
+ schema,
+ transaction,
+ networkIdentifier,
+ Buffer.from(privateKey, 'hex'),
+ );
+
+// eslint-disable-next-line max-statements
+const signUsingHW = async (
+ schema, transaction, account, networkIdentifier, network, keys, rawTransaction,
+ isMultiSignatureRegistration,
+) => {
+ const signingBytes = transactions.getSigningBytes(schema, transaction);
+ const [error, signedTransaction] = await to(signTransactionByHW(
+ account,
+ networkIdentifier,
+ transaction,
+ signingBytes,
+ keys,
+ ));
+ if (error) {
+ throw error;
+ }
+
+ const transactionKeys = {
+ mandatoryKeys: rawTransaction.mandatoryKeys ?? [],
+ optionalKeys: rawTransaction.optionalKeys ?? [],
+ };
+
+ const members = [
+ ...transactionKeys.mandatoryKeys.sort(),
+ ...transactionKeys.optionalKeys.sort(),
+ ];
+ const senderIndex = members.indexOf(account.summary.publicKey);
+ const isSender = rawTransaction.senderPublicKey === account.summary.publicKey;
+
+ if (isMultiSignatureRegistration && isSender && senderIndex > -1) {
+ const signatures = Array.from(Array(members.length + 1).keys()).map((index) => {
+ if (signedTransaction.signatures[index]) return signedTransaction.signatures[index];
+ if (index === senderIndex + 1) return signedTransaction.signatures[0];
+ return Buffer.from('');
+ });
+ signedTransaction.signatures = signatures;
+ }
+
+ const id = computeTransactionId({ transaction: signedTransaction, network });
+ return { ...signedTransaction, id };
+};
+
+export const sign = async (
+ account, schema, transaction, network, networkIdentifier,
+ isMultisignature, isMultiSignatureRegistration, keys, publicKey,
+ moduleAssetId, rawTransaction, privateKey,
+) => {
+ if (!isEmpty(account.hwInfo)) {
+ const signedTx = await signUsingHW(
+ schema, transaction, account, networkIdentifier, network, keys, rawTransaction,
+ isMultiSignatureRegistration,
+ );
+ return signedTx;
+ }
+ if (isMultisignature || isMultiSignatureRegistration) {
+ return signMultisigUsingPrivateKey(
+ schema, transaction, networkIdentifier, keys, privateKey,
+ isMultiSignatureRegistration, publicKey, rawTransaction,
+ );
+ }
+ return signUsingPrivateKey(schema, transaction, networkIdentifier, privateKey);
+};
+
/**
* Signs a given multisignature tx with a given passphrase
*
@@ -418,57 +559,54 @@ export const removeExcessSignatures = (signatures, mandatoryKeysNo, hasSenderSig
* @returns [Object, Object] - Signed transaction and err
*/
// eslint-disable-next-line max-statements
-const signTransaction = (
+const signMultisigTransaction = async (
transaction,
- passphrase,
- networkIdentifier,
+ account,
senderAccount,
txStatus,
network,
) => {
- let signedTransaction;
- let err;
-
- const isGroupRegistration = transaction.moduleAssetId
- === MODULE_ASSETS_NAME_ID_MAP.registerMultisignatureGroup;
+ /**
+ * Define keys.
+ * Since the sender is different, the keys are defined based on that
+ */
+ const isGroupRegistration = transaction.moduleAssetId === registerMultisignatureGroup;
+ const schema = network.networks.LSK.moduleAssetSchemas[transaction.moduleAssetId];
+ const networkIdentifier = Buffer.from(network.networks.LSK.networkIdentifier, 'hex');
const { mandatoryKeys, optionalKeys } = getKeys({
senderAccount: senderAccount.data, transaction, isGroupRegistration,
});
-
- const flatTransaction = flattenTransaction(transaction);
- const transactionObject = createTransactionObject(flatTransaction, transaction.moduleAssetId);
const keys = {
mandatoryKeys: mandatoryKeys.map(key => Buffer.from(key, 'hex')),
optionalKeys: optionalKeys.map(key => Buffer.from(key, 'hex')),
};
- const includeSender = transaction.moduleAssetId
- === MODULE_ASSETS_NAME_ID_MAP.registerMultisignatureGroup;
+ /**
+ * To do so, we have to flatten, then create txObject
+ */
+ const flatTransaction = flattenTransaction(transaction);
+ const transactionObject = createTransactionObject(flatTransaction, transaction.moduleAssetId);
+
+ /**
+ * remove excess optional signatures
+ */
+ if (txStatus === signatureCollectionStatus.occupiedByOptionals) {
+ transactionObject.signatures = removeExcessSignatures(
+ transactionObject.signatures, keys.mandatoryKeys.length, isGroupRegistration,
+ );
+ }
try {
- // remove excess optionals
- if (txStatus === signatureCollectionStatus.occupiedByOptionals) {
- transactionObject.signatures = removeExcessSignatures(
- transactionObject.signatures, keys.mandatoryKeys.length, includeSender,
- );
- }
-
- signedTransaction = transactions.signMultiSignatureTransaction(
- network.networks.LSK.moduleAssetSchemas[transaction.moduleAssetId],
- transactionObject,
- Buffer.from(networkIdentifier, 'hex'),
- passphrase,
- keys,
- includeSender,
+ const result = await sign(
+ account, schema, transactionObject, network, networkIdentifier,
+ !!senderAccount.data, isGroupRegistration, keys, account.summary.publicKey,
+ transaction.moduleAssetId, flatTransaction, account.summary.privateKey,
);
+ return [result];
} catch (e) {
- // eslint-disable-next-line no-console
- console.error(e);
- err = e;
+ return [null, e];
}
-
- return [signedTransaction, err];
};
/**
@@ -482,7 +620,7 @@ const signTransaction = (
* @returns {number} the number of signatures required
*/
const getNumberOfSignatures = (account, transaction) => {
- if (transaction?.moduleAssetId === MODULE_ASSETS_NAME_ID_MAP.registerMultisignatureGroup) {
+ if (transaction?.moduleAssetId === registerMultisignatureGroup) {
return transaction.optionalKeys.length + transaction.mandatoryKeys.length + 1;
}
if (account?.summary?.isMultisignature) {
@@ -500,6 +638,6 @@ export {
containsTransactionType,
createTransactionObject,
normalizeTransactionParams,
- signTransaction,
+ signMultisigTransaction,
getNumberOfSignatures,
};
diff --git a/test/cypress/features/common/common.js b/test/cypress/features/common/common.js
index c487c8dfa2..c009870d9e 100644
--- a/test/cypress/features/common/common.js
+++ b/test/cypress/features/common/common.js
@@ -6,7 +6,6 @@ const txConfirmationTimeout = 15000;
Given(/^Network switcher is (enabled|disabled)$/, function (status) {
const showNetwork = status === 'enabled';
- console.log('showNetwork', showNetwork);
window.localStorage.setItem('settings',
JSON.stringify({ ...settings, 'showNetwork': showNetwork }));
});
diff --git a/test/cypress/features/search.feature b/test/cypress/features/search.feature
index eb7d2088a6..bd8ad77fd3 100644
--- a/test/cypress/features/search.feature
+++ b/test/cypress/features/search.feature
@@ -20,8 +20,9 @@ Feature: Search
Scenario: Search for non-existent account
Given Network is set to testnet
Given I login as genesis on testnet
- When I click on searchIcon
+ When I wait 3 seconds
+ And I click on searchIcon
And I search for delegate 43th3j4bt324
- And I wait 3 seconds
+ And I wait 2 seconds
Then I should see no results