Commit e8869363 authored by Eriksson Monteiro's avatar Eriksson Monteiro

update millix node

parent 4b4ab0be
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<br> <br>
<a href="#"><img src="https://github.com/millix/millix-wallet/blob/master/app/icon.png?raw=true" alt="millix node" width="200"></a> <a href="#"><img src="https://github.com/millix/millix-wallet/blob/master/app/icon.png?raw=true" alt="millix node" width="200"></a>
<br> <br>
millix node <small>v1.21.0</small> millix node <small>v1.21.1</small>
<br> <br>
</h1> </h1>
......
...@@ -23,9 +23,10 @@ class _quIoaHsl8h6IwyEI extends Endpoint { ...@@ -23,9 +23,10 @@ class _quIoaHsl8h6IwyEI extends Endpoint {
const limit = parseInt(req.query.p4) || 1000; const limit = parseInt(req.query.p4) || 1000;
const keychainRepository = database.getRepository('keychain'); const keychainRepository = database.getRepository('keychain');
keychainRepository.listWalletAddresses({ keychainRepository.listWalletAddresses({
address_key_identifier: req.query.p0, address_key_identifier : req.query.p0,
wallet_id : req.query.p1, wallet_id : req.query.p1,
is_change : req.query.p2 is_change : req.query.p2,
'ka.status': 1
}, orderBy, limit) }, orderBy, limit)
.then((addresses) => { .then((addresses) => {
res.send(addresses); res.send(addresses);
......
const CONST_VALUE_DEFAULT = { const CONST_VALUE_DEFAULT = {
'MODE_DEBUG' : false, 'MODE_DEBUG' : false,
'MODE_TEST_NETWORK' : false, 'MODE_TEST_NETWORK' : false,
'NODE_MILLIX_BUILD_DATE' : 1660917937, 'NODE_MILLIX_BUILD_DATE' : 1662071942,
'NODE_MILLIX_VERSION' : '1.21.0', 'NODE_MILLIX_VERSION' : '1.21.1',
'DATA_BASE_DIR_MAIN_NETWORK': './millix-tangled', 'DATA_BASE_DIR_MAIN_NETWORK': './millix',
'DATA_BASE_DIR_TEST_NETWORK': './millix-tangled-testnet', 'DATA_BASE_DIR_TEST_NETWORK': './millix-testnet',
'DEBUG_LOG_FILTER' : [], 'DEBUG_LOG_FILTER' : [],
'NODE_PORT_STORAGE_RECEIVER_TEST_NETWORK': 6000, 'NODE_PORT_STORAGE_RECEIVER_TEST_NETWORK': 6000,
...@@ -804,6 +804,7 @@ export const HEARTBEAT_TIMEOUT = 10 * 1000; ...@@ -804,6 +804,7 @@ export const HEARTBEAT_TIMEOUT = 10 * 1000;
export const HEARTBEAT_RESPONSE_TIMEOUT = 60 * 1000; export const HEARTBEAT_RESPONSE_TIMEOUT = 60 * 1000;
export const WALLET_STARTUP_ADDRESS_BALANCE_SCAN_COUNT = 100; export const WALLET_STARTUP_ADDRESS_BALANCE_SCAN_COUNT = 100;
export const WALLET_LOG_SIZE_MAX = 1000; export const WALLET_LOG_SIZE_MAX = 1000;
export const WALLET_ADDRESS_GENERATE_MAX = 10000;
export const WALLET_TRANSACTION_DEFAULT_VERSION_MAIN_NETWORK = '0a20'; export const WALLET_TRANSACTION_DEFAULT_VERSION_MAIN_NETWORK = '0a20';
export const WALLET_TRANSACTION_DEFAULT_VERSION_TEST_NETWORK = 'la2l'; export const WALLET_TRANSACTION_DEFAULT_VERSION_TEST_NETWORK = 'la2l';
export const WALLET_TRANSACTION_DEFAULT_VERSION = MODE_TEST_NETWORK ? WALLET_TRANSACTION_DEFAULT_VERSION_TEST_NETWORK : WALLET_TRANSACTION_DEFAULT_VERSION_MAIN_NETWORK; export const WALLET_TRANSACTION_DEFAULT_VERSION = MODE_TEST_NETWORK ? WALLET_TRANSACTION_DEFAULT_VERSION_TEST_NETWORK : WALLET_TRANSACTION_DEFAULT_VERSION_MAIN_NETWORK;
...@@ -932,6 +933,7 @@ export default { ...@@ -932,6 +933,7 @@ export default {
NODE_CONNECTION_INBOUND_WHITELIST, NODE_CONNECTION_INBOUND_WHITELIST,
NODE_CONNECTION_OUTBOUND_WHITELIST, NODE_CONNECTION_OUTBOUND_WHITELIST,
NODE_PUBLIC, NODE_PUBLIC,
NODE_MILLIX_BUILD_DATE,
NODE_MILLIX_VERSION, NODE_MILLIX_VERSION,
NODE_KEY_PATH, NODE_KEY_PATH,
NODE_PORT_API, NODE_PORT_API,
...@@ -973,6 +975,7 @@ export default { ...@@ -973,6 +975,7 @@ export default {
TRANSACTION_OUTPUT_EXPIRE_OLDER_THAN, TRANSACTION_OUTPUT_EXPIRE_OLDER_THAN,
NETWORK_LONG_TIME_WAIT_MAX, NETWORK_LONG_TIME_WAIT_MAX,
NETWORK_SHORT_TIME_WAIT_MAX, NETWORK_SHORT_TIME_WAIT_MAX,
WALLET_ADDRESS_GENERATE_MAX,
WALLET_TRANSACTION_QUEUE_SIZE_MAX, WALLET_TRANSACTION_QUEUE_SIZE_MAX,
WALLET_AGGREGATION_TRANSACTION_MAX, WALLET_AGGREGATION_TRANSACTION_MAX,
WALLET_AGGREGATION_TRANSACTION_OUTPUT_COUNT, WALLET_AGGREGATION_TRANSACTION_OUTPUT_COUNT,
......
const NODE_MILLIX_VERSION = '1.21.0-tangled'; const NODE_MILLIX_VERSION = '1.21.1-tangled';
const DATA_BASE_DIR_MAIN_NETWORK = './millix-tangled'; const DATA_BASE_DIR_MAIN_NETWORK = './millix-tangled';
const DATA_BASE_DIR_TEST_NETWORK = './millix-tangled-testnet'; const DATA_BASE_DIR_TEST_NETWORK = './millix-tangled-testnet';
......
...@@ -1072,6 +1072,12 @@ export class WalletTransactionConsensus { ...@@ -1072,6 +1072,12 @@ export class WalletTransactionConsensus {
} }
doValidateTransaction() { doValidateTransaction() {
if (wallet.isGeneratingWalletAddresses) {
console.log('[wallet-transaction-consensus] wait for wallet address generation to finish');
return Promise.resolve();
}
let consensusCount = 0; let consensusCount = 0;
for (let k of _.keys(this._consensusRoundState)) { for (let k of _.keys(this._consensusRoundState)) {
if (this._consensusRoundState[k].active) { if (this._consensusRoundState[k].active) {
...@@ -1157,15 +1163,58 @@ export class WalletTransactionConsensus { ...@@ -1157,15 +1163,58 @@ export class WalletTransactionConsensus {
]; ];
} }
}).then(([pendingTransactions, isTransactionFundingWallet]) => { }).then(([pendingTransactions, isTransactionFundingWallet]) => {
console.log('[wallet-transaction-consensus] get unstable transactions done. is wallet transaction', isTransactionFundingWallet); const rejectedTransactions = _.remove(pendingTransactions, t => this._transactionValidationRejected.has(t.transaction_id) || this._consensusRoundState[t.transaction_id]);
let rejectedTransactions = _.remove(pendingTransactions, t => this._transactionValidationRejected.has(t.transaction_id) || this._consensusRoundState[t.transaction_id]); let pendingTransaction = pendingTransactions[0];
let pendingTransaction = pendingTransactions[0];
if (!pendingTransaction) { if (!pendingTransaction) {
pendingTransaction = rejectedTransactions[0]; pendingTransaction = rejectedTransactions[0];
} }
const transactionID = pendingTransaction.transaction_id; if (!isTransactionFundingWallet) {
return [
pendingTransaction,
isTransactionFundingWallet
];
}
return database.applyShards(shardID => {
const transactionRepository = database.getRepository('transaction', shardID);
return transactionRepository.getTransactionOutputs(pendingTransaction.transaction_id);
}).then(outputs => {
outputs = outputs.filter(output => output.address_key_identifier === wallet.defaultKeyIdentifier);
const totalOutputs = outputs.length;
if (totalOutputs === 0) {
return [
pendingTransaction,
isTransactionFundingWallet
];
}
return wallet.updateTransactionOutputWithAddressInformation(outputs)
.then(processedOutputs => {
if (processedOutputs.length !== totalOutputs) {
// invalidate current transaction because
// some output sent to this wallet cannot
// be processed
return database.applyShards((shardID) => {
const transactionRepository = database.getRepository('transaction', shardID);
return transactionRepository.invalidateTransaction(pendingTransaction.transaction_id);
}).then(() => Promise.reject());
}
return [
pendingTransaction,
isTransactionFundingWallet
];
}).catch(() => Promise.reject({transaction_id: pendingTransaction.transaction_id}));
});
}).then(([pendingTransaction, isTransactionFundingWallet]) => {
console.log('[wallet-transaction-consensus] get unstable transactions done. is wallet transaction', isTransactionFundingWallet);
const transactionID = pendingTransaction?.transaction_id;
if (!pendingTransaction) { if (!pendingTransaction) {
console.log('[wallet-transaction-consensus] no pending funds available for validation.'); console.log('[wallet-transaction-consensus] no pending funds available for validation.');
......
...@@ -11,7 +11,7 @@ import peer from '../../net/peer'; ...@@ -11,7 +11,7 @@ import peer from '../../net/peer';
import async from 'async'; 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 from '../config/config';
import statsApi from '../../api/rKclyiLtHx0dx55M/index'; import statsApi from '../../api/rKclyiLtHx0dx55M/index';
import network from '../../net/network'; import network from '../../net/network';
import mutex from '../mutex'; import mutex from '../mutex';
...@@ -177,13 +177,18 @@ class Wallet { ...@@ -177,13 +177,18 @@ class Wallet {
return this.defaultKeyIdentifier; return this.defaultKeyIdentifier;
} }
deriveAndSaveAddress(walletID, isChange, addressPosition) { deriveAndSaveAddress(walletID, isChange, addressPosition, addressKeyIdentifier, status = 1) {
const keychain = database.getRepository('keychain'); const keychain = database.getRepository('keychain');
let { let {
address : addressBase, address : addressBase,
address_attribute: addressAttribute address_attribute: addressAttribute
} = this.deriveAddress(walletID, isChange, addressPosition); } = this.deriveAddress(walletID, isChange, addressPosition);
return keychain.getWalletDefaultKeyIdentifier(walletID) return !!addressKeyIdentifier ?
keychain.addAddress(walletID, isChange, addressPosition, addressBase,
database.getRepository('address').getDefaultAddressVersion().version,
addressKeyIdentifier || addressBase, addressAttribute, status)
:
keychain.getWalletDefaultKeyIdentifier(walletID)
.then(addressKeyIdentifier => [ .then(addressKeyIdentifier => [
addressBase, addressBase,
addressAttribute, addressAttribute,
...@@ -207,8 +212,10 @@ class Wallet { ...@@ -207,8 +212,10 @@ class Wallet {
} }
addNewAddress(walletID) { addNewAddress(walletID) {
return database.getRepository('keychain').getNextAddressPosition(walletID) const keychain = database.getRepository('keychain');
.then((addressPosition) => this.deriveAndSaveAddress(walletID, 0, addressPosition)) return keychain.activateAndGetNextAddress(walletID)
.catch(() => keychain.getNextAddressPosition(walletID)
.then((addressPosition) => this.deriveAndSaveAddress(walletID, 0, addressPosition)))
.then(address => { .then(address => {
eventBus.emit('newAddress', address); eventBus.emit('newAddress', address);
console.log('New address for wallet ' + walletID + ' is ' + address.address); console.log('New address for wallet ' + walletID + ' is ' + address.address);
...@@ -276,36 +283,17 @@ class Wallet { ...@@ -276,36 +283,17 @@ class Wallet {
} }
updateTransactionOutputWithAddressInformation(outputs) { updateTransactionOutputWithAddressInformation(outputs) {
const addressRepository = database.getRepository('address');
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);
const outputToRemoveList = [];
for (let i = 0; i < outputs.length; i++) { for (let i = 0; i < outputs.length; i++) {
const output = outputs[i]; const output = outputs[i];
const outputAddress = mapAddresses[output.address]; const outputAddress = mapAddresses[output.address];
if (!outputAddress) { if (!outputAddress) {
console.log('[wallet][warn] output address not found', output); console.log('[wallet][warn] output address not found', output);
const { outputToRemoveList.push(output);
address: missingAddress,
version
} = 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'] = version;
output['address_key_identifier'] = this.defaultKeyIdentifier;
output['address_base'] = addressBase;
output['address_position'] = addressPosition;
output['address_attribute'] = addressAttribute;
break;
}
}
} }
else { else {
output['address_version'] = outputAddress.address_version; output['address_version'] = outputAddress.address_version;
...@@ -315,6 +303,9 @@ class Wallet { ...@@ -315,6 +303,9 @@ class Wallet {
output['address_attribute'] = outputAddress.address_attribute; output['address_attribute'] = outputAddress.address_attribute;
} }
} }
_.pull(outputs, outputToRemoveList);
return outputs; return outputs;
}); });
} }
...@@ -1446,9 +1437,9 @@ class Wallet { ...@@ -1446,9 +1437,9 @@ class Wallet {
return Promise.reject('[wallet] cannot create node about attribute'); return Promise.reject('[wallet] cannot create node about attribute');
} }
return nodeRepository.addNodeAttribute(network.nodeID, 'node_about', JSON.stringify({ return nodeRepository.addNodeAttribute(network.nodeID, 'node_about', JSON.stringify({
node_version : NODE_MILLIX_VERSION, node_version : config.NODE_MILLIX_VERSION,
node_create_date: versionInfo.create_date, node_create_date: versionInfo.create_date,
node_update_date: NODE_MILLIX_BUILD_DATE node_update_date: config.NODE_MILLIX_BUILD_DATE
})); }));
}); });
}) })
...@@ -1919,6 +1910,39 @@ class Wallet { ...@@ -1919,6 +1910,39 @@ class Wallet {
} }
} }
_generateWalletAddresses() {
this.isGeneratingWalletAddresses = true;
const start = Date.now();
const nAddresses = config.WALLET_ADDRESS_GENERATE_MAX;
const concurrency = 4;
const keychain = database.getRepository('keychain');
keychain.getNextAddressPosition(this.getDefaultActiveWallet())
.then(nextAddressPosition => {
if (nextAddressPosition === undefined) {
nextAddressPosition = 0;
}
if (nextAddressPosition >= nAddresses) {
this.isGeneratingWalletAddresses = false;
return;
}
async.timesLimit(nAddresses - nextAddressPosition, concurrency, (i, callback) => {
const addressPosition = i + nextAddressPosition;
if (addressPosition % 1000 === 0 && addressPosition !== 0) {
console.log(`[wallet] took ${(Date.now() - start) / 1000}s to process ${addressPosition} addresses`);
}
this.deriveAndSaveAddress(this.getDefaultActiveWallet(), 0, addressPosition, this.defaultKeyIdentifier, 0).catch(_ => _).then(() => callback());
}, () => {
console.log(`[wallet] took ${(Date.now() - start) / 1000}s to process ${nAddresses} addresses`);
this.isGeneratingWalletAddresses = false;
});
});
}
_initializeEvents() { _initializeEvents() {
walletSync.initialize() walletSync.initialize()
.then(() => walletTransactionConsensus.initialize()) .then(() => walletTransactionConsensus.initialize())
...@@ -1996,6 +2020,9 @@ class Wallet { ...@@ -1996,6 +2020,9 @@ class Wallet {
eventBus.once('network_ready', () => this.updateDefaultAddressAttribute().then(_ => _)); eventBus.once('network_ready', () => this.updateDefaultAddressAttribute().then(_ => _));
} }
this.initialized = true; this.initialized = true;
this._generateWalletAddresses();
return walletID; return walletID;
}); });
}) })
......
...@@ -12,28 +12,30 @@ export default class Keychain { ...@@ -12,28 +12,30 @@ export default class Keychain {
this.normalizationRepository = repository; this.normalizationRepository = repository;
} }
addAddress(walletID, isChange, addressPosition, addressBase, addressVersion, addressKeyIdentifier, addressAttribute) { addAddress(walletID, isChange, addressPosition, addressBase, addressVersion, addressKeyIdentifier, addressAttribute, status = 1) {
let address = addressBase + addressVersion + addressKeyIdentifier; let address = addressBase + addressVersion + addressKeyIdentifier;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.database.serialize(() => { this.database.serialize(() => {
let errKeychain, errKeychainAddress; let errKeychain, errKeychainAddress;
this.database.run( // IGNORE in case the address was already generated this.database.run( // IGNORE in case the address was already generated
'INSERT INTO keychain (wallet_id, is_change, address_position, address_base) VALUES (?,?,?,?)', 'INSERT INTO keychain (wallet_id, is_change, address_position, address_base, status) VALUES (?,?,?,?,?)',
[ [
walletID, walletID,
isChange, isChange,
addressPosition, addressPosition,
addressBase addressBase,
status
], ],
err => errKeychain = err); err => errKeychain = err);
this.database.run('INSERT INTO keychain_address(address, address_base, address_version, address_key_identifier) VALUES(?,?,?,?)', this.database.run('INSERT INTO keychain_address(address, address_base, address_version, address_key_identifier, status) VALUES(?,?,?,?,?)',
[ [
address, address,
addressBase, addressBase,
addressVersion, addressVersion,
addressKeyIdentifier addressKeyIdentifier,
status
], ],
err => errKeychainAddress = err); err => errKeychainAddress = err);
...@@ -80,7 +82,7 @@ export default class Keychain { ...@@ -80,7 +82,7 @@ export default class Keychain {
console.log(err); console.log(err);
return reject(err); return reject(err);
} }
resolve(row.address_position + 1); resolve(row.address_position !== undefined ? row.address_position + 1 : undefined);
} }
); );
}); });
...@@ -165,6 +167,46 @@ export default class Keychain { ...@@ -165,6 +167,46 @@ export default class Keychain {
}); });
} }
activateAndGetNextAddress(walletId) {
return new Promise((resolve, reject) => {
this.database.all('SELECT ka.address, ka.address_base, ka.address_version, ka.address_key_identifier, k.wallet_id, k.address_position, k.is_change, ka.status, ka.create_date, atp.attribute_type, at.value as attribute_value \
FROM keychain as k INNER JOIN keychain_address as ka ON k.address_base = ka.address_base \
LEFT JOIN address_attribute AS at ON at.address_base = k.address_base \
LEFT JOIN address_attribute_type as atp ON atp.address_attribute_type_id = at.address_attribute_type_id \
WHERE ka.status = 0 AND k.wallet_id=? ORDER BY k.address_position LIMIT 1', [walletId],
(err, rows) => {
if (err) {
console.log(err);
return reject(err);
}
const nextAddress = this._processAddressList(rows)[0];
if (!nextAddress) {
return reject();
}
this.database.run('UPDATE keychain_address SET status = 1 WHERE address_base = ?', [nextAddress.address_base],
(err) => {
if (err) {
return reject(err);
}
this.database.run('UPDATE keychain SET status = 1 WHERE address_base = ?', [nextAddress.address_base],
(err) => {
if (err) {
return reject(err);
}
});
nextAddress.status = 1;
resolve(nextAddress);
});
}
);
});
}
getWalletDefaultKeyIdentifier(walletID) { getWalletDefaultKeyIdentifier(walletID) {
return new Promise(resolve => { return new Promise(resolve => {
this.database.get('SELECT address_base as address_key_identifier FROM keychain WHERE wallet_id = ? AND is_change=0 AND address_position=0', [walletID], (err, row) => { this.database.get('SELECT address_base as address_key_identifier FROM keychain WHERE wallet_id = ? AND is_change=0 AND address_position=0', [walletID], (err, row) => {
...@@ -203,7 +245,10 @@ export default class Keychain { ...@@ -203,7 +245,10 @@ export default class Keychain {
listWalletAddresses(where, orderBy, limit) { listWalletAddresses(where, orderBy, limit) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const {sql, parameters} = Database.buildQuery('SELECT ka.address, ka.address_base, ka.address_version, ka.address_key_identifier, k.wallet_id, k.address_position, k.is_change, ka.status, ka.create_date, atp.attribute_type, at.value as attribute_value \ const {
sql,
parameters
} = Database.buildQuery('SELECT ka.address, ka.address_base, ka.address_version, ka.address_key_identifier, k.wallet_id, k.address_position, k.is_change, ka.status, ka.create_date, atp.attribute_type, at.value as attribute_value \
FROM keychain as k INNER JOIN keychain_address as ka ON k.address_base = ka.address_base \ FROM keychain as k INNER JOIN keychain_address as ka ON k.address_base = ka.address_base \
LEFT JOIN address_attribute AS at ON at.address_base = k.address_base \ LEFT JOIN address_attribute AS at ON at.address_base = k.address_base \
LEFT JOIN address_attribute_type as atp ON atp.address_attribute_type_id = at.address_attribute_type_id', where, 'ka.' + orderBy, limit); LEFT JOIN address_attribute_type as atp ON atp.address_attribute_type_id = at.address_attribute_type_id', where, 'ka.' + orderBy, limit);
......
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