Skip to content

Commit

Permalink
fix(epochs): move epoch to class that fetches epoch information from …
Browse files Browse the repository at this point in the history
…contract

Since we are not computing epochs ourselves, we need to fetch the epoch information from the contract when we run a new report. This moves that logic to a class that fetches epoch information on every execution of running a report. It will cache the result until the epoch is over, based on network time rather than local system time.
  • Loading branch information
dtfiedler committed Jun 11, 2024
1 parent 2ffb3e2 commit 4a51a7a
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 97 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"url": "https://github.com/ar-io/ar-io-observer"
},
"dependencies": {
"@ar.io/sdk": "1.2.0-alpha.1",
"@ar.io/sdk": "1.2.0-alpha.2",
"@ardrive/ardrive-promise-cache": "^1.1.4",
"@ardrive/turbo-sdk": "^1.0.1",
"ajv": "^8.12.0",
Expand Down
80 changes: 80 additions & 0 deletions src/epochs/contract-epoch-source.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* AR.IO Observer
* Copyright (C) 2023 Permanent Data Solutions, Inc. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { AoIORead, IO } from '@ar.io/sdk';

import * as config from '../config.js';
import { BlockSource, EpochTimestampParams, HeightSource } from '../types.js';
import { EpochTimestampSource as IEpochTimestampSource } from '../types.js';

export class ContractEpochSource implements IEpochTimestampSource {
private contract: AoIORead;
private blockSource: BlockSource;
private heightSource: HeightSource;
private epochParams: EpochTimestampParams | undefined;

constructor({
contract = IO.init({ processId: config.IO_PROCESS_ID }),
blockSource,
heightSource,
}: {
contract?: AoIORead;
blockSource: BlockSource;
heightSource: HeightSource;
}) {
this.contract = contract;
this.blockSource = blockSource;
this.heightSource = heightSource;
}

async getEpochParams(): Promise<EpochTimestampParams> {
// cache the epoch params for the duration of the epoch to avoid unnecessary contract calls
const height = await this.heightSource.getHeight();
const block = await this.blockSource.getBlockByHeight(height);
const networkTimestamp = block.timestamp * 1000;
if (
this.epochParams !== undefined &&
this.epochParams.epochEndTimestamp > networkTimestamp
) {
return this.epochParams;
}
const { startTimestamp, startHeight, endTimestamp, epochIndex } =
await this.contract.getCurrentEpoch();
return {
epochStartTimestamp: startTimestamp,
epochStartHeight: startHeight,
epochEndTimestamp: endTimestamp,
epochIndex,
};
}

async getEpochStartTimestamp(): Promise<number> {
return this.getEpochParams().then((params) => params.epochStartTimestamp);
}

async getEpochEndTimestamp(): Promise<number> {
return this.getEpochParams().then((params) => params.epochEndTimestamp);
}

async getEpochStartHeight(): Promise<number> {
return this.getEpochParams().then((params) => params.epochStartHeight);
}

async getEpochIndex(): Promise<number> {
return this.getEpochParams().then((params) => params.epochIndex);
}
}
54 changes: 1 addition & 53 deletions src/protocol.ts → src/epochs/static-epoch-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,10 @@
import {
HeightSource,
EpochHeightSource as IEpochHeightSource,
EpochTimestampSource as IEpochTimestampSource,
} from './types.js';
} from '../types.js';

export const START_HEIGHT = 0;
export const START_TIMESTAMP = 0;
export const EPOCH_BLOCK_LENGTH_MS = 60 * 1000 * 60 * 24; // 1 day
export const EPOCH_BLOCK_LENGTH = 5000;
export const TESTNET_CONTRACT_SETTINGS = {
minLockLength: 5,
maxLockLength: 720 * 365 * 3,
minNetworkJoinStakeAmount: 5_000,
minGatewayJoinLength: 2,
gatewayLeaveLength: 2,
operatorStakeWithdrawLength: 5,
};

export function getEpochEnd({
startHeight,
Expand Down Expand Up @@ -72,13 +61,6 @@ interface EpochParams {
epochBlockLength: number;
}

interface EpochTimestampParams {
epochStartTimestamp: number;
epochStartHeight: number;
epochEndTimestamp: number;
epochIndex: number;
}

export class EpochHeightSource implements IEpochHeightSource {
private heightSource: HeightSource;
private epochParams: EpochParams;
Expand Down Expand Up @@ -113,37 +95,3 @@ export class EpochHeightSource implements IEpochHeightSource {
});
}
}

export class EpochTimestampSource implements IEpochTimestampSource {
private epochParams: EpochTimestampParams;

constructor({
epochParams = {
epochStartTimestamp: 0,
epochEndTimestamp: 1000 * 60 * 60 * 24, // 1 day
epochStartHeight: 0,
epochIndex: 0,
},
}: {
epochParams?: EpochTimestampParams;
heightSource: HeightSource;
}) {
this.epochParams = epochParams;
}

async getEpochStartTimestamp(): Promise<number> {
return this.epochParams.epochStartTimestamp;
}

async getEpochEndTimestamp(): Promise<number> {
return this.epochParams.epochEndTimestamp;
}

async getEpochStartHeight(): Promise<number> {
return this.epochParams.epochStartHeight;
}

async getEpochIndex(): Promise<number> {
return this.epochParams.epochIndex;
}
}
45 changes: 6 additions & 39 deletions src/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,14 @@ import { CachedEntropySource } from './entropy/cached-entropy-source.js';
import { ChainEntropySource } from './entropy/chain-entropy-source.js';
import { CompositeEntropySource } from './entropy/composite-entropy-source.js';
import { RandomEntropySource } from './entropy/random-entropy-source.js';
import { ContractEpochSource } from './epochs/contract-epoch-source.js';
import { ContractHostsSource } from './hosts/contract-hosts-source.js';
import { StaticHostsSource } from './hosts/static-hosts-source.js';
import log from './log.js';
import { ContractNamesSource } from './names/contract-names-source.js';
import { RandomArnsNamesSource } from './names/random-arns-names-source.js';
import { StaticArnsNameList } from './names/static-arns-name-list.js';
import { Observer } from './observer.js';
import {
EPOCH_BLOCK_LENGTH_MS,
EpochTimestampSource,
START_TIMESTAMP,
} from './protocol.js';
import { ContractReportSink } from './store/contract-report-sink.js';
import { FsReportStore } from './store/fs-report-store.js';
import {
Expand Down Expand Up @@ -122,40 +118,10 @@ const observedGatewayHostList =
contract: networkContract,
});

// Attempt to read the start height and epoch block length from the contract - default to constants if it fails
const {
startTimestamp: epochStartTimestamp,
endTimestamp: epochEndTimestamp,
startHeight: epochStartHeight,
epochIndex,
} = await networkContract.getCurrentEpoch().catch((error: any) => {
log.error(`Unable to get start height from contract - using default values`, {
message: error?.message,
stack: error?.stack,
});
return {
startTimestamp: START_TIMESTAMP,
endTimestamp: EPOCH_BLOCK_LENGTH_MS,
epochIndex: 0,
startHeight: 0,
};
});

log.info('Using epoch information for entropy and reports', {
epochStartTimestamp,
epochEndTimestamp,
epochIndex,
epochStartHeight,
});

export const epochSelector = new EpochTimestampSource({
export const epochSource = new ContractEpochSource({
contract: networkContract,
blockSource: chainSource,
heightSource: chainSource,
epochParams: {
epochStartTimestamp: epochStartTimestamp,
epochEndTimestamp: epochEndTimestamp,
epochIndex: epochIndex,
epochStartHeight: epochStartHeight,
},
});

const namesSource = new ContractNamesSource({
Expand Down Expand Up @@ -193,7 +159,7 @@ const chosenNamesSource = new RandomArnsNamesSource({
export const observer = new Observer({
observerAddress: config.OBSERVER_WALLET,
referenceGatewayHost: config.REFERENCE_GATEWAY_HOST,
epochSource: epochSelector,
epochSource,
observedGatewayHostList,
prescribedNamesSource: namesSource,
chosenNamesSource,
Expand Down Expand Up @@ -306,6 +272,7 @@ export async function updateAndSaveCurrentReport() {
log.info('Report cached');

log.info('Getting observers from contract state...');
const epochIndex = await epochSource.getEpochIndex();
// Get selected observers for the current epoch from the contract
const observers: string[] = await networkContract
.getPrescribedObservers({ epochIndex })
Expand Down
12 changes: 12 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ export interface HeightSource {
getHeight(): Promise<number>;
}

//
// Epochs
//

export interface EpochTimestampParams {
epochStartTimestamp: number;
epochStartHeight: number;
epochEndTimestamp: number;
epochIndex: number;
}

// deprecated
export interface EpochHeightSource {
getEpochStartHeight(): Promise<number>;
getEpochEndHeight(): Promise<number>;
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@
call-me-maybe "^1.0.1"
js-yaml "^4.1.0"

"@ar.io/sdk@1.2.0-alpha.1":
version "1.2.0-alpha.1"
resolved "https://registry.yarnpkg.com/@ar.io/sdk/-/sdk-1.2.0-alpha.1.tgz#16891e85feab3b4197191c4100272225bf866f34"
integrity sha512-4C4JBo6GuaqEm4FnRvJ6n9z4dYA2g++duDRiErsRnOeqmt0ZpuHA4p3vucMBT5d6Bp3nrgckPx+estxRGUqDzg==
"@ar.io/sdk@1.2.0-alpha.2":
version "1.2.0-alpha.2"
resolved "https://registry.yarnpkg.com/@ar.io/sdk/-/sdk-1.2.0-alpha.2.tgz#5a9c94ee1574d7868dae008be218e492ec6d4dc2"
integrity sha512-9qyMXXFJAYZ8bVIQpH43aLVyXNwn47Qpk0roSPsJ6LBkjOuDZUeiEQoEnPkdhJZciWiKcvPJC9D3Zs0jExxn+g==
dependencies:
"@permaweb/aoconnect" "^0.0.55"
arbundles "0.11.0"
Expand Down

0 comments on commit 4a51a7a

Please sign in to comment.