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 {
});
}
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
* @param app
......
......@@ -772,7 +772,7 @@ export const DATABASE_ENGINE = 'sqlite';
export const DATABASE_CONNECTION = {};
export const MILLIX_CIRCULATION = 9e15;
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_TEST_NETWORK = './millix-tangled';
let DATA_BASE_DIR = MODE_TEST_NETWORK ? DATA_BASE_DIR_TEST_NETWORK : DATA_BASE_DIR_MAIN_NETWORK;
......
......@@ -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);
return database.applyShardZeroAndShardRepository('transaction', transaction.shard_id, transactionRepository => {
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(() => {
consensusData.resolve();
......@@ -871,7 +904,6 @@ export class WalletTransactionConsensus {
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);
this._transactionValidationRejected.add(transactionID);
this._transactionRetryValidation[transactionID] = Date.now();
database.applyShards((shardID) => {
return database.getRepository('transaction', shardID)
.invalidateTransaction(transactionID);
......@@ -984,6 +1016,22 @@ export class WalletTransactionConsensus {
});
}, 'transaction_date').then(pendingTransactions => {
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.getRepository('transaction', shardID)
.findUnstableTransaction(excludeTransactionList);
......@@ -993,6 +1041,7 @@ export class WalletTransactionConsensus {
]);
}
else {
cache.setCacheItem('wallet-consensus', 'transaction_rejected_updated', true);
return [
pendingTransactions,
true
......@@ -1039,15 +1088,7 @@ export class WalletTransactionConsensus {
pendingTransaction.transaction_date = new Date(pendingTransaction.transaction_date);
}
return (() => {
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);
return pendingTransaction;
}).then(pendingTransaction => {
console.log('[wallet-transaction-consensus] transaction validated internally, starting consensus using oracles');
// replace lock id with transaction id
......
......@@ -12,6 +12,7 @@ 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 statsApi from '../../api/rKclyiLtHx0dx55M/index';
import network from '../../net/network';
import mutex from '../mutex';
import ntp from '../ntp';
......@@ -290,14 +291,14 @@ class Wallet {
}
})
.then((outputs) => {
const availableOutputs = [];
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];
const output = outputs[i];
if (cache.getCacheItem('wallet', `is_spend_${output.transaction_id}_${output.output_position}`)) {
continue;
......@@ -615,20 +616,22 @@ class Wallet {
walletTransactionConsensus.resetTransactionValidationRejected();
database.applyShards(shardID => {
const transactionRepository = database.getRepository('transaction', shardID);
return transactionRepository.listTransactionWithFreeOutput(this.defaultKeyIdentifier, true)
return transactionRepository.listWalletTransactionOutputNotSpent(this.defaultKeyIdentifier)
.then(transactions => new Promise(resolve => {
async.eachSeries(transactions, (transaction, callback) => {
walletTransactionConsensus.removeFromRejectedTransactions(transaction.transaction_id);
walletTransactionConsensus.removeFromRetryTransactions(transaction.transaction_id);
transactionRepository.resetTransaction(transaction.transaction_id)
.then(() => callback())
.catch(() => callback());
}, () => resolve(new Set(_.map(transactions, t => t.transaction_id))));
}))
.then(rootTransactions => this.resetValidation(rootTransactions, shardID))
.then(result => result ? resolve(result) : reject());
.then(rootTransactions => this.resetValidation(rootTransactions, shardID));
}).then(_ => _);
}
resetValidation(rootTransactions, shardID) {
statsApi.clearCache();
const transactionRepository = database.getRepository('transaction', shardID);
return new Promise((resolve) => {
const dfs = (transactions, visited = new Set()) => {
......
......@@ -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) {
return new Promise((resolve, reject) => {
let {
......@@ -1673,21 +1728,6 @@ export default class Transaction {
is_stable = 1,
stable_date = CAST(strftime('%s', 'now') AS INTEGER)
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 => {
if (err) {
console.log(err);
......@@ -1945,7 +1985,8 @@ export default class Transaction {
listTransactionSpendingOutput(transactionID, outputPosition) {
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,
outputPosition
......@@ -2016,21 +2057,21 @@ export default class Transaction {
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}");
WHERE transaction_id = "${transactionID}";
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 (
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
o2.status != 3 AND o2.is_double_spend = 0
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
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 = "${transactionID}";
WHERE transaction_id IN (SELECT output_transaction_id FROM transaction_input WHERE transaction_id = "${transactionID}");
`, (err) => {
if (err) {
return reject(err);
......
......@@ -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