Commit e8869363 authored by Eriksson Monteiro's avatar Eriksson Monteiro

update millix node

parent 4b4ab0be
......@@ -2,7 +2,7 @@
<br>
<a href="#"><img src="https://github.com/millix/millix-wallet/blob/master/app/icon.png?raw=true" alt="millix node" width="200"></a>
<br>
millix node <small>v1.21.0</small>
millix node <small>v1.21.1</small>
<br>
</h1>
......
......@@ -23,9 +23,10 @@ class _quIoaHsl8h6IwyEI extends Endpoint {
const limit = parseInt(req.query.p4) || 1000;
const keychainRepository = database.getRepository('keychain');
keychainRepository.listWalletAddresses({
address_key_identifier: req.query.p0,
wallet_id : req.query.p1,
is_change : req.query.p2
address_key_identifier : req.query.p0,
wallet_id : req.query.p1,
is_change : req.query.p2,
'ka.status': 1
}, orderBy, limit)
.then((addresses) => {
res.send(addresses);
......
const CONST_VALUE_DEFAULT = {
'MODE_DEBUG' : false,
'MODE_TEST_NETWORK' : false,
'NODE_MILLIX_BUILD_DATE' : 1660917937,
'NODE_MILLIX_VERSION' : '1.21.0',
'DATA_BASE_DIR_MAIN_NETWORK': './millix-tangled',
'DATA_BASE_DIR_TEST_NETWORK': './millix-tangled-testnet',
'NODE_MILLIX_BUILD_DATE' : 1662071942,
'NODE_MILLIX_VERSION' : '1.21.1',
'DATA_BASE_DIR_MAIN_NETWORK': './millix',
'DATA_BASE_DIR_TEST_NETWORK': './millix-testnet',
'DEBUG_LOG_FILTER' : [],
'NODE_PORT_STORAGE_RECEIVER_TEST_NETWORK': 6000,
......@@ -804,6 +804,7 @@ export const HEARTBEAT_TIMEOUT = 10 * 1000;
export const HEARTBEAT_RESPONSE_TIMEOUT = 60 * 1000;
export const WALLET_STARTUP_ADDRESS_BALANCE_SCAN_COUNT = 100;
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_TEST_NETWORK = 'la2l';
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 {
NODE_CONNECTION_INBOUND_WHITELIST,
NODE_CONNECTION_OUTBOUND_WHITELIST,
NODE_PUBLIC,
NODE_MILLIX_BUILD_DATE,
NODE_MILLIX_VERSION,
NODE_KEY_PATH,
NODE_PORT_API,
......@@ -973,6 +975,7 @@ export default {
TRANSACTION_OUTPUT_EXPIRE_OLDER_THAN,
NETWORK_LONG_TIME_WAIT_MAX,
NETWORK_SHORT_TIME_WAIT_MAX,
WALLET_ADDRESS_GENERATE_MAX,
WALLET_TRANSACTION_QUEUE_SIZE_MAX,
WALLET_AGGREGATION_TRANSACTION_MAX,
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_TEST_NETWORK = './millix-tangled-testnet';
......
......@@ -1072,6 +1072,12 @@ export class WalletTransactionConsensus {
}
doValidateTransaction() {
if (wallet.isGeneratingWalletAddresses) {
console.log('[wallet-transaction-consensus] wait for wallet address generation to finish');
return Promise.resolve();
}
let consensusCount = 0;
for (let k of _.keys(this._consensusRoundState)) {
if (this._consensusRoundState[k].active) {
......@@ -1157,15 +1163,58 @@ export class WalletTransactionConsensus {
];
}
}).then(([pendingTransactions, isTransactionFundingWallet]) => {
console.log('[wallet-transaction-consensus] get unstable transactions done. is wallet transaction', isTransactionFundingWallet);
let rejectedTransactions = _.remove(pendingTransactions, t => this._transactionValidationRejected.has(t.transaction_id) || this._consensusRoundState[t.transaction_id]);
let pendingTransaction = pendingTransactions[0];
const rejectedTransactions = _.remove(pendingTransactions, t => this._transactionValidationRejected.has(t.transaction_id) || this._consensusRoundState[t.transaction_id]);
let pendingTransaction = pendingTransactions[0];
if (!pendingTransaction) {
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) {
console.log('[wallet-transaction-consensus] no pending funds available for validation.');
......
......@@ -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 from '../config/config';
import statsApi from '../../api/rKclyiLtHx0dx55M/index';
import network from '../../net/network';
import mutex from '../mutex';
......@@ -177,13 +177,18 @@ class Wallet {
return this.defaultKeyIdentifier;
}
deriveAndSaveAddress(walletID, isChange, addressPosition) {
deriveAndSaveAddress(walletID, isChange, addressPosition, addressKeyIdentifier, status = 1) {
const keychain = database.getRepository('keychain');
let {
address : addressBase,
address_attribute: addressAttribute
} = 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 => [
addressBase,
addressAttribute,
......@@ -207,8 +212,10 @@ class Wallet {
}
addNewAddress(walletID) {
return database.getRepository('keychain').getNextAddressPosition(walletID)
.then((addressPosition) => this.deriveAndSaveAddress(walletID, 0, addressPosition))
const keychain = database.getRepository('keychain');
return keychain.activateAndGetNextAddress(walletID)
.catch(() => keychain.getNextAddressPosition(walletID)
.then((addressPosition) => this.deriveAndSaveAddress(walletID, 0, addressPosition)))
.then(address => {
eventBus.emit('newAddress', address);
console.log('New address for wallet ' + walletID + ' is ' + address.address);
......@@ -276,36 +283,17 @@ 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);
const outputToRemoveList = [];
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,
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;
}
}
outputToRemoveList.push(output);
}
else {
output['address_version'] = outputAddress.address_version;
......@@ -315,6 +303,9 @@ class Wallet {
output['address_attribute'] = outputAddress.address_attribute;
}
}
_.pull(outputs, outputToRemoveList);
return outputs;
});
}
......@@ -1446,9 +1437,9 @@ class Wallet {
return Promise.reject('[wallet] cannot create node about attribute');
}
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_update_date: NODE_MILLIX_BUILD_DATE
node_update_date: config.NODE_MILLIX_BUILD_DATE
}));
});
})
......@@ -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() {
walletSync.initialize()
.then(() => walletTransactionConsensus.initialize())
......@@ -1996,6 +2020,9 @@ class Wallet {
eventBus.once('network_ready', () => this.updateDefaultAddressAttribute().then(_ => _));
}
this.initialized = true;
this._generateWalletAddresses();
return walletID;
});
})
......
......@@ -12,28 +12,30 @@ export default class Keychain {
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;
return new Promise((resolve, reject) => {
this.database.serialize(() => {
let errKeychain, errKeychainAddress;
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,
isChange,
addressPosition,
addressBase
addressBase,
status
],
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,
addressBase,
addressVersion,
addressKeyIdentifier
addressKeyIdentifier,
status
],
err => errKeychainAddress = err);
......@@ -80,7 +82,7 @@ export default class Keychain {
console.log(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 {
});
}
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) {
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) => {
......@@ -203,7 +245,10 @@ export default class Keychain {
listWalletAddresses(where, orderBy, limit) {
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 \
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);
......
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