Skip to content

Commit 82d0064

Browse files
dtfiedlerdjwhitt
dtfiedler
authored andcommitted
feat(PE-5859): support standalone arns-resolver
Implements the `NameResolver` interface in the `StandaloneArNSResolver` class whucg arns name data from locally hosted arns-resolver service. Continues to use gateway by default - but allows for configurability via two new environment variables to select which resolver service to use. The changes do not remove any env vars or change default behavior. Additionally, the APIs on the arns-resolver service follow existing patterns and can be added to the envoy template once testing validates the expected behavior.
1 parent a474b76 commit 82d0064

File tree

6 files changed

+183
-11
lines changed

6 files changed

+183
-11
lines changed

docker-compose.yaml

+18
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ services:
7070
- WEBHOOK_TARGET_SERVERS=${WEBHOOK_TARGET_SERVERS:-}
7171
- WEBHOOK_INDEX_FILTER=${WEBHOOK_INDEX_FILTER:-}
7272
- CONTIGUOUS_DATA_CACHE_CLEANUP_THRESHOLD=${CONTIGUOUS_DATA_CACHE_CLEANUP_THRESHOLD:-}
73+
- TRUSTED_ARNS_RESOLVER_TYPE=${TRUSTED_ARNS_RESOLVER_TYPE:-gateway}
74+
- TRUSTED_ARNS_RESOLVER_URL=${TRUSTED_ARNS_RESOLVER_URL:-https://__NAME__.arweave.dev}
7375
depends_on:
7476
- redis
7577

@@ -103,3 +105,19 @@ services:
103105
- RUN_OBSERVER=${RUN_OBSERVER:-true}
104106
- MIN_RELEASE_NUMBER=${MIN_RELEASE_NUMBER:-0}
105107
- AR_IO_NODE_RELEASE=${AR_IO_NODE_RELEASE:-9-pre}
108+
109+
resolver:
110+
image: ghcr.io/ar-io/arns-resolver:${RESOLVER_IMAGE_TAG:-latest}
111+
restart: on-failure:5
112+
ports:
113+
- 6000:6000
114+
environment:
115+
- PORT=6000
116+
- LOG_LEVEL=${LOG_LEVEL:-info}
117+
- CONTRACT_TX_ID=${CONTRACT_ID:-}
118+
- RUN_RESOLVER=${RUN_RESOLVER:-false}
119+
- EVALUATION_INTERVAL_MS=${EVALUATION_INTERVAL_MS:-}
120+
- CONTRACT_CACHE_URL=${CONTRACT_CACHE_URL:-}
121+
- ARNS_CACHE_PATH=${ARNS_CACHE_PATH:-./data/arns}
122+
volumes:
123+
- ${ARNS_CACHE_PATH:-./data/arns}:/app/data/arns

src/config.ts

+19-6
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,6 @@ export const TRUSTED_GATEWAY_URL = env.varOrDefault(
5757
'https://arweave.net',
5858
);
5959

60-
// Trusted ArNS gateway URL (for resolving ArNS names)
61-
export const TRUSTED_ARNS_GATEWAY_URL = env.varOrDefault(
62-
'TRUSTED_ARNS_GATEWAY_URL',
63-
'https://__NAME__.arweave.dev',
64-
);
65-
6660
//
6761
// Data
6862
//
@@ -196,6 +190,25 @@ export const WEBHOOK_INDEX_FILTER = createFilter(
196190
JSON.parse(WEBHOOK_INDEX_FILTER_STRING),
197191
);
198192

193+
//
194+
// ArNS Resolution
195+
//
196+
197+
export const TRUSTED_ARNS_GATEWAY_URL = env.varOrDefault(
198+
'TRUSTED_ARNS_GATEWAY_URL',
199+
'https://__NAME__.arweave.dev',
200+
);
201+
202+
export const TRUSTED_ARNS_RESOLVER_TYPE = env.varOrDefault(
203+
'TRUSTED_ARNS_RESOLVER_TYPE',
204+
'gateway',
205+
);
206+
207+
export const TRUSTED_ARNS_RESOLVER_URL = env.varOrDefault(
208+
'TRUSTED_ARNS_RESOLVER_URL',
209+
TRUSTED_ARNS_GATEWAY_URL,
210+
);
211+
199212
//
200213
// Development and testing
201214
//

src/init/resolvers.ts

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* AR.IO Gateway
3+
* Copyright (C) 2022-2023 Permanent Data Solutions, Inc. All Rights Reserved.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
import { Logger } from 'winston';
19+
import { StandaloneArNSResolver } from '../resolution/standalone-arns-resolver.js';
20+
import { TrustedGatewayArNSResolver } from '../resolution/trusted-gateway-arns-resolver.js';
21+
import { NameResolver } from '../types.js';
22+
23+
export const createArNSResolver = ({
24+
log,
25+
type,
26+
url,
27+
}: {
28+
log: Logger;
29+
type: string;
30+
url: string;
31+
}): NameResolver => {
32+
log.info(`Using ${type} for arns name resolution`);
33+
switch (type) {
34+
case 'resolver': {
35+
return new StandaloneArNSResolver({
36+
log,
37+
resolverUrl: url,
38+
});
39+
}
40+
case 'gateway': {
41+
return new TrustedGatewayArNSResolver({
42+
log,
43+
trustedGatewayUrl: url,
44+
});
45+
}
46+
default: {
47+
throw new Error(`Unknown ArNSResolver type: ${type}`);
48+
}
49+
}
50+
};
+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* AR.IO Gateway
3+
* Copyright (C) 2022-2023 Permanent Data Solutions, Inc. All Rights Reserved.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
import { default as axios } from 'axios';
19+
import winston from 'winston';
20+
21+
import { isValidDataId } from '../lib/validation.js';
22+
import { NameResolution, NameResolver } from '../types.js';
23+
import { DEFAULT_ARNS_TTL_SECONDS } from './trusted-gateway-arns-resolver.js';
24+
25+
export class StandaloneArNSResolver implements NameResolver {
26+
private log: winston.Logger;
27+
private resolverUrl: string;
28+
29+
constructor({
30+
log,
31+
resolverUrl,
32+
}: {
33+
log: winston.Logger;
34+
resolverUrl: string;
35+
}) {
36+
this.log = log.child({
37+
class: 'StandaloneArNSResolver',
38+
resolverUrl: resolverUrl,
39+
});
40+
this.resolverUrl = resolverUrl;
41+
}
42+
43+
async resolve(name: string): Promise<NameResolution> {
44+
this.log.info('Resolving name...', { name });
45+
try {
46+
const { data } = await axios<{ txId: string; ttlSeconds: number }>({
47+
method: 'GET',
48+
url: `/ar-io/resolver/records/${name}`,
49+
baseURL: this.resolverUrl,
50+
validateStatus: (status) => status === 200,
51+
});
52+
53+
const resolvedId = data.txId;
54+
const ttl = data.ttlSeconds || DEFAULT_ARNS_TTL_SECONDS;
55+
if (isValidDataId(resolvedId)) {
56+
this.log.info('Resolved name', {
57+
name,
58+
resolvedId,
59+
ttl,
60+
});
61+
return {
62+
name,
63+
resolvedId,
64+
resolvedAt: Date.now(),
65+
ttl,
66+
};
67+
} else {
68+
this.log.warn('Invalid resolved data ID', {
69+
name,
70+
resolvedId,
71+
ttl,
72+
});
73+
}
74+
} catch (error: any) {
75+
this.log.warn('Unable to resolve name:', {
76+
name,
77+
message: error.message,
78+
stack: error.stack,
79+
});
80+
}
81+
82+
return {
83+
name,
84+
resolvedId: undefined,
85+
resolvedAt: undefined,
86+
ttl: undefined,
87+
};
88+
}
89+
}

src/resolution/trusted-gateway-arns-resolver.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import winston from 'winston';
2121
import { isValidDataId } from '../lib/validation.js';
2222
import { NameResolution, NameResolver } from '../types.js';
2323

24-
const DEFAULT_TTL = 60 * 15; // 15 minutes
24+
export const DEFAULT_ARNS_TTL_SECONDS = 60 * 15; // 15 minutes
2525

2626
export class TrustedGatewayArNSResolver implements NameResolver {
2727
private log: winston.Logger;
@@ -50,7 +50,8 @@ export class TrustedGatewayArNSResolver implements NameResolver {
5050
});
5151
const resolvedId = response.headers['x-arns-resolved-id'];
5252
const ttl =
53-
parseInt(response.headers['x-arns-ttl-seconds']) || DEFAULT_TTL;
53+
parseInt(response.headers['x-arns-ttl-seconds']) ||
54+
DEFAULT_ARNS_TTL_SECONDS;
5455
if (isValidDataId(resolvedId)) {
5556
this.log.info('Resolved name', { name, nameUrl, resolvedId, ttl });
5657
return {

src/system.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import log from './log.js';
3838
import * as metrics from './metrics.js';
3939
import { MemoryCacheArNSResolver } from './resolution/memory-cache-arns-resolver.js';
4040
import { StreamingManifestPathResolver } from './resolution/streaming-manifest-path-resolver.js';
41-
import { TrustedGatewayArNSResolver } from './resolution/trusted-gateway-arns-resolver.js';
4241
import { FsChunkDataStore } from './store/fs-chunk-data-store.js';
4342
import { FsDataStore } from './store/fs-data-store.js';
4443
import {
@@ -66,6 +65,7 @@ import { TransactionRepairWorker } from './workers/transaction-repair-worker.js'
6665
import { TransactionOffsetImporter } from './workers/transaction-offset-importer.js';
6766
import { TransactionOffsetRepairWorker } from './workers/transaction-offset-repair-worker.js';
6867
import { WebhookEmitter } from './workers/webhook-emitter.js';
68+
import { createArNSResolver } from './init/resolvers.js';
6969

7070
process.on('uncaughtException', (error) => {
7171
metrics.uncaughtExceptionCounter.inc();
@@ -390,9 +390,10 @@ export const manifestPathResolver = new StreamingManifestPathResolver({
390390

391391
export const nameResolver = new MemoryCacheArNSResolver({
392392
log,
393-
resolver: new TrustedGatewayArNSResolver({
393+
resolver: createArNSResolver({
394394
log,
395-
trustedGatewayUrl: config.TRUSTED_ARNS_GATEWAY_URL,
395+
type: config.TRUSTED_ARNS_RESOLVER_TYPE,
396+
url: config.TRUSTED_ARNS_RESOLVER_URL,
396397
}),
397398
});
398399

0 commit comments

Comments
 (0)