Skip to content

Commit

Permalink
Multi voting file support
Browse files Browse the repository at this point in the history
  • Loading branch information
fboucquez committed Mar 30, 2021
1 parent f4a0457 commit c631e13
Show file tree
Hide file tree
Showing 21 changed files with 419 additions and 262 deletions.
3 changes: 3 additions & 0 deletions cmds/config-testnet-supernode.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash
set -e
symbol-bootstrap config -p testnet -a dual -t target/testnet-supernode -c test/supernode.yml --noPassword $1 $2 $3
3 changes: 0 additions & 3 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ OPTIONS
can be provided in the command line (--password=XXXX) or disabled in the
command line (--noPassword).
--pullImages It pulls the utility images from DockerHub when running the configuration.
It only affects alpha/dev docker images.
--report It generates reStructuredText (.rst) reports describing the configuration of
each node.
Expand Down
2 changes: 2 additions & 0 deletions presets/bootstrap/network.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ harvestingName: 'harvest'
explorerUrl: http://localhost:90/
faucetUrl: http://localhost:100/
beneficiaryAddress: ''
votingKeyDesiredEpochLength: 720
lastKnownNetworkEpoch: 1
nemesis:
mosaics:
- name: '{{currencyName}}'
Expand Down
2 changes: 2 additions & 0 deletions presets/mainnet/network.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ importanceGrouping: 720
votingSetGrouping: 1440
minVotingKeyLifetime: 112
maxVotingKeyLifetime: 360
votingKeyDesiredEpochLength: 360
lastKnownNetworkEpoch: 28
stepDuration: 5m
maxBlockFutureTime: 300ms
maxAccountRestrictionValues: 100
Expand Down
4 changes: 0 additions & 4 deletions presets/shared.yml
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,4 @@ maxConnectionAttempts: 15
baseRetryDelay: 750
connectionPoolSize: 10
maxSubscriptions: 300

#voting
votingKeyStartEpoch: 1
votingKeyEndEpoch: 360
enableRevoteOnBoot: true
2 changes: 2 additions & 0 deletions presets/testnet/network.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ rewardProgramControllerPublicKey: 68B6A1D2F292E75F9BB8E9EDDA086A7C293A198C9968FF
rewardProgramCaFile: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZpekNDQTNNQ0ZGWkpmSjRMaXVxOXJzRVJUbllOa0ZQQ3dySTJNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1JR0IKTVFzd0NRWURWUVFHRXdKVlV6RUxNQWtHQTFVRUNBd0NUVUV4RHpBTkJnTlZCQWNNQmtKdmMzUnZiakVUTUJFRwpBMVVFQ2d3S1JYaGhiWEJzWlNCRGJ6RVFNQTRHQTFVRUN3d0hkR1ZqYUc5d2N6RUxNQWtHQTFVRUF3d0NZMkV4CklEQWVCZ2txaGtpRzl3MEJDUUVXRVdObGNuUnpRR1Y0WVcxd2JHVXVZMjl0TUI0WERUSXhNREV4TVRFME16QXoKTVZvWERUUTRNRFV5T0RFME16QXpNVm93Z1lFeEN6QUpCZ05WQkFZVEFsVlRNUXN3Q1FZRFZRUUlEQUpOUVRFUApNQTBHQTFVRUJ3d0dRbTl6ZEc5dU1STXdFUVlEVlFRS0RBcEZlR0Z0Y0d4bElFTnZNUkF3RGdZRFZRUUxEQWQwClpXTm9iM0J6TVFzd0NRWURWUVFEREFKallURWdNQjRHQ1NxR1NJYjNEUUVKQVJZUlkyVnlkSE5BWlhoaGJYQnMKWlM1amIyMHdnZ0lpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElDRHdBd2dnSUtBb0lDQVFDbHhWVzlZZ2RmSFlGcwpad1BIcVlOaUhWcUtaMnlLZ2VnYXdqYTRuZlN5WkFmWHpIRzdiMmNaRXZUbDkwY2EreFhSdjhCR0J1RmM0QksyCmJCRGpMQk9VMmpQaXVqLytNTEFHR1dYUGtReGtLYzZjc3M1Q1RWUkx0SXhucERHZk1GSmhLNDFTZlcydmZleEUKYThXMldHWjdmc3dGV1IyMlFJL2RwSkM2TEtqdE15VmdhUi9EUDNXTE45MmZRbE5VajdIQ1ZFbVg4ZWI1Y3I3egpnK2I0cWt3S1Z4cWJWNGhmektqT0w0ZURWSksxVlZieFJLaE1iRmNhWVBlTnUrUzc3a05xajh6aE1MSTBTK0xpCnMvckNpUFBjelFuakcraFNiZHUvSnhYL0psR282VTlvb1BKKzNsNHBHUE9nTmNYOW9NS01TK2NoWXJ0aE5uWnMKbUMvMWpZUm5jcXdKRUlGVTBqYzBtSFhyTytTM2tSMEpjUVRPU05lMVVYMExrOWdjaFl2aUVOaFV6VVUxRHEvSwpTbGJQdjBNdGluaTNQVUs5endlL2J6d3VTMDlldkFhNjdDWDNSRWdoTnFienlwS2c2ZHY4LzdUUTBldFUvcmxWClo5UEVodW84R1I1bEpNbUJSaTY5MlQ2NEg1QmZQdHlpbk04NWw5NzBacCsvTjAxOUJlTDQ2NkUzdWFMdmhHQS8KTXV5NEd0ZVI5N0p4UFJzN2d3ZGY0VDdNbXQ1Vk1jVWFiMGFNc1pwdjNzZlBYNWZxYWNYckVxMjJMaldlVW93KwoxaDFWRmhFdXVwcnZOalViY25zNTNpZE0wZHNRN2xuOU5iY0pJdjZhQkFCZndYQ3FsSXU2OUNiYStGWnYxRTI5ClRhRlF3UERONTFsOU9IbEo4OFpWTXpOY2FUVjdVUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQ0FRQ0UKQ2RkQXJxUHVrV1ZITkFmdXZWcjc0akZPWXQ2cU5vTDA0cGoxalhnOEkzUkpiQzJwRU4vYisrNmFRbUp3R1ZlaQpLZ1BLWmgwVU1lYm9qR1hrcllBVDJKMjRXa1NNb2V5MmFyYzFEa2xlN2x3d1MvQ2xsMVgrVWVkd2JmWmxjYTRFCkJJRzNveXJPeVlRR1hMRGFKcEh1Z28zY3dndkNZNlZVRmIwQUhSeExHN0dIVmt3VzU3aDRkWSt5cWg4QnJHalIKcjRKRlBtcWlPVHUyTTNWdjJoV1dGUVgwSkZUQlJsMldldENVa1FjVEZVK2I2R1VTMnBRUTRpZkFITXdYY05aagpCUHpoT2JVYmd5Mkg0enhlVStENmFORTJDell5TlFCWHFUVDJPcHVDV3B3VmlzRHNRMUVZdmdZc3ZUenF2b1pDCmpnZVlVWi9hdXJIQkRnRGNDc3g1NkE0MUtNbFV3Y0Zub3FsOUdDZVNWY2ErTmR4UnEwWTJGVjNQVDJZVjJRc24KeXRxWndGTVcyWm1PVUl1N202SjMzdkptdS9YTncwMlVQZm53c1ppK3dkbDJ2cFdWQmNJRko2eXc0U0lVYXNLeQp3anVENVhuQTdwR2FEZjdkcVNiOUl0eGNZaFhSR3IySU1RbEppUHpnUVJCSS9UYnh5YjFKdWZlb1M2cU1kRitOCnRjbE5TaGlpeFJxOE1XSUxEdngzMC9WdU9uanEvY1NyV0NlQ3JwSHVySU1MclN2OXVUTk9MRW1EMkxtVXo1Q3EKdEJQNkZqOEE2b1pCUWxqc2pGQU5WaGwwcXJQRCtLL0NaS1lVeStiOG50bHM2dnJWQVppSXl2U2YxT1NTRldTYgpLUjZFdDZhZGc3TVRPd1EreXI1UlFTbGNBVTZlNVl6RE5ETC9zSFdJS2c9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
harvestingName: 'xym'
votingSetGrouping: 720
votingKeyDesiredEpochLength: 720
lastKnownNetworkEpoch: 323
maxSecretLockDuration: 365d
maxNamespaceDuration: 1825d
namespaceGracePeriodDuration: 1d
Expand Down
4 changes: 2 additions & 2 deletions src/model/Addresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import { NetworkType } from 'symbol-sdk';
import { VotingKeyFile } from '../service';

export interface CertificatePair {
privateKey?: string;
Expand All @@ -35,8 +36,7 @@ export interface NodeAccount {
remote?: ConfigAccount;
// VRF key is produced if node is peer
vrf?: ConfigAccount;
// Voting key is produced if node is voting
voting?: ConfigAccount;
voting?: VotingKeyFile[];
roles: string;
name: string;
friendlyName: string;
Expand Down
6 changes: 3 additions & 3 deletions src/model/ConfigPreset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,8 +370,6 @@ export interface CommonConfigPreset extends NodeConfigPreset, GatewayConfigPrese
dockerComposeVersion: number | string;
dockerComposeServiceRestart: string;
dockerComposeDebugMode: boolean;
votingKeyEndEpoch: number;
votingKeyStartEpoch: number;
mongoComposeRunParam: string;
peersP2PListLimit: number;
peersApiListLimit: number;
Expand All @@ -390,8 +388,10 @@ export interface CommonConfigPreset extends NodeConfigPreset, GatewayConfigPrese
baseNamespace: string;
rewardProgramControllerPublicKey?: string;
networkType: NetworkType;
votingKeyDesiredEpochLength: number;
useExperimentalNativeVotingKeyGeneration?: boolean;
lastKnownNetworkEpoch: number;
//Nested Objects

knownRestGateways?: string[];
inflation?: Record<string, number>;
knownPeers?: Record<NodeType, PeerInfo[]>;
Expand Down
4 changes: 3 additions & 1 deletion src/service/AnnounceService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export interface TransactionFactoryParams {
mainAccountInfo: AccountInfo;
mainAccount: PublicAccount;
deadline: Deadline;
target: string;
maxFee: UInt64;
}

Expand Down Expand Up @@ -82,7 +83,6 @@ export class AnnounceService {
description:
'Use the best NEM node available when announcing. Otherwise the command will use the node provided by the --url parameter.',
}),

ready: flags.boolean({
description: 'If --ready is provided, the command will not ask for confirmation when announcing transactions.',
}),
Expand All @@ -95,6 +95,7 @@ export class AnnounceService {
providedMaxFee: number | undefined,
useKnownRestGateways: boolean,
ready: boolean | undefined,
target: string,
presetData: ConfigPreset,
addresses: Addresses,
transactionFactory: TransactionFactory,
Expand Down Expand Up @@ -174,6 +175,7 @@ export class AnnounceService {
nodePreset,
nodeAccount,
mainAccountInfo,
target,
mainAccount,
deadline,
maxFee: defaultMaxFee,
Expand Down
21 changes: 1 addition & 20 deletions src/service/BootstrapUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { get } from 'https';
import * as _ from 'lodash';
import { platform, totalmem } from 'os';
import { basename, join, resolve } from 'path';
import { Convert, Deadline, DtoMapping, LinkAction, NetworkType, Transaction, UInt64, VotingKeyLinkTransaction } from 'symbol-sdk';
import { Convert, DtoMapping, NetworkType } from 'symbol-sdk';
import * as util from 'util';
import { LogType } from '../logger';
import Logger from '../logger/Logger';
Expand Down Expand Up @@ -309,25 +309,6 @@ export class BootstrapUtils {
});
}

public static createVotingKeyTransaction(
shortPublicKey: string,
action: LinkAction,
presetData: { networkType: NetworkType; votingKeyStartEpoch: number; votingKeyEndEpoch: number },
deadline: Deadline,
maxFee: UInt64,
): Transaction {
return VotingKeyLinkTransaction.create(
deadline,
shortPublicKey,
presetData.votingKeyStartEpoch,
presetData.votingKeyEndEpoch,
action,
presetData.networkType,
1,
maxFee,
);
}

public static poll(promiseFunction: () => Promise<boolean>, totalPollingTime: number, pollIntervalMs: number): Promise<boolean> {
const startTime = new Date().getMilliseconds();
return promiseFunction().then(async (result) => {
Expand Down
19 changes: 7 additions & 12 deletions src/service/ConfigLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,22 +198,23 @@ export class ConfigLoader {
): ConfigAccount {
const oldAccount = this.getAccount(networkType, oldStoredAccount?.publicKey, oldStoredAccount?.privateKey);
const newAccount = this.getAccount(networkType, publicKey, privateKey);
const getAccountLog = (account: Account | PublicAccount) =>
`${keyName} Account ${account.address.plain()} Public Ley ${account.publicKey} `;

if (oldAccount && !newAccount) {
logger.info(`Reusing ${keyName} account ${oldAccount.address.plain()}`);
logger.info(`Reusing ${getAccountLog(oldAccount)}...`);
return this.toConfig(oldAccount);
}
if (!oldAccount && newAccount) {
logger.info(`${keyName} Account ${newAccount.address.plain()} has been provided`);
logger.info(`${getAccountLog(newAccount)} has been provided`);
return this.toConfig(newAccount);
}
if (oldAccount && newAccount) {
if (oldAccount.address.equals(newAccount.address)) {
logger.info(`Reusing ${keyName} account ${oldAccount.address.plain()}`);
logger.info(`Reusing ${getAccountLog(newAccount)}`);
return { ...this.toConfig(oldAccount), ...this.toConfig(newAccount) };
}
logger.info(
`Old ${keyName} Account ${oldAccount.address.plain()} has been changed. New ${keyName} Account is ${newAccount.address.plain()}`,
);
logger.info(`Old ${getAccountLog(oldAccount)} has been changed. New ${getAccountLog(newAccount)} replaces it.`);
return this.toConfig(newAccount);
}

Expand Down Expand Up @@ -294,12 +295,6 @@ export class ConfigLoader {
nodePreset.remotePrivateKey,
nodePreset.remotePublicKey,
);
if (nodePreset.voting)
nodeAccount.voting = this.toConfig(
oldNodeAccount?.voting
? PublicAccount.createFromPublicKey(oldNodeAccount.voting.publicKey, networkType)
: Account.generateNewAccount(networkType),
);
if (nodePreset.harvesting)
nodeAccount.vrf = this.generateAccount(
networkType,
Expand Down
46 changes: 22 additions & 24 deletions src/service/ConfigService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
Transaction,
TransactionMapping,
UInt64,
VotingKeyLinkTransaction,
VrfKeyLinkTransaction,
} from 'symbol-sdk';
import { LogType } from '../logger';
Expand Down Expand Up @@ -467,11 +468,7 @@ export class ConfigService {
.map((n) => this.createAccountKeyLinkTransaction(transactionsDirectory, presetData, n)),
);

await Promise.all(
(addresses.nodes || [])
.filter((n) => n.voting)
.map((n) => this.createVotingKeyTransaction(transactionsDirectory, presetData, n)),
);
await Promise.all((addresses.nodes || []).map((n) => this.createVotingKeyTransactions(transactionsDirectory, presetData, n)));

if (presetData.nemesis.mosaics && (presetData.nemesis.transactions || presetData.nemesis.balances)) {
logger.info('Opt In mode is ON!!! balances or transactions have been provided');
Expand Down Expand Up @@ -593,35 +590,36 @@ export class ConfigService {
return await this.storeTransaction(transactionsDirectory, `remote_${node.name}`, signedTransaction.payload);
}

private async createVotingKeyTransaction(
private async createVotingKeyTransactions(
transactionsDirectory: string,
presetData: ConfigPreset,
node: NodeAccount,
): Promise<Transaction> {
if (!node.voting) {
throw new Error('Voting keys should have been generated!!');
}

if (!node.main) {
throw new Error('Main keys should have been generated!!');
}
const voting = BootstrapUtils.createVotingKeyTransaction(
node.voting.publicKey,
LinkAction.Link,
presetData,
Deadline.createFromDTO('1'),
UInt64.fromUint(0),
);
): Promise<Transaction[]> {
const votingFiles = node.voting || [];
const mainPrivateKey = await CommandUtils.resolvePrivateKey(
presetData.networkType,
node.main,
KeyName.Main,
node.name,
'creating the voting key link transactions',
);
const account = Account.createFromPrivateKey(mainPrivateKey, presetData.networkType);
const signedTransaction = account.sign(voting, presetData.nemesisGenerationHashSeed);
return await this.storeTransaction(transactionsDirectory, `voting_${node.name}`, signedTransaction.payload);
return Promise.all(
votingFiles.map(async (votingFile) => {
const voting = VotingKeyLinkTransaction.create(
Deadline.createFromDTO('1'),
votingFile.publicKey,
votingFile.startEpoch,
votingFile.endEpoch,
LinkAction.Link,
presetData.networkType,
1,
UInt64.fromUint(0),
);
const account = Account.createFromPrivateKey(mainPrivateKey, presetData.networkType);
const signedTransaction = account.sign(voting, presetData.nemesisGenerationHashSeed);
return this.storeTransaction(transactionsDirectory, `voting_${node.name}`, signedTransaction.payload);
}),
);
}

private async storeTransaction(transactionsDirectory: string, name: string, payload: string): Promise<Transaction> {
Expand Down
78 changes: 40 additions & 38 deletions src/service/LinkService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
import { LogType } from '../logger';
import Logger from '../logger/Logger';
import LoggerFactory from '../logger/LoggerFactory';
import { Addresses, ConfigPreset, NodeAccount } from '../model';
import { Addresses, ConfigPreset, NodeAccount, NodePreset } from '../model';
import { AnnounceService, TransactionFactory } from './AnnounceService';
import { BootstrapUtils } from './BootstrapUtils';
import { ConfigLoader } from './ConfigLoader';
Expand All @@ -52,9 +52,11 @@ const logger: Logger = LoggerFactory.getLogger(LogType.System);
export interface LinkServiceTransactionFactoryParams {
presetData: ConfigPreset;
nodeAccount: NodeAccount;
nodePreset: NodePreset;
mainAccountInfo: AccountInfo;
deadline: Deadline;
maxFee: UInt64;
target: string;
removeOldLinked?: boolean;
}

Expand Down Expand Up @@ -84,6 +86,7 @@ export class LinkService implements TransactionFactory {
this.params.maxFee,
this.params.useKnownRestGateways,
this.params.ready,
this.params.target,
presetData,
addresses,
this,
Expand All @@ -93,10 +96,12 @@ export class LinkService implements TransactionFactory {
async createTransactions({
presetData,
nodeAccount,
nodePreset,
mainAccountInfo,
deadline,
maxFee,
removeOldLinked,
target,
}: LinkServiceTransactionFactoryParams): Promise<Transaction[]> {
const transactions: Transaction[] = [];
const unlink = this.params.unlink;
Expand Down Expand Up @@ -140,44 +145,41 @@ export class LinkService implements TransactionFactory {
(account) => `public key ${account.publicKey}`,
);
}
if (nodeAccount.voting) {
const accountTobeLinked = {
publicKey: nodeAccount.voting.publicKey,
startEpoch: presetData.votingKeyStartEpoch,
endEpoch: presetData.votingKeyEndEpoch,
};
const alreadyLinkedAccount = (mainAccountInfo.supplementalPublicKeys?.voting || []).find((a) =>
LinkService.overlapsVotingAccounts(accountTobeLinked, a),
);

const isAlreadyLinkedSameAccount =
alreadyLinkedAccount?.publicKey.toUpperCase() === accountTobeLinked.publicKey.toUpperCase() &&
alreadyLinkedAccount?.startEpoch === accountTobeLinked.startEpoch &&
alreadyLinkedAccount?.endEpoch === accountTobeLinked.endEpoch;
if (nodePreset.voting) {
const votingFiles = nodeAccount.voting || [];
for (const accountTobeLinked of votingFiles) {
const alreadyLinkedAccount = (mainAccountInfo.supplementalPublicKeys?.voting || []).find((a) =>
LinkService.overlapsVotingAccounts(accountTobeLinked, a),
);
const isAlreadyLinkedSameAccount =
alreadyLinkedAccount?.publicKey.toUpperCase() === accountTobeLinked.publicKey.toUpperCase() &&
alreadyLinkedAccount?.startEpoch === accountTobeLinked.startEpoch &&
alreadyLinkedAccount?.endEpoch === accountTobeLinked.endEpoch;

await this.addTransaction(
alreadyLinkedAccount,
isAlreadyLinkedSameAccount,
unlink,
(votingKeyAccount, action) => {
return VotingKeyLinkTransaction.create(
deadline,
votingKeyAccount.publicKey,
votingKeyAccount.startEpoch,
votingKeyAccount.endEpoch,
action,
presetData.networkType,
1,
maxFee,
);
},
nodeAccount,
'Voting',
accountTobeLinked,
transactions,
removeOldLinked,
(account) => `public key ${account.publicKey}, start epoch ${account.startEpoch}, end epoch ${account.endEpoch}`,
);
await this.addTransaction(
alreadyLinkedAccount,
isAlreadyLinkedSameAccount,
unlink,
(votingKeyAccount, action) => {
return VotingKeyLinkTransaction.create(
deadline,
votingKeyAccount.publicKey,
votingKeyAccount.startEpoch,
votingKeyAccount.endEpoch,
action,
presetData.networkType,
1,
maxFee,
);
},
nodeAccount,
'Voting',
accountTobeLinked,
transactions,
removeOldLinked,
(account) => `public key ${account.publicKey}, start epoch ${account.startEpoch}, end epoch ${account.endEpoch}`,
);
}
}
return transactions;
}
Expand Down
Loading

0 comments on commit c631e13

Please sign in to comment.