Skip to content

Commit

Permalink
Merge branch 'relay-switch-rewarding' into 'main'
Browse files Browse the repository at this point in the history
Songbird & Coston contract change handling for reward calculation

See merge request flarenetwork/ftso-scaling!139
  • Loading branch information
alenabelium committed Oct 18, 2024
2 parents 30bf4a7 + 00303e9 commit 4913b99
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 48 deletions.
10 changes: 10 additions & 0 deletions libs/ftso-core/src/DataManagerForRewarding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,16 @@ export class DataManagerForRewarding extends DataManager {
for (let votingRoundId = firstVotingRoundId; votingRoundId <= lastVotingRoundId; votingRoundId++) {
const fastUpdateFeeds = feedValuesResponse.data[votingRoundId - firstVotingRoundId];
const fastUpdateSubmissions = feedUpdates.data[votingRoundId - firstVotingRoundId];
// Handles the 'undefined' value in fastUpdateFeeds - this can happen on FastUpdater contract change
if(!fastUpdateFeeds) {
throw new Error(`FastUpdateFeeds is undefined for voting round ${votingRoundId}`);
}

if (fastUpdateFeeds as any === "CONTRACT_CHANGE") {
result.push(undefined);
this.logger.error(`WARN: FastUpdateFeeds contract change for voting round ${votingRoundId}`);
continue;
}
const value: FastUpdatesDataForVotingRound = {
votingRoundId,
feedValues: fastUpdateFeeds.feeds,
Expand Down
171 changes: 153 additions & 18 deletions libs/ftso-core/src/IndexerClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import { Address, VotingEpochId } from "./voting-types";

import { IPayloadMessage } from "../../fsp-utils/src/PayloadMessage";
import { IRelayMessage } from "../../fsp-utils/src/RelayMessage";
import { ContractDefinitions, ContractMethodNames } from "./configs/contracts";
import {
CONTRACTS,
EPOCH_SETTINGS,
FIRST_DATABASE_INDEX_STATE,
LAST_DATABASE_INDEX_STATE,
networks,
} from "./configs/networks";
import { ContractDefinitions, ContractMethodNames } from "./configs/contracts";
import {
FullVoterRegistrationInfo,
InflationRewardsOffered,
Expand Down Expand Up @@ -49,12 +49,14 @@ export type SubmissionData = GenericSubmissionData<IPayloadMessage<string>[]>;
*/
export interface FinalizationData extends GenericSubmissionData<string> {
successfulOnChain: boolean;
isOldRelay?: boolean;
}
/**
* Parsed finalization data from finalization calls (relay()) on Relay contract.
*/
export interface ParsedFinalizationData extends GenericSubmissionData<IRelayMessage> {
successfulOnChain: boolean;
isOldRelay?: boolean;
}

/**
Expand Down Expand Up @@ -137,7 +139,7 @@ export class IndexerClient {
protected readonly entityManager: EntityManager,
public readonly requiredHistoryTimeSec: number,
protected readonly logger: ILogger
) {}
) { }

protected readonly encoding = EncodingUtils.instance;

Expand Down Expand Up @@ -403,16 +405,35 @@ export class IndexerClient {
...(await this.queryEvents({ ...CONTRACTS.Relay, address: oldSongbirdRelay }, eventName, fromStartTime))
);
}
const oldCostonRelay = "0xA300E71257547e645CD7241987D3B75f2012E0E3";

const secondOldSongbirdRelay = "0x0D462d2Fec11554D64F52D7c5A5C269d748037aD";
if (network == "songbird" && CONTRACTS.Relay.address != secondOldSongbirdRelay) {
this.logger.log(`Querying second old Relay address for Songbird: ${secondOldSongbirdRelay}`);
result.push(
...(await this.queryEvents({ ...CONTRACTS.Relay, address: secondOldSongbirdRelay }, eventName, fromStartTime))
);
}

const oldCostonRelay = "0x32D46A1260BB2D8C9d5Ab1C9bBd7FF7D7CfaabCC";
if (network == "coston" && CONTRACTS.Relay.address != oldCostonRelay) {
this.logger.log(`Querying old Relay address for Coston: ${oldCostonRelay}`);
result.push(
...(await this.queryEvents({ ...CONTRACTS.Relay, address: oldCostonRelay }, eventName, fromStartTime))
);
}

const secondOldCostonRelay = "0xA300E71257547e645CD7241987D3B75f2012E0E3";
if (network == "coston" && CONTRACTS.Relay.address != secondOldCostonRelay) {
this.logger.log(`Querying second old Relay address for Coston: ${secondOldCostonRelay}`);
result.push(
...(await this.queryEvents({ ...CONTRACTS.Relay, address: secondOldCostonRelay }, eventName, fromStartTime))
);
}

// END TEMP CHANGE

result.push(...(await this.queryEvents(CONTRACTS.Relay, eventName, fromStartTime)));
IndexerClient.sortEvents(result);

const data = result.map(event => SigningPolicyInitialized.fromRawEvent(event));
return {
Expand Down Expand Up @@ -558,6 +579,7 @@ export class IndexerClient {
/**
* Queries indexer database for all finalization transactions on the Relay contract in a given timestamp range.
* It returns the result if the indexer database ensures the data availability in the given timestamp range.
* The data may not be in order as it appears on blockchain.
*/
public async getFinalizationDataInRange(
startTime: number,
Expand All @@ -570,30 +592,143 @@ export class IndexerClient {
data: [],
};
}
const transactionsResults = await this.queryTransactions(
// TEMP CHANGE
let oldTransactionsResults: TLPTransaction[] = [];
let secondOldTransactionsResults: TLPTransaction[] = [];
let oldRelay: ContractDefinitions | undefined;
let secondOldRelay: ContractDefinitions | undefined;
const network = process.env.NETWORK as networks;

// Do this for every network with change
const oldCostonRelayAddress = "0x32D46A1260BB2D8C9d5Ab1C9bBd7FF7D7CfaabCC";
if (network === "coston" && CONTRACTS.Relay.address != oldCostonRelayAddress) {
oldRelay = {
...CONTRACTS.Relay,
address: oldCostonRelayAddress,
};
}

const secondOldCostonRelayAddress = "0xA300E71257547e645CD7241987D3B75f2012E0E3";
if (network === "coston" && CONTRACTS.Relay.address != secondOldCostonRelayAddress) {
secondOldRelay = {
...CONTRACTS.Relay,
address: secondOldCostonRelayAddress,
};
}


const oldSongbirdRelayAddress = "0xbA35e39D01A3f5710d1e43FC61dbb738B68641c4";
if (network === "songbird" && CONTRACTS.Relay.address != oldSongbirdRelayAddress) {
oldRelay = {
...CONTRACTS.Relay,
address: oldSongbirdRelayAddress,
};
}

const secondOldSongbirdRelayAddress = "0x0D462d2Fec11554D64F52D7c5A5C269d748037aD";
if (network === "songbird" && CONTRACTS.Relay.address != secondOldSongbirdRelayAddress) {
secondOldRelay = {
...CONTRACTS.Relay,
address: secondOldSongbirdRelayAddress,
};
}

if (oldRelay !== undefined) {
oldTransactionsResults = await this.queryTransactions(
oldRelay,
ContractMethodNames.relay,
startTime,
endTime
);
}

if (secondOldRelay !== undefined) {
secondOldTransactionsResults = await this.queryTransactions(
secondOldRelay,
ContractMethodNames.relay,
startTime,
endTime
);
}

// END TEMP CHANGE
let newTransactionsResults = await this.queryTransactions(
CONTRACTS.Relay,
ContractMethodNames.relay,
startTime,
endTime
);
const finalizations: FinalizationData[] = transactionsResults.map(tx => {
const timestamp = tx.timestamp;
const votingEpochId = EPOCH_SETTINGS().votingEpochForTimeSec(timestamp);
return {
submitAddress: "0x" + tx.from_address,
relativeTimestamp: timestamp - EPOCH_SETTINGS().votingEpochStartSec(votingEpochId),
votingEpochIdFromTimestamp: votingEpochId,
transactionIndex: tx.transaction_index,
timestamp,
blockNumber: tx.block_number,
messages: tx.input,
successfulOnChain: tx.status > 0,
} as FinalizationData;
});

interface Pair {
address: string | undefined;
transactionsResults: TLPTransaction[];
}
const jointTransactionResults: Pair[] = [
{
address: oldRelay?.address,
transactionsResults: oldTransactionsResults
},
{
address: secondOldRelay?.address,
transactionsResults: secondOldTransactionsResults
},
{
address: CONTRACTS.Relay.address,
transactionsResults: newTransactionsResults
}
];

let finalizations: FinalizationData[] = [];
for (let txListPair of jointTransactionResults) {
const { address, transactionsResults } = txListPair;
const isOldRelay = (oldRelay !== undefined && address === oldRelay.address)
|| (secondOldRelay !== undefined && address === secondOldRelay.address);
const tmpFinalizations: FinalizationData[] = transactionsResults.map(tx => {
const timestamp = tx.timestamp;
const votingEpochId = EPOCH_SETTINGS().votingEpochForTimeSec(timestamp);
return {
submitAddress: "0x" + tx.from_address,
relativeTimestamp: timestamp - EPOCH_SETTINGS().votingEpochStartSec(votingEpochId),
votingEpochIdFromTimestamp: votingEpochId,
transactionIndex: tx.transaction_index,
timestamp,
blockNumber: tx.block_number,
messages: tx.input,
successfulOnChain: tx.status > 0,
isOldRelay
} as FinalizationData;
});
finalizations.push(...tmpFinalizations);
}

return {
status: ensureRange,
data: finalizations,
};
}


public static sortEvents(events: TLPEvents[]): TLPEvents[] {
return events.sort((a, b) => {
if (a.timestamp < b.timestamp) {
return -1;
}
if (a.timestamp > b.timestamp) {
return 1;
}
if (a.block_number < b.block_number) {
return -1;
}
if (a.block_number > b.block_number) {
return 1;
}
if (a.log_index < b.log_index) {
return -1;
}
if (a.log_index > b.log_index) {
return 1;
}
return 0;
});
}
}
79 changes: 70 additions & 9 deletions libs/ftso-core/src/IndexerClientForRewarding.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { EntityManager } from "typeorm";
import { BlockAssuranceResult, IndexerClient, IndexerResponse } from "./IndexerClient";
import { ILogger } from "./utils/ILogger";
import { CONTRACTS, EPOCH_SETTINGS } from "./configs/networks";
import { CONTRACTS, COSTON_FAST_UPDATER_SWITCH_VOTING_ROUND_ID, EPOCH_SETTINGS, SONGBIRD_FAST_UPDATER_SWITCH_VOTING_ROUND_ID, networks } from "./configs/networks";
import { FastUpdateFeeds } from "./events/FastUpdateFeeds";
import { FastUpdateFeedsSubmitted } from "./events/FastUpdateFeedsSubmitted";
import { IncentiveOffered } from "./events/IncentiveOffered";
import { FUInflationRewardsOffered } from "./events/FUInflationRewardsOffered";

import { TLPEvents } from "./orm/entities";
export class IndexerClientForRewarding extends IndexerClient {
constructor(
protected readonly entityManager: EntityManager,
Expand All @@ -28,12 +28,38 @@ export class IndexerClientForRewarding extends IndexerClient {
const endTime = EPOCH_SETTINGS().votingEpochStartSec(endVotingRoundId + 2);
const eventName = FastUpdateFeeds.eventName;
const status = await this.ensureEventRange(startTime, endTime);
const result = await this.queryEvents(CONTRACTS.FastUpdater, eventName, startTime, endTime);

const data: FastUpdateFeeds[] = [];
if (status !== BlockAssuranceResult.OK) {
return { status };
}

const result: TLPEvents[] = [];

// TEMP CHANGE for upgrading Relay contract, can be removed in December 2024
const network = process.env.NETWORK as networks;

const oldSongbirdFastUpdater = "0x70e8870ef234EcD665F96Da4c669dc12c1e1c116";
if (network == "songbird" && CONTRACTS.FastUpdater.address != oldSongbirdFastUpdater
&& startVotingRoundId <= SONGBIRD_FAST_UPDATER_SWITCH_VOTING_ROUND_ID) {
this.logger.log(`Querying old FastUpdater address for Songbird: ${oldSongbirdFastUpdater}`);
result.push(
...(await this.queryEvents({ ...CONTRACTS.FastUpdater, address: oldSongbirdFastUpdater }, eventName, startTime, endTime))
);
}

const oldCostonFastUpdater = "0x9B931f5d3e24fc8C9064DB35bDc8FB4bE0E862f9";
if (network == "coston" && CONTRACTS.FastUpdater.address !== oldCostonFastUpdater && startVotingRoundId <= COSTON_FAST_UPDATER_SWITCH_VOTING_ROUND_ID) {
this.logger.log(`Querying old FastUpdater address for Coston: ${oldCostonFastUpdater}`);
result.push(
...(await this.queryEvents({ ...CONTRACTS.FastUpdater, address: oldCostonFastUpdater }, eventName, startTime, endTime))
);
}

// END TEMP CHANGE

result.push(...(await this.queryEvents(CONTRACTS.FastUpdater, eventName, startTime, endTime)));
IndexerClient.sortEvents(result);

const data: FastUpdateFeeds[] = [];
let processed = -1;
for (let i = 0; i < result.length; i++) {
const event = FastUpdateFeeds.fromRawEvent(result[i]);
Expand All @@ -43,9 +69,19 @@ export class IndexerClientForRewarding extends IndexerClient {
data.push(event);
processed = event.votingRoundId;
} else {
// On Coston one event is missing hence special handling
if (network == "coston" && processed + 1 == COSTON_FAST_UPDATER_SWITCH_VOTING_ROUND_ID) {
while (processed + 1 < event.votingRoundId) {
this.logger.error(`Missing FastUpdateFeeds event for Coston: ${processed + 1}`);
data.push("CONTRACT_CHANGE" as any);
processed++;
}
data.push(event);
processed++;
continue;
}
throw new Error(
`FastUpdateFeeds events are not continuous from ${startVotingRoundId} to ${endVotingRoundId}: expected ${
processed + 1
`FastUpdateFeeds events are not continuous from ${startVotingRoundId} to ${endVotingRoundId}: expected ${processed + 1
}, got ${event.votingRoundId}`
);
}
Expand All @@ -70,13 +106,38 @@ export class IndexerClientForRewarding extends IndexerClient {
endVotingRoundId: number
): Promise<IndexerResponse<FastUpdateFeedsSubmitted[][]>> {
const startTime = EPOCH_SETTINGS().votingEpochStartSec(startVotingRoundId);
const endTime = EPOCH_SETTINGS().votingEpochStartSec(endVotingRoundId + 1);
const endTime = EPOCH_SETTINGS().votingEpochStartSec(endVotingRoundId + 1) - 1;
const eventName = FastUpdateFeedsSubmitted.eventName;
const status = await this.ensureEventRange(startTime, endTime);
const result = await this.queryEvents(CONTRACTS.FastUpdater, eventName, startTime, endTime);
if (status !== BlockAssuranceResult.OK) {
return { status };
}

const result: TLPEvents[] = [];

// TEMP CHANGE for upgrading Relay contract, can be removed in December 2024
const network = process.env.NETWORK as networks;

const oldSongbirdFastUpdater = "0x70e8870ef234EcD665F96Da4c669dc12c1e1c116";
if (network == "songbird" && CONTRACTS.FastUpdater.address != oldSongbirdFastUpdater && startVotingRoundId <= SONGBIRD_FAST_UPDATER_SWITCH_VOTING_ROUND_ID) {
this.logger.log(`Querying old FastUpdater address for Songbird: ${oldSongbirdFastUpdater}`);
result.push(
...(await this.queryEvents({ ...CONTRACTS.FastUpdater, address: oldSongbirdFastUpdater }, eventName, startTime, endTime))
);
}

const oldCostonFastUpdater = "0x9B931f5d3e24fc8C9064DB35bDc8FB4bE0E862f9";
if (network == "coston" && CONTRACTS.FastUpdater.address !== oldCostonFastUpdater && startVotingRoundId <= COSTON_FAST_UPDATER_SWITCH_VOTING_ROUND_ID) {
this.logger.log(`Querying old FastUpdater address for Coston: ${oldCostonFastUpdater}`);
result.push(
...(await this.queryEvents({ ...CONTRACTS.FastUpdater, address: oldCostonFastUpdater }, eventName, startTime, endTime))
);
}

// END TEMP CHANGE

result.push(...(await this.queryEvents(CONTRACTS.FastUpdater, eventName, startTime, endTime)));
IndexerClient.sortEvents(result);
const votingRoundIdToEvents = new Map<number, FastUpdateFeedsSubmitted[]>();

for (let i = 0; i < result.length; i++) {
Expand Down
Loading

0 comments on commit 4913b99

Please sign in to comment.