Commit 4af3dde5 authored by Eriksson Monteiro's avatar Eriksson Monteiro

update millix wallet: fix wallet balance

parent 2da59a39
...@@ -26,6 +26,15 @@ class _rKclyiLtHx0dx55M extends Endpoint { ...@@ -26,6 +26,15 @@ class _rKclyiLtHx0dx55M extends Endpoint {
}); });
} }
clearCache() {
cache.removeCacheItem('api_stats', 'wallet_balance_stable');
cache.removeCacheItem('api_stats', 'wallet_balance_unstable');
cache.removeCacheItem('api_stats', 'count_wallet_total');
cache.removeCacheItem('api_stats', 'count_wallet_unstable');
cache.removeCacheItem('api_stats', 'count_unstable');
cache.removeCacheItem('api_stats', 'count_all');
}
/** /**
* returns the node stat summary * returns the node stat summary
* @param app * @param app
......
...@@ -772,7 +772,7 @@ export const DATABASE_ENGINE = 'sqlite'; ...@@ -772,7 +772,7 @@ export const DATABASE_ENGINE = 'sqlite';
export const DATABASE_CONNECTION = {}; export const DATABASE_CONNECTION = {};
export const MILLIX_CIRCULATION = 9e15; export const MILLIX_CIRCULATION = 9e15;
export const NODE_MILLIX_BUILD_DATE = 1640009160; export const NODE_MILLIX_BUILD_DATE = 1640009160;
export const NODE_MILLIX_VERSION = '1.14.6-tangled'; export const NODE_MILLIX_VERSION = '1.14.7-tangled';
export const DATA_BASE_DIR_MAIN_NETWORK = './millix-tangled'; export const DATA_BASE_DIR_MAIN_NETWORK = './millix-tangled';
export const DATA_BASE_DIR_TEST_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; let DATA_BASE_DIR = MODE_TEST_NETWORK ? DATA_BASE_DIR_TEST_NETWORK : DATA_BASE_DIR_MAIN_NETWORK;
......
...@@ -838,6 +838,39 @@ export class WalletTransactionConsensus { ...@@ -838,6 +838,39 @@ export class WalletTransactionConsensus {
console.log('[wallet-transaction-consensus] the transaction ', transactionID, ' was not validated (due to double spend) during consensus round number ', consensusData.consensus_round_count); console.log('[wallet-transaction-consensus] the transaction ', transactionID, ' was not validated (due to double spend) during consensus round number ', consensusData.consensus_round_count);
return database.applyShardZeroAndShardRepository('transaction', transaction.shard_id, transactionRepository => { return database.applyShardZeroAndShardRepository('transaction', transaction.shard_id, transactionRepository => {
return transactionRepository.updateTransactionAsDoubleSpend(transaction.transaction_id, data.transaction_input_double_spend /*double spend input*/); return transactionRepository.updateTransactionAsDoubleSpend(transaction.transaction_id, data.transaction_input_double_spend /*double spend input*/);
}).then(() => {
return new Promise(resolve => {
async.eachSeries(transaction.transaction_input_list, (input, callback) => {
if(this._transactionValidationRejected[input.output_transaction_id]) {
return callback();
}
database.applyShards(shardID => {
const transactionRepository = database.getRepository('transaction', shardID);
return transactionRepository.listTransactionSpendingOutput(input.output_transaction_id, input.output_position);
}).then(transactionSpendingOutputList => {
let isDoubleSpend = false;
for (let transactionSpendingOutput of transactionSpendingOutputList) {
if (transactionSpendingOutput.transaction_id !== transaction.transaction_id &&
transactionSpendingOutput.status !== 3 && (transactionSpendingOutput.is_stable === 0 || transactionSpendingOutput.is_double_spend === 0)) {
isDoubleSpend = true;
break;
}
}
if (!isDoubleSpend) {
database.applyShardZeroAndShardRepository('transaction', input.output_shard_id,
transactionRepository => transactionRepository.resetTransaction(input.output_transaction_id, input.output_shard_id))
.then(() => callback())
.catch(() => callback());
}
else {
callback();
}
});
}, () => resolve());
});
}).then(() => wallet._checkIfWalletUpdate(new Set(_.map(transaction.transaction_output_list, o => o.address_key_identifier)))) }).then(() => wallet._checkIfWalletUpdate(new Set(_.map(transaction.transaction_output_list, o => o.address_key_identifier))))
.then(() => { .then(() => {
consensusData.resolve(); consensusData.resolve();
...@@ -871,7 +904,6 @@ export class WalletTransactionConsensus { ...@@ -871,7 +904,6 @@ export class WalletTransactionConsensus {
consensusData.active = false; consensusData.active = false;
console.log('[wallet-transaction-consensus] the transaction ', transactionID, ' was not validated (due to not invalid tx) during consensus round number ', consensusData.consensus_round_count); console.log('[wallet-transaction-consensus] the transaction ', transactionID, ' was not validated (due to not invalid tx) during consensus round number ', consensusData.consensus_round_count);
this._transactionValidationRejected.add(transactionID); this._transactionValidationRejected.add(transactionID);
this._transactionRetryValidation[transactionID] = Date.now();
database.applyShards((shardID) => { database.applyShards((shardID) => {
return database.getRepository('transaction', shardID) return database.getRepository('transaction', shardID)
.invalidateTransaction(transactionID); .invalidateTransaction(transactionID);
...@@ -984,6 +1016,22 @@ export class WalletTransactionConsensus { ...@@ -984,6 +1016,22 @@ export class WalletTransactionConsensus {
}); });
}, 'transaction_date').then(pendingTransactions => { }, 'transaction_date').then(pendingTransactions => {
if (pendingTransactions.length === 0) { if (pendingTransactions.length === 0) {
if(!cache.getCacheItem('wallet-consensus', 'update_transaction_rejected')) {
cache.setCacheItem('wallet-consensus', 'update_transaction_rejected', false, 60000);
database.applyShards(shardID => {
const transactionRepository = database.getRepository('transaction', shardID);
return transactionRepository.listTransactionOutput({
address_key_identifier : wallet.defaultKeyIdentifier,
is_double_spend : 0,
'transaction_output.is_stable' : 0,
is_spent : 0
});
}).then(unstableTransactionList => {
unstableTransactionList.forEach(transaction => this._transactionValidationRejected.delete(transaction.transaction_id));
})
}
return database.applyShards((shardID) => { return database.applyShards((shardID) => {
return database.getRepository('transaction', shardID) return database.getRepository('transaction', shardID)
.findUnstableTransaction(excludeTransactionList); .findUnstableTransaction(excludeTransactionList);
...@@ -993,6 +1041,7 @@ export class WalletTransactionConsensus { ...@@ -993,6 +1041,7 @@ export class WalletTransactionConsensus {
]); ]);
} }
else { else {
cache.setCacheItem('wallet-consensus', 'transaction_rejected_updated', true);
return [ return [
pendingTransactions, pendingTransactions,
true true
...@@ -1039,15 +1088,7 @@ export class WalletTransactionConsensus { ...@@ -1039,15 +1088,7 @@ export class WalletTransactionConsensus {
pendingTransaction.transaction_date = new Date(pendingTransaction.transaction_date); pendingTransaction.transaction_date = new Date(pendingTransaction.transaction_date);
} }
return (() => { return pendingTransaction;
if (unstableDateStart.getTime() < pendingTransaction.transaction_date.getTime()) { // if not hibernated yet, we try to do a local validation first
return this._validateTransaction(transactionID, null, 0)
.catch(() => Promise.resolve());
}
else {
return Promise.resolve();
}
})().then(() => pendingTransaction);
}).then(pendingTransaction => { }).then(pendingTransaction => {
console.log('[wallet-transaction-consensus] transaction validated internally, starting consensus using oracles'); console.log('[wallet-transaction-consensus] transaction validated internally, starting consensus using oracles');
// replace lock id with transaction id // replace lock id with transaction id
......
...@@ -12,6 +12,7 @@ import async from 'async'; ...@@ -12,6 +12,7 @@ import async from 'async';
import _ from 'lodash'; import _ from 'lodash';
import genesisConfig from '../genesis/genesis-config'; 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} from '../config/config';
import statsApi from '../../api/rKclyiLtHx0dx55M/index';
import network from '../../net/network'; import network from '../../net/network';
import mutex from '../mutex'; import mutex from '../mutex';
import ntp from '../ntp'; import ntp from '../ntp';
...@@ -290,14 +291,14 @@ class Wallet { ...@@ -290,14 +291,14 @@ class Wallet {
} }
}) })
.then((outputs) => { .then((outputs) => {
const availableOutputs = []; const availableOutputs = [];
const keychainRepository = database.getRepository('keychain'); const keychainRepository = database.getRepository('keychain');
return keychainRepository.getAddresses(_.uniq(_.map(outputs, output => output.address))).then(addresses => { return keychainRepository.getAddresses(_.uniq(_.map(outputs, output => output.address))).then(addresses => {
const mapAddresses = {}; const mapAddresses = {};
addresses.forEach(address => mapAddresses[address.address] = address); addresses.forEach(address => mapAddresses[address.address] = address);
for (let i = 0; i < outputs.length; i++) { for (let i = 0; i < outputs.length; i++) {
const output = outputs[i]; const output = outputs[i];
if (cache.getCacheItem('wallet', `is_spend_${output.transaction_id}_${output.output_position}`)) { if (cache.getCacheItem('wallet', `is_spend_${output.transaction_id}_${output.output_position}`)) {
continue; continue;
...@@ -615,20 +616,22 @@ class Wallet { ...@@ -615,20 +616,22 @@ class Wallet {
walletTransactionConsensus.resetTransactionValidationRejected(); walletTransactionConsensus.resetTransactionValidationRejected();
database.applyShards(shardID => { database.applyShards(shardID => {
const transactionRepository = database.getRepository('transaction', shardID); const transactionRepository = database.getRepository('transaction', shardID);
return transactionRepository.listTransactionWithFreeOutput(this.defaultKeyIdentifier, true) return transactionRepository.listWalletTransactionOutputNotSpent(this.defaultKeyIdentifier)
.then(transactions => new Promise(resolve => { .then(transactions => new Promise(resolve => {
async.eachSeries(transactions, (transaction, callback) => { async.eachSeries(transactions, (transaction, callback) => {
walletTransactionConsensus.removeFromRejectedTransactions(transaction.transaction_id);
walletTransactionConsensus.removeFromRetryTransactions(transaction.transaction_id);
transactionRepository.resetTransaction(transaction.transaction_id) transactionRepository.resetTransaction(transaction.transaction_id)
.then(() => callback()) .then(() => callback())
.catch(() => callback()); .catch(() => callback());
}, () => resolve(new Set(_.map(transactions, t => t.transaction_id)))); }, () => resolve(new Set(_.map(transactions, t => t.transaction_id))));
})) }))
.then(rootTransactions => this.resetValidation(rootTransactions, shardID)) .then(rootTransactions => this.resetValidation(rootTransactions, shardID));
.then(result => result ? resolve(result) : reject());
}).then(_ => _); }).then(_ => _);
} }
resetValidation(rootTransactions, shardID) { resetValidation(rootTransactions, shardID) {
statsApi.clearCache();
const transactionRepository = database.getRepository('transaction', shardID); const transactionRepository = database.getRepository('transaction', shardID);
return new Promise((resolve) => { return new Promise((resolve) => {
const dfs = (transactions, visited = new Set()) => { const dfs = (transactions, visited = new Set()) => {
......
...@@ -1387,6 +1387,61 @@ export default class Transaction { ...@@ -1387,6 +1387,61 @@ export default class Transaction {
}); });
} }
listWalletTransactionOutputNotSpent(addressKeyIdentifier) {
return new Promise((resolve, reject) => {
const sql = `SELECT *
FROM transaction_output
WHERE address_key_identifier = ?1
AND transaction_id NOT IN (SELECT o.transaction_id
FROM transaction_output o
INNER JOIN transaction_input i
ON o.transaction_id = i.output_transaction_id AND
o.output_position = i.output_position
INNER JOIN transaction_output o2
ON o2.transaction_id = i.transaction_id AND
o2.is_double_spend = 0 AND
o2.status != 3
WHERE o.address_key_identifier = ?1
UNION SELECT o.transaction_id
FROM transaction_output o
INNER JOIN shard_zero.transaction_input i
ON o.transaction_id = i.output_transaction_id AND
o.output_position = i.output_position
INNER JOIN shard_zero.transaction_output o2
ON o2.transaction_id = i.transaction_id AND
o2.is_double_spend = 0 AND
o2.status != 3
WHERE o.address_key_identifier = ?1
UNION SELECT o.transaction_id
FROM shard_zero.transaction_output o
INNER JOIN transaction_input i
ON o.transaction_id = i.output_transaction_id AND
o.output_position = i.output_position
INNER JOIN transaction_output o2
ON o2.transaction_id = i.transaction_id AND
o2.is_double_spend = 0 AND
o2.status != 3
WHERE o.address_key_identifier = ?1
UNION SELECT o.transaction_id
FROM shard_zero.transaction_output o
INNER JOIN shard_zero.transaction_input i
ON o.transaction_id = i.output_transaction_id AND
o.output_position = i.output_position
INNER JOIN shard_zero.transaction_output o2
ON o2.transaction_id = i.transaction_id AND
o2.is_double_spend = 0 AND
o2.status != 3
WHERE o.address_key_identifier = ?1)`;
this.database.all(sql,
[addressKeyIdentifier], (err, rows) => {
if (err) {
return reject(err);
}
resolve(rows);
});
});
}
getTransactionInput(where) { getTransactionInput(where) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let { let {
...@@ -1673,21 +1728,6 @@ export default class Transaction { ...@@ -1673,21 +1728,6 @@ export default class Transaction {
is_stable = 1, is_stable = 1,
stable_date = CAST(strftime('%s', 'now') AS INTEGER) stable_date = CAST(strftime('%s', 'now') AS INTEGER)
WHERE transaction_id = "${transactionID}"; WHERE transaction_id = "${transactionID}";
UPDATE transaction_output AS o
SET is_spent = EXISTS (
SELECT i.output_transaction_id FROM transaction_input i
INNER JOIN transaction_output o2 ON i.transaction_id = o2.transaction_id
WHERE i.output_transaction_id = o.transaction_id AND i.output_position = o.output_position AND
o2.status != 3 AND o2.is_double_spend = 0
), spent_date = (
SELECT t.transaction_date FROM \`transaction\` t
INNER JOIN transaction_input i ON i.transaction_id = t.transaction_id
INNER JOIN transaction_output o2 ON i.transaction_id = o2.transaction_id
WHERE i.output_transaction_id = o.transaction_id AND i.output_position = o.output_position AND
o2.status != 3 and o2.is_double_spend = 0
)
WHERE transaction_id IN (SELECT output_transaction_id FROM transaction_input WHERE transaction_id = "${transactionID}");
`, err => { `, err => {
if (err) { if (err) {
console.log(err); console.log(err);
...@@ -1945,7 +1985,8 @@ export default class Transaction { ...@@ -1945,7 +1985,8 @@ export default class Transaction {
listTransactionSpendingOutput(transactionID, outputPosition) { listTransactionSpendingOutput(transactionID, outputPosition) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.database.all('SELECT * FROM `transaction` INNER JOIN transaction_input ON `transaction`.transaction_id = transaction_input.transaction_id WHERE output_transaction_id = ? AND output_position = ?', this.database.all('SELECT t.*, o.is_double_spend, o.is_spent FROM `transaction` t INNER JOIN transaction_input i ON t.transaction_id = i.transaction_id INNER JOIN transaction_output o ON o.transaction_id = t.transaction_id '+
'WHERE i.output_transaction_id = ? AND i.output_position = ?',
[ [
transactionID, transactionID,
outputPosition outputPosition
...@@ -2016,21 +2057,21 @@ export default class Transaction { ...@@ -2016,21 +2057,21 @@ export default class Transaction {
WHERE i.output_transaction_id = o.transaction_id AND i.output_position = o.output_position AND WHERE i.output_transaction_id = o.transaction_id AND i.output_position = o.output_position AND
o2.status != 3 and o2.is_double_spend = 0 o2.status != 3 and o2.is_double_spend = 0
) )
WHERE transaction_id IN (SELECT output_transaction_id FROM transaction_input WHERE transaction_id = "${transactionID}"); WHERE transaction_id = "${transactionID}";
UPDATE transaction_output AS o UPDATE transaction_output AS o
SET is_double_spend = 0, double_spend_date = NULL, is_stable = 1, stable_date = CAST(strftime('%s', 'now') AS INTEGER), is_spent = EXISTS ( SET is_double_spend = 0, double_spend_date = NULL, is_stable = 1, stable_date = CAST(strftime('%s', 'now') AS INTEGER), is_spent = EXISTS (
SELECT i.output_transaction_id FROM transaction_input i SELECT i.output_transaction_id FROM transaction_input i
INNER JOIN transaction_output o2 ON i.transaction_id = o2.transaction_id INNER JOIN transaction_output o2 ON i.transaction_id = o2.transaction_id
WHERE i.output_transaction_id = o.transaction_id AND i.output_position = o.output_position AND WHERE i.output_transaction_id = o.transaction_id AND i.output_position = o.output_position AND
o2.status != 3 AND o2.is_double_spend = 0 o2.status != 3 AND o2.is_double_spend = 0
), spent_date = ( ), spent_date = (
SELECT t.transaction_date FROM 'transaction' t SELECT t.transaction_date FROM 'transaction' t
INNER JOIN transaction_input i ON i.transaction_id = t.transaction_id INNER JOIN transaction_input i ON i.transaction_id = t.transaction_id
INNER JOIN transaction_output o2 ON i.transaction_id = o2.transaction_id INNER JOIN transaction_output o2 ON i.transaction_id = o2.transaction_id
WHERE i.output_transaction_id = o.transaction_id AND i.output_position = o.output_position AND WHERE i.output_transaction_id = o.transaction_id AND i.output_position = o.output_position AND
o2.status != 3 and o2.is_double_spend = 0 o2.status != 3 and o2.is_double_spend = 0
) )
WHERE transaction_id = "${transactionID}"; WHERE transaction_id IN (SELECT output_transaction_id FROM transaction_input WHERE transaction_id = "${transactionID}");
`, (err) => { `, (err) => {
if (err) { if (err) {
return reject(err); return reject(err);
......
...@@ -126,6 +126,6 @@ db.initialize() ...@@ -126,6 +126,6 @@ db.initialize()
}); });
} }
}); });
//millix v1.14.6-tangled //millix v1.14.7-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