Skip to content
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

[feat]: Implement better bounty evaluation #29

Merged
merged 51 commits into from
Jun 25, 2024
Merged
Changes from 1 commit
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
f3fa4c4
feat: Bounty evaluation (untested)
jsanmigimeno May 1, 2024
92a258c
fix: Pricing service config and loading
jsanmigimeno May 1, 2024
0a49ee6
fix: Replace node-fetch with axios ('require' problem)
jsanmigimeno May 1, 2024
607760e
fix: Pricing provider fixes
jsanmigimeno May 1, 2024
4fea8db
fix: delivery 'gasUsed' logic for evaluation
jsanmigimeno May 1, 2024
269702f
chore: Improve evaluation logging
jsanmigimeno May 1, 2024
16b1787
chore: Fix pricing 'coinId's
jsanmigimeno May 1, 2024
dd811a6
chore: Add comments
jsanmigimeno May 2, 2024
45f850c
fix: Revert pricing coinIds to 'ethereum'
jsanmigimeno May 2, 2024
89b74c3
chore: Remove 'gasLimitBuffer'
jsanmigimeno May 2, 2024
818ee7b
fix: gas cost calculation
jsanmigimeno May 3, 2024
513d22b
chore: Improve pricing evaluation logging
jsanmigimeno May 3, 2024
058a62f
feat: Mainnet example configuration
jsanmigimeno May 20, 2024
80c7a7a
Merge branch 'jsanmi/new-escrow-events'
jsanmigimeno May 20, 2024
487bb64
feat: Update docker compose for mainnet
jsanmigimeno May 20, 2024
5f9686e
Revert "feat: Update docker compose for mainnet"
jsanmigimeno May 20, 2024
c969fe7
feat: Update docker compose for mainnet
jsanmigimeno May 20, 2024
793eff3
chore: Add some comments back to config.example.yaml
jsanmigimeno May 20, 2024
4c73a4c
fix: Relayer engine wormhole-sdk dependency
jsanmigimeno May 20, 2024
015f4da
Merge branch 'jsanmi/fix-relayer-engine-dependency' into mainnet
jsanmigimeno May 20, 2024
4612646
Merge pull request #11 from catalystdao/mainnet
reednaa May 21, 2024
2c5e0f0
fix: relayer-engine import
jsanmigimeno May 21, 2024
a9c4006
fix: Wormhole recovery api endpoint
jsanmigimeno May 21, 2024
152e02c
chore: Refactor wallet ports (route via wallet service)
jsanmigimeno May 23, 2024
775a194
feat: Wallet worker restart mechanism
jsanmigimeno May 25, 2024
6507633
Merge branch 'testnet' into jsanmi/pricing
jsanmigimeno May 28, 2024
0bc5341
Merge branch 'testnet' into jsanmi/pricing
jsanmigimeno May 31, 2024
67e11b2
fix: pnpm-lock after merge
jsanmigimeno May 31, 2024
92bdfad
feat: Take into account 'unrewarded' gas on bounty evaluation
jsanmigimeno Jun 3, 2024
db01791
fix: Variable name
jsanmigimeno Jun 3, 2024
a235165
fix: Variable name
jsanmigimeno Jun 3, 2024
4790da2
feat: Better structure wallet communication
jsanmigimeno Jun 4, 2024
74e3ab0
feat: Add 'getFeeData' query to wallet worker and overhaul gas calcul…
jsanmigimeno Jun 4, 2024
7d9083b
Merge branch 'jsanmi/enhance-wallet-port' into jsanmi/pricing
jsanmigimeno Jun 4, 2024
da8e8b0
chore: Overhaul relay eval (WIP)
jsanmigimeno Jun 5, 2024
d65a56d
chore: Overhaul and refactor the bounty evaluation logic
jsanmigimeno Jun 5, 2024
44bb35f
chore: Remove the 'evaluator' module
jsanmigimeno Jun 5, 2024
a60e77f
feat: Add order reevaluation logic
jsanmigimeno Jun 5, 2024
0381090
chore: Fix confusing log statement
jsanmigimeno Jun 5, 2024
243dc8a
feat: Implement a 'profitability factor'
jsanmigimeno Jun 6, 2024
bf7f0f6
feat: Add CoinGecko config validation
jsanmigimeno Jun 6, 2024
b014a87
chore: Recover testnet config
jsanmigimeno Jun 6, 2024
b64a98b
chore: Add 'coinId' to example config
jsanmigimeno Jun 7, 2024
17397b7
fix: Overhaul the max ack gas loss
jsanmigimeno Jun 10, 2024
0a87507
feat: Implement addtional fee estimation via resolvers
jsanmigimeno Jun 19, 2024
b84eec7
feat: Add OP and Base resolvers
jsanmigimeno Jun 19, 2024
5598c12
chore: Add 'additionalFeeEstimation' to eval logs
jsanmigimeno Jun 19, 2024
4419a59
chore: Improve delivery evaluation logging
jsanmigimeno Jun 20, 2024
502cceb
feat: Improve bounty evaluation on arbitrum-like chains
jsanmigimeno Jun 21, 2024
89552e5
Merge branch 'testnet' into jsanmi/pricing
jsanmigimeno Jun 21, 2024
00df820
Merge branch 'testnet' into jsanmi/pricing
jsanmigimeno Jun 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: Add order reevaluation logic
jsanmigimeno committed Jun 5, 2024
commit a60e77fa385e2b13c5cbc505b89352701588635c
2 changes: 2 additions & 0 deletions config.example.yaml
Original file line number Diff line number Diff line change
@@ -19,6 +19,8 @@ global:
maxPendingTransactions: 50 # Maximum number of transactions within the 'submit' pipeline.

# Evaluation properties
evaluationRetryInterval: 3600000 # Interval at which to reevaluate whether to relay a message.
maxEvaluationDuration: 86400000 # Time after which to drop an undelivered message.
unrewardedDeliveryGas: '100000' # Gas amount that will be unrewarded on delivery submission.
minDeliveryReward: 0.001 # In the 'pricingDenomination' specified below
relativeMinDeliveryReward: 0.001
2 changes: 2 additions & 0 deletions src/config/config.schema.ts
Original file line number Diff line number Diff line change
@@ -147,6 +147,8 @@ const SUBMITTER_SCHEMA = {
maxPendingTransactions: { $ref: "positive-number-schema" },

//TODO define 'evaluation' configuration somewhere else?
evaluationRetryInterval: { $ref: "positive-number-schema" },
maxEvaluationDuration: { $ref: "positive-number-schema" },
unrewardedDeliveryGas: { $ref: "gas-field-schema" },
minDeliveryReward: { $ref: "positive-number-schema" },
relativeMinDeliveryReward: { $ref: "positive-number-schema" },
2 changes: 2 additions & 0 deletions src/config/config.types.ts
Original file line number Diff line number Diff line change
@@ -45,6 +45,8 @@ export interface SubmitterGlobalConfig {
maxTries?: number;
maxPendingTransactions?: number;

evaluationRetryInterval?: number;
maxEvaluationDuration?: number;
unrewardedDeliveryGas?: bigint;
minDeliveryReward?: number;
relativeMinDeliveryReward?: number;
3 changes: 3 additions & 0 deletions src/submitter/queues/eval-queue.ts
Original file line number Diff line number Diff line change
@@ -80,6 +80,9 @@ export class EvalQueue extends ProcessingQueue<EvalOrder, SubmitOrder> {
// Move the order to the submit queue
return { result: { ...order, gasLimit: gasEstimation, isDelivery } };
} else {
// Request the order to be retried in the future.
order.retryEvaluation = true;

return null;
}
}
20 changes: 20 additions & 0 deletions src/submitter/submitter.service.ts
Original file line number Diff line number Diff line change
@@ -15,6 +15,8 @@ const PROCESSING_INTERVAL_DEFAULT = 100;
const MAX_TRIES_DEFAULT = 3;
const MAX_PENDING_TRANSACTIONS = 50;
const NEW_ORDERS_DELAY_DEFAULT = 0;
const EVALUATION_RETRY_INTERVAL_DEFAULT = 60 * 60 * 1000;
const MAX_EVALUATION_DURATION_DEFAULT = 24 * 60 * 60 * 1000;
const UNREWARDED_DELIVERY_GAS_DEFAULT = 0n;
const MIN_DELIVERY_REWARD_DEFAULT = 0;
const RELATIVE_MIN_DELIVERY_REWARD_DEFAULT = 0;
@@ -29,6 +31,8 @@ interface GlobalSubmitterConfig {
processingInterval: number;
maxTries: number;
maxPendingTransactions: number;
evaluationRetryInterval: number;
maxEvaluationDuration: number;
unrewardedDeliveryGas: bigint;
minDeliveryReward: number;
relativeMinDeliveryReward: number;
@@ -48,6 +52,8 @@ export interface SubmitterWorkerData {
processingInterval: number;
maxTries: number;
maxPendingTransactions: number;
evaluationRetryInterval: number;
maxEvaluationDuration: number;
unrewardedDeliveryGas: bigint;
minDeliveryReward: number;
relativeMinDeliveryReward: number;
@@ -132,6 +138,10 @@ export class SubmitterService {
const maxPendingTransactions =
submitterConfig.maxPendingTransactions ?? MAX_PENDING_TRANSACTIONS;

const evaluationRetryInterval =
submitterConfig.evaluationRetryInterval ?? EVALUATION_RETRY_INTERVAL_DEFAULT;
const maxEvaluationDuration =
submitterConfig.maxEvaluationDuration ?? MAX_EVALUATION_DURATION_DEFAULT;
const unrewardedDeliveryGas =
submitterConfig.unrewardedDeliveryGas ?? UNREWARDED_DELIVERY_GAS_DEFAULT;
const minDeliveryReward =
@@ -155,6 +165,8 @@ export class SubmitterService {
maxTries,
maxPendingTransactions,
walletPublicKey,
evaluationRetryInterval,
maxEvaluationDuration,
unrewardedDeliveryGas,
minDeliveryReward,
relativeMinDeliveryReward,
@@ -205,9 +217,17 @@ export class SubmitterService {
chainConfig.submitter.maxPendingTransactions ??
globalConfig.maxPendingTransactions,

evaluationRetryInterval:
chainConfig.submitter.evaluationRetryInterval ??
globalConfig.evaluationRetryInterval,

unrewardedDeliveryGas:
chainConfig.submitter.unrewardedDeliveryGas ??
globalConfig.unrewardedDeliveryGas,

maxEvaluationDuration:
chainConfig.submitter.maxEvaluationDuration ??
globalConfig.maxEvaluationDuration,

minDeliveryReward:
chainConfig.submitter.minDeliveryReward ??
6 changes: 5 additions & 1 deletion src/submitter/submitter.types.ts
Original file line number Diff line number Diff line change
@@ -10,6 +10,8 @@ export interface Order {

export interface EvalOrder extends Order {
priority: boolean;
evaluationDeadline: number;
retryEvaluation?: boolean;
}

export interface SubmitOrder extends Order {
@@ -24,13 +26,15 @@ export interface SubmitOrderResult extends SubmitOrder {
txReceipt: TransactionReceipt;
}

export interface NewOrder<OrderType> {
export interface PendingOrder<OrderType> {
order: OrderType;
processAt: number;
}


export interface BountyEvaluationConfig {
evaluationRetryInterval: number,
maxEvaluationDuration: number,
unrewardedDeliveryGas: bigint;
minDeliveryReward: number;
relativeMinDeliveryReward: number,
95 changes: 77 additions & 18 deletions src/submitter/submitter.worker.ts
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ import { IncentivizedMessageEscrow__factory } from 'src/contracts/factories/Ince
import { workerData } from 'worker_threads';
import { AmbPayload } from 'src/store/types/store.types';
import { STATUS_LOG_INTERVAL } from 'src/logger/logger.service';
import { BountyEvaluationConfig, EvalOrder, NewOrder } from './submitter.types';
import { BountyEvaluationConfig, EvalOrder, PendingOrder } from './submitter.types';
import { EvalQueue } from './queues/eval-queue';
import { SubmitQueue } from './queues/submit-queue';
import { wait } from 'src/common/utils';
@@ -32,7 +32,7 @@ class SubmitterWorker {
private readonly pricing: PricingInterface;
private readonly wallet: WalletInterface;

private readonly newOrdersQueue: NewOrder<EvalOrder>[] = [];
private readonly pendingQueue: PendingOrder<EvalOrder>[] = [];
private readonly evalQueue: EvalQueue;
private readonly submitQueue: SubmitQueue;

@@ -65,6 +65,8 @@ class SubmitterWorker {
this.loadIncentivesContracts(this.config.incentivesAddresses),
this.config.chainId,
{
evaluationRetryInterval: this.config.evaluationRetryInterval,
maxEvaluationDuration: this.config.maxEvaluationDuration,
unrewardedDeliveryGas: this.config.unrewardedDeliveryGas,
minDeliveryReward: this.config.minDeliveryReward,
relativeMinDeliveryReward: this.config.relativeMinDeliveryReward,
@@ -140,7 +142,7 @@ class SubmitterWorker {
const logStatus = () => {
const status = {
capacity: this.getSubmitterCapacity(),
newOrdersQueue: this.newOrdersQueue.length,
pendingQueue: this.pendingQueue.length,
evalQueue: this.evalQueue.size,
evalRetryQueue: this.evalQueue.retryQueue.length,
submitQueue: this.submitQueue.size,
@@ -181,21 +183,57 @@ class SubmitterWorker {
await this.listenForOrders();

while (true) {
const evalOrders = await this.processNewOrdersQueue();
const evalOrders = await this.processPendingQueue();

await this.evalQueue.addOrders(...evalOrders);
await this.evalQueue.processOrders();

const [newSubmitOrders, ,] = this.evalQueue.getFinishedOrders();
const [newSubmitOrders, noSubmitEvalOrders,] = this.evalQueue.getFinishedOrders();
this.processNoSubmitEvalOrders(noSubmitEvalOrders);
await this.submitQueue.addOrders(...newSubmitOrders);
await this.submitQueue.processOrders();

//TODO process failed submissions in any way?

this.submitQueue.getFinishedOrders(); // Flush the internal queues

await wait(this.config.processingInterval);
}
}

private processNoSubmitEvalOrders(evalOrders: EvalOrder[]): void {
for (const order of evalOrders) {
if (order.retryEvaluation) {

// Clear the 'retryEvaluation' flag to avoid unexpected retries.
order.retryEvaluation = undefined;

const nextEvaluationTime = Date.now() + this.config.evaluationRetryInterval;
if (nextEvaluationTime > order.evaluationDeadline) {
this.logger.info(
{
messageIdentifier: order.messageIdentifier,
nextEvaluationTime,
evaluationDeadline: order.evaluationDeadline,
},
'Dropping evaluation order.'
);
}
else {
this.logger.info(
{
messageIdentifier: order.messageIdentifier,
nextEvaluationTime,
evaluationDeadline: order.evaluationDeadline,
},
'Queueing order for reevaluation.'
);
this.addPendingOrder(nextEvaluationTime, order);
}
}
}
}

/**
* Subscribe to the Store to listen for relevant payloads to submit.
*/
@@ -247,48 +285,69 @@ class SubmitterWorker {
`Submit order received.`,
);
if (priority) {
// Push directly into the submit queue
// Push directly into the eval queue
await this.evalQueue.addOrders({
amb,
messageIdentifier,
message,
messageCtx,
priority: true,
evaluationDeadline: Date.now() + this.config.maxEvaluationDuration,
incentivesPayload,
});
} else {
// Push into the evaluation queue
this.newOrdersQueue.push({
processAt: Date.now() + this.config.newOrdersDelay,
order: {
// Push into the pending queue
this.addPendingOrder(
Date.now() + this.config.newOrdersDelay,
{
amb,
messageIdentifier,
message,
messageCtx,
priority: false,
evaluationDeadline: Date.now() + this.config.maxEvaluationDuration,
incentivesPayload,
},
});
);
}
}

/*************** New Order Queue ***************/
/*************** Pending Orders Queue ***************/

private addPendingOrder(processAt: number, order: EvalOrder): void {

// Insert the new order into the 'pendingQueue' keeping the queue order.
const insertIndex = this.pendingQueue.findIndex(order => {
return order.processAt > processAt;
});

const pendingOrder: PendingOrder<EvalOrder> = {
order,
processAt
};

if (insertIndex == -1) {
this.pendingQueue.push(pendingOrder);
} else {
this.pendingQueue.splice(insertIndex, 0, pendingOrder);
}
}

private async processNewOrdersQueue(): Promise<EvalOrder[]> {
private async processPendingQueue(): Promise<EvalOrder[]> {
const currentTimestamp = Date.now();
const capacity = this.getSubmitterCapacity();

let i;
for (i = 0; i < this.newOrdersQueue.length; i++) {
const nextNewOrder = this.newOrdersQueue[i]!;
for (i = 0; i < this.pendingQueue.length; i++) {
const nextPendingOrder = this.pendingQueue[i]!;

if (nextNewOrder.processAt > currentTimestamp || i + 1 > capacity) {
if (nextPendingOrder.processAt > currentTimestamp || i + 1 > capacity) {
break;
}
}

const ordersToEval = this.newOrdersQueue.splice(0, i).map((newOrder) => {
return newOrder.order;
const ordersToEval = this.pendingQueue.splice(0, i).map((pendingOrder) => {
return pendingOrder.order;
});

return ordersToEval;