-
Notifications
You must be signed in to change notification settings - Fork 7
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
SHARD-1708: Add network account listOfChanges update script #126
Open
S0naliThakur
wants to merge
1
commit into
dev
Choose a base branch
from
SHARD-1708
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+191
−1
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
import { readFileSync } from 'fs' | ||
import { resolve, join } from 'path' | ||
import { overrideDefaultConfig, config } from '../src/Config' | ||
import * as Crypto from '../src/Crypto' | ||
import * as dbstore from '../src/dbstore' | ||
import * as AccountDB from '../src/dbstore/accounts' | ||
import { startSaving } from '../src/saveConsoleOutput' | ||
import * as Logger from '../src/Logger' | ||
import { accountSpecificHash } from '../src/shardeum/calculateAccountHash' | ||
import { addSigListeners } from '../src/State' | ||
import { Utils as StringUtils } from '@shardeum-foundation/lib-types' | ||
import { initAjvSchemas } from '../src/types/ajv/Helpers' | ||
import { initializeSerialization } from '../src/utils/serialization/SchemaHelpers' | ||
import * as CycleDB from '../src/dbstore/cycles' | ||
import * as readline from 'readline' | ||
|
||
// Configuration schema and default values | ||
interface ConfigSchema { | ||
p2p: { | ||
baselineNodes: number | ||
minNodes: number | ||
maxNodes: number | ||
} | ||
// Add more configs as needed | ||
// sharding: { | ||
// nodesPerConsensusGroup: number | ||
// executeInOneShard: boolean | ||
// } | ||
} | ||
|
||
const defaultConfig: ConfigSchema = { | ||
p2p: { | ||
baselineNodes: 1280, | ||
minNodes: 1280, | ||
maxNodes: 1280, | ||
}, | ||
// Add default values for new configurations | ||
// sharding: { | ||
// nodesPerConsensusGroup: 1280, | ||
// executeInOneShard: false, | ||
// }, | ||
} | ||
|
||
// Readline interface for terminal input | ||
const rl = readline.createInterface({ | ||
input: process.stdin as NodeJS.ReadableStream, | ||
output: process.stdout as NodeJS.WritableStream, | ||
}) | ||
|
||
const askQuestion = (query: string): Promise<string> => { | ||
return new Promise((resolve) => rl.question(query, resolve)) | ||
} | ||
|
||
// Utility function to validate and parse input based on expected type | ||
const parseInput = (value: string, expectedType: string, defaultValue: any): any => { | ||
if (value === '') return defaultValue | ||
|
||
switch (expectedType) { | ||
case 'number': | ||
const parsedNumber = parseFloat(value) | ||
return isNaN(parsedNumber) ? defaultValue : parsedNumber | ||
case 'boolean': | ||
return value.toLowerCase() === 'true' | ||
case 'string': | ||
return value | ||
default: | ||
return defaultValue | ||
} | ||
} | ||
|
||
// Function to dynamically ask configuration questions | ||
const askConfigQuestions = async (): Promise<ConfigSchema> => { | ||
const userConfig: Partial<ConfigSchema> = {} | ||
|
||
for (const section in defaultConfig) { | ||
userConfig[section] = {} | ||
for (const key in defaultConfig[section]) { | ||
let attempts = 0 | ||
const maxAttempts = 3 | ||
let isValid = false | ||
|
||
while (!isValid && attempts < maxAttempts) { | ||
const value = await askQuestion(`Enter ${section}.${key} (default: ${defaultConfig[section][key]}): `) | ||
const expectedType = typeof defaultConfig[section][key] | ||
const parsedValue = parseInput(value, expectedType, defaultConfig[section][key]) | ||
|
||
if (parsedValue !== defaultConfig[section][key] || value === '') { | ||
userConfig[section][key] = parsedValue | ||
isValid = true | ||
} else { | ||
attempts++ | ||
console.log(`Invalid input. Please enter a valid ${expectedType} for ${section}.${key}. (${attempts}/${maxAttempts} attempts)`) | ||
} | ||
} | ||
|
||
if (!isValid) { | ||
console.log(`Maximum attempts reached. Using default value for ${section}.${key}.`) | ||
userConfig[section][key] = defaultConfig[section][key] | ||
} | ||
} | ||
} | ||
|
||
return userConfig as ConfigSchema | ||
} | ||
|
||
// Function to get the latest cycle from db | ||
const getCycleNumber = async (): Promise<number> => { | ||
const latestCycle = await CycleDB.queryLatestCycleRecords(1) | ||
const latestCycleRecord = latestCycle[0] | ||
const latestCycleNumber = latestCycleRecord.counter | ||
|
||
let cycleNumberInput: number | ||
do { | ||
cycleNumberInput = parseInt( | ||
await askQuestion('Enter cycle number (must be less than the latest cycle number, latest cycle number is: ' + latestCycleNumber + '): '), | ||
10 | ||
) | ||
if (isNaN(cycleNumberInput) || cycleNumberInput >= latestCycleNumber) { | ||
console.log(`Please enter a valid cycle number less than ${latestCycleNumber}.`) | ||
} | ||
} while (isNaN(cycleNumberInput) || cycleNumberInput >= latestCycleNumber) | ||
|
||
return cycleNumberInput | ||
} | ||
|
||
const runProgram = async (): Promise<void> => { | ||
initAjvSchemas() | ||
initializeSerialization() | ||
|
||
// Load configuration from file | ||
const file = join(process.cwd(), 'archiver-config.json') | ||
overrideDefaultConfig(file) | ||
|
||
const hashKey = config.ARCHIVER_HASH_KEY | ||
Crypto.setCryptoHashKey(hashKey) | ||
|
||
let logsConfig | ||
try { | ||
logsConfig = StringUtils.safeJsonParse(readFileSync(resolve(__dirname, '../archiver-log.json'), 'utf8')) | ||
} catch (err) { | ||
console.log('Failed to parse archiver log file:', err) | ||
} | ||
const logDir = `${config.ARCHIVER_LOGS}/${config.ARCHIVER_IP}_${config.ARCHIVER_PORT}` | ||
const baseDir = '.' | ||
logsConfig.dir = logDir | ||
Logger.initLogger(baseDir, logsConfig) | ||
if (logsConfig.saveConsoleOutput) { | ||
startSaving(join(baseDir, logsConfig.dir)) | ||
} | ||
|
||
await dbstore.initializeDB(config) | ||
|
||
const userConfig = await askConfigQuestions() // Get config values | ||
const cycleNumber = await getCycleNumber() // Get the latest cycle number | ||
|
||
addSigListeners() | ||
|
||
const networkAccountId = config.globalNetworkAccount | ||
const networkAccount = (await AccountDB.queryAccountByAccountId(networkAccountId)) as AccountDB.AccountsCopy | ||
|
||
// Add changes to listOfChanges | ||
const changes = { | ||
change: userConfig, | ||
cycle: cycleNumber, | ||
} | ||
|
||
console.log('Proposed Changes:', JSON.stringify(changes, null, 2)) | ||
|
||
const confirmation = ( | ||
await askQuestion('Are you sure you want to proceed with these changes? (yes/no): ') | ||
).trim() | ||
console.log(`User input: ${confirmation}`) | ||
if (confirmation.toLowerCase() === 'yes' || confirmation.toLowerCase() === 'y') { | ||
networkAccount.data.listOfChanges.push(changes) | ||
|
||
const calculatedAccountHash = accountSpecificHash(networkAccount.data) | ||
networkAccount.hash = calculatedAccountHash | ||
networkAccount.data.hash = calculatedAccountHash | ||
|
||
await AccountDB.insertAccount(networkAccount) | ||
console.log('✅ Changes were applied.') | ||
} else { | ||
console.log('❌ Changes were not applied.') | ||
} | ||
|
||
await dbstore.closeDatabase() | ||
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 have a try/finally setup here to ensure DB closure? |
||
rl.close() | ||
} | ||
|
||
runProgram() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
I am not sure how I feel about assigning a defaultValue. If we're using this code to overwrite the network account, I somewhat feel that there should only be intentional and explicit changes.
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.
Clarifying... the question is should we treat the results differently if we ended up with value or defaultValue?