Skip to content

Commit

Permalink
Ensure mempool estimates take precedence
Browse files Browse the repository at this point in the history
  • Loading branch information
mrfelton committed Feb 19, 2024
1 parent f8f1e74 commit bc5ca80
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 11 deletions.
28 changes: 22 additions & 6 deletions src/lib/DataProviderManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import NodeCache from "node-cache";
import { LOGLEVEL } from "./util";
import { logger } from "./logger";
import { MempoolProvider } from "../providers/mempool";

const log = logger(LOGLEVEL);

Expand All @@ -13,7 +14,7 @@ export class DataProviderManager {
private cacheKey: string = "data";

constructor(
cacheConfig: CacheConfig,
cacheConfig: CacheConfig = { stdTTL: 0, checkperiod: 0 },
maxHeightDelta: number = 1,
feeMultiplier: number = 1,
feeMinimum: number = 1,
Expand Down Expand Up @@ -113,13 +114,28 @@ export class DataProviderManager {
*
* @returns A promise that resolves to an array of sorted data points.
*/
private async getSortedDataPoints(): Promise<DataPoint[]> {
public async getSortedDataPoints(): Promise<DataPoint[]> {
const dataPoints = await this.fetchDataPoints();
dataPoints.sort(
(a, b) =>
dataPoints.sort((a, b) => {
// Prioritize mempool-based estimates
if (
a.provider instanceof MempoolProvider &&
!(b.provider instanceof MempoolProvider)
) {
return -1;
} else if (
!(a.provider instanceof MempoolProvider) &&
b.provider instanceof MempoolProvider
) {
return 1;
}

// If both are the same type, sort by block height and then by provider order
return (
b.blockHeight - a.blockHeight ||
this.providers.indexOf(a.provider) - this.providers.indexOf(b.provider),
);
this.providers.indexOf(a.provider) - this.providers.indexOf(b.provider)
);
});
return dataPoints;
}

Expand Down
6 changes: 3 additions & 3 deletions src/providers/esplora.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fetchData, LOGLEVEL } from "../lib/util";
import { fetchData, LOGLEVEL, TIMEOUT } from "../lib/util";
import { logger } from "../lib/logger";

const log = logger(LOGLEVEL);
Expand All @@ -14,7 +14,7 @@ const log = logger(LOGLEVEL);
* methods and properties for a data provider.
*
* @example
* const provider = new EsploraProvider('https://blockstream.info/api/');
* const provider = new EsploraProvider('https://blockstream.info');
* const data = await provider.getAllData();
*/
export class EsploraProvider implements Provider {
Expand All @@ -32,7 +32,7 @@ export class EsploraProvider implements Provider {
constructor(
url: string,
defaultDepth: number,
defaultTimeout: number = 5000,
defaultTimeout: number = TIMEOUT,
) {
this.url = url;
this.depth = defaultDepth;
Expand Down
4 changes: 2 additions & 2 deletions src/providers/mempool.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fetchData, LOGLEVEL } from "../lib/util";
import { fetchData, LOGLEVEL, TIMEOUT } from "../lib/util";
import { logger } from "../lib/logger";

const log = logger(LOGLEVEL);
Expand Down Expand Up @@ -29,7 +29,7 @@ export class MempoolProvider implements Provider {
constructor(
url: string,
defaultDepth: number,
defaultTimeout: number = 5000,
defaultTimeout: number = TIMEOUT,
) {
this.url = url;
this.depth = defaultDepth;
Expand Down
77 changes: 77 additions & 0 deletions test/DataProviderManager.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { expect, test } from "bun:test";
import { DataProviderManager } from "../src/lib/DataProviderManager";
import { MempoolProvider } from "../src/providers/mempool";
import { EsploraProvider } from "../src/providers/esplora";

class MockProvider1 implements Provider {
getBlockHeight = () => Promise.resolve(998);
Expand Down Expand Up @@ -78,3 +80,78 @@ test("should merge fee estimates from multiple providers correctly", async () =>
"5": 3000,
});
});

test("sorts providers correctly", async () => {
const mempoolProvider = new MempoolProvider("https://mempool.space", 6);
const esploraProvider = new EsploraProvider("https://blockstream.info", 6);

// Register the providers in the correct order
const manager = new DataProviderManager();
manager.registerProvider(mempoolProvider);
manager.registerProvider(esploraProvider);
const dataPoints = await manager.getSortedDataPoints();
expect(dataPoints[0].provider).toBe(mempoolProvider);
expect(dataPoints[1].provider).toBe(esploraProvider);

// Register the providers in reverse order
const manager2 = new DataProviderManager();
manager2.registerProvider(esploraProvider);
manager2.registerProvider(mempoolProvider);
const dataPoints2 = await manager.getSortedDataPoints();
expect(dataPoints2[0].provider).toBe(mempoolProvider);
expect(dataPoints2[1].provider).toBe(esploraProvider);

// Register multiple mempool providers with different block heights
class mempool1 extends MempoolProvider {
getBlockHeight = () => Promise.resolve(999);
getBlockHash = () => Promise.resolve("hash1");
getFeeEstimates = () => Promise.resolve({});
getAllData = () =>
Promise.resolve({
blockHeight: 998,
blockHash: "hash1",
feeEstimates: {
"1": 20,
"10": 1,
},
});
}

class mempool2 extends MempoolProvider {
getBlockHeight = () => Promise.resolve(1000);
getBlockHash = () => Promise.resolve("hash1");
getFeeEstimates = () => Promise.resolve({});
getAllData = () =>
Promise.resolve({
blockHeight: 998,
blockHash: "hash1",
feeEstimates: {
"1": 20,
"10": 1,
},
});
}

class mempool3 extends MempoolProvider {
getBlockHeight = () => Promise.resolve(999);
getBlockHash = () => Promise.resolve("hash1");
getFeeEstimates = () => Promise.resolve({});
getAllData = () =>
Promise.resolve({
blockHeight: 998,
blockHash: "hash1",
feeEstimates: {
"1": 20,
"10": 1,
},
});
}

// Register the providers in reverse order
const manager3 = new DataProviderManager();
manager3.registerProvider(new mempool1("https://mempool.space", 6));
manager3.registerProvider(new mempool2("https://mempool.space", 6));
manager3.registerProvider(new mempool3("https://mempool.space", 6));
const dataPoints3 = await manager3.getSortedDataPoints();
expect(dataPoints3[0].blockHeight).toBe(1000);
});

0 comments on commit bc5ca80

Please sign in to comment.