-
Notifications
You must be signed in to change notification settings - Fork 54
/
index.ts
158 lines (146 loc) · 6.17 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#!/usr/bin/env node
/* eslint-disable no-console */
import { AddressBalancesResponse, getOnChainAddressBalances } from './AddressBalance';
import { Command } from 'commander';
import { GeneratorMetadata, prepareContent } from './Content';
import { GetChainSyncEventsResponse, getChainSyncEvents as chainSync } from './ChainSyncEvents';
import { Options, SingleBar } from 'cli-progress';
import { createLogger } from 'bunyan';
import { ensureDir, writeFile } from 'fs-extra';
import { toSerializableObject } from '@cardano-sdk/util';
import chalk from 'chalk';
import hash from 'object-hash';
import path from 'path';
const clear = require('clear');
const packageJson = require('../../package.json');
clear();
console.log(chalk.blue('Cardano Golden Test Generator'));
const createProgressBar = (lastblockHeight: number) =>
new SingleBar({
barCompleteChar: '\u2588',
barIncompleteChar: '\u2591',
format: `Syncing from genesis to block ${lastblockHeight} | ${chalk.blue(
'{bar}'
)} | {percentage}% || {value}/{total} Blocks`,
hideCursor: false,
renderThrottle: 300
} as Options);
const program = new Command('cardano-golden-test-generator');
program
.option('--ogmios-host [ogmiosHost]', 'Ogmios host. Defaults to localhost')
.option('--ogmios-port [ogmiosPort]', 'Ogmios TCP port. Defaults to 1337', (port) => Number.parseInt(port))
.option('--ogmios-tls [ogmiosTls]', 'Is Ogmios being served over a secure connection?. Defaults to false', (port) =>
Number.parseInt(port)
);
program
.command('address-balance')
.description('Balance of addresses, determined by syncing the chain from genesis')
.argument('[addresses]', 'Comma-separated list of addresses', (addresses) =>
addresses.split(',').filter((a) => a !== '')
)
.requiredOption('--at-blocks [atBlocks]', 'Balance of the addresses at block heights', (heights) =>
heights
.split(',')
.filter((b) => b !== '')
.map((height) => Number.parseInt(height))
)
.option('--log-level [logLevel]', 'Minimum log level', 'info')
.requiredOption('--out-dir [outDir]', 'File path to write results to')
.action(async (addresses: string[], { atBlocks, logLevel, outDir }) => {
try {
const { ogmiosHost, ogmiosPort, ogmiosTls } = program.opts();
const atBlockHeights = atBlocks.sort((a: number, b: number) => a - b);
const lastBlockHeight = atBlockHeights[atBlockHeights.length - 1];
const logger = createLogger({ level: logLevel, name: 'address-balance' });
const progress = createProgressBar(lastBlockHeight);
await ensureDir(outDir);
progress.start(lastBlockHeight, 0);
const { balances, metadata } = await getOnChainAddressBalances(addresses, atBlockHeights, {
logger,
ogmiosConnectionConfig: { host: ogmiosHost, port: ogmiosPort, tls: ogmiosTls },
onBlock: (blockHeight) => progress.update(blockHeight)
});
const content = await prepareContent<AddressBalancesResponse['balances']>(metadata, balances);
progress.stop();
const fileName = path.join(outDir, `address-balances-${hash(content)}.json`);
logger.info(`Writing ${fileName}`);
await writeFile(fileName, JSON.stringify(toSerializableObject(content), undefined, 2));
process.exit(0);
} catch (error) {
console.error(error);
process.exit(1);
}
});
const mapBlockHeights = (blockHeights: string) =>
blockHeights
.split(',')
.filter((b) => b !== '')
.flatMap((blockHeightSpec) => {
const [from, to] = blockHeightSpec.split('..').map((blockHeight) => Number.parseInt(blockHeight));
if (!to) {
// 0 is not supported, as such range doesn't make sense
if (!Number.isNaN(from)) return [from]; // single block
throw new Error('blockHeights must be either numbers or ranges, see --help');
}
const result: number[] = [];
for (let blockHeight = from; blockHeight <= to; blockHeight++) {
result.push(blockHeight);
}
return result;
});
program
.command('chain-sync-events')
.description('Dump the requested blocks (rollForward) in their raw structure and simulate rollbacks')
.argument(
'[blockHeights]',
`Comma-separated sorted list of blocks by number.
Use "-" for rollback to a block, e.g. 10,11,-10,11
Use ".." for block ranges (inclusive), e.g. 0..9`
)
.requiredOption('--out-dir [outDir]', 'File path to write results to')
.option('--log-level [logLevel]', 'Minimum log level', 'info')
.action(async (blockHeightsInput: string, { logLevel, outDir }) => {
try {
const { ogmiosHost, ogmiosPort, ogmiosTls } = program.opts();
const blockHeights = mapBlockHeights(blockHeightsInput);
const lastblockHeight = blockHeights[blockHeights.length - 1];
const logger = createLogger({ level: logLevel, name: 'chain-sync-events' });
const progress = createProgressBar(lastblockHeight);
await ensureDir(outDir);
progress.start(lastblockHeight, 0);
const { events: data, metadata } = await chainSync(blockHeights, {
logger,
ogmiosConnectionConfig: { host: ogmiosHost, port: ogmiosPort, tls: ogmiosTls },
onBlock: (blockHeight) => {
progress.update(blockHeight);
}
});
const fullMetadata: GeneratorMetadata['metadata'] = {
...metadata,
options: {
blockHeights: blockHeightsInput
}
};
progress.stop();
const content = await prepareContent<GetChainSyncEventsResponse['events']>(fullMetadata, data);
const fileName = path.join(outDir, `blocks-${hash(content)}.json`);
logger.info(`Writing ${fileName}`);
await writeFile(fileName, JSON.stringify(toSerializableObject(content), undefined, 2));
process.exit(0);
} catch (error) {
console.error(error);
process.exit(1);
}
});
program.version(packageJson.version);
if (process.argv.slice(2).length === 0) {
program.outputHelp();
process.exit(1);
} else {
program.parseAsync(process.argv).catch((error) => {
console.error(error);
process.exit(0);
});
}
type PromiseType<P> = P extends Promise<infer T> ? T : never;
export type ChainSyncData = PromiseType<ReturnType<typeof prepareContent<GetChainSyncEventsResponse['events']>>>;