* Fix wallet view (#6597) * Add safe fail for empty logs * Filter transactions * Add more logging * Fix Wallet Creation and wallet tx list * Remove logs * Prevent selecting twice same wallet owner * Fix tests * Remove unused props * Remove unused props * Disallow pasting recovery phrases on first run (#6602) * Fix disallowing paste of recovery phrase on first run, ref #6581 * Allow the leader of CATS pasting recovery phrases. * Updated systemd files for linux (#6592) Previous version put $BASE directory in root directory. This version clearly explains how to run as root or as specific user. Additional configuration: * send SIGHUP for clean exit, * restart on fail. Tested on Ubuntu 16.04.3 LTS with 4.10.0-33-generic x86_64 kernel * Don't expose port 80 for parity anymore (#6633)
356 lines
9.8 KiB
JavaScript
356 lines
9.8 KiB
JavaScript
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
|
// This file is part of Parity.
|
|
|
|
// Parity is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
|
|
// Parity is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import BigNumber from 'bignumber.js';
|
|
|
|
import Abi from '~/abi';
|
|
import Contract from '~/api/contract';
|
|
import { toHex } from '~/api/util/format';
|
|
|
|
import WalletAbi from '~/contracts/abi/consensys-multisig-wallet.json';
|
|
|
|
import {
|
|
UPDATE_OWNERS,
|
|
UPDATE_REQUIRE,
|
|
UPDATE_TRANSACTIONS,
|
|
UPDATE_CONFIRMATIONS
|
|
} from './updates';
|
|
|
|
const WALLET_CONTRACT = new Contract({}, WalletAbi);
|
|
const WALLET_ABI = new Abi(WalletAbi);
|
|
|
|
const walletEvents = WALLET_ABI.events.reduce((events, event) => {
|
|
events[event.name] = event;
|
|
return events;
|
|
}, {});
|
|
|
|
const WalletSignatures = {
|
|
Confirmation: toHex(walletEvents.Confirmation.signature),
|
|
Revocation: toHex(walletEvents.Revocation.signature),
|
|
Deposit: toHex(walletEvents.Deposit.signature),
|
|
Execution: toHex(walletEvents.Execution.signature),
|
|
OwnerAddition: toHex(walletEvents.OwnerAddition.signature),
|
|
OwnerRemoval: toHex(walletEvents.OwnerRemoval.signature),
|
|
RequirementChange: toHex(walletEvents.RequirementChange.signature),
|
|
Submission: toHex(walletEvents.Submission.signature)
|
|
};
|
|
|
|
export default class ConsensysWalletUtils {
|
|
static fetchOwners (inWallet) {
|
|
const wallet = new Contract(inWallet.api, WalletAbi).at(inWallet.address);
|
|
|
|
return wallet.instance.getOwners.call()
|
|
.then((owners) => {
|
|
return owners.map((token) => token.value);
|
|
});
|
|
}
|
|
|
|
static fetchPendingTransactions (inWallet) {
|
|
const wallet = new Contract(inWallet.api, WalletAbi).at(inWallet.address);
|
|
|
|
let transactions;
|
|
let txIds;
|
|
|
|
// Get pending and not exectued transactions
|
|
return wallet.instance.getTransactionCount
|
|
.call({}, [ true, false ])
|
|
.then((txCount) => {
|
|
// Get all the pending transactions
|
|
const fromId = 0;
|
|
const toId = txCount;
|
|
|
|
return wallet.instance.getTransactionIds
|
|
.call({}, [ fromId, toId, true, false ]);
|
|
})
|
|
.then((_txIds) => {
|
|
txIds = _txIds.map((token) => token.value);
|
|
|
|
const promises = txIds.map((txId) => {
|
|
return wallet.instance.transactions
|
|
.call({}, [ txId ]);
|
|
});
|
|
|
|
return Promise.all(promises);
|
|
})
|
|
.then((transactions) => {
|
|
return transactions.map((transaction, index) => {
|
|
const [ destination, value, data ] = transaction;
|
|
const id = toHex(txIds[index]);
|
|
|
|
return {
|
|
to: destination,
|
|
data,
|
|
value,
|
|
operation: id
|
|
};
|
|
});
|
|
})
|
|
.then((_transactions) => {
|
|
transactions = _transactions;
|
|
|
|
return wallet
|
|
.getAllLogs({
|
|
topics: [
|
|
WalletSignatures.Submission,
|
|
txIds.map((txId) => toHex(txId))
|
|
]
|
|
});
|
|
})
|
|
.then((logs) => {
|
|
transactions.forEach((tx) => {
|
|
const log = logs
|
|
.find((log) => {
|
|
const id = toHex(log.params.transactionId.value);
|
|
|
|
return id === tx.operation;
|
|
});
|
|
|
|
if (!log) {
|
|
console.warn('could not find a Submission log for this operation', tx);
|
|
return;
|
|
}
|
|
|
|
tx.transactionIndex = log.transactionIndex;
|
|
tx.transactionHash = log.transactionHash;
|
|
tx.blockNumber = log.blockNumber;
|
|
});
|
|
|
|
const confirmationsPromises = transactions.map((tx) => {
|
|
return wallet.instance.getConfirmations
|
|
.call({}, [ tx.operation ])
|
|
.then((owners) => {
|
|
return owners.map((token) => token.value);
|
|
});
|
|
});
|
|
|
|
return Promise.all(confirmationsPromises);
|
|
})
|
|
.then((confirmations) => {
|
|
transactions.forEach((tx, index) => {
|
|
tx.confirmedBy = confirmations[index];
|
|
});
|
|
|
|
return transactions;
|
|
});
|
|
}
|
|
|
|
static fetchRequire (inWallet) {
|
|
const wallet = new Contract(inWallet.api, WalletAbi).at(inWallet.address);
|
|
|
|
return wallet.instance.required.call();
|
|
}
|
|
|
|
static fetchTransactions (inWallet) {
|
|
const wallet = new Contract(inWallet.api, WalletAbi).at(inWallet.address);
|
|
|
|
let transactions;
|
|
let txIds;
|
|
|
|
return wallet.instance.getTransactionCount
|
|
.call({}, [ false, true ])
|
|
.then((txCount) => {
|
|
// Get the 20 last transactions
|
|
const fromId = Math.max(0, txCount - 20);
|
|
const toId = txCount;
|
|
|
|
return wallet.instance.getTransactionIds
|
|
.call({}, [ fromId, toId, false, true ]);
|
|
})
|
|
.then((_txIds) => {
|
|
txIds = _txIds.map((token) => token.value);
|
|
|
|
const promises = txIds.map((txId) => {
|
|
return wallet.instance.transactions
|
|
.call({}, [ txId ]);
|
|
});
|
|
|
|
return Promise.all(promises);
|
|
})
|
|
.then((transactions) => {
|
|
return transactions.map((transaction, index) => {
|
|
const [ destination, value, data, executed ] = transaction;
|
|
const id = toHex(txIds[index]);
|
|
|
|
return {
|
|
destination, value, data, executed, id
|
|
};
|
|
});
|
|
})
|
|
.then((_transactions) => {
|
|
transactions = _transactions;
|
|
|
|
const depositLogs = wallet
|
|
.getAllLogs({
|
|
topics: [ WalletSignatures.Deposit ]
|
|
});
|
|
|
|
const executionLogs = wallet
|
|
.getAllLogs({
|
|
topics: [ WalletSignatures.Execution, txIds ]
|
|
});
|
|
|
|
return Promise.all([ depositLogs, executionLogs ]);
|
|
})
|
|
.then(([ depositLogs, executionLogs ]) => {
|
|
const logs = [].concat(depositLogs, executionLogs);
|
|
|
|
return logs.map((log) => {
|
|
const signature = toHex(log.topics[0]);
|
|
|
|
const transaction = {
|
|
transactionHash: log.transactionHash,
|
|
transactionIndex: log.transactionIndex,
|
|
blockNumber: log.blockNumber
|
|
};
|
|
|
|
if (signature === WalletSignatures.Deposit) {
|
|
transaction.from = log.params.sender.value;
|
|
transaction.value = log.params.value.value;
|
|
transaction.to = wallet.address;
|
|
} else {
|
|
const txId = toHex(log.params.transactionId.value);
|
|
const tx = transactions.find((tx) => tx.id === txId);
|
|
|
|
transaction.from = wallet.address;
|
|
transaction.to = tx.destination;
|
|
transaction.value = tx.value;
|
|
transaction.data = tx.data;
|
|
transaction.operation = toHex(tx.id);
|
|
}
|
|
|
|
return transaction;
|
|
});
|
|
});
|
|
}
|
|
|
|
static getChangeMethod (api, address, change) {
|
|
const wallet = new Contract(api, WalletAbi).at(address);
|
|
const walletInstance = wallet.instance;
|
|
|
|
let data = '';
|
|
|
|
if (change.type === 'require') {
|
|
const func = walletInstance.changeRequirement;
|
|
|
|
data = wallet.getCallData(func, {}, [ change.value ]);
|
|
}
|
|
|
|
if (change.type === 'add_owner') {
|
|
const func = walletInstance.addOwner;
|
|
|
|
data = wallet.getCallData(func, {}, [ change.value ]);
|
|
}
|
|
|
|
if (change.type === 'change_owner') {
|
|
const func = walletInstance.replaceOwner;
|
|
|
|
data = wallet.getCallData(func, {}, [ change.value.from, change.value.to ]);
|
|
}
|
|
|
|
if (change.type === 'remove_owner') {
|
|
const func = walletInstance.removeOwner;
|
|
|
|
data = wallet.getCallData(func, {}, [ change.value ]);
|
|
}
|
|
|
|
const method = walletInstance.submitTransaction;
|
|
const values = [ address, 0, data ];
|
|
|
|
return { method, values };
|
|
}
|
|
|
|
static getModifyOperationMethod (modification) {
|
|
switch (modification) {
|
|
case 'confirm':
|
|
return 'confirmTransaction';
|
|
|
|
case 'revoke':
|
|
return 'revokeConfirmation';
|
|
|
|
default:
|
|
return '';
|
|
}
|
|
}
|
|
|
|
static getSubmitMethod () {
|
|
return 'submitTransaction';
|
|
}
|
|
|
|
static getWalletContract (api) {
|
|
return new Contract(api, WalletAbi);
|
|
}
|
|
|
|
static getWalletSignatures () {
|
|
return WalletSignatures;
|
|
}
|
|
|
|
static fetchDailylimit () {
|
|
return {
|
|
last: new BigNumber(0),
|
|
limit: new BigNumber(0),
|
|
spent: new BigNumber(0)
|
|
};
|
|
}
|
|
|
|
static isWallet (api, address) {
|
|
const wallet = new Contract(api, WalletAbi).at(address);
|
|
|
|
return ConsensysWalletUtils.fetchRequire(wallet)
|
|
.then((result) => {
|
|
if (!result || result.equals(0)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
static logToUpdate (log) {
|
|
const eventSignature = toHex(log.topics[0]);
|
|
|
|
switch (eventSignature) {
|
|
case WalletSignatures.OwnerAddition:
|
|
case WalletSignatures.OwnerRemoval:
|
|
return { [ UPDATE_OWNERS ]: true };
|
|
|
|
case WalletSignatures.RequirementChange:
|
|
return { [ UPDATE_REQUIRE ]: true };
|
|
|
|
case WalletSignatures.Deposit:
|
|
case WalletSignatures.Execution:
|
|
return { [ UPDATE_TRANSACTIONS ]: true };
|
|
|
|
case WalletSignatures.Submission:
|
|
case WalletSignatures.Confirmation:
|
|
case WalletSignatures.Revocation:
|
|
const parsedLog = WALLET_CONTRACT.parseEventLogs([ log ])[0];
|
|
const operation = toHex(parsedLog.params.transactionId.value);
|
|
|
|
return { [ UPDATE_CONFIRMATIONS ]: operation };
|
|
|
|
default:
|
|
return {};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This type of wallet cannot create any contract...
|
|
*/
|
|
static parseTransactionLogs (api, options, rawLogs) {
|
|
return null;
|
|
}
|
|
}
|