@helia/trustless-gateway-client 0.0.0-9114743f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -0
- package/dist/index.min.js +33 -0
- package/dist/index.min.js.map +7 -0
- package/dist/src/broker.d.ts +44 -0
- package/dist/src/broker.d.ts.map +1 -0
- package/dist/src/broker.js +75 -0
- package/dist/src/broker.js.map +1 -0
- package/dist/src/index.d.ts +64 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +19 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/session.d.ts +30 -0
- package/dist/src/session.d.ts.map +1 -0
- package/dist/src/session.js +89 -0
- package/dist/src/session.js.map +1 -0
- package/dist/src/trustless-gateway.d.ts +64 -0
- package/dist/src/trustless-gateway.d.ts.map +1 -0
- package/dist/src/trustless-gateway.js +217 -0
- package/dist/src/trustless-gateway.js.map +1 -0
- package/dist/src/utils.d.ts +27 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +106 -0
- package/dist/src/utils.js.map +1 -0
- package/package.json +78 -0
- package/src/broker.ts +112 -0
- package/src/index.ts +81 -0
- package/src/session.ts +119 -0
- package/src/trustless-gateway.ts +279 -0
- package/src/utils.ts +141 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { TrustlessGatewayBlockBrokerInit, TrustlessGatewayBlockBrokerComponents, TrustlessGatewayGetBlockProgressEvents } from './index.ts';
|
|
2
|
+
import type { TransformRequestInit } from './trustless-gateway.ts';
|
|
3
|
+
import type { BlockRetrievalOptions, BlockBroker, CreateSessionOptions, SessionBlockBroker } from '@helia/interface';
|
|
4
|
+
import type { CID } from 'multiformats/cid';
|
|
5
|
+
export interface CreateTrustlessGatewaySessionOptions extends CreateSessionOptions<TrustlessGatewayGetBlockProgressEvents> {
|
|
6
|
+
/**
|
|
7
|
+
* By default we will only connect to peers with HTTPS addresses, pass true
|
|
8
|
+
* to also connect to HTTP addresses.
|
|
9
|
+
*
|
|
10
|
+
* @default false
|
|
11
|
+
*/
|
|
12
|
+
allowInsecure?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* By default we will only connect to peers with public or DNS addresses, pass
|
|
15
|
+
* true to also connect to private addresses.
|
|
16
|
+
*
|
|
17
|
+
* @default false
|
|
18
|
+
*/
|
|
19
|
+
allowLocal?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Provide a function that will be called before querying trustless-gateways.
|
|
22
|
+
*
|
|
23
|
+
* This lets you modify the fetch options to pass custom headers or other
|
|
24
|
+
* necessary things.
|
|
25
|
+
*/
|
|
26
|
+
transformRequestInit?: TransformRequestInit;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A class that accepts a list of trustless gateways that are queried
|
|
30
|
+
* for blocks.
|
|
31
|
+
*/
|
|
32
|
+
export declare class TrustlessGatewayBlockBroker implements BlockBroker<TrustlessGatewayGetBlockProgressEvents> {
|
|
33
|
+
readonly name = "trustless-gateway";
|
|
34
|
+
private readonly allowInsecure;
|
|
35
|
+
private readonly allowLocal;
|
|
36
|
+
private readonly transformRequestInit?;
|
|
37
|
+
private readonly routing;
|
|
38
|
+
private readonly log;
|
|
39
|
+
private readonly logger;
|
|
40
|
+
constructor(components: TrustlessGatewayBlockBrokerComponents, init?: TrustlessGatewayBlockBrokerInit);
|
|
41
|
+
retrieve(cid: CID, options?: BlockRetrievalOptions<TrustlessGatewayGetBlockProgressEvents>): Promise<Uint8Array>;
|
|
42
|
+
createSession(options?: CreateTrustlessGatewaySessionOptions): SessionBlockBroker<TrustlessGatewayGetBlockProgressEvents>;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=broker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"broker.d.ts","sourceRoot":"","sources":["../../src/broker.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,+BAA+B,EAAE,qCAAqC,EAAE,sCAAsC,EAAE,MAAM,YAAY,CAAA;AAChJ,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAClE,OAAO,KAAK,EAAW,qBAAqB,EAAE,WAAW,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE7H,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAE3C,MAAM,WAAW,oCAAqC,SAAQ,oBAAoB,CAAC,sCAAsC,CAAC;IACxH;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IAEvB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IAEpB;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;CAC5C;AAED;;;GAGG;AACH,qBAAa,2BAA4B,YAAW,WAAW,CAAC,sCAAsC,CAAC;IACrG,SAAgB,IAAI,uBAAsB;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAsB;IAC5D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;gBAE3B,UAAU,EAAE,qCAAqC,EAAE,IAAI,GAAE,+BAAoC;IASpG,QAAQ,CAAE,GAAG,EAAE,GAAG,EAAE,OAAO,GAAE,qBAAqB,CAAC,sCAAsC,CAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IA2C3H,aAAa,CAAE,OAAO,GAAE,oCAAyC,GAAG,kBAAkB,CAAC,sCAAsC,CAAC;CAW/H"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { DEFAULT_ALLOW_INSECURE, DEFAULT_ALLOW_LOCAL } from "./index.js";
|
|
2
|
+
import { createTrustlessGatewaySession } from "./session.js";
|
|
3
|
+
import { findHttpGatewayProviders } from "./utils.js";
|
|
4
|
+
/**
|
|
5
|
+
* A class that accepts a list of trustless gateways that are queried
|
|
6
|
+
* for blocks.
|
|
7
|
+
*/
|
|
8
|
+
export class TrustlessGatewayBlockBroker {
|
|
9
|
+
name = 'trustless-gateway';
|
|
10
|
+
allowInsecure;
|
|
11
|
+
allowLocal;
|
|
12
|
+
transformRequestInit;
|
|
13
|
+
routing;
|
|
14
|
+
log;
|
|
15
|
+
logger;
|
|
16
|
+
constructor(components, init = {}) {
|
|
17
|
+
this.log = components.logger.forComponent('helia:trustless-gateway-block-broker');
|
|
18
|
+
this.logger = components.logger;
|
|
19
|
+
this.routing = components.routing;
|
|
20
|
+
this.allowInsecure = init.allowInsecure ?? DEFAULT_ALLOW_INSECURE;
|
|
21
|
+
this.allowLocal = init.allowLocal ?? DEFAULT_ALLOW_LOCAL;
|
|
22
|
+
this.transformRequestInit = init.transformRequestInit;
|
|
23
|
+
}
|
|
24
|
+
async retrieve(cid, options = {}) {
|
|
25
|
+
const aggregateErrors = [];
|
|
26
|
+
for await (const gateway of findHttpGatewayProviders(cid, this.routing, this.logger, this.allowInsecure, this.allowLocal, { ...options, transformRequestInit: this.transformRequestInit })) {
|
|
27
|
+
this.log('getting block for %c from %s', cid, gateway.url);
|
|
28
|
+
try {
|
|
29
|
+
const block = await gateway.getRawBlock(cid, options);
|
|
30
|
+
this.log.trace('got block for %c from %s', cid, gateway.url);
|
|
31
|
+
try {
|
|
32
|
+
await options.validateFn?.(block);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
this.log.error('failed to validate block for %c from %s - %e', cid, gateway.url, err);
|
|
36
|
+
// try another gateway
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
return block;
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
this.log.error('failed to get block for %c from %s - %e', cid, gateway.url, err);
|
|
43
|
+
if (err instanceof Error) {
|
|
44
|
+
aggregateErrors.push(err);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
aggregateErrors.push(new Error(`Unable to fetch raw block for CID ${cid} from gateway ${gateway.url}`));
|
|
48
|
+
}
|
|
49
|
+
// if signal was aborted, exit the loop
|
|
50
|
+
if (options.signal?.aborted === true) {
|
|
51
|
+
this.log.trace('request aborted while fetching raw block for CID %c from gateway %s', cid, gateway.url);
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (aggregateErrors.length > 0) {
|
|
57
|
+
throw new AggregateError(aggregateErrors, `Unable to fetch raw block for CID ${cid} from any gateway`);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
throw new Error(`Unable to fetch raw block for CID ${cid} from any gateway`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
createSession(options = {}) {
|
|
64
|
+
return createTrustlessGatewaySession({
|
|
65
|
+
logger: this.logger,
|
|
66
|
+
routing: this.routing
|
|
67
|
+
}, {
|
|
68
|
+
...options,
|
|
69
|
+
allowLocal: this.allowLocal,
|
|
70
|
+
allowInsecure: this.allowInsecure,
|
|
71
|
+
transformRequestInit: this.transformRequestInit
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=broker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"broker.js","sourceRoot":"","sources":["../../src/broker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
*
|
|
4
|
+
* A Trustless Gateway is an HTTP endpoint that can be used to download blocks
|
|
5
|
+
* or CAR files in a verifiable way.
|
|
6
|
+
*/
|
|
7
|
+
import type { TransformRequestInit } from './trustless-gateway.ts';
|
|
8
|
+
import type { Routing, BlockBroker, RoutingFindProvidersProgressEvents, BlockBrokerGetBlockProgressEvents } from '@helia/interface';
|
|
9
|
+
import type { ComponentLogger } from 'birnam';
|
|
10
|
+
import type { CID } from 'multiformats';
|
|
11
|
+
import type { ProgressEvent } from 'progress-events';
|
|
12
|
+
export declare const DEFAULT_ALLOW_INSECURE = false;
|
|
13
|
+
export declare const DEFAULT_ALLOW_LOCAL = false;
|
|
14
|
+
/**
|
|
15
|
+
* The maximum number of bytes to allow when fetching a raw block.
|
|
16
|
+
*
|
|
17
|
+
* @see https://specs.ipfs.tech/bitswap-protocol/#block-sizes
|
|
18
|
+
*/
|
|
19
|
+
export declare const DEFAULT_MAX_SIZE = 2097152;
|
|
20
|
+
export interface TrustlessGatewayProvider {
|
|
21
|
+
/**
|
|
22
|
+
* The type of provider
|
|
23
|
+
*/
|
|
24
|
+
type: 'trustless-gateway';
|
|
25
|
+
/**
|
|
26
|
+
* The CID that the provider can provide the block for
|
|
27
|
+
*/
|
|
28
|
+
cid: CID;
|
|
29
|
+
/**
|
|
30
|
+
* The provider's URL
|
|
31
|
+
*/
|
|
32
|
+
url: string;
|
|
33
|
+
/**
|
|
34
|
+
* Which routing implementation found the provider
|
|
35
|
+
*/
|
|
36
|
+
routing: string;
|
|
37
|
+
}
|
|
38
|
+
export type TrustlessGatewayGetBlockProgressEvents = ProgressEvent<'trustless-gateway:get-block:fetch', URL> | ProgressEvent<'trustless-gateway:found-provider', TrustlessGatewayProvider> | RoutingFindProvidersProgressEvents | BlockBrokerGetBlockProgressEvents;
|
|
39
|
+
export interface TrustlessGatewayBlockBrokerInit {
|
|
40
|
+
/**
|
|
41
|
+
* By default we will only connect to peers with HTTPS addresses, pass true
|
|
42
|
+
* to also connect to HTTP addresses.
|
|
43
|
+
*
|
|
44
|
+
* @default false
|
|
45
|
+
*/
|
|
46
|
+
allowInsecure?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* By default we will only connect to peers with public or DNS addresses, pass
|
|
49
|
+
* true to also connect to private addresses.
|
|
50
|
+
*
|
|
51
|
+
* @default false
|
|
52
|
+
*/
|
|
53
|
+
allowLocal?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Provide a function that will be called before querying trustless-gateways. This lets you modify the fetch options to pass custom headers or other necessary things.
|
|
56
|
+
*/
|
|
57
|
+
transformRequestInit?: TransformRequestInit;
|
|
58
|
+
}
|
|
59
|
+
export interface TrustlessGatewayBlockBrokerComponents {
|
|
60
|
+
routing: Routing;
|
|
61
|
+
logger: ComponentLogger;
|
|
62
|
+
}
|
|
63
|
+
export declare function trustlessGatewayBlockBroker(init?: TrustlessGatewayBlockBrokerInit): (components: TrustlessGatewayBlockBrokerComponents) => BlockBroker<TrustlessGatewayGetBlockProgressEvents>;
|
|
64
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAClE,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,kCAAkC,EAAE,iCAAiC,EAAE,MAAM,kBAAkB,CAAA;AACnI,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD,eAAO,MAAM,sBAAsB,QAAQ,CAAA;AAC3C,eAAO,MAAM,mBAAmB,QAAQ,CAAA;AACxC;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,UAAY,CAAA;AAEzC,MAAM,WAAW,wBAAwB;IACvC;;OAEG;IACH,IAAI,EAAE,mBAAmB,CAAA;IAEzB;;OAEG;IACH,GAAG,EAAE,GAAG,CAAA;IAER;;OAEG;IACH,GAAG,EAAE,MAAM,CAAA;IAEX;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,MAAM,sCAAsC,GAChD,aAAa,CAAC,mCAAmC,EAAE,GAAG,CAAC,GACvD,aAAa,CAAC,kCAAkC,EAAE,wBAAwB,CAAC,GAC3E,kCAAkC,GAClC,iCAAiC,CAAA;AAEnC,MAAM,WAAW,+BAA+B;IAC9C;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IAEvB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;OAEG;IACH,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;CAC5C;AAED,MAAM,WAAW,qCAAqC;IACpD,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,eAAe,CAAA;CACxB;AAED,wBAAgB,2BAA2B,CAAE,IAAI,GAAE,+BAAoC,GAAG,CAAC,UAAU,EAAE,qCAAqC,KAAK,WAAW,CAAC,sCAAsC,CAAC,CAEnM"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
*
|
|
4
|
+
* A Trustless Gateway is an HTTP endpoint that can be used to download blocks
|
|
5
|
+
* or CAR files in a verifiable way.
|
|
6
|
+
*/
|
|
7
|
+
import { TrustlessGatewayBlockBroker } from "./broker.js";
|
|
8
|
+
export const DEFAULT_ALLOW_INSECURE = false;
|
|
9
|
+
export const DEFAULT_ALLOW_LOCAL = false;
|
|
10
|
+
/**
|
|
11
|
+
* The maximum number of bytes to allow when fetching a raw block.
|
|
12
|
+
*
|
|
13
|
+
* @see https://specs.ipfs.tech/bitswap-protocol/#block-sizes
|
|
14
|
+
*/
|
|
15
|
+
export const DEFAULT_MAX_SIZE = 2_097_152;
|
|
16
|
+
export function trustlessGatewayBlockBroker(init = {}) {
|
|
17
|
+
return (components) => new TrustlessGatewayBlockBroker(components, init);
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAA;AAOzD,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,CAAA;AAC3C,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,CAAA;AACxC;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAA;AAyDzC,MAAM,UAAU,2BAA2B,CAAE,OAAwC,EAAE;IACrF,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,2BAA2B,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;AAC1E,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { AbstractSession } from '@helia/utils';
|
|
2
|
+
import { CID } from 'multiformats/cid';
|
|
3
|
+
import { TrustlessGateway } from './trustless-gateway.ts';
|
|
4
|
+
import type { CreateTrustlessGatewaySessionOptions } from './broker.ts';
|
|
5
|
+
import type { TrustlessGatewayGetBlockProgressEvents } from './index.ts';
|
|
6
|
+
import type { BlockRetrievalOptions, Routing } from '@helia/interface';
|
|
7
|
+
import type { Multiaddr } from '@multiformats/multiaddr';
|
|
8
|
+
import type { AbortOptions } from 'abort-error';
|
|
9
|
+
import type { ComponentLogger } from 'birnam';
|
|
10
|
+
export interface TrustlessGatewaySessionComponents {
|
|
11
|
+
logger: ComponentLogger;
|
|
12
|
+
routing: Routing;
|
|
13
|
+
}
|
|
14
|
+
declare class TrustlessGatewaySession extends AbstractSession<TrustlessGateway, TrustlessGatewayGetBlockProgressEvents> {
|
|
15
|
+
readonly name = "trustless-gateway-session";
|
|
16
|
+
private readonly routing;
|
|
17
|
+
private readonly allowInsecure;
|
|
18
|
+
private readonly allowLocal;
|
|
19
|
+
private readonly transformRequestInit?;
|
|
20
|
+
constructor(components: TrustlessGatewaySessionComponents, init: CreateTrustlessGatewaySessionOptions);
|
|
21
|
+
queryProvider(cid: CID, provider: TrustlessGateway, options: BlockRetrievalOptions): Promise<Uint8Array>;
|
|
22
|
+
findNewProviders(cid: CID, options?: AbortOptions): AsyncGenerator<TrustlessGateway>;
|
|
23
|
+
toFilterKey(provider: TrustlessGateway): Uint8Array | string;
|
|
24
|
+
equals(providerA: TrustlessGateway, providerB: TrustlessGateway): boolean;
|
|
25
|
+
convertToProvider(provider: CID | Multiaddr | Multiaddr[], routing: string, options?: AbortOptions): Promise<TrustlessGateway | undefined>;
|
|
26
|
+
emitFoundProviderProgressEvent(cid: CID, provider: TrustlessGateway, options: BlockRetrievalOptions<TrustlessGatewayGetBlockProgressEvents>): void;
|
|
27
|
+
}
|
|
28
|
+
export declare function createTrustlessGatewaySession(components: TrustlessGatewaySessionComponents, init: CreateTrustlessGatewaySessionOptions): TrustlessGatewaySession;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAS,MAAM,cAAc,CAAA;AAErD,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAGtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAEzD,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,aAAa,CAAA;AACvE,OAAO,KAAK,EAAE,sCAAsC,EAA4B,MAAM,YAAY,CAAA;AAElG,OAAO,KAAK,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAA;AAE7C,MAAM,WAAW,iCAAiC;IAChD,MAAM,EAAE,eAAe,CAAA;IACvB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,cAAM,uBAAwB,SAAQ,eAAe,CAAC,gBAAgB,EAAE,sCAAsC,CAAC;IAC7G,SAAgB,IAAI,+BAA8B;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAsB;gBAE/C,UAAU,EAAE,iCAAiC,EAAE,IAAI,EAAE,oCAAoC;IAYhG,aAAa,CAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,gBAAgB,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,UAAU,CAAC;IA6BvG,gBAAgB,CAAE,GAAG,EAAE,GAAG,EAAE,OAAO,GAAE,YAAiB,GAAG,cAAc,CAAC,gBAAgB,CAAC;IAIjG,WAAW,CAAE,QAAQ,EAAE,gBAAgB,GAAG,UAAU,GAAG,MAAM;IAI7D,MAAM,CAAE,SAAS,EAAE,gBAAgB,EAAE,SAAS,EAAE,gBAAgB,GAAG,OAAO;IAIpE,iBAAiB,CAAE,QAAQ,EAAE,GAAG,GAAG,SAAS,GAAG,SAAS,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;IA0BjJ,8BAA8B,CAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,gBAAgB,EAAE,OAAO,EAAE,qBAAqB,CAAC,sCAAsC,CAAC,GAAG,IAAI;CAQpJ;AAED,wBAAgB,6BAA6B,CAAE,UAAU,EAAE,iCAAiC,EAAE,IAAI,EAAE,oCAAoC,GAAG,uBAAuB,CAEjK"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { AbstractSession, isCID } from '@helia/utils';
|
|
2
|
+
import { multiaddrToUri } from '@multiformats/multiaddr-to-uri';
|
|
3
|
+
import { CID } from 'multiformats/cid';
|
|
4
|
+
import { CustomProgressEvent } from 'progress-events';
|
|
5
|
+
import { DEFAULT_ALLOW_INSECURE, DEFAULT_ALLOW_LOCAL } from "./index.js";
|
|
6
|
+
import { TrustlessGateway } from "./trustless-gateway.js";
|
|
7
|
+
import { filterNonHTTPMultiaddrs, findHttpGatewayProviders } from "./utils.js";
|
|
8
|
+
class TrustlessGatewaySession extends AbstractSession {
|
|
9
|
+
name = 'trustless-gateway-session';
|
|
10
|
+
routing;
|
|
11
|
+
allowInsecure;
|
|
12
|
+
allowLocal;
|
|
13
|
+
transformRequestInit;
|
|
14
|
+
constructor(components, init) {
|
|
15
|
+
super(components, {
|
|
16
|
+
...init,
|
|
17
|
+
name: 'helia:trustless-gateway:session'
|
|
18
|
+
});
|
|
19
|
+
this.routing = components.routing;
|
|
20
|
+
this.allowInsecure = init.allowInsecure ?? DEFAULT_ALLOW_INSECURE;
|
|
21
|
+
this.allowLocal = init.allowLocal ?? DEFAULT_ALLOW_LOCAL;
|
|
22
|
+
this.transformRequestInit = init.transformRequestInit;
|
|
23
|
+
}
|
|
24
|
+
async queryProvider(cid, provider, options) {
|
|
25
|
+
this.log('fetching BLOCK for %c from %s', cid, provider.url);
|
|
26
|
+
options?.onProgress?.(new CustomProgressEvent('helia:block-brokers:query-provider:start', {
|
|
27
|
+
blockBroker: 'trustless-gateway',
|
|
28
|
+
provider: provider.url,
|
|
29
|
+
transport: 'http',
|
|
30
|
+
cid
|
|
31
|
+
}));
|
|
32
|
+
let block;
|
|
33
|
+
try {
|
|
34
|
+
block = await provider.getRawBlock(cid, options);
|
|
35
|
+
this.log.trace('got block for %c from %s', cid, provider.url);
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
options?.onProgress?.(new CustomProgressEvent('helia:block-brokers:query-provider:end', {
|
|
39
|
+
blockBroker: 'trustless-gateway',
|
|
40
|
+
provider: provider.url,
|
|
41
|
+
transport: 'http',
|
|
42
|
+
cid
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
await options.validateFn?.(block);
|
|
46
|
+
return block;
|
|
47
|
+
}
|
|
48
|
+
async *findNewProviders(cid, options = {}) {
|
|
49
|
+
yield* findHttpGatewayProviders(cid, this.routing, this.logger, this.allowInsecure, this.allowLocal, { ...options, transformRequestInit: this.transformRequestInit });
|
|
50
|
+
}
|
|
51
|
+
toFilterKey(provider) {
|
|
52
|
+
return provider.url.toString();
|
|
53
|
+
}
|
|
54
|
+
equals(providerA, providerB) {
|
|
55
|
+
return providerA.url.toString() === providerB.url.toString();
|
|
56
|
+
}
|
|
57
|
+
async convertToProvider(provider, routing, options) {
|
|
58
|
+
options?.signal?.throwIfAborted();
|
|
59
|
+
if (isCID(provider)) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const httpAddresses = filterNonHTTPMultiaddrs(Array.isArray(provider) ? provider : [provider], this.allowInsecure, this.allowLocal);
|
|
63
|
+
if (httpAddresses.length === 0) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
// take first address?
|
|
67
|
+
// /ip4/x.x.x.x/tcp/31337/http
|
|
68
|
+
// /ip4/x.x.x.x/tcp/31337/https
|
|
69
|
+
// etc
|
|
70
|
+
const uri = multiaddrToUri(httpAddresses[0]);
|
|
71
|
+
return new TrustlessGateway(uri, {
|
|
72
|
+
logger: this.logger,
|
|
73
|
+
transformRequestInit: this.transformRequestInit,
|
|
74
|
+
routing
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
emitFoundProviderProgressEvent(cid, provider, options) {
|
|
78
|
+
options?.onProgress?.(new CustomProgressEvent('trustless-gateway:found-provider', {
|
|
79
|
+
type: 'trustless-gateway',
|
|
80
|
+
cid,
|
|
81
|
+
url: provider.url.toJSON(),
|
|
82
|
+
routing: provider.routing
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function createTrustlessGatewaySession(components, init) {
|
|
87
|
+
return new TrustlessGatewaySession(components, init);
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { CID } from 'multiformats/cid';
|
|
2
|
+
import type { BlockBrokerGetBlockProgressEvents } from '@helia/interface';
|
|
3
|
+
import type { ComponentLogger } from 'birnam';
|
|
4
|
+
import type { ProgressOptions } from 'progress-events';
|
|
5
|
+
export interface TrustlessGatewayStats {
|
|
6
|
+
attempts: number;
|
|
7
|
+
errors: number;
|
|
8
|
+
invalidBlocks: number;
|
|
9
|
+
successes: number;
|
|
10
|
+
pendingResponses?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface TransformRequestInit {
|
|
13
|
+
(defaultReqInit: RequestInit): Promise<RequestInit> | RequestInit;
|
|
14
|
+
}
|
|
15
|
+
export interface TrustlessGatewayComponents {
|
|
16
|
+
logger: ComponentLogger;
|
|
17
|
+
transformRequestInit?: TransformRequestInit;
|
|
18
|
+
routing: string;
|
|
19
|
+
}
|
|
20
|
+
export interface GetRawBlockOptions extends ProgressOptions<BlockBrokerGetBlockProgressEvents> {
|
|
21
|
+
signal?: AbortSignal;
|
|
22
|
+
/**
|
|
23
|
+
* The maximum number of bytes to allow when fetching a raw block.
|
|
24
|
+
*
|
|
25
|
+
* @default 2_097_152 (2MiB)
|
|
26
|
+
*/
|
|
27
|
+
maxSize?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A `TrustlessGateway` keeps track of the number of attempts, errors, and
|
|
31
|
+
* successes for a given gateway url so that we can prioritize gateways that
|
|
32
|
+
* have been more reliable in the past, and ensure that requests are distributed
|
|
33
|
+
* across all gateways within a given `TrustlessGatewayBlockBroker` instance.
|
|
34
|
+
*/
|
|
35
|
+
export declare class TrustlessGateway {
|
|
36
|
+
#private;
|
|
37
|
+
readonly url: URL;
|
|
38
|
+
private readonly peer;
|
|
39
|
+
private readonly log;
|
|
40
|
+
private readonly transformRequestInit?;
|
|
41
|
+
readonly routing: string;
|
|
42
|
+
constructor(url: URL | string, { logger, transformRequestInit, routing }: TrustlessGatewayComponents);
|
|
43
|
+
/**
|
|
44
|
+
* Fetch a raw block from `this.url` following the specification defined at
|
|
45
|
+
* https://specs.ipfs.tech/http-gateways/trustless-gateway/
|
|
46
|
+
*/
|
|
47
|
+
getRawBlock(cid: CID, options?: GetRawBlockOptions): Promise<Uint8Array>;
|
|
48
|
+
/**
|
|
49
|
+
* Encapsulate the logic for determining whether a gateway is considered
|
|
50
|
+
* reliable, for prioritization. This is based on the number of successful attempts made
|
|
51
|
+
* and the number of errors encountered.
|
|
52
|
+
*
|
|
53
|
+
* Unused gateways have 100% reliability; They will be prioritized over
|
|
54
|
+
* gateways with a 100% success rate to ensure that we attempt all gateways.
|
|
55
|
+
*/
|
|
56
|
+
reliability(): number;
|
|
57
|
+
/**
|
|
58
|
+
* Increment the number of invalid blocks returned by this gateway.
|
|
59
|
+
*/
|
|
60
|
+
incrementInvalidBlocks(): void;
|
|
61
|
+
getStats(): TrustlessGatewayStats;
|
|
62
|
+
toString(): string;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=trustless-gateway.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trustless-gateway.d.ts","sourceRoot":"","sources":["../../src/trustless-gateway.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAMtC,OAAO,KAAK,EAAsE,iCAAiC,EAA8E,MAAM,kBAAkB,CAAA;AACzN,OAAO,KAAK,EAAE,eAAe,EAAU,MAAM,QAAQ,CAAA;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAItD,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,CAAC,cAAc,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CAAA;CAClE;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,eAAe,CAAA;IACvB,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;IAC3C,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,kBAAmB,SAAQ,eAAe,CAAC,iCAAiC,CAAC;IAC5F,MAAM,CAAC,EAAE,WAAW,CAAA;IAEpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;GAKG;AACH,qBAAa,gBAAgB;;IAC3B,SAAgB,GAAG,EAAE,GAAG,CAAA;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAK;IAqC1B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;IAC5B,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAsB;IAE5D,SAAgB,OAAO,EAAE,MAAM,CAAA;gBAElB,GAAG,EAAE,GAAG,GAAG,MAAM,EAAE,EAAE,MAAM,EAAE,oBAAoB,EAAE,OAAO,EAAE,EAAE,0BAA0B;IAsBrG;;;OAGG;IACG,WAAW,CAAE,GAAG,EAAE,GAAG,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,UAAU,CAAC;IA0GnF;;;;;;;OAOG;IACH,WAAW,IAAK,MAAM;IAyBtB;;OAEG;IACH,sBAAsB,IAAK,IAAI;IAI/B,QAAQ,IAAK,qBAAqB;IAUlC,QAAQ,IAAK,MAAM;CAGpB"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { uriToMultiaddr } from '@multiformats/uri-to-multiaddr';
|
|
2
|
+
import { base64 } from 'multiformats/bases/base64';
|
|
3
|
+
import { CID } from 'multiformats/cid';
|
|
4
|
+
import { identity } from 'multiformats/hashes/identity';
|
|
5
|
+
import { CustomProgressEvent } from 'progress-events';
|
|
6
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
|
|
7
|
+
import { DEFAULT_MAX_SIZE } from "./index.js";
|
|
8
|
+
import { limitedResponse } from "./utils.js";
|
|
9
|
+
const TRANSPORT_IPFS_GATEWAY_HTTP_CODE = 0x0920;
|
|
10
|
+
/**
|
|
11
|
+
* A `TrustlessGateway` keeps track of the number of attempts, errors, and
|
|
12
|
+
* successes for a given gateway url so that we can prioritize gateways that
|
|
13
|
+
* have been more reliable in the past, and ensure that requests are distributed
|
|
14
|
+
* across all gateways within a given `TrustlessGatewayBlockBroker` instance.
|
|
15
|
+
*/
|
|
16
|
+
export class TrustlessGateway {
|
|
17
|
+
url;
|
|
18
|
+
peer;
|
|
19
|
+
/**
|
|
20
|
+
* The number of times this gateway has been attempted to be used to fetch a
|
|
21
|
+
* block. This includes successful, errored, and aborted attempts. By counting
|
|
22
|
+
* even aborted attempts, slow gateways that are out-raced by others will be
|
|
23
|
+
* considered less reliable.
|
|
24
|
+
*/
|
|
25
|
+
#attempts = 0;
|
|
26
|
+
/**
|
|
27
|
+
* The number of times this gateway has errored while attempting to fetch a
|
|
28
|
+
* block. This includes `response.ok === false` and any other errors that
|
|
29
|
+
* throw while attempting to fetch a block. This does not include aborted
|
|
30
|
+
* attempts.
|
|
31
|
+
*/
|
|
32
|
+
#errors = 0;
|
|
33
|
+
/**
|
|
34
|
+
* The number of times this gateway has returned an invalid block. A gateway
|
|
35
|
+
* that returns the wrong blocks for a CID should be considered for removal
|
|
36
|
+
* from the list of gateways to fetch blocks from.
|
|
37
|
+
*/
|
|
38
|
+
#invalidBlocks = 0;
|
|
39
|
+
/**
|
|
40
|
+
* The number of times this gateway has successfully fetched a block.
|
|
41
|
+
*/
|
|
42
|
+
#successes = 0;
|
|
43
|
+
/**
|
|
44
|
+
* A map of pending responses for this gateway. This is used to ensure that
|
|
45
|
+
* only one request per CID is made to a given gateway at a time, and that we
|
|
46
|
+
* don't make multiple in-flight requests for the same CID to the same gateway.
|
|
47
|
+
*/
|
|
48
|
+
#pendingResponses = new Map();
|
|
49
|
+
log;
|
|
50
|
+
transformRequestInit;
|
|
51
|
+
routing;
|
|
52
|
+
constructor(url, { logger, transformRequestInit, routing }) {
|
|
53
|
+
this.url = url instanceof URL ? url : new URL(url);
|
|
54
|
+
this.transformRequestInit = transformRequestInit;
|
|
55
|
+
this.log = logger.forComponent(`helia:trustless-gateway-block-broker:${this.url.host}`);
|
|
56
|
+
this.routing = routing;
|
|
57
|
+
this.peer = CID.createV1(TRANSPORT_IPFS_GATEWAY_HTTP_CODE, identity.digest(uint8ArrayFromString(this.url.toString())));
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* This function returns a unique string for the multihash.bytes of the CID.
|
|
61
|
+
*
|
|
62
|
+
* Some useful resources for why this is needed can be found using the links below:
|
|
63
|
+
*
|
|
64
|
+
* - https://github.com/ipfs/helia/pull/503#discussion_r1572451331
|
|
65
|
+
* - https://github.com/ipfs/kubo/issues/6815
|
|
66
|
+
* - https://www.notion.so/pl-strflt/Handling-ambiguity-around-CIDs-9d5e14f6516f438980b01ef188efe15d#d9d45cd1ed8b4d349b96285de4aed5ab
|
|
67
|
+
*/
|
|
68
|
+
#uniqueBlockId(cid) {
|
|
69
|
+
const multihashBytes = cid.multihash.bytes;
|
|
70
|
+
return base64.encode(multihashBytes);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Fetch a raw block from `this.url` following the specification defined at
|
|
74
|
+
* https://specs.ipfs.tech/http-gateways/trustless-gateway/
|
|
75
|
+
*/
|
|
76
|
+
async getRawBlock(cid, options = {}) {
|
|
77
|
+
const gwUrl = new URL(this.url.toString());
|
|
78
|
+
gwUrl.pathname = `/ipfs/${cid.toString()}`;
|
|
79
|
+
const maxSize = options.maxSize ?? DEFAULT_MAX_SIZE;
|
|
80
|
+
// necessary as not every gateway supports dag-cbor, but every should support
|
|
81
|
+
// sending raw block as-is
|
|
82
|
+
gwUrl.search = '?format=raw';
|
|
83
|
+
if (options.signal?.aborted === true) {
|
|
84
|
+
throw new Error(`Signal to fetch raw block for CID ${cid} from gateway ${this.url} was aborted prior to fetch`);
|
|
85
|
+
}
|
|
86
|
+
const blockId = this.#uniqueBlockId(cid);
|
|
87
|
+
// workaround for https://github.com/nodejs/node/issues/52635
|
|
88
|
+
const innerController = new AbortController();
|
|
89
|
+
const abortInnerSignal = () => {
|
|
90
|
+
innerController.abort();
|
|
91
|
+
};
|
|
92
|
+
options.signal?.addEventListener('abort', abortInnerSignal);
|
|
93
|
+
try {
|
|
94
|
+
let pendingResponse = this.#pendingResponses.get(blockId);
|
|
95
|
+
if (pendingResponse == null) {
|
|
96
|
+
this.#attempts++;
|
|
97
|
+
const defaultReqInit = {
|
|
98
|
+
signal: innerController.signal,
|
|
99
|
+
headers: {
|
|
100
|
+
Accept: 'application/vnd.ipld.raw'
|
|
101
|
+
},
|
|
102
|
+
cache: 'force-cache'
|
|
103
|
+
};
|
|
104
|
+
const reqInit = this.transformRequestInit != null ? await this.transformRequestInit(defaultReqInit) : defaultReqInit;
|
|
105
|
+
const headers = new Headers(reqInit.headers);
|
|
106
|
+
this.log(`sending request
|
|
107
|
+
%s %s HTTP/1.1
|
|
108
|
+
%s
|
|
109
|
+
`, reqInit.method ?? 'GET', gwUrl, [...headers.entries()].map(([key, value]) => `${key}: ${value}`).join('\n'));
|
|
110
|
+
options.onProgress?.(new CustomProgressEvent('helia:block-broker:connect', {
|
|
111
|
+
broker: 'trustless-gateway',
|
|
112
|
+
type: 'connect',
|
|
113
|
+
provider: this.peer,
|
|
114
|
+
cid
|
|
115
|
+
}));
|
|
116
|
+
pendingResponse = fetch(gwUrl.toString(), reqInit).then(async (res) => {
|
|
117
|
+
this.log(`received response
|
|
118
|
+
HTTP/1.1 %d %s
|
|
119
|
+
%s
|
|
120
|
+
`, res.status, res.statusText, [...res.headers.entries()].map(([key, value]) => `${key}: ${value}`).join('\n'));
|
|
121
|
+
if (!res.ok) {
|
|
122
|
+
this.#errors++;
|
|
123
|
+
throw new Error(`Unable to fetch raw block for CID ${cid} from gateway ${this.url}, received ${res.status} ${res.statusText}`);
|
|
124
|
+
}
|
|
125
|
+
options.onProgress?.(new CustomProgressEvent('helia:block-broker:connected', {
|
|
126
|
+
broker: 'trustless-gateway',
|
|
127
|
+
type: 'connected',
|
|
128
|
+
provider: this.peer,
|
|
129
|
+
address: uriToMultiaddr(gwUrl.toString()),
|
|
130
|
+
cid
|
|
131
|
+
}));
|
|
132
|
+
options.onProgress?.(new CustomProgressEvent('helia:block-broker:request-block', {
|
|
133
|
+
broker: 'trustless-gateway',
|
|
134
|
+
type: 'request-block',
|
|
135
|
+
provider: this.peer,
|
|
136
|
+
cid
|
|
137
|
+
}));
|
|
138
|
+
// limited Response ensures the body is less than 2MiB (or configurable maxSize)
|
|
139
|
+
// see https://github.com/ipfs/helia/issues/790
|
|
140
|
+
const body = await limitedResponse(res, maxSize, { signal: innerController.signal, log: this.log });
|
|
141
|
+
options.onProgress?.(new CustomProgressEvent('helia:block-broker:receive-block', {
|
|
142
|
+
broker: 'trustless-gateway',
|
|
143
|
+
type: 'receive-block',
|
|
144
|
+
provider: this.peer,
|
|
145
|
+
cid
|
|
146
|
+
}));
|
|
147
|
+
this.#successes++;
|
|
148
|
+
return body;
|
|
149
|
+
});
|
|
150
|
+
this.#pendingResponses.set(blockId, pendingResponse);
|
|
151
|
+
}
|
|
152
|
+
return await pendingResponse;
|
|
153
|
+
}
|
|
154
|
+
catch (cause) {
|
|
155
|
+
// @ts-expect-error - TS thinks signal?.aborted can only be false now
|
|
156
|
+
// because it was checked for true above.
|
|
157
|
+
if (options.signal?.aborted === true) {
|
|
158
|
+
throw new Error(`Fetching raw block for CID ${cid} from gateway ${this.url} was aborted`);
|
|
159
|
+
}
|
|
160
|
+
this.#errors++;
|
|
161
|
+
throw new Error(`Unable to fetch raw block for CID ${cid} - ${cause.message}`);
|
|
162
|
+
}
|
|
163
|
+
finally {
|
|
164
|
+
options.signal?.removeEventListener('abort', abortInnerSignal);
|
|
165
|
+
this.#pendingResponses.delete(blockId);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Encapsulate the logic for determining whether a gateway is considered
|
|
170
|
+
* reliable, for prioritization. This is based on the number of successful attempts made
|
|
171
|
+
* and the number of errors encountered.
|
|
172
|
+
*
|
|
173
|
+
* Unused gateways have 100% reliability; They will be prioritized over
|
|
174
|
+
* gateways with a 100% success rate to ensure that we attempt all gateways.
|
|
175
|
+
*/
|
|
176
|
+
reliability() {
|
|
177
|
+
/**
|
|
178
|
+
* if we have never tried to use this gateway, it is considered the most
|
|
179
|
+
* reliable until we determine otherwise (prioritize unused gateways)
|
|
180
|
+
*/
|
|
181
|
+
if (this.#attempts === 0) {
|
|
182
|
+
return 1;
|
|
183
|
+
}
|
|
184
|
+
if (this.#invalidBlocks > 0) {
|
|
185
|
+
// this gateway may not be trustworthy..
|
|
186
|
+
return -Infinity;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* We have attempted the gateway, so we need to calculate the reliability
|
|
190
|
+
* based on the number of attempts, errors, and successes. Gateways that
|
|
191
|
+
* return a single error should drop their reliability score more than a
|
|
192
|
+
* single success increases it.
|
|
193
|
+
*
|
|
194
|
+
* Play around with the below reliability function at https://www.desmos.com/calculator/d6hfhf5ukm
|
|
195
|
+
*/
|
|
196
|
+
return this.#successes / (this.#attempts + (this.#errors * 3));
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Increment the number of invalid blocks returned by this gateway.
|
|
200
|
+
*/
|
|
201
|
+
incrementInvalidBlocks() {
|
|
202
|
+
this.#invalidBlocks++;
|
|
203
|
+
}
|
|
204
|
+
getStats() {
|
|
205
|
+
return {
|
|
206
|
+
attempts: this.#attempts,
|
|
207
|
+
errors: this.#errors,
|
|
208
|
+
invalidBlocks: this.#invalidBlocks,
|
|
209
|
+
successes: this.#successes,
|
|
210
|
+
pendingResponses: this.#pendingResponses.size
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
toString() {
|
|
214
|
+
return `TrustlessGateway(${this.url})`;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=trustless-gateway.js.map
|