diff --git a/elements/lisk-chain/src/account.ts b/elements/lisk-chain/src/account.ts index 7c1a6582b65..95110e1a73b 100644 --- a/elements/lisk-chain/src/account.ts +++ b/elements/lisk-chain/src/account.ts @@ -15,47 +15,79 @@ import { AccountJSON } from './types'; export const accountDefaultValues = { publicKey: undefined, - // tslint:disable-next-line:no-null-keyword - username: null, - isDelegate: 0, balance: '0', + // tslint:disable-next-line no-null-keyword + username: null, nonce: '0', - missedBlocks: 0, producedBlocks: 0, fees: '0', rewards: '0', - voteWeight: '0', - nameExist: false, - // tslint:disable-next-line:no-null-keyword - votedDelegatesPublicKeys: [], + totalVotesReceived: '0', + votes: [], + unlocking: [], + delegate: { + lastForgedHeight: 0, + registeredHeight: 0, + consecutiveMissedBlocks: 0, + isBanned: false, + pomHeights: [], + }, asset: {}, keys: { mandatoryKeys: [], optionalKeys: [], numberOfSignatures: 0, }, + // TODO: Remove once new DPoS implementation is done + missedBlocks: 0, + voteWeight: '0', + nameExist: false, + votedDelegatesPublicKeys: [], + isDelegate: 0, }; +interface Vote { + readonly delegateAddress: string; + readonly amount: bigint; +} + +interface Unlocking { + readonly delegateAddress: string; + readonly amount: bigint; + readonly unvoteHeight: number; +} + export class Account { public address: string; public balance: bigint; public nonce: bigint; public fees: bigint; public rewards: bigint; - public voteWeight: bigint; - public missedBlocks: number; public producedBlocks: number; public publicKey: string | undefined; + public totalVotesReceived: bigint; public username: string | null; - public isDelegate: number; - public nameExist: boolean; public asset: object; - public votedDelegatesPublicKeys: string[]; + public votes: Vote[]; + public unlocking: Unlocking[]; + public delegate: { + lastForgedHeight: number; + registeredHeight: number; + consecutiveMissedBlocks: number; + isBanned: boolean; + pomHeights: number[]; + }; public keys: { mandatoryKeys: string[]; optionalKeys: string[]; numberOfSignatures: number; }; + // TODO: Remove once new DPoS implementation is done + public voteWeight: bigint; + public missedBlocks: number; + public isDelegate: number; + public votedDelegatesPublicKeys: string[]; + public nameExist: boolean; public constructor(accountInfo: AccountJSON) { this.address = accountInfo.address; @@ -63,25 +95,36 @@ export class Account { ? BigInt(accountInfo.balance) : BigInt(0); this.nonce = accountInfo.nonce ? BigInt(accountInfo.nonce) : BigInt(0); - this.missedBlocks = accountInfo.missedBlocks; this.producedBlocks = accountInfo.producedBlocks; - this.isDelegate = accountInfo.isDelegate; this.publicKey = accountInfo.publicKey; this.username = accountInfo.username; this.fees = accountInfo.fees ? BigInt(accountInfo.fees) : BigInt(0); this.rewards = accountInfo.rewards ? BigInt(accountInfo.rewards) : BigInt(0); - this.voteWeight = accountInfo.voteWeight - ? BigInt(accountInfo.voteWeight) - : BigInt(0); - this.nameExist = accountInfo.nameExist; this.asset = accountInfo.asset; - this.votedDelegatesPublicKeys = - accountInfo.votedDelegatesPublicKeys === undefined || - accountInfo.votedDelegatesPublicKeys === null - ? [] - : accountInfo.votedDelegatesPublicKeys; + this.votes = accountInfo.votes?.length + ? accountInfo.votes.map(vote => ({ + delegateAddress: vote.delegateAddress, + amount: BigInt(vote.amount), + })) + : []; + this.unlocking = accountInfo.unlocking?.length + ? accountInfo.unlocking.map(unlock => ({ + delegateAddress: unlock.delegateAddress, + amount: BigInt(unlock.amount), + unvoteHeight: unlock.unvoteHeight, + })) + : []; + this.totalVotesReceived = BigInt(accountInfo.totalVotesReceived ?? 0); + this.delegate = { + lastForgedHeight: accountInfo.delegate?.lastForgedHeight ?? 0, + registeredHeight: accountInfo.delegate?.registeredHeight ?? 0, + consecutiveMissedBlocks: + accountInfo.delegate?.consecutiveMissedBlocks ?? 0, + isBanned: accountInfo.delegate?.isBanned ?? false, + pomHeights: accountInfo.delegate?.pomHeights ?? [], + }; this.keys = { mandatoryKeys: accountInfo.keys?.mandatoryKeys?.length ? accountInfo.keys?.mandatoryKeys @@ -91,6 +134,13 @@ export class Account { : [], numberOfSignatures: accountInfo.keys?.numberOfSignatures || 0, }; + + // TODO: Remove once new DPoS implementation is done + this.missedBlocks = accountInfo.missedBlocks; + this.isDelegate = accountInfo.isDelegate; + this.voteWeight = BigInt(accountInfo.voteWeight ?? 0); + this.nameExist = accountInfo.nameExist; + this.votedDelegatesPublicKeys = accountInfo.votedDelegatesPublicKeys ?? []; } public static getDefaultAccount = (address: string): Account => @@ -105,13 +155,37 @@ export class Account { publicKey: this.publicKey, // tslint:disable-next-line:no-null-keyword username: this.username, - isDelegate: this.isDelegate, balance: this.balance.toString(), nonce: this.nonce.toString(), - missedBlocks: this.missedBlocks, producedBlocks: this.producedBlocks, fees: this.fees.toString(), rewards: this.rewards.toString(), + totalVotesReceived: this.totalVotesReceived.toString(), + asset: this.asset, + votes: this.votes.map(v => ({ + delegateAddress: v.delegateAddress, + amount: v.amount.toString(), + })), + unlocking: this.unlocking.map(v => ({ + delegateAddress: v.delegateAddress, + amount: v.amount.toString(), + unvoteHeight: v.unvoteHeight, + })), + delegate: { + lastForgedHeight: this.delegate.lastForgedHeight, + registeredHeight: this.delegate.registeredHeight, + consecutiveMissedBlocks: this.delegate.consecutiveMissedBlocks, + isBanned: this.delegate.isBanned, + pomHeights: this.delegate.pomHeights, + }, + keys: { + mandatoryKeys: this.keys.mandatoryKeys, + optionalKeys: this.keys.optionalKeys, + numberOfSignatures: this.keys.numberOfSignatures, + }, + // TODO: Remove once new DPoS implementation is done + isDelegate: this.isDelegate, + missedBlocks: this.missedBlocks, voteWeight: this.voteWeight.toString(), nameExist: this.nameExist, votedDelegatesPublicKeys: @@ -119,12 +193,6 @@ export class Account { ? // tslint:disable-next-line:no-null-keyword null : this.votedDelegatesPublicKeys, - asset: this.asset, - keys: { - mandatoryKeys: this.keys.mandatoryKeys, - optionalKeys: this.keys.optionalKeys, - numberOfSignatures: this.keys.numberOfSignatures, - }, }; } } diff --git a/elements/lisk-chain/src/types.ts b/elements/lisk-chain/src/types.ts index 553c72135b7..f2f027c5d9e 100644 --- a/elements/lisk-chain/src/types.ts +++ b/elements/lisk-chain/src/types.ts @@ -27,28 +27,49 @@ export type IndexableAccount = Account & Indexable; export type IndexableTransactionJSON = TransactionJSON & Indexable; +export interface AccountVoteJSON { + readonly delegateAddress: string; + readonly amount: string; +} + +export interface AccountUnlockingJSON { + readonly delegateAddress: string; + readonly amount: string; + readonly unvoteHeight: number; +} + export interface AccountJSON { readonly address: string; readonly balance: string; readonly nonce: string; - readonly missedBlocks: number; readonly producedBlocks: number; readonly publicKey: string | undefined; readonly username: string | null; - readonly isDelegate: number; readonly fees: string; readonly rewards: string; - // tslint:disable-next-line readonly-keyword - readonly voteWeight: string; - readonly nameExist: boolean; + readonly totalVotesReceived: string; readonly asset: object; - // tslint:disable-next-line readonly-keyword - readonly votedDelegatesPublicKeys: string[] | null; readonly keys?: { readonly mandatoryKeys?: string[]; readonly optionalKeys?: string[]; readonly numberOfSignatures?: number; }; + readonly votes?: AccountVoteJSON[]; + readonly unlocking?: AccountUnlockingJSON[]; + readonly delegate?: { + readonly lastForgedHeight: number; + readonly registeredHeight: number; + readonly consecutiveMissedBlocks: number; + readonly isBanned: boolean; + readonly pomHeights: number[]; + }; + + // TODO: Remove once new DPoS implementation is done + readonly missedBlocks: number; + readonly isDelegate: number; + readonly voteWeight: string; + readonly nameExist: boolean; + readonly votedDelegatesPublicKeys: string[] | null; } export interface Context { diff --git a/elements/lisk-chain/test/unit/account.spec.ts b/elements/lisk-chain/test/unit/account.spec.ts index 5d62234ae5f..07110259376 100644 --- a/elements/lisk-chain/test/unit/account.spec.ts +++ b/elements/lisk-chain/test/unit/account.spec.ts @@ -34,6 +34,9 @@ describe('account', () => { expect(defaultAccount.balance).toEqual( BigInt(accountDefaultValues.balance), ); + expect(defaultAccount.totalVotesReceived).toEqual( + BigInt(accountDefaultValues.totalVotesReceived), + ); expect(defaultAccount.fees).toEqual(BigInt(accountDefaultValues.fees)); expect(defaultAccount.voteWeight).toEqual( BigInt(accountDefaultValues.voteWeight), @@ -41,6 +44,15 @@ describe('account', () => { expect(defaultAccount.rewards).toEqual( BigInt(accountDefaultValues.rewards), ); + expect(defaultAccount.votes).toEqual([]); + expect(defaultAccount.unlocking).toEqual([]); + expect(defaultAccount.delegate).toEqual({ + lastForgedHeight: 0, + registeredHeight: 0, + consecutiveMissedBlocks: 0, + isBanned: false, + pomHeights: [], + }); }); }); @@ -56,6 +68,7 @@ describe('account', () => { expect(accountObj.fees).toEqual(BigInt('0')); expect(accountObj.voteWeight).toEqual(BigInt('0')); expect(accountObj.rewards).toEqual(BigInt('0')); + expect(accountObj.totalVotesReceived).toEqual(BigInt('0')); expect(accountObj.votedDelegatesPublicKeys).toEqual([]); expect(accountObj.username).toBeNull; expect(accountObj.publicKey).toEqual(undefined); @@ -69,30 +82,50 @@ describe('account', () => { optionalKeys: [], numberOfSignatures: 0, }); + expect(accountObj.votes).toEqual([]); + expect(accountObj.unlocking).toEqual([]); + expect(accountObj.delegate).toEqual({ + lastForgedHeight: 0, + registeredHeight: 0, + consecutiveMissedBlocks: 0, + isBanned: false, + pomHeights: [], + }); }); }); describe('toJSON', () => { defaultAccount = Account.getDefaultAccount(accountAddress1); const accountJSON = defaultAccount.toJSON(); + it('should return default account JSON object with relevant types', () => { expect(accountJSON.address).toEqual(accountAddress1); - expect(accountJSON.balance).toBeString; - expect(accountJSON.fees).toBeString; - expect(accountJSON.voteWeight).toBeString; - expect(accountJSON.rewards).toBeString; - expect(accountJSON.votedDelegatesPublicKeys).toBeNull; - expect(accountJSON.username).toBeNull; - expect(accountJSON.publicKey).toBeUndefined; - expect(accountJSON.isDelegate).toBeNumber; - expect(accountJSON.missedBlocks).toBeNumber; - expect(accountJSON.producedBlocks).toBeNumber; - expect(accountJSON.nameExist).toBeBoolean; - expect(accountJSON.asset).toBeObject; - expect(accountJSON.keys?.mandatoryKeys).toBeArray; - expect(accountJSON.keys?.optionalKeys).toBeArray; - expect(accountJSON.keys?.numberOfSignatures).toBeNumber; + expect(accountJSON.balance).toBeString(); + expect(accountJSON.fees).toBeString(); + expect(accountJSON.voteWeight).toBeString(); + expect(accountJSON.rewards).toBeString(); + expect(accountJSON.votedDelegatesPublicKeys).toBeNull(); + expect(accountJSON.username).toBeNull(); + expect(accountJSON.publicKey).toBeUndefined(); + expect(accountJSON.isDelegate).toBeNumber(); + expect(accountJSON.missedBlocks).toBeNumber(); + expect(accountJSON.producedBlocks).toBeNumber(); + expect(accountJSON.nameExist).toBeBoolean(); + expect(accountJSON.asset).toBeObject(); + expect(accountJSON.keys?.mandatoryKeys).toBeArray(); + expect(accountJSON.keys?.optionalKeys).toBeArray(); + expect(accountJSON.keys?.numberOfSignatures).toBeNumber(); + expect(accountJSON.votes).toBeArray(); + expect(accountJSON.totalVotesReceived).toBeString(); + expect(accountJSON.unlocking).toBeArray(); + expect(accountJSON.delegate?.consecutiveMissedBlocks).toBeNumber(); + expect(accountJSON.delegate?.lastForgedHeight).toBeNumber(); + expect(accountJSON.delegate?.registeredHeight).toBeNumber(); + expect(accountJSON.delegate?.consecutiveMissedBlocks).toBeNumber(); + expect(accountJSON.delegate?.isBanned).toBeBoolean(); + expect(accountJSON.delegate?.pomHeights).toBeArray(); }); + it('should return account JSON object with relevant values', () => { expect(accountJSON.address).toEqual(accountAddress1); expect(accountJSON.balance).toEqual('0'); @@ -105,6 +138,7 @@ describe('account', () => { expect(accountJSON.isDelegate).toEqual(0); expect(accountJSON.missedBlocks).toEqual(0); expect(accountJSON.producedBlocks).toEqual(0); + expect(accountJSON.totalVotesReceived).toEqual('0'); expect(accountJSON.nameExist).toEqual(false); expect(accountJSON.asset).toEqual({}); expect(accountJSON.keys).toEqual({ @@ -112,6 +146,15 @@ describe('account', () => { optionalKeys: [], numberOfSignatures: 0, }); + expect(accountJSON.votes).toEqual([]); + expect(accountJSON.unlocking).toEqual([]); + expect(accountJSON.delegate).toEqual({ + lastForgedHeight: 0, + registeredHeight: 0, + consecutiveMissedBlocks: 0, + isBanned: false, + pomHeights: [], + }); }); }); }); diff --git a/elements/lisk-chain/test/unit/state_store_account.spec.ts b/elements/lisk-chain/test/unit/state_store_account.spec.ts index 4734e487960..5523d614cae 100644 --- a/elements/lisk-chain/test/unit/state_store_account.spec.ts +++ b/elements/lisk-chain/test/unit/state_store_account.spec.ts @@ -14,36 +14,17 @@ import { when } from 'jest-when'; import { StateStore } from '../../src'; import { StorageTransaction } from '../../src/types'; -import { Account } from '../../src'; +import { Account, accountDefaultValues } from '../../src/account'; describe('state store / account', () => { - const defaultAccount = { - publicKey: undefined, - username: null, - isDelegate: 0, - balance: '0', - nonce: '0', - nameExist: false, - missedBlocks: 0, - producedBlocks: 0, - fees: '0', - rewards: '0', - voteWeight: '0', - multiMin: 0, - multiLifetime: 0, - votedDelegatesPublicKeys: null, - asset: {}, - membersPublicKeys: null, - }; - const defaultAccounts = [ { - ...defaultAccount, + ...accountDefaultValues, address: '1276152240083265771L', balance: '100', }, { - ...defaultAccount, + ...accountDefaultValues, address: '11237980039345381032L', balance: '555', }, @@ -51,12 +32,12 @@ describe('state store / account', () => { const stateStoreAccounts = [ new Account({ - ...defaultAccount, + ...accountDefaultValues, address: '1276152240083265771L', balance: '100', }), new Account({ - ...defaultAccount, + ...accountDefaultValues, address: '11237980039345381032L', balance: '555', }), @@ -192,7 +173,7 @@ describe('state store / account', () => { const account = await stateStore.account.getOrDefault('123L'); // Assert expect(account).toEqual( - new Account({ ...defaultAccount, address: '123L' }), + new Account({ ...accountDefaultValues, address: '123L' }), ); expect(account.balance).toBe(BigInt(0)); }); @@ -232,7 +213,7 @@ describe('state store / account', () => { }); it('should update the updateKeys property', async () => { - const updatedKeys = ['missedBlocks', 'producedBlocks']; + const updatedKeys = ['producedBlocks', 'missedBlocks']; const existingAccount = await stateStore.account.get( defaultAccounts[0].address, ); diff --git a/framework/src/application/storage/entities/account.js b/framework/src/application/storage/entities/account.js index 0034503c3d9..4bb989d67ae 100644 --- a/framework/src/application/storage/entities/account.js +++ b/framework/src/application/storage/entities/account.js @@ -24,18 +24,28 @@ const { const defaultCreateValues = { publicKey: null, username: null, - isDelegate: false, balance: '0', nonce: '0', + keys: { mandatoryKeys: [], optionalKeys: [], numberOfSignatures: 0 }, + votes: null, + unlocking: null, + totalVotesReceived: '0', + delegate: { + lastForgedHeight: 0, + registeredHeight: 0, + consecutiveMissedBlocks: 0, + isBanned: false, + pomHeights: [], + }, + asset: {}, missedBlocks: 0, producedBlocks: 0, fees: '0', rewards: '0', voteWeight: '0', nameExist: false, - asset: {}, + isDelegate: false, votedDelegatesPublicKeys: null, - keys: {}, }; const readOnlyFields = ['address']; @@ -102,6 +112,8 @@ class ChainAccount extends AccountEntity { let parsedAccount = _.defaults(account, defaultCreateValues); parsedAccount = ChainAccount._stringifyVotedDelegates(parsedAccount); parsedAccount = ChainAccount._stringifyMembersPublicKeys(parsedAccount); + parsedAccount = ChainAccount._stringifyVotes(parsedAccount); + parsedAccount = ChainAccount._stringifyUnlocking(parsedAccount); return parsedAccount; }); @@ -117,6 +129,8 @@ class ChainAccount extends AccountEntity { sanitizedCreateData = ChainAccount._stringifyMembersPublicKeys( sanitizedCreateData, ); + sanitizedCreateData = ChainAccount._stringifyVotes(sanitizedCreateData); + sanitizedCreateData = ChainAccount._stringifyUnlocking(sanitizedCreateData); const objectData = _.omit(sanitizedCreateData, readOnlyFields); @@ -236,6 +250,26 @@ class ChainAccount extends AccountEntity { ); } + static _stringifyVotes(data) { + if (data.votes) { + return { + ...data, + votes: JSON.stringify(data.votes), + }; + } + return data; + } + + static _stringifyUnlocking(data) { + if (data.unlocking) { + return { + ...data, + unlocking: JSON.stringify(data.unlocking), + }; + } + return data; + } + static _stringifyVotedDelegates(data) { if (data.votedDelegatesPublicKeys) { return { diff --git a/framework/src/application/storage/migrations/node/20200316000000_add_new_dpos_to_mem_accounts.sql b/framework/src/application/storage/migrations/node/20200316000000_add_new_dpos_to_mem_accounts.sql new file mode 100644 index 00000000000..54de693e977 --- /dev/null +++ b/framework/src/application/storage/migrations/node/20200316000000_add_new_dpos_to_mem_accounts.sql @@ -0,0 +1,18 @@ +/* + * Copyright © 2020 Lisk Foundation + * + * See the LICENSE file at the top-level directory of this distribution + * for licensing information. + * + * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation, + * no part of this software, including this file, may be copied, modified, + * propagated, or distributed except according to the terms contained in the + * LICENSE file. + * + * Removal or modification of this copyright notice is prohibited. + */ + +ALTER TABLE "mem_accounts" ADD COLUMN IF NOT EXISTS "totalVotesReceived" BIGINT NOT NULL DEFAULT 0; +ALTER TABLE "mem_accounts" ADD COLUMN IF NOT EXISTS "delegate" jsonb; +ALTER TABLE "mem_accounts" ADD COLUMN IF NOT EXISTS "votes" jsonb; +ALTER TABLE "mem_accounts" ADD COLUMN IF NOT EXISTS "unlocking" jsonb; diff --git a/framework/src/components/storage/entities/account.js b/framework/src/components/storage/entities/account.js index d3016e71df0..1974dd7ada3 100644 --- a/framework/src/components/storage/entities/account.js +++ b/framework/src/components/storage/entities/account.js @@ -41,38 +41,19 @@ class Account extends BaseEntity { stringToByte, ); this.addField('username', 'string', { filter: ft.TEXT }); - this.addField( - 'isDelegate', - 'boolean', - { filter: ft.BOOLEAN }, - booleanToInt, - ); + this.addField('balance', 'string', { filter: ft.NUMBER }); this.addField('nonce', 'string', { filter: ft.BOOLEAN }); - this.addField( - 'nameExist', - 'boolean', - { - filter: ft.BOOLEAN, - fieldName: 'nameexist', - }, - booleanToInt, - ); this.addField('fees', 'string', { filter: ft.NUMBER }); this.addField('rewards', 'string', { filter: ft.NUMBER }); this.addField('producedBlocks', 'string', { filter: ft.NUMBER }); - this.addField('missedBlocks', 'string', { filter: ft.NUMBER }); - this.addField('voteWeight', 'string', { filter: ft.NUMBER }); + this.addField('totalVotesReceived', 'string', { filter: ft.NUMBER }); + this.addField('delegate', 'string'); + this.addField('votes', 'string'); + this.addField('unlocking', 'string'); this.addField('asset', 'string'); - this.addField('votedDelegatesPublicKeys', 'string'); this.addField('keys', 'string'); - this.addFilter('votedDelegatesPublicKeys', ft.CUSTOM, { - condition: - // eslint-disable-next-line no-template-curly-in-string - 'mem_accounts."votedDelegatesPublicKeys" @> ${votedDelegatesPublicKeys}', - }); - this.addFilter('asset_contains', ft.CUSTOM, { condition: // eslint-disable-next-line no-template-curly-in-string @@ -84,6 +65,31 @@ class Account extends BaseEntity { // eslint-disable-next-line no-template-curly-in-string "asset ? '${asset_exists:value}'", }); + // TODO: Remove once new DPoS implementation is done + this.addField( + 'isDelegate', + 'boolean', + { filter: ft.BOOLEAN }, + booleanToInt, + ); + this.addFilter('votedDelegatesPublicKeys', ft.CUSTOM, { + condition: + // eslint-disable-next-line no-template-curly-in-string + 'mem_accounts."votedDelegatesPublicKeys" @> ${votedDelegatesPublicKeys}', + }); + this.addField('missedBlocks', 'string', { filter: ft.NUMBER }); + this.addField('votedDelegatesPublicKeys', 'string'); + this.addField('voteWeight', 'string', { filter: ft.NUMBER }); + this.addField( + 'nameExist', + 'boolean', + { + filter: ft.BOOLEAN, + fieldName: 'nameexist', + }, + booleanToInt, + ); + // //TODO: Remove until here const defaultSort = { sort: 'balance:asc' }; this.extendDefaultOptions(defaultSort); diff --git a/framework/src/components/storage/sql/accounts/get.sql b/framework/src/components/storage/sql/accounts/get.sql index 2f4278de0d5..801ad72a778 100644 --- a/framework/src/components/storage/sql/accounts/get.sql +++ b/framework/src/components/storage/sql/accounts/get.sql @@ -19,6 +19,10 @@ SELECT "isDelegate"::int::boolean, "nonce", "balance", + "votes", + "unlocking", + "totalVotesReceived", + "delegate", "asset", "nameexist"::int::boolean as "nameExist", "missedBlocks", diff --git a/framework/test/jest/integration/specs/application/node/components/storage/entities/account/get.spec.js b/framework/test/jest/integration/specs/application/node/components/storage/entities/account/get.spec.js index cdeb07e588d..cc296190473 100644 --- a/framework/test/jest/integration/specs/application/node/components/storage/entities/account/get.spec.js +++ b/framework/test/jest/integration/specs/application/node/components/storage/entities/account/get.spec.js @@ -63,19 +63,23 @@ describe('storage.entities.Account.get', () => { const expectedAccount = { ...account, balance: '55566', - isDelegate: false, username: null, + keys: null, + votes: null, + unlocking: null, + delegate: null, + totalVotesReceived: '0', asset: null, - nameExist: false, - missedBlocks: 0, producedBlocks: 0, fees: '0', nonce: '0', rewards: '0', - voteWeight: '0', productivity: 0, + voteWeight: '0', + nameExist: false, + missedBlocks: 0, + isDelegate: false, votedDelegatesPublicKeys: null, - keys: null, }; await pgHelper.createAccount(account); diff --git a/framework/test/jest/integration/specs/application/node/components/storage/entities/account/update.spec.js b/framework/test/jest/integration/specs/application/node/components/storage/entities/account/update.spec.js index f4228b144db..486311e8e39 100644 --- a/framework/test/jest/integration/specs/application/node/components/storage/entities/account/update.spec.js +++ b/framework/test/jest/integration/specs/application/node/components/storage/entities/account/update.spec.js @@ -126,5 +126,187 @@ describe('storage.entities.Account.update', () => { ); expect(updatedAccount).toMatchObject(expectedAccount); }); + + it('should update totalVotesReceived field for each given account', async () => { + // Arrange + const account = { + address: 'delegateAddress', + publicKey: + '399a7d14610c4da8800ed929fc6a05133deb8fbac8403dec93226e96fa7590ee', + totalVotesReceived: '1000000', + }; + + const expectedTotalVotesReceived = '2000'; + + const expectedAccount = { + ...account, + totalVotesReceived: expectedTotalVotesReceived, + }; + + await pgHelper.createAccount(account); + + // Act + await db.tx(async tx => { + await storage.entities.Account.update( + { + publicKey: account.publicKey, + }, + { + totalVotesReceived: expectedTotalVotesReceived, + }, + {}, + tx, + ); + }); + + // Assert + const updatedAccount = await pgHelper.getAccountByPublicKey( + account.publicKey, + ); + expect(updatedAccount).toMatchObject(expectedAccount); + }); + + it('should update votes field for each given account', async () => { + // Arrange + const account = { + address: 'delegateAddress', + publicKey: + '399a7d14610c4da8800ed929fc6a05133deb8fbac8403dec93226e96fa7590ee', + votes: JSON.stringify([{ delegateAddress: '123L', amount: '100' }]), + }; + + const expectedAccount = { + ...account, + votes: [ + { delegateAddress: '123L', amount: '100' }, + { delegateAddress: '456L', amount: '10' }, + ], + }; + + await pgHelper.createAccount(account); + + // Act + await db.tx(async tx => { + await storage.entities.Account.update( + { + publicKey: account.publicKey, + }, + { + votes: [ + { delegateAddress: '123L', amount: '100' }, + { delegateAddress: '456L', amount: '10' }, + ], + }, + {}, + tx, + ); + }); + + // Assert + const updatedAccount = await pgHelper.getAccountByPublicKey( + account.publicKey, + ); + expect(updatedAccount).toMatchObject(expectedAccount); + }); + + it('should update unlocking field for each given account', async () => { + // Arrange + const account = { + address: 'delegateAddress', + publicKey: + '399a7d14610c4da8800ed929fc6a05133deb8fbac8403dec93226e96fa7590ee', + unlocking: JSON.stringify([ + { delegateAddress: '123L', amount: '100', unvoteHeight: 10 }, + ]), + }; + + const expectedAccount = { + ...account, + unlocking: [ + { delegateAddress: '123L', amount: '100', unvoteHeight: 10 }, + { delegateAddress: '123L', amount: '50', unvoteHeight: 20 }, + ], + }; + + await pgHelper.createAccount(account); + + // Act + await db.tx(async tx => { + await storage.entities.Account.update( + { + publicKey: account.publicKey, + }, + { + unlocking: [ + { delegateAddress: '123L', amount: '100', unvoteHeight: 10 }, + { delegateAddress: '123L', amount: '50', unvoteHeight: 20 }, + ], + }, + {}, + tx, + ); + }); + + // Assert + const updatedAccount = await pgHelper.getAccountByPublicKey( + account.publicKey, + ); + expect(updatedAccount).toMatchObject(expectedAccount); + }); + + it('should update delegate field for each given account', async () => { + // Arrange + const account = { + address: 'delegateAddress', + publicKey: + '399a7d14610c4da8800ed929fc6a05133deb8fbac8403dec93226e96fa7590ee', + delegate: { + lastForgedHeight: 0, + registeredHeight: 0, + consecutiveMissedBlocks: 0, + isBanned: false, + pomHeights: [], + }, + }; + + const expectedAccount = { + ...account, + delegate: { + lastForgedHeight: 10, + registeredHeight: 1, + consecutiveMissedBlocks: 0, + isBanned: false, + pomHeights: [5], + }, + }; + + await pgHelper.createAccount(account); + + // Act + await db.tx(async tx => { + await storage.entities.Account.update( + { + publicKey: account.publicKey, + }, + { + delegate: { + lastForgedHeight: 10, + registeredHeight: 1, + consecutiveMissedBlocks: 0, + isBanned: false, + pomHeights: [5], + }, + }, + {}, + tx, + ); + }); + + // Assert + const updatedAccount = await pgHelper.getAccountByPublicKey( + account.publicKey, + ); + expect(updatedAccount).toMatchObject(expectedAccount); + }); }); }); diff --git a/framework/test/mocha/unit/application/node/components/storage/entities/account.js b/framework/test/mocha/unit/application/node/components/storage/entities/account.js index 6d969fb7850..9c26cf954ac 100644 --- a/framework/test/mocha/unit/application/node/components/storage/entities/account.js +++ b/framework/test/mocha/unit/application/node/components/storage/entities/account.js @@ -32,13 +32,23 @@ const defaultCreateValues = { isDelegate: false, balance: '0', nonce: '0', + votes: null, + unlocking: null, + totalVotesReceived: '0', + delegate: { + lastForgedHeight: 0, + registeredHeight: 0, + consecutiveMissedBlocks: 0, + isBanned: false, + pomHeights: [], + }, missedBlocks: 0, producedBlocks: 0, fees: '0', rewards: '0', voteWeight: '0', nameExist: false, - keys: null, + keys: { mandatoryKeys: [], optionalKeys: [], numberOfSignatures: 0 }, }; describe('ChainAccount', () => { @@ -259,7 +269,17 @@ describe('ChainAccount', () => { isDelegate: false, balance: '0', nonce: '0', - keys: {}, + votes: null, + unlocking: null, + totalVotesReceived: '0', + delegate: { + lastForgedHeight: 0, + registeredHeight: 0, + consecutiveMissedBlocks: 0, + isBanned: false, + pomHeights: [], + }, + keys: { mandatoryKeys: [], optionalKeys: [], numberOfSignatures: 0 }, nameExist: false, missedBlocks: 0, producedBlocks: 0, diff --git a/framework/test/mocha/unit/components/storage/entities/account.js b/framework/test/mocha/unit/components/storage/entities/account.js index 65825fb085a..c668c54d7b2 100644 --- a/framework/test/mocha/unit/components/storage/entities/account.js +++ b/framework/test/mocha/unit/components/storage/entities/account.js @@ -59,6 +59,10 @@ describe('Account', () => { 'username', 'isDelegate', 'balance', + 'totalVotesReceived', + 'delegate', + 'votes', + 'unlocking', 'nameExist', 'fees', 'rewards', @@ -75,6 +79,10 @@ describe('Account', () => { 'username', 'isDelegate', 'balance', + 'totalVotesReceived', + 'delegate', + 'votes', + 'unlocking', 'nonce', 'asset', 'nameExist', @@ -104,9 +112,6 @@ describe('Account', () => { 'username_ne', 'username_in', 'username_like', - 'isDelegate', - 'isDelegate_eql', - 'isDelegate_ne', 'balance', 'balance_eql', 'balance_ne', @@ -118,9 +123,6 @@ describe('Account', () => { 'nonce', 'nonce_eql', 'nonce_ne', - 'nameExist', - 'nameExist_eql', - 'nameExist_ne', 'fees', 'fees_eql', 'fees_ne', @@ -145,6 +147,20 @@ describe('Account', () => { 'producedBlocks_lt', 'producedBlocks_lte', 'producedBlocks_in', + 'totalVotesReceived', + 'totalVotesReceived_eql', + 'totalVotesReceived_ne', + 'totalVotesReceived_gt', + 'totalVotesReceived_gte', + 'totalVotesReceived_lt', + 'totalVotesReceived_lte', + 'totalVotesReceived_in', + 'asset_contains', + 'asset_exists', + 'isDelegate', + 'isDelegate_eql', + 'isDelegate_ne', + 'votedDelegatesPublicKeys', 'missedBlocks', 'missedBlocks_eql', 'missedBlocks_ne', @@ -161,9 +177,9 @@ describe('Account', () => { 'voteWeight_lt', 'voteWeight_lte', 'voteWeight_in', - 'votedDelegatesPublicKeys', - 'asset_contains', - 'asset_exists', + 'nameExist', + 'nameExist_eql', + 'nameExist_ne', ]; validOptions = { diff --git a/framework/test/utils/storage/pg-helper/index.js b/framework/test/utils/storage/pg-helper/index.js index 396269bf58f..fdb1cfe28ad 100644 --- a/framework/test/utils/storage/pg-helper/index.js +++ b/framework/test/utils/storage/pg-helper/index.js @@ -139,6 +139,10 @@ class PgHelper { "isDelegate", "balance", "nonce", + "votes", + "unlocking", + "totalVotesReceived", + "delegate", "asset", "keys", "nameexist" as "nameExist",