Skip to content

Commit

Permalink
add fallback for web3 json-rpc...
Browse files Browse the repository at this point in the history
  • Loading branch information
Jagden committed Dec 18, 2024
1 parent f40fec4 commit 7e05827
Show file tree
Hide file tree
Showing 18 changed files with 251 additions and 51 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ jobs:
steps:
- name: Check out repository
uses: actions/checkout@v3
with:
fetch-depth: 0
# with:
# fetch-depth: 0
- name: Setup node
uses: actions/setup-node@v3
with:
Expand Down
15 changes: 14 additions & 1 deletion boyar/create-version-file.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
#!/bin/bash -e

set -x

git status

#git fetch --tags
git pull --tags

echo boo

git tag

if [[ ! -z "$CIRCLE_TAG" ]]; then
echo "This is a release run - Updating the .version file to indicate the correct Semver"
echo "For this release ($CIRCLE_TAG)..."
Expand All @@ -13,7 +24,9 @@ if [[ ! -z "$CIRCLE_TAG" ]]; then

echo "$CIRCLE_TAG" > .version
else
LATEST_SEMVER=$(git describe --tags --abbrev=0)
#LATEST_SEMVER=$(git describe --tags --abbrev=0)
LATEST_SEMVER=$(git tag --sort=-v:refname | head -n 1)
SHORT_COMMIT=$(git rev-parse HEAD | cut -c1-8)
echo "$LATEST_SEMVER-$SHORT_COMMIT" > .version
echo "$LATEST_SEMVER-$SHORT_COMMIT"
fi
8 changes: 2 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"clean": "rimraf ./dist/",
"prebuild": "npm run clean",
"build": "tsc --skipLibCheck -p ./tsconfig.prod.json && ./boyar/create-version-file.sh && docker build -t local/management-service .",
"buildonly": "tsc --skipLibCheck -p ./tsconfig.prod.json && ./boyar/create-version-file.sh",
"test": "ava --verbose --timeout=10m --serial --fail-fast",
"test:quick": "echo '-- TEST --' && ava --verbose --timeout=10m --serial",
"test:e2e": "ava --verbose --timeout=10m --serial --config ./ava.config.e2e.js",
Expand All @@ -44,8 +45,8 @@
"@types/node": "^14.14.16",
"@types/node-fetch": "^2.5.5",
"@types/yargs": "^15.0.4",
"@typescript-eslint/eslint-plugin": "^2.25.0",
"@typescript-eslint/parser": "^2.25.0",
"@typescript-eslint/eslint-plugin": "^2.34.0",
"@typescript-eslint/parser": "^2.34.0",
"ava": "^3.5.1",
"docker-compose-mocha": "^1.2.0",
"eslint": "^6.8.0",
Expand Down
2 changes: 1 addition & 1 deletion src/api/render-node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ test.serial('[integration] getNodeManagement responds according to Ethereum and
t.timeout(5 * 60 * 1000);

const ethereum = new EthereumTestDriver(true);
const ethereumEndpoint = 'http://localhost:7545';
const ethereumEndpoint = ['http://localhost:7545'];
const maticEndpoint = 'mock-endpoint';
const finalityBufferBlocks = 5;

Expand Down
6 changes: 3 additions & 3 deletions src/cli-args.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const configPath = 'some/path/config.json';

const minimalConfigValue = {
EthereumGenesisContract: 'bar',
EthereumEndpoint: 'http://localhost:7545',
EthereumEndpoint: ['http://localhost:7545'],
'node-address': 'ecfcccbc1e54852337298c7e90f5ecee79439e67',
};
const inputConfigValue = {
Expand Down Expand Up @@ -71,7 +71,7 @@ test('parseOptions: environment variables and no config', (t) => {

t.assert((output.ExternalLaunchConfig = {}));
t.assert((output.StatusJsonPath = './status/status.json'));
t.assert((output.EthereumEndpoint = mockEthereumEndpoint));
t.assert((output.EthereumEndpoint = [mockEthereumEndpoint]));
t.assert((output['node-address'] = mockNodeAddress));
});

Expand All @@ -87,7 +87,7 @@ test('parseOptions: env vars take precedence', (t) => {

const output = parseArgs(['--config', configPath]);

t.assert((output.EthereumEndpoint = mockEthereumEndpoint));
t.assert((output.EthereumEndpoint = [mockEthereumEndpoint]));
t.assert((output['node-address'] = mockNodeAddress));
});

Expand Down
10 changes: 10 additions & 0 deletions src/cli-args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ import * as Logger from './logger';

import { setConfigEnvVars } from './env-var-args';

function ensureEthereumEndpointIsArray(obj: ServiceConfiguration): void {
if (!obj.EthereumEndpoint) {
obj.EthereumEndpoint = []; // Initialize as an empty array if the field is undefined or null
} else if (!Array.isArray(obj.EthereumEndpoint)) {
obj.EthereumEndpoint = [obj.EthereumEndpoint]; // Convert to array if it's not already one
}
}

export function parseArgs(argv: string[]): ServiceConfiguration {
const options = yargs(argv)
.option('config', {
Expand All @@ -28,6 +36,8 @@ export function parseArgs(argv: string[]): ServiceConfiguration {
// Support passing required config values via environment variables
setConfigEnvVars(config);

ensureEthereumEndpointIsArray(config);

const validationErrors = validateServiceConfiguration(config);
if (validationErrors) {
Logger.error(`Invalid JSON config: '${JSON.stringify(config)}'.`);
Expand Down
2 changes: 1 addition & 1 deletion src/config.example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const exampleConfig: ServiceConfiguration = {
Port: 8080,
EthereumGenesisContract: '0xD859701C81119aB12A1e62AF6270aD2AE05c7AB3',
EthereumFirstBlock: 11191390,
EthereumEndpoint: 'http://ganache:7545',
EthereumEndpoint: ['http://ganache:7545'],
DeploymentDescriptorUrl: 'https://deployment.orbs.network/mainnet.json',
ElectionsAuditOnly: false,
StatusJsonPath: './status/status.json',
Expand Down
8 changes: 4 additions & 4 deletions src/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ test('accepts legal config', (t) => {
BootstrapMode: false,
Port: 2,
EthereumGenesisContract: 'foo',
EthereumEndpoint: 'http://localhost:7545',
EthereumEndpoint: ['http://localhost:7545'],
EthereumPollIntervalSeconds: 0.1,
EthereumRequestsPerSecondLimit: 0,
ElectionsStaleUpdateSeconds: 7 * 24 * 60 * 60,
Expand All @@ -34,7 +34,7 @@ test('declines illegal config (1)', (t) => {
BootstrapMode: false,
Port: 2,
EthereumGenesisContract: 'foo',
EthereumEndpoint: 'http://localhost:7545',
EthereumEndpoint: ['http://localhost:7545'],
EthereumPollIntervalSeconds: 0.1,
EthereumRequestsPerSecondLimit: 0,
ElectionsStaleUpdateSeconds: 7 * 24 * 60 * 60,
Expand All @@ -60,7 +60,7 @@ test('declines illegal config (2)', (t) => {
BootstrapMode: false,
Port: 2,
EthereumGenesisContract: 'foo',
EthereumEndpoint: 'foo-bar:123',
EthereumEndpoint: ['foo-bar:123'],
EthereumPollIntervalSeconds: 0.1,
EthereumRequestsPerSecondLimit: 0,
ElectionsStaleUpdateSeconds: 7 * 24 * 60 * 60,
Expand All @@ -77,6 +77,6 @@ test('declines illegal config (2)', (t) => {
Verbose: true,
'node-address': 'ecfcccbc1e54852337298c7e90f5ecee79439e67',
}),
['Ethereum endpoint is not a valid url']
['Ethereum endpoint Item 1: must be a valid URL']
);
});
55 changes: 48 additions & 7 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export interface ServiceConfiguration {
BootstrapMode: boolean;
Port: number;
EthereumGenesisContract: string;
EthereumEndpoint: string;
EthereumEndpoint: string[];
/** @deprecated Use `EthereumEndpoint` instead */
MaticEndpoint?: string;
DeploymentDescriptorUrl: string;
Expand Down Expand Up @@ -47,6 +47,39 @@ export const defaultServiceConfiguration = {
Verbose: false,
};

// Define the types for the custom validator function
validate.validators.array = function (
value: unknown,
options: { item?: validate.ValidateOption },
key: string
): string | undefined {
// Check if the value is an array
if (!Array.isArray(value)) {
return `${key} must be an array.`;
}

// If there are item-level validation options, validate each item
if (options && options.item) {
const errors = value
.map((item, index) => {
const error = validate.single(item, options.item);
if (error) {
return `Item ${index + 1}: ${error.join(', ')}`;
}
return undefined;
})
.filter((error): error is string => !!error); // Narrow the type to strings

// If there are errors, return them as a single string
if (errors.length > 0) {
return errors.join('; ');
}
}

// Return undefined if there are no errors
return undefined;
};

export function validateServiceConfiguration(c: Partial<ServiceConfiguration>): string[] | undefined {
const serviceConfigConstraints = {
BootstrapMode: {
Expand Down Expand Up @@ -101,12 +134,20 @@ export function validateServiceConfiguration(c: Partial<ServiceConfiguration>):
numericality: { noStrings: true },
},
EthereumEndpoint: {
presence: { allowEmpty: false },
type: 'string',
url: {
allowLocal: true,
},
},
presence: true, // Ensure the attribute is present
type: "array", // Ensure it's an array
array: {
item: {
presence: true, // Ensure each item is not empty
type: "string", // Ensure each item in the array is a string
format: {
pattern: /^(https?:\/\/[^\s$.?#].[^\s]*)$/i, // URL regex pattern
message: "must be a valid URL"
}
}
}
},

EthereumGenesisContract: {
presence: { allowEmpty: false },
type: 'string',
Expand Down
6 changes: 6 additions & 0 deletions src/env-var-args.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ test('setConfigEnvVars uses environment variables when set', (t) => {
continue;
}

if (key == 'EthereumEndpoint') {
t.deepEqual(input[key as keyof ServiceConfiguration], [mockEnv.ETHEREUM_ENDPOINT]);
continue;
}

//console.log (key, input[key as keyof ServiceConfiguration], mockEnv[camelCaseToSnakeCase(key) as keyof typeof mockEnv]);
t.assert(input[key as keyof ServiceConfiguration] === mockEnv[camelCaseToSnakeCase(key) as keyof typeof mockEnv]);
}
});
Expand Down
3 changes: 2 additions & 1 deletion src/env-var-args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export function setConfigEnvVars(config: ServiceConfiguration): void {
config.BootstrapMode = process.env.BOOTSTRAP_MODE ? process.env.BOOTSTRAP_MODE === 'true' : config.BootstrapMode;
config.Port = process.env.PORT ? Number(process.env.PORT) : config.Port;
config.EthereumGenesisContract = process.env.ETHEREUM_GENESIS_CONTRACT ?? config.EthereumGenesisContract;
config.EthereumEndpoint = process.env.ETHEREUM_ENDPOINT ?? config.EthereumEndpoint;
// parse ETHEREUM_ENDPOINT, if it has multiple values, split by comma
config.EthereumEndpoint = process.env.ETHEREUM_ENDPOINT ? process.env.ETHEREUM_ENDPOINT.split(',') : config.EthereumEndpoint;
config.DeploymentDescriptorUrl = process.env.DEPLOYMENT_DESCRIPTOR_URL ?? config.DeploymentDescriptorUrl;
config.ElectionsAuditOnly = process.env.ELECTIONS_AUDIT_ONLY
? process.env.ELECTIONS_AUDIT_ONLY === 'true'
Expand Down
2 changes: 1 addition & 1 deletion src/ethereum/block-sync.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ test.serial('[integration] BlockSync reads registry for contract addresses', asy
t.timeout(5 * 60 * 1000);

const ethereum = new EthereumTestDriver(true);
const ethereumEndpoint = 'http://localhost:7545';
const ethereumEndpoint = ['http://localhost:7545'];
const finalityBufferBlocks = 5;

// setup Ethereum state
Expand Down
33 changes: 31 additions & 2 deletions src/ethereum/block-sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class BlockSync {
private eventFetchers: { [T in EventName]: EventFetcher };

constructor(private state: StateManager, private config: BlockSyncConfiguration) {
this.reader = new EthereumReader(config);
this.reader = new EthereumReader(config, () => this.resetAllContracts());
this.lastProcessedBlock = config.EthereumFirstBlock;
this.eventFetchers = {
ContractAddressUpdated: new LookaheadEventFetcher('ContractAddressUpdated', this.reader),
Expand Down Expand Up @@ -63,6 +63,14 @@ export class BlockSync {
// we read blocks 1-1000 from old address and blocks 1001+ from the new address.
// This simplification is ok because contracts will be locked from emitting events during transition.

resetAllContracts() {
console.log('resetAllContracts');
for (const eventName of eventNames) {
console.log ('resetContract', eventName);
this.eventFetchers[eventName].resetContract();
}
}

async processEventsInBlock(blockNumber: number, latestAllowedBlock: number) {
// update all contract addresses according to state to track changes in contract registry
for (const eventName of eventNames) {
Expand All @@ -82,7 +90,28 @@ export class BlockSync {
const blockTime = await this.reader.getRefTime(blockNumber);
this.state.applyNewEvents(blockNumber, blockTime, sorted);
this.state.applyNewTimeRef(blockTime, blockNumber);
Logger.log(`BlockSync: processed ${sorted.length} events in block ${blockNumber} with time ${blockTime}.`);
Logger.log(`BlockSync: processed ${sorted.length} events in block ${blockNumber} with time ${blockTime}, (${this.secondsDeltaToTimeString(blockTime)}).`);
}

secondsDeltaToTimeString(totalSeconds : number) : string {
// Get the current timestamp in seconds
const nowInSeconds = Math.floor(Date.now() / 1000);

// Calculate the delta (difference)
const deltaSeconds = Math.abs(totalSeconds - nowInSeconds);

// Calculate days, hours, minutes, and seconds
const days = Math.floor(deltaSeconds / (24 * 60 * 60));
let remainingSeconds = deltaSeconds % (24 * 60 * 60);

const hours = Math.floor(remainingSeconds / (60 * 60));
remainingSeconds %= 60 * 60;

const minutes = Math.floor(remainingSeconds / 60);
const seconds = remainingSeconds % 60;

// Pad with leading zeros and format as DD:HH:MM:SS
return `${String(days).padStart(2, '0')}d:${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
}

getRequestStats(): DailyStatsData {
Expand Down
Loading

0 comments on commit 7e05827

Please sign in to comment.