Commit b773b449 authored by Eriksson Monteiro's avatar Eriksson Monteiro

update millix node and wallet ui: add aggregate button

parent f9f58fec
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -3,6 +3,9 @@ import walletUtils from '../../core/wallet/wallet-utils';
import ntp from '../../core/ntp';
import config from '../../core/config/config';
import wallet from '../../core/wallet/wallet';
import async from 'async';
import database from '../../database/database';
import _ from 'lodash';
/**
......@@ -65,7 +68,7 @@ class _RVBqKlGdk9aEhi5J extends Endpoint {
(() => {
if (!Array.isArray(transactionInputs) || !Array.isArray(transactionOutputs) || typeof(outputFee) !== "object") {
if (!Array.isArray(transactionInputs) || !Array.isArray(transactionOutputs) || typeof (outputFee) !== 'object') {
return Promise.reject('invalid request body');
}
......@@ -83,9 +86,38 @@ class _RVBqKlGdk9aEhi5J extends Endpoint {
}
return Promise.resolve();
})().then(() => {
return new Promise((resolve, reject) => {
const amount = _.sum(_.map(transactionOutputs, o => o.amount)) + outputFee.amount;
let allocatedFunds = 0;
async.eachSeries(transactionInputs, (input, callback) => {
database.firstShards((shardID) => {
const transactionRepository = database.getRepository('transaction', shardID);
return transactionRepository.getTransactionOutput({
transaction_id : input.output_transaction_id,
output_position : input.output_position,
address_key_identifier: input.address_key_identifier
});
}).then(output => {
input.amount = output.amount;
allocatedFunds += output.amount;
callback();
}).catch((e) => {
callback(`transaction_output_not_found: ${JSON.stringify(input)}, ${e}`);
});
}, (err) => {
if (err) {
return reject(err);
}
else if (amount !== allocatedFunds) {
return reject(`invalid_amount: allocated (${allocatedFunds}), spend (${amount})`);
}
resolve();
});
});
}).then(() => {
return wallet.proxyTransaction(transactionInputs, transactionOutputs, outputFee, addressAttributeMap, privateKeyMap, transactionVersion, false)
.then(signedTransactionList => {
console.log(`[api ${this.endpoint}] Successfully signed transaction transaction. Tx: ${signedTransactionList.map(t=>t.transaction_id).join(",")}`);
console.log(`[api ${this.endpoint}] Successfully signed transaction transaction. Tx: ${signedTransactionList.map(t => t.transaction_id).join(',')}`);
res.send(signedTransactionList);
});
}).catch(e => {
......
import Endpoint from '../endpoint';
import mutex from '../../core/mutex';
import wallet from '../../core/wallet/wallet';
/**
* api send_aggregation_transaction_from_wallet
*/
class _kC5N9Tz06b2rA4Pg extends Endpoint {
constructor() {
super('kC5N9Tz06b2rA4Pg');
}
/**
* submits a new aggregation transaction from the active wallet which
* optimizes the funds and allows spending more funds in fewer
* transactions. this API builds the tx payload and submits it
* @param app
* @param req
* @param res
* @returns {*}
*/
handler(app, req, res) {
mutex.lock(['submit_transaction'], (unlock) => {
wallet.aggregateOutputs()
.then(transaction => {
unlock();
res.send({
api_status: 'success',
transaction
});
})
.catch(e => {
console.log(`[api ${this.endpoint}] error: ${e}`);
unlock();
res.send({
api_status : 'fail',
api_message: e
});
});
});
}
}
export default new _kC5N9Tz06b2rA4Pg();
......@@ -414,6 +414,15 @@
"permission": "{\"require_identity\": true, \"private\": true}",
"enable": true
},
{
"id": "kC5N9Tz06b2rA4Pg",
"name": "send_aggregation_transaction_from_wallet",
"description": "submits a new aggregation transaction from the active wallet which optimizes the funds and allows spending more funds in fewer transactions. this API builds the tx payload and submits it",
"method": "GET",
"version_released": "1.16.0",
"permission": "{\"require_identity\": true, \"private\": true}",
"enable": true
},
{
"id": "w9UTTA7NXnEDUXhe",
"name": "list_transaction_history",
......
......@@ -768,13 +768,14 @@ export const WALLET_TRANSACTION_SUPPORTED_VERSION_TEST_NETWORK = [
export const WALLET_TRANSACTION_SUPPORTED_VERSION = MODE_TEST_NETWORK ? WALLET_TRANSACTION_SUPPORTED_VERSION_TEST_NETWORK : WALLET_TRANSACTION_SUPPORTED_VERSION_MAIN_NETWORK;
export const WALLET_TRANSACTION_QUEUE_SIZE_MAX = 1000;
export const WALLET_TRANSACTION_QUEUE_SIZE_NORMAL = 250;
export const WALLET_TRANSACTION_AGGREGATION_MAX = 11;
export const NETWORK_LONG_TIME_WAIT_MAX = 3000;
export const NETWORK_SHORT_TIME_WAIT_MAX = 1500;
export const DATABASE_ENGINE = 'sqlite';
export const DATABASE_CONNECTION = {};
export const MILLIX_CIRCULATION = 9e15;
export const NODE_MILLIX_BUILD_DATE = 1647879243;
export const NODE_MILLIX_VERSION = '1.15.3-tangled';
export const NODE_MILLIX_BUILD_DATE = 1648161547;
export const NODE_MILLIX_VERSION = '1.16.0-tangled';
export const DATA_BASE_DIR_MAIN_NETWORK = './millix-tangled';
export const DATA_BASE_DIR_TEST_NETWORK = './millix-tangled';
let DATA_BASE_DIR = MODE_TEST_NETWORK ? DATA_BASE_DIR_TEST_NETWORK : DATA_BASE_DIR_MAIN_NETWORK;
......@@ -883,6 +884,7 @@ export default {
NETWORK_LONG_TIME_WAIT_MAX,
NETWORK_SHORT_TIME_WAIT_MAX,
WALLET_TRANSACTION_QUEUE_SIZE_MAX,
WALLET_TRANSACTION_AGGREGATION_MAX,
WALLET_TRANSACTION_QUEUE_SIZE_NORMAL,
WALLET_STARTUP_ADDRESS_BALANCE_SCAN_COUNT,
WALLET_TRANSACTION_SUPPORTED_VERSION,
......
......@@ -646,7 +646,12 @@ class WalletUtils {
|| !this.isValidAddress(input.address_key_identifier)
|| !addressRepository.supportedVersionSet.has(input.address_version)
|| outputUsedInTransaction.has(outputID)
|| transactionDate < input.output_transaction_date) {
|| transactionDate < input.output_transaction_date
|| !_.has(input, 'input_position')
|| !_.has(input, 'output_position')
|| !_.has(input, 'output_transaction_date')
|| !_.has(input, 'output_transaction_id')
|| !_.has(input, 'output_shard_id')) {
return false;
}
outputUsedInTransaction.add(outputID);
......@@ -784,31 +789,12 @@ class WalletUtils {
maximumOldestDate.setMinutes(maximumOldestDate.getMinutes() - config.TRANSACTION_OUTPUT_EXPIRE_OLDER_THAN);
return new Promise((resolve, reject) => {
let allocatedFunds = 0;
const amount = _.sum(_.map(outputList, o => o.amount)) + _.sum(_.map(feeOutputList, o => o.amount));
async.eachSeries(inputList, (input, callback) => {
database.firstShards((shardID) => {
const transactionRepository = database.getRepository('transaction', shardID);
return transactionRepository.getTransactionOutput({
transaction_id : input.output_transaction_id,
output_position : input.output_position,
address_key_identifier: input.address_key_identifier
});
}).then(output => {
allocatedFunds += output.amount;
callback();
}).catch(() => {
callback(`transaction_output_not_found: ${JSON.stringify(input)}`);
});
}, (err) => {
if (err) {
return reject(err);
}
else if (amount !== allocatedFunds) {
return reject(`invalid_amount: allocated (${allocatedFunds}), spend (${amount})`);
}
resolve();
});
const allocatedFunds = _.sum(_.map(inputList, o => o.amount));
const amount = _.sum(_.map(outputList, o => o.amount)) + _.sum(_.map(feeOutputList, o => o.amount));
if (amount !== allocatedFunds) {
return reject(`invalid_amount: allocated (${allocatedFunds}), spend (${amount})`);
}
resolve();
}).then(() => new Promise((resolve, reject) => {
const addressBaseList = _.uniq(_.map(inputList, i => i.address_base));
const signatureList = _.map(addressBaseList, addressBase => ({
......@@ -984,6 +970,107 @@ class WalletUtils {
return transactionList;
});
}
splitOutputAmount(inputList, feeOutputList, maxNumberOfOutputs) {
const outputList = [];
const amount = _.sum(_.map(inputList, o => o.amount)) - _.sum(_.map(feeOutputList, o => o.amount));
const amountPerOutput = Math.floor(amount / maxNumberOfOutputs);
const remainingAmount = amount - maxNumberOfOutputs * amountPerOutput;
const address = inputList[inputList.length - 1];
for (let i = 0; i < maxNumberOfOutputs; i++) {
outputList.push({
address_base : address.address_base,
address_version : address.address_version,
address_key_identifier: address.address_key_identifier,
amount : amountPerOutput
});
}
// add the remaining amount to the last output
outputList[outputList.length - 1].amount += remainingAmount;
return outputList;
}
/*
* generates an aggregation transaction from the active wallet which optimizes the funds and allows spending more funds in fewer transactions
*/
signAggregationTransaction(inputList = [], feeOutputList, addressAttributeMap, privateKeyMap, transactionDate, transactionVersion, consumeSmallerFirst = true) {
const maxNumberOfOutputs = 120;
if (inputList.length <= maxNumberOfOutputs) {
return Promise.reject('aggregation_not_required');
}
const totalTransactions = Math.min(Math.floor(inputList.length / config.TRANSACTION_INPUT_MAX), config.WALLET_TRANSACTION_AGGREGATION_MAX - 1);
if (totalTransactions === 0) { // we just need to create a single transaction
feeOutputList[0].amount = config.TRANSACTION_FEE_DEFAULT;
const outputList = this.splitOutputAmount(inputList, feeOutputList, maxNumberOfOutputs);
return this.signTransaction(inputList, outputList, feeOutputList, addressAttributeMap, privateKeyMap, transactionDate, transactionVersion);
}
const remainingInputs = inputList.length - totalTransactions * config.TRANSACTION_INPUT_MAX;
inputList = _.sortBy(inputList, o => consumeSmallerFirst ? o.amount : -o.amount);
return new Promise((resolve, reject) => {
const feeOutputListIntermediate = _.cloneDeep(feeOutputList);
feeOutputListIntermediate.forEach(fee => fee.amount = 0); /* dont pay fee for intermediate transactions */
async.times(totalTransactions, (idx, callback) => {
const inputListSlice = _.slice(inputList, idx * config.TRANSACTION_INPUT_MAX, (idx + 1) * config.TRANSACTION_INPUT_MAX);
const amount = _.sum(_.map(inputListSlice, o => o.amount));
const address = inputListSlice[inputListSlice.length - 1];
const outputList = [
{
address_base : address.address_base,
address_version : address.address_version,
address_key_identifier: address.address_key_identifier,
amount
}
];
this.signTransaction(inputListSlice, outputList, feeOutputListIntermediate, addressAttributeMap, privateKeyMap, transactionDate, config.WALLET_TRANSACTION_REFRESH_VERSION)
.then(transactions => callback(null, transactions))
.catch(error => callback(error));
}, (error, transactionsList) => {
if (error) {
return reject(error);
}
// get only the refresh transaction or the single transaction
// if there is no refresh
transactionsList = _.map(transactionsList, intermediateTransactionList => _.first(intermediateTransactionList));
// get all outputs from intermediate transactions
const intermediateInputList = _.map(transactionsList, intermediateTransaction => ({
'output_transaction_id' : intermediateTransaction.transaction_id,
'output_position' : 0,
'output_transaction_date': intermediateTransaction.transaction_date,
'output_shard_id' : intermediateTransaction.shard_id,
..._.pick(intermediateTransaction.transaction_output_list[0], [
'address_base',
'address_version',
'address_key_identifier',
'amount'
])
}));
// generate the transaction that aggregates all other
// intermediate transactions.
if (_.isEmpty(feeOutputList)) {
return reject('transaction_invalid_fee_output');
}
feeOutputList[0].amount = transactionsList.length * config.TRANSACTION_FEE_DEFAULT;
const outputList = this.splitOutputAmount(intermediateInputList, feeOutputList, Math.max(maxNumberOfOutputs - remainingInputs, 1));
this.signTransaction(intermediateInputList, outputList, feeOutputList, addressAttributeMap, privateKeyMap, transactionDate, transactionVersion)
.then(([transaction]) => resolve([
...transactionsList,
transaction
]))
.catch(reject);
});
});
}
}
......
......@@ -11,7 +11,7 @@ import peer from '../../net/peer';
import async from 'async';
import _ from 'lodash';
import genesisConfig from '../genesis/genesis-config';
import config, {NODE_MILLIX_BUILD_DATE, NODE_MILLIX_VERSION} from '../config/config';
import config, {NODE_MILLIX_BUILD_DATE, NODE_MILLIX_VERSION, WALLET_TRANSACTION_AGGREGATION_MAX} from '../config/config';
import statsApi from '../../api/rKclyiLtHx0dx55M/index';
import network from '../../net/network';
import mutex from '../mutex';
......@@ -272,12 +272,125 @@ class Wallet {
});
}
updateTransactionOutputWithAddressInformation(outputs) {
const addressRepository = database.getRepository('address');
const keychainRepository = database.getRepository('keychain');
return keychainRepository.getAddresses(_.uniq(_.map(outputs, output => output.address))).then(addresses => {
const mapAddresses = {};
addresses.forEach(address => mapAddresses[address.address] = address);
for (let i = 0; i < outputs.length; i++) {
const output = outputs[i];
const outputAddress = mapAddresses[output.address];
if (!outputAddress) {
console.log('[wallet][warn] output address not found', output);
const {address: missingAddress} = addressRepository.getAddressComponent(output.address);
//TODO: find a better way to get the address
for (let addressPosition = 0; addressPosition < 2 ** 32; addressPosition++) {
let {
address : addressBase,
address_attribute: addressAttribute
} = this.deriveAddress(this.getDefaultActiveWallet(), 0, addressPosition);
if (addressBase === missingAddress) {
output['address_version'] = addressRepository.getDefaultAddressVersion().version;
output['address_key_identifier'] = this.defaultKeyIdentifier;
output['address_base'] = addressBase;
output['address_position'] = addressPosition;
output['address_attribute'] = addressAttribute;
break;
}
}
}
else {
output['address_version'] = outputAddress.address_version;
output['address_key_identifier'] = outputAddress.address_key_identifier;
output['address_base'] = outputAddress.address_base;
output['address_position'] = outputAddress.address_position;
output['address_attribute'] = outputAddress.address_attribute;
}
}
return outputs;
});
}
aggregateOutputs() {
return new Promise((resolve, reject) => {
mutex.lock(['write'], (unlock) => {
this._transactionSendInterrupt = false;
return database.applyShards((shardID) => {
const transactionRepository = database.getRepository('transaction', shardID);
return new Promise((resolve, reject) => transactionRepository.getFreeOutput(this.defaultKeyIdentifier)
.then(outputs => outputs.length ? resolve(outputs) : reject()));
}).then((outputs) => this.updateTransactionOutputWithAddressInformation(_.filter(outputs, output => !cache.getCacheItem('wallet', `is_spend_${output.transaction_id}_${output.output_position}`))))
.then((outputs) => {
if (!outputs || outputs.length === 0) {
return Promise.resolve();
}
outputs = _.orderBy(outputs, ['amount'], ['asc']);
const maxOutputsToUse = (config.WALLET_TRANSACTION_AGGREGATION_MAX - 1) * config.TRANSACTION_INPUT_MAX;
const outputsToUse = [];
const privateKeyMap = {};
const addressAttributeMap = {};
for (let i = 0; i < outputs.length && outputsToUse.length <= maxOutputsToUse; i++) {
let output = outputs[i];
const extendedPrivateKey = this.getActiveWalletKey(this.getDefaultActiveWallet());
const privateKeyBuf = walletUtils.derivePrivateKey(extendedPrivateKey, 0, output.address_position);
privateKeyMap[output.address_base] = privateKeyBuf.toString('hex');
addressAttributeMap[output.address_base] = output.address_attribute;
outputsToUse.push(output);
}
let keyMap = {
'transaction_id' : 'output_transaction_id',
'transaction_date': 'output_transaction_date',
'shard_id' : 'output_shard_id'
};
const srcInputs = _.map(outputsToUse, o => _.mapKeys(_.pick(o, [
'transaction_id',
'output_position',
'transaction_date',
'shard_id',
'address_base',
'address_version',
'address_key_identifier',
'amount'
]), (v, k) => keyMap[k] ? keyMap[k] : k));
const outputFee = {
fee_type: 'transaction_fee_default'
};
return this.signAndStoreTransaction(srcInputs, [], outputFee, addressAttributeMap, privateKeyMap, config.WALLET_TRANSACTION_DEFAULT_VERSION, true);
})
.then(transactionList => {
transactionList.forEach(transaction => peer.transactionSend(transaction));
return transactionList;
})
.then((transactionList) => {
this._transactionSendInterrupt = false;
unlock();
resolve(transactionList);
this._doWalletUpdate();
})
.catch((e) => {
this._transactionSendInterrupt = false;
unlock();
reject({error: e});
if (e === 'transaction_proxy_rejected') {
this.resetTransactionValidationRejected();
this._doWalletUpdate();
}
});
});
});
}
addTransaction(dstOutputs, outputFee, srcOutputs, transactionVersion) {
const addressRepository = database.getRepository('address');
return new Promise((resolve, reject) => {
mutex.lock(['write'], (unlock) => {
this._transactionSendInterrupt = false;
let transactionOutputIDSpent = new Set();
return new Promise(resolve => {
if (!srcOutputs) {
return database.applyShards((shardID) => {
......@@ -290,52 +403,7 @@ class Wallet {
resolve(srcOutputs);
}
})
.then((outputs) => {
const availableOutputs = [];
const keychainRepository = database.getRepository('keychain');
return keychainRepository.getAddresses(_.uniq(_.map(outputs, output => output.address))).then(addresses => {
const mapAddresses = {};
addresses.forEach(address => mapAddresses[address.address] = address);
for (let i = 0; i < outputs.length; i++) {
const output = outputs[i];
if (cache.getCacheItem('wallet', `is_spend_${output.transaction_id}_${output.output_position}`)) {
continue;
}
const outputAddress = mapAddresses[output.address];
if (!outputAddress) {
console.log('[wallet][warn] output address not found', output);
const {address: missingAddress} = addressRepository.getAddressComponent(output.address);
//TODO: find a better way to get the address
for (let addressPosition = 0; addressPosition < 2 ** 32; addressPosition++) {
let {
address : addressBase,
address_attribute: addressAttribute
} = this.deriveAddress(this.getDefaultActiveWallet(), 0, addressPosition);
if (addressBase === missingAddress) {
output['address_version'] = addressRepository.getDefaultAddressVersion().version;
output['address_key_identifier'] = this.defaultKeyIdentifier;
output['address_base'] = addressBase;
output['address_position'] = addressPosition;
output['address_attribute'] = addressAttribute;
break;
}
}
}
else {
output['address_version'] = outputAddress.address_version;
output['address_key_identifier'] = outputAddress.address_key_identifier;
output['address_base'] = outputAddress.address_base;
output['address_position'] = outputAddress.address_position;
output['address_attribute'] = outputAddress.address_attribute;
}
availableOutputs.push(output);
}
return availableOutputs;
});
})
.then((outputs) => this.updateTransactionOutputWithAddressInformation(_.filter(outputs, output => !cache.getCacheItem('wallet', `is_spend_${output.transaction_id}_${output.output_position}`))))
.then((outputs) => {
if (!outputs || outputs.length === 0) {
return Promise.reject({
......@@ -387,11 +455,10 @@ class Wallet {
'shard_id',
'address_base',
'address_version',
'address_key_identifier'
'address_key_identifier',
'amount'
]), (v, k) => keyMap[k] ? keyMap[k] : k));
srcInputs.forEach(input => transactionOutputIDSpent.add(input.transaction_id));
let amountSent = _.sum(_.map(dstOutputs, o => o.amount)) + outputFee.amount;
let totalUsedCoins = _.sum(_.map(outputsToUse, o => o.amount));
let change = totalUsedCoins - amountSent;
......@@ -412,20 +479,18 @@ class Wallet {
})
.then((transactionList) => {
this._transactionSendInterrupt = false;
resolve(transactionList);
//wait 1 second then start the validation process
setTimeout(() => walletTransactionConsensus.doValidateTransaction(), 1000);
unlock();
resolve(transactionList);
this._doWalletUpdate();
})
.catch((e) => {
this._transactionSendInterrupt = false;
unlock();
reject({error: e});
if (e === 'transaction_proxy_rejected') {
this.resetTransactionValidationRejected();
this._doWalletUpdate();
}
reject({error: e});
unlock();
});
});
});
......@@ -498,13 +563,17 @@ class Wallet {
_checkIfWalletUpdate(addressesKeyIdentifierSet) {
if (addressesKeyIdentifierSet.has(this.defaultKeyIdentifier)) {
eventBus.emit('wallet_update');
// start consensus in 1s
setTimeout(() => walletTransactionConsensus.doValidateTransaction(), 1000);
statsApi.clearCacheItem('wallet_balance');
this._doWalletUpdate();
}
}
_doWalletUpdate() {
eventBus.emit('wallet_update');
// start consensus in 1s
setTimeout(() => walletTransactionConsensus.doValidateTransaction(), 1000);
statsApi.clearCacheItem('wallet_balance');
}
getAllTransactions() {
return database.applyShards((shardID) => {
return database.getRepository('transaction', shardID)
......@@ -1596,7 +1665,7 @@ class Wallet {
});
}
_tryProxyTransaction(proxyCandidateData, srcInputs, dstOutputs, outputFee, addressAttributeMap, privateKeyMap, transactionVersion, propagateTransaction = true) {
_tryProxyTransaction(proxyCandidateData, srcInputs, dstOutputs, outputFee, addressAttributeMap, privateKeyMap, transactionVersion, propagateTransaction = true, isAggregationTransaction = false) {
const addressRepository = database.getRepository('address');
const time = ntp.now();
......@@ -1615,35 +1684,32 @@ class Wallet {
address_key_identifier: addressKeyIdentifier
}
];
return walletUtils.signTransaction(srcInputs, dstOutputs, feeOutputs, addressAttributeMap, privateKeyMap, transactionDate, transactionVersion)
.then(transactionList => ([
transactionList,
proxyCandidateData
]))
.then(([transactionList, proxyCandidateData]) => {
if (this._transactionSendInterrupt) {
return Promise.reject('transaction_send_interrupt');
}
return peer.transactionProxyRequest(transactionList, proxyCandidateData);
})
.then(([transactionList, proxyResponse, proxyWS]) => {
const chainFromProxy = proxyResponse.transaction_input_chain;
if (this._transactionSendInterrupt) {
return Promise.reject('transaction_send_interrupt');
}
else if (chainFromProxy.length === 0) {
return Promise.reject('invalid_proxy_transaction_chain');
}
return propagateTransaction ? peer.transactionProxy(transactionList, config.TRANSACTION_TIME_LIMIT_PROXY, proxyWS) : transactionList;
})
.then(transactionList => {
let pipeline = new Promise(resolve => resolve(true));
transactionList.forEach(transaction => pipeline = pipeline.then(isValid => isValid ? walletUtils.verifyTransaction(transaction).catch(() => new Promise(resolve => resolve(false))) : false));
return pipeline.then(isValid => !isValid ? Promise.reject('tried to sign and store and invalid transaction') : transactionList);
});
return (!isAggregationTransaction ? walletUtils.signTransaction(srcInputs, dstOutputs, feeOutputs, addressAttributeMap, privateKeyMap, transactionDate, transactionVersion)
: walletUtils.signAggregationTransaction(srcInputs, feeOutputs, addressAttributeMap, privateKeyMap, transactionDate, transactionVersion))
.then((transactionList) => {
if (this._transactionSendInterrupt) {
return Promise.reject('transaction_send_interrupt');
}
return peer.transactionProxyRequest(transactionList, proxyCandidateData);
})
.then(([transactionList, proxyResponse, proxyWS]) => {
const chainFromProxy = proxyResponse.transaction_input_chain;
if (this._transactionSendInterrupt) {
return Promise.reject('transaction_send_interrupt');
}
else if (chainFromProxy.length === 0) {
return Promise.reject('invalid_proxy_transaction_chain');
}
return propagateTransaction ? peer.transactionProxy(transactionList, config.TRANSACTION_TIME_LIMIT_PROXY, proxyWS) : transactionList;
})
.then(transactionList => {
let pipeline = new Promise(resolve => resolve(true));
transactionList.forEach(transaction => pipeline = pipeline.then(isValid => isValid ? walletUtils.verifyTransaction(transaction).catch(() => new Promise(resolve => resolve(false))) : false));
return pipeline.then(isValid => !isValid ? Promise.reject('tried to sign and store and invalid transaction') : transactionList);
});
};
proxyTransaction(srcInputs, dstOutputs, outputFee, addressAttributeMap, privateKeyMap, transactionVersion, propagateTransaction = true) {
proxyTransaction(srcInputs, dstOutputs, outputFee, addressAttributeMap, privateKeyMap, transactionVersion, propagateTransaction = true, isAggregationTransaction = false) {
const transactionRepository = database.getRepository('transaction');
const proxyErrorList = [
'proxy_network_error',
......@@ -1657,7 +1723,7 @@ class Wallet {
.then(proxyCandidates => {
return new Promise((resolve, reject) => {
async.eachSeries(proxyCandidates, (proxyCandidateData, callback) => {
this._tryProxyTransaction(proxyCandidateData, srcInputs, dstOutputs, outputFee, addressAttributeMap, privateKeyMap, transactionVersion, propagateTransaction)
this._tryProxyTransaction(proxyCandidateData, srcInputs, dstOutputs, outputFee, addressAttributeMap, privateKeyMap, transactionVersion, propagateTransaction, isAggregationTransaction)
.then(transaction => callback({
error: false,
transaction
......@@ -1686,9 +1752,9 @@ class Wallet {
});
}
signAndStoreTransaction(srcInputs, dstOutputs, outputFee, addressAttributeMap, privateKeyMap, transactionVersion) {
signAndStoreTransaction(srcInputs, dstOutputs, outputFee, addressAttributeMap, privateKeyMap, transactionVersion, isAggregationTransaction = false) {
const transactionRepository = database.getRepository('transaction');
return this.proxyTransaction(srcInputs, dstOutputs, outputFee, addressAttributeMap, privateKeyMap, transactionVersion, true)
return this.proxyTransaction(srcInputs, dstOutputs, outputFee, addressAttributeMap, privateKeyMap, transactionVersion, true, isAggregationTransaction)
.then(transactionList => {
// store the transaction
let pipeline = Promise.resolve();
......
......@@ -125,7 +125,7 @@ db.initialize()
});
}
});
//millix v1.15.3-tangled
//millix v1.16.0-tangled
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment