New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: build delegate list from memory #1134
Changes from 16 commits
8d40c23
82991ea
d4dd3bb
f032d1a
ff8cadc
38e71e5
42d4b72
2158326
72954ff
733bfd9
0e4efb0
4ed5105
3dfe02a
d34160f
365c30d
b92664b
5936fce
732a594
27bbfd6
8603d40
feb0077
922b7b0
7449a67
7b4b12f
f2dd03f
abd77f4
d01df04
d1e1875
60bfa48
9f2903b
05f6474
e5deb34
9f8ca22
901dc66
dadf49a
62eeaa4
bb0f54a
b80d196
a80e6b8
6ff1e62
db08358
01282d5
655fc50
a89718c
222e2e3
0e4dd45
df786a6
037eb2a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,11 @@ | ||
'use strict' | ||
|
||
const Promise = require('bluebird') | ||
const { sumBy, orderBy } = require('lodash') | ||
|
||
const { Bignum, crypto } = require('@arkecosystem/crypto') | ||
const { Wallet } = require('@arkecosystem/crypto').models | ||
const { TRANSACTION_TYPES } = require('@arkecosystem/crypto').constants | ||
const { ARKTOSHI, TRANSACTION_TYPES } = require('@arkecosystem/crypto').constants | ||
const container = require('@arkecosystem/core-container') | ||
const config = container.resolvePlugin('config') | ||
const logger = container.resolvePlugin('logger') | ||
|
@@ -349,39 +350,91 @@ module.exports = class WalletManager { | |
recipient.applyTransactionToRecipient(data) | ||
} | ||
|
||
this.__updateVoteBalance(sender, transaction) | ||
|
||
return transaction | ||
} | ||
|
||
/** | ||
* Remove the given transaction from a delegate. | ||
* @param {Number} type | ||
* @param {Object} data | ||
* @param {Transaction} transaction | ||
* @return {Transaction} | ||
*/ | ||
revertTransaction ({ type, data }) { | ||
revertTransaction (transaction) { | ||
const { type, data } = transaction | ||
const sender = this.findByPublicKey(data.senderPublicKey) // Should exist | ||
const recipient = this.byAddress[data.recipientId] | ||
|
||
sender.revertTransactionForSender(data) | ||
|
||
// removing the wallet from the delegates index | ||
if (data.type === TRANSACTION_TYPES.DELEGATE_REGISTRATION) { | ||
if (type === TRANSACTION_TYPES.DELEGATE_REGISTRATION) { | ||
delete this.byUsername[data.asset.delegate.username] | ||
} | ||
|
||
if (recipient && type === TRANSACTION_TYPES.TRANSFER) { | ||
recipient.revertTransactionForRecipient(data) | ||
} | ||
|
||
this.__updateVoteBalance(sender, transaction) | ||
|
||
return data | ||
} | ||
|
||
/** | ||
* Load a list of all active delegates. | ||
* @param {Number} maxDelegates | ||
* @return {Array} | ||
*/ | ||
allActiveDelegates (maxDelegates) { | ||
let delegates = this.allByUsername() | ||
|
||
// NOTE: At the launch of the blockchain we may not have enough delegates. | ||
// In order to have enough forging delegates we complete the list in a | ||
// deterministic way (alphabetical order of publicKey). | ||
if (delegates.length < maxDelegates) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it should throw if there is not enough delegates (ie genesisBlock should register at least maxDelegates) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we shutdown the node in case there are not enough delegates? Doesn't seem to make sense to keep it running without enough delegates. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, shut down is required, you could say genesis block is illformed with not enough registered delegates |
||
const publicKeys = delegates.map(delegate => delegate.publicKey) | ||
|
||
let fillerWallets = this.allByUsername() | ||
|
||
if (publicKeys.length) { | ||
fillerWallets = fillerWallets.filter(wallet => !publicKeys.includes(wallet.publicKey)) | ||
} | ||
|
||
fillerWallets = orderBy(fillerWallets, ['publicKey'], ['asc']) | ||
|
||
delegates = delegates.concat(fillerWallets.slice(0, maxDelegates - delegates.length)) | ||
} | ||
|
||
return delegates.sort((a, b) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. before doing that i would make a function that recalculate all vote balance:
|
||
const aBalance = +a.voteBalance.toFixed() | ||
const bBalance = +b.voteBalance.toFixed() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mmm toFixed gives you a string, this aBalance - bBalance is alphabetical order There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The unary plus (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Unary_plus_()) turns the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i expect however a performance hit, that might be an issue in the future (lots of registered delegates) |
||
|
||
// FIXME: The issues with block 178 and 441 are resolved on devnet but | ||
// now other blocks cause issues where delegates have the same balance and | ||
// sorting the public keys alphabetically doesn't solve the issue. | ||
|
||
if (aBalance === bBalance) { | ||
logger.warn(`Delegate ${a.username} (${a.publicKey}) and ${b.username} (${b.publicKey}) have a matching vote balance of ${a.voteBalance.dividedBy(ARKTOSHI).toLocaleString()}.`) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good idea to log that event |
||
|
||
// if (a.publicKey === b.publicKey) { | ||
// throw new Error(`The balance and public key of both delegates are identical! Delegate "${a.username}" appears twice in the list.`) | ||
// } | ||
|
||
// return a.publicKey.localeCompare(b.publicKey, 'en-US-u-kf-lower') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why this is commented out? |
||
} | ||
|
||
return aBalance - bBalance | ||
}).slice(0, maxDelegates) | ||
} | ||
|
||
/** | ||
* Checks if a given publicKey is a registered delegate | ||
* @param {String} publicKey | ||
*/ | ||
__isDelegate (publicKey) { | ||
const delegateWallet = this.byPublicKey[publicKey] | ||
|
||
if (delegateWallet && delegateWallet.username) { | ||
return !!this.byUsername[delegateWallet.username] | ||
} | ||
|
@@ -398,4 +451,25 @@ module.exports = class WalletManager { | |
return wallet.balance.isZero() && !wallet.secondPublicKey && !wallet.multisignature && !wallet.username | ||
} | ||
|
||
/** | ||
* Update the vote balance of the delegate the vote is for. | ||
* @param {Object} sender | ||
* @param {Transaction} transaction | ||
* @return {void} | ||
*/ | ||
__updateVoteBalance (sender, transaction) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that is quite complex operation.
|
||
if (sender.vote && transaction.type === TRANSACTION_TYPES.TRANSFER) { | ||
const delegate = this.findByPublicKey(sender.vote) | ||
const voters = this.allByPublicKey().filter(wallet => (wallet.vote === sender.vote)) | ||
|
||
delegate.voteBalance = new Bignum(sumBy(voters, 'balance')) | ||
} | ||
|
||
if (transaction.type === TRANSACTION_TYPES.VOTE) { | ||
const vote = transaction.asset.votes[0] | ||
const delegate = this.findByPublicKey(vote.substr(1)) | ||
|
||
delegate.voteBalance[vote.startsWith('+') ? 'plus' : 'minus'](sender.balance) | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The list is FIXED for 1 round