@ar.io/sdk 3.10.0-alpha.6 → 3.10.1
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 +66 -125
- package/bundles/web.bundle.min.js +147 -126
- package/lib/cjs/cli/commands/antCommands.js +3 -4
- package/lib/cjs/cli/commands/arnsPurchaseCommands.js +5 -6
- package/lib/cjs/cli/commands/gatewayWriteCommands.js +11 -12
- package/lib/cjs/cli/commands/readCommands.js +21 -22
- package/lib/cjs/cli/commands/transfer.js +6 -7
- package/lib/cjs/cli/options.js +0 -12
- package/lib/cjs/cli/utils.js +52 -74
- package/lib/cjs/common/ant-versions.js +5 -5
- package/lib/cjs/common/faucet.js +2 -2
- package/lib/cjs/common/index.js +0 -2
- package/lib/cjs/common/io.js +5 -64
- package/lib/cjs/types/ant.js +2 -13
- package/lib/cjs/types/index.js +0 -1
- package/lib/cjs/types/io.js +5 -5
- package/lib/cjs/utils/ao.js +9 -14
- package/lib/cjs/utils/arweave.js +4 -4
- package/lib/cjs/utils/base64.js +4 -5
- package/lib/cjs/utils/json.js +1 -2
- package/lib/cjs/utils/schema.js +1 -2
- package/lib/cjs/version.js +1 -1
- package/lib/cjs/web/index.js +1 -3
- package/lib/esm/cli/options.js +0 -12
- package/lib/esm/cli/utils.js +10 -31
- package/lib/esm/common/ant-versions.js +5 -5
- package/lib/esm/common/index.js +0 -2
- package/lib/esm/common/io.js +5 -64
- package/lib/esm/types/ant.js +0 -11
- package/lib/esm/types/index.js +0 -1
- package/lib/esm/types/io.js +1 -1
- package/lib/esm/utils/ao.js +0 -5
- package/lib/esm/version.js +1 -1
- package/lib/esm/web/index.js +1 -1
- package/lib/types/cli/commands/antCommands.d.ts +3 -3
- package/lib/types/cli/commands/arnsPurchaseCommands.d.ts +1 -1
- package/lib/types/cli/commands/gatewayWriteCommands.d.ts +9 -9
- package/lib/types/cli/commands/readCommands.d.ts +3 -4
- package/lib/types/cli/commands/transfer.d.ts +3 -3
- package/lib/types/cli/options.d.ts +0 -9
- package/lib/types/cli/types.d.ts +0 -3
- package/lib/types/cli/utils.d.ts +0 -4
- package/lib/types/common/ant-versions.d.ts +6 -3
- package/lib/types/common/http.d.ts +0 -1
- package/lib/types/common/index.d.ts +0 -1
- package/lib/types/common/io.d.ts +9 -15
- package/lib/types/types/ant.d.ts +1 -53
- package/lib/types/types/common.d.ts +2 -8
- package/lib/types/types/index.d.ts +0 -1
- package/lib/types/types/io.d.ts +7 -9
- package/lib/types/types/token.d.ts +0 -2
- package/lib/types/utils/ao.d.ts +12 -1
- package/lib/types/utils/base64.d.ts +0 -2
- package/lib/types/version.d.ts +1 -1
- package/lib/types/web/index.d.ts +1 -1
- package/package.json +3 -5
- package/lib/cjs/common/turbo.js +0 -208
- package/lib/cjs/common/wayfinder/gateways.js +0 -75
- package/lib/cjs/common/wayfinder/index.js +0 -36
- package/lib/cjs/common/wayfinder/routers/fixed.js +0 -14
- package/lib/cjs/common/wayfinder/routers/fixed.test.js +0 -14
- package/lib/cjs/common/wayfinder/routers/priority.js +0 -38
- package/lib/cjs/common/wayfinder/routers/priority.test.js +0 -155
- package/lib/cjs/common/wayfinder/routers/random.js +0 -23
- package/lib/cjs/common/wayfinder/routers/random.test.js +0 -104
- package/lib/cjs/common/wayfinder/routers/simple-cache.js +0 -25
- package/lib/cjs/common/wayfinder/routers/simple-cache.test.js +0 -41
- package/lib/cjs/common/wayfinder/wayfinder.js +0 -194
- package/lib/cjs/common/wayfinder/wayfinder.test.js +0 -254
- package/lib/cjs/types/wayfinder.js +0 -2
- package/lib/cjs/utils/url.js +0 -28
- package/lib/cjs/utils/url.test.js +0 -24
- package/lib/esm/common/turbo.js +0 -200
- package/lib/esm/common/wayfinder/gateways.js +0 -69
- package/lib/esm/common/wayfinder/index.js +0 -20
- package/lib/esm/common/wayfinder/routers/fixed.js +0 -10
- package/lib/esm/common/wayfinder/routers/fixed.test.js +0 -12
- package/lib/esm/common/wayfinder/routers/priority.js +0 -34
- package/lib/esm/common/wayfinder/routers/priority.test.js +0 -153
- package/lib/esm/common/wayfinder/routers/random.js +0 -19
- package/lib/esm/common/wayfinder/routers/random.test.js +0 -102
- package/lib/esm/common/wayfinder/routers/simple-cache.js +0 -21
- package/lib/esm/common/wayfinder/routers/simple-cache.test.js +0 -39
- package/lib/esm/common/wayfinder/wayfinder.js +0 -187
- package/lib/esm/common/wayfinder/wayfinder.test.js +0 -249
- package/lib/esm/types/wayfinder.js +0 -1
- package/lib/esm/utils/url.js +0 -24
- package/lib/esm/utils/url.test.js +0 -19
- package/lib/types/common/turbo.d.ts +0 -62
- package/lib/types/common/wayfinder/gateways.d.ts +0 -44
- package/lib/types/common/wayfinder/index.d.ts +0 -19
- package/lib/types/common/wayfinder/routers/fixed.d.ts +0 -25
- package/lib/types/common/wayfinder/routers/fixed.test.d.ts +0 -1
- package/lib/types/common/wayfinder/routers/priority.d.ts +0 -34
- package/lib/types/common/wayfinder/routers/priority.test.d.ts +0 -1
- package/lib/types/common/wayfinder/routers/random.d.ts +0 -28
- package/lib/types/common/wayfinder/routers/random.test.d.ts +0 -1
- package/lib/types/common/wayfinder/routers/simple-cache.d.ts +0 -29
- package/lib/types/common/wayfinder/routers/simple-cache.test.d.ts +0 -1
- package/lib/types/common/wayfinder/wayfinder.d.ts +0 -106
- package/lib/types/common/wayfinder/wayfinder.test.d.ts +0 -1
- package/lib/types/types/wayfinder.d.ts +0 -20
- package/lib/types/utils/url.d.ts +0 -19
- package/lib/types/utils/url.test.d.ts +0 -1
package/lib/esm/common/turbo.js
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
|
|
3
|
-
*
|
|
4
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
* you may not use this file except in compliance with the License.
|
|
6
|
-
* You may obtain a copy of the License at
|
|
7
|
-
*
|
|
8
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
*
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
* See the License for the specific language governing permissions and
|
|
14
|
-
* limitations under the License.
|
|
15
|
-
*/
|
|
16
|
-
import { ArconnectSigner, ArweaveSigner, EthereumSigner, InjectedEthereumSigner, SignatureConfig, } from '@dha-team/arbundles';
|
|
17
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
18
|
-
import { mARIOToken } from '../types/token.js';
|
|
19
|
-
import { toB64Url } from '../utils/base64.js';
|
|
20
|
-
import { createAxiosInstance } from '../utils/http-client.js';
|
|
21
|
-
import { urlWithSearchParams } from '../utils/url.js';
|
|
22
|
-
import { Logger } from './logger.js';
|
|
23
|
-
export async function signedRequestHeadersFromSigner({ signer, nonce = uuidv4(), }) {
|
|
24
|
-
let signature = undefined;
|
|
25
|
-
let publicKey = undefined;
|
|
26
|
-
const signatureType = isWanderArweaveBrowserSigner(signer)
|
|
27
|
-
? SignatureConfig.ARWEAVE
|
|
28
|
-
: signer.signatureType;
|
|
29
|
-
// equivalent to window.arweaveWallet
|
|
30
|
-
if (isWanderArweaveBrowserSigner(signer)) {
|
|
31
|
-
signature = toB64Url(Buffer.from(await signer.signMessage(Uint8Array.from(Buffer.from(nonce)))));
|
|
32
|
-
}
|
|
33
|
-
else if (signer instanceof ArconnectSigner) {
|
|
34
|
-
signature = toB64Url(Buffer.from(await signer['signer'].signMessage(Uint8Array.from(Buffer.from(nonce)))));
|
|
35
|
-
}
|
|
36
|
-
else if (signer instanceof ArweaveSigner ||
|
|
37
|
-
signer instanceof EthereumSigner ||
|
|
38
|
-
signer instanceof InjectedEthereumSigner) {
|
|
39
|
-
if ('setPublicKey' in signer && signer['publicKey'] === undefined) {
|
|
40
|
-
await signer.setPublicKey();
|
|
41
|
-
}
|
|
42
|
-
signature = toB64Url(Buffer.from(await signer.sign(Uint8Array.from(Buffer.from(nonce)))));
|
|
43
|
-
}
|
|
44
|
-
switch (signatureType) {
|
|
45
|
-
case SignatureConfig.ARWEAVE:
|
|
46
|
-
if (isWanderArweaveBrowserSigner(signer)) {
|
|
47
|
-
publicKey = await signer.getActivePublicKey();
|
|
48
|
-
}
|
|
49
|
-
else if ('setPublicKey' in signer) {
|
|
50
|
-
await signer.setPublicKey();
|
|
51
|
-
publicKey = toB64Url(signer.publicKey);
|
|
52
|
-
}
|
|
53
|
-
break;
|
|
54
|
-
case SignatureConfig.ETHEREUM:
|
|
55
|
-
if ('publicKey' in signer) {
|
|
56
|
-
publicKey = '0x' + signer.publicKey.toString('hex');
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
throw new Error('Public key not found');
|
|
60
|
-
}
|
|
61
|
-
break;
|
|
62
|
-
// TODO: solana sig support
|
|
63
|
-
// case SignatureConfig.SOLANA:
|
|
64
|
-
// case SignatureConfig.ED25519:
|
|
65
|
-
default:
|
|
66
|
-
throw new Error(`Unsupported signer type for signing requests: ${signatureType}`);
|
|
67
|
-
}
|
|
68
|
-
if (publicKey === undefined || signature === undefined) {
|
|
69
|
-
throw new Error('Public key or signature not found');
|
|
70
|
-
}
|
|
71
|
-
return {
|
|
72
|
-
'x-public-key': publicKey,
|
|
73
|
-
'x-nonce': nonce,
|
|
74
|
-
'x-signature': signature,
|
|
75
|
-
'x-signature-type': signatureType,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
export class TurboArNSPaymentFactory {
|
|
79
|
-
static init(config) {
|
|
80
|
-
const { signer, paymentUrl, axios, logger } = config ?? {};
|
|
81
|
-
if (signer !== undefined) {
|
|
82
|
-
return new TurboArNSPaymentProviderAuthenticated({
|
|
83
|
-
signer,
|
|
84
|
-
paymentUrl,
|
|
85
|
-
axios,
|
|
86
|
-
logger,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
return new TurboArNSPaymentProviderUnauthenticated({
|
|
90
|
-
paymentUrl,
|
|
91
|
-
axios,
|
|
92
|
-
logger,
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
// Base class for unauthenticated operations
|
|
97
|
-
export class TurboArNSPaymentProviderUnauthenticated {
|
|
98
|
-
paymentUrl;
|
|
99
|
-
axios;
|
|
100
|
-
logger;
|
|
101
|
-
constructor({ paymentUrl = 'https://payment.ardrive.io', axios = createAxiosInstance(), logger = Logger.default, }) {
|
|
102
|
-
this.paymentUrl = paymentUrl;
|
|
103
|
-
this.axios = axios;
|
|
104
|
-
this.logger = logger;
|
|
105
|
-
}
|
|
106
|
-
async getArNSPriceDetails({ intent, name, quantity, type, years, }) {
|
|
107
|
-
const url = urlWithSearchParams({
|
|
108
|
-
baseUrl: `${this.paymentUrl}/v1/arns/price/${intent}/${name}`,
|
|
109
|
-
params: {
|
|
110
|
-
increaseQty: quantity,
|
|
111
|
-
type,
|
|
112
|
-
years,
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
const { data, status } = await this.axios.get(url);
|
|
116
|
-
this.logger.debug('getArNSPriceDetails', {
|
|
117
|
-
intent,
|
|
118
|
-
name,
|
|
119
|
-
quantity,
|
|
120
|
-
type,
|
|
121
|
-
years,
|
|
122
|
-
data,
|
|
123
|
-
status,
|
|
124
|
-
});
|
|
125
|
-
if (status !== 200) {
|
|
126
|
-
throw new Error('Failed to get ArNS purchase price ' + JSON.stringify(data));
|
|
127
|
-
}
|
|
128
|
-
if (!data.winc || !data.mARIO) {
|
|
129
|
-
throw new Error('Invalid response from Turbo ' + JSON.stringify(data));
|
|
130
|
-
}
|
|
131
|
-
return {
|
|
132
|
-
winc: data.winc,
|
|
133
|
-
mARIO: new mARIOToken(+data.mARIO),
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
async getPrice(params) {
|
|
137
|
-
const { winc } = await this.getArNSPriceDetails(params);
|
|
138
|
-
return +winc;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
// Class for authenticated operations, extending the base class
|
|
142
|
-
export class TurboArNSPaymentProviderAuthenticated extends TurboArNSPaymentProviderUnauthenticated {
|
|
143
|
-
signer;
|
|
144
|
-
constructor({ signer, ...restConfig }) {
|
|
145
|
-
super(restConfig); // Pass unauthenticated config to base class+
|
|
146
|
-
if (!isTurboArNSSigner(signer)) {
|
|
147
|
-
throw new Error('Signer must be a TurboArNSSigner');
|
|
148
|
-
}
|
|
149
|
-
this.signer = signer;
|
|
150
|
-
}
|
|
151
|
-
async initiateArNSPurchase({ intent, name, quantity, type, processId, years, }) {
|
|
152
|
-
// Signer check is implicitly handled by requiring it in the constructor
|
|
153
|
-
const url = urlWithSearchParams({
|
|
154
|
-
baseUrl: `${this.paymentUrl}/v1/arns/purchase/${intent}/${name}`,
|
|
155
|
-
params: {
|
|
156
|
-
increaseQty: quantity,
|
|
157
|
-
processId,
|
|
158
|
-
type,
|
|
159
|
-
years,
|
|
160
|
-
},
|
|
161
|
-
});
|
|
162
|
-
const headers = await signedRequestHeadersFromSigner({
|
|
163
|
-
signer: this.signer,
|
|
164
|
-
});
|
|
165
|
-
const { data, status } = await this.axios.post(url, null, {
|
|
166
|
-
headers,
|
|
167
|
-
});
|
|
168
|
-
this.logger.debug('Initiated ArNS purchase', {
|
|
169
|
-
intent,
|
|
170
|
-
name,
|
|
171
|
-
quantity,
|
|
172
|
-
processId,
|
|
173
|
-
type,
|
|
174
|
-
years,
|
|
175
|
-
data,
|
|
176
|
-
status,
|
|
177
|
-
});
|
|
178
|
-
if (status !== 200) {
|
|
179
|
-
throw new Error('Failed to initiate ArNS purchase ' + JSON.stringify(data));
|
|
180
|
-
}
|
|
181
|
-
return {
|
|
182
|
-
id: data.arioWriteResult.id,
|
|
183
|
-
result: data.purchaseReceipt,
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
function isWanderArweaveBrowserSigner(signer) {
|
|
188
|
-
return (typeof signer === 'object' &&
|
|
189
|
-
signer !== null &&
|
|
190
|
-
'signMessage' in signer &&
|
|
191
|
-
'getActivePublicKey' in signer);
|
|
192
|
-
}
|
|
193
|
-
export function isTurboArNSSigner(signer) {
|
|
194
|
-
const isWanderWallet = isWanderArweaveBrowserSigner(signer);
|
|
195
|
-
const isSigner = signer instanceof EthereumSigner ||
|
|
196
|
-
signer instanceof InjectedEthereumSigner ||
|
|
197
|
-
signer instanceof ArweaveSigner ||
|
|
198
|
-
signer instanceof ArconnectSigner;
|
|
199
|
-
return isWanderWallet || isSigner;
|
|
200
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
export class ARIOGatewaysProvider {
|
|
2
|
-
ario;
|
|
3
|
-
constructor({ ario }) {
|
|
4
|
-
this.ario = ario;
|
|
5
|
-
}
|
|
6
|
-
async getGateways() {
|
|
7
|
-
let cursor;
|
|
8
|
-
let attempts = 0;
|
|
9
|
-
const gateways = [];
|
|
10
|
-
do {
|
|
11
|
-
try {
|
|
12
|
-
const { items: newGateways, nextCursor } = await this.ario.getGateways({
|
|
13
|
-
limit: 1000,
|
|
14
|
-
cursor,
|
|
15
|
-
});
|
|
16
|
-
gateways.push(...newGateways);
|
|
17
|
-
cursor = nextCursor;
|
|
18
|
-
attempts = 0; // reset attempts if we get a new cursor
|
|
19
|
-
}
|
|
20
|
-
catch (error) {
|
|
21
|
-
console.error('Error fetching gateways', {
|
|
22
|
-
cursor,
|
|
23
|
-
attempts,
|
|
24
|
-
error,
|
|
25
|
-
});
|
|
26
|
-
attempts++;
|
|
27
|
-
}
|
|
28
|
-
} while (cursor !== undefined && attempts < 3);
|
|
29
|
-
// filter out any gateways that are not joined
|
|
30
|
-
return gateways.filter((g) => g.status === 'joined');
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
export class StaticGatewaysProvider {
|
|
34
|
-
gateways;
|
|
35
|
-
constructor({ gateways }) {
|
|
36
|
-
this.gateways = gateways;
|
|
37
|
-
}
|
|
38
|
-
async getGateways() {
|
|
39
|
-
return this.gateways;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
export class SimpleCacheGatewaysProvider {
|
|
43
|
-
gatewaysProvider;
|
|
44
|
-
ttlSeconds;
|
|
45
|
-
lastUpdated;
|
|
46
|
-
gatewaysCache;
|
|
47
|
-
constructor({ gatewaysProvider, ttlSeconds = 5 * 60, // 5 minutes
|
|
48
|
-
}) {
|
|
49
|
-
this.gatewaysCache = [];
|
|
50
|
-
this.gatewaysProvider = gatewaysProvider;
|
|
51
|
-
this.ttlSeconds = ttlSeconds;
|
|
52
|
-
}
|
|
53
|
-
async getGateways() {
|
|
54
|
-
const now = Date.now();
|
|
55
|
-
if (this.gatewaysCache.length === 0 ||
|
|
56
|
-
now - this.lastUpdated > this.ttlSeconds * 1000) {
|
|
57
|
-
try {
|
|
58
|
-
// preserve the cache if the fetch fails
|
|
59
|
-
const allGateways = await this.gatewaysProvider.getGateways();
|
|
60
|
-
this.gatewaysCache = allGateways.filter((g) => g.status === 'joined');
|
|
61
|
-
this.lastUpdated = now;
|
|
62
|
-
}
|
|
63
|
-
catch (error) {
|
|
64
|
-
console.error('Error fetching gateways', error);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return this.gatewaysCache;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
|
|
3
|
-
*
|
|
4
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
* you may not use this file except in compliance with the License.
|
|
6
|
-
* You may obtain a copy of the License at
|
|
7
|
-
*
|
|
8
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
*
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
* See the License for the specific language governing permissions and
|
|
14
|
-
* limitations under the License.
|
|
15
|
-
*/
|
|
16
|
-
export * from './wayfinder.js';
|
|
17
|
-
// routers
|
|
18
|
-
export * from './routers/random.js';
|
|
19
|
-
export * from './routers/priority.js';
|
|
20
|
-
export * from './routers/fixed.js';
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from 'node:assert';
|
|
2
|
-
import { describe, it } from 'node:test';
|
|
3
|
-
import { FixedGatewayRouter } from './fixed.js';
|
|
4
|
-
describe('FixedRouter', () => {
|
|
5
|
-
it('should return the provided gateway', async () => {
|
|
6
|
-
const router = new FixedGatewayRouter({
|
|
7
|
-
gateway: new URL('http://test-gateway.net'),
|
|
8
|
-
});
|
|
9
|
-
const result = await router.getTargetGateway();
|
|
10
|
-
assert.deepStrictEqual(result, new URL('http://test-gateway.net'));
|
|
11
|
-
});
|
|
12
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { randomInt } from '../wayfinder.js';
|
|
2
|
-
// TODO: one of N where N are in the last time window have met certain performance thresholds
|
|
3
|
-
// TODO: look at bitorrent routing protocols for inspiration
|
|
4
|
-
// TODO: router that looks at local stats/metrics and adjusts based on those
|
|
5
|
-
export class PriorityGatewayRouter {
|
|
6
|
-
name = 'priority';
|
|
7
|
-
gatewaysProvider;
|
|
8
|
-
limit;
|
|
9
|
-
sortBy;
|
|
10
|
-
sortOrder;
|
|
11
|
-
blocklist;
|
|
12
|
-
constructor({ gatewaysProvider, limit = 1, sortBy = 'operatorStake', sortOrder = 'desc', blocklist = [], }) {
|
|
13
|
-
this.gatewaysProvider = gatewaysProvider;
|
|
14
|
-
this.limit = limit;
|
|
15
|
-
this.sortBy = sortBy;
|
|
16
|
-
this.sortOrder = sortOrder;
|
|
17
|
-
this.blocklist = blocklist;
|
|
18
|
-
}
|
|
19
|
-
async getTargetGateway() {
|
|
20
|
-
const allGateways = await this.gatewaysProvider.getGateways();
|
|
21
|
-
const gateways = allGateways.filter((gateway) => gateway.status === 'joined' &&
|
|
22
|
-
!this.blocklist.includes(gateway.settings.fqdn));
|
|
23
|
-
const sortedGateways = gateways
|
|
24
|
-
.sort(this.sortOrder === 'asc'
|
|
25
|
-
? (a, b) => a[this.sortBy] - b[this.sortBy]
|
|
26
|
-
: (a, b) => b[this.sortBy] - a[this.sortBy])
|
|
27
|
-
.slice(0, this.limit);
|
|
28
|
-
const targetGateway = sortedGateways[randomInt(0, sortedGateways.length)];
|
|
29
|
-
if (targetGateway === undefined) {
|
|
30
|
-
throw new Error('No target gateway found');
|
|
31
|
-
}
|
|
32
|
-
return new URL(`${targetGateway.settings.protocol}://${targetGateway.settings.fqdn}:${targetGateway.settings.port}`);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from 'node:assert';
|
|
2
|
-
import { describe, it } from 'node:test';
|
|
3
|
-
import { PriorityGatewayRouter } from './priority.js';
|
|
4
|
-
describe('PriorityRouter', () => {
|
|
5
|
-
const mockGateways = [
|
|
6
|
-
{
|
|
7
|
-
settings: {
|
|
8
|
-
fqdn: 'gateway1.net',
|
|
9
|
-
port: 443,
|
|
10
|
-
protocol: 'https',
|
|
11
|
-
allowDelegatedStaking: false,
|
|
12
|
-
delegateRewardShareRatio: 0.5,
|
|
13
|
-
allowedDelegates: [],
|
|
14
|
-
minDelegatedStake: 0,
|
|
15
|
-
autoStake: false,
|
|
16
|
-
properties: '',
|
|
17
|
-
label: '',
|
|
18
|
-
note: '',
|
|
19
|
-
},
|
|
20
|
-
gatewayAddress: 'addr1',
|
|
21
|
-
observerAddress: 'addr1',
|
|
22
|
-
totalDelegatedStake: 1000,
|
|
23
|
-
startTimestamp: 0,
|
|
24
|
-
endTimestamp: 0,
|
|
25
|
-
operatorStake: 100,
|
|
26
|
-
status: 'joined',
|
|
27
|
-
weights: {
|
|
28
|
-
normalizedCompositeWeight: 0.5,
|
|
29
|
-
stakeWeight: 0.5,
|
|
30
|
-
tenureWeight: 0.5,
|
|
31
|
-
gatewayPerformanceRatio: 0.5,
|
|
32
|
-
observerPerformanceRatio: 0.5,
|
|
33
|
-
compositeWeight: 0.5,
|
|
34
|
-
gatewayRewardRatioWeight: 0.5,
|
|
35
|
-
observerRewardRatioWeight: 0.5,
|
|
36
|
-
},
|
|
37
|
-
stats: {
|
|
38
|
-
passedConsecutiveEpochs: 10,
|
|
39
|
-
failedConsecutiveEpochs: 5,
|
|
40
|
-
totalEpochCount: 15,
|
|
41
|
-
passedEpochCount: 10,
|
|
42
|
-
failedEpochCount: 5,
|
|
43
|
-
observedEpochCount: 15,
|
|
44
|
-
prescribedEpochCount: 20,
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
settings: {
|
|
49
|
-
fqdn: 'gateway1.net',
|
|
50
|
-
port: 443,
|
|
51
|
-
protocol: 'https',
|
|
52
|
-
allowDelegatedStaking: false,
|
|
53
|
-
delegateRewardShareRatio: 0.5,
|
|
54
|
-
allowedDelegates: [],
|
|
55
|
-
minDelegatedStake: 0,
|
|
56
|
-
autoStake: false,
|
|
57
|
-
properties: '',
|
|
58
|
-
label: '',
|
|
59
|
-
note: '',
|
|
60
|
-
},
|
|
61
|
-
gatewayAddress: 'addr1',
|
|
62
|
-
observerAddress: 'addr1',
|
|
63
|
-
totalDelegatedStake: 2000,
|
|
64
|
-
startTimestamp: 0,
|
|
65
|
-
endTimestamp: 0,
|
|
66
|
-
operatorStake: 2000,
|
|
67
|
-
status: 'joined',
|
|
68
|
-
weights: {
|
|
69
|
-
normalizedCompositeWeight: 0.5,
|
|
70
|
-
stakeWeight: 0.5,
|
|
71
|
-
tenureWeight: 0.5,
|
|
72
|
-
gatewayPerformanceRatio: 0.5,
|
|
73
|
-
observerPerformanceRatio: 0.5,
|
|
74
|
-
compositeWeight: 0.5,
|
|
75
|
-
gatewayRewardRatioWeight: 0.5,
|
|
76
|
-
observerRewardRatioWeight: 0.5,
|
|
77
|
-
},
|
|
78
|
-
stats: {
|
|
79
|
-
passedConsecutiveEpochs: 10,
|
|
80
|
-
failedConsecutiveEpochs: 5,
|
|
81
|
-
totalEpochCount: 15,
|
|
82
|
-
passedEpochCount: 10,
|
|
83
|
-
failedEpochCount: 5,
|
|
84
|
-
observedEpochCount: 15,
|
|
85
|
-
prescribedEpochCount: 20,
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
settings: {
|
|
90
|
-
fqdn: 'gateway2.net',
|
|
91
|
-
port: 443,
|
|
92
|
-
protocol: 'https',
|
|
93
|
-
allowDelegatedStaking: false,
|
|
94
|
-
delegateRewardShareRatio: 0.5,
|
|
95
|
-
allowedDelegates: [],
|
|
96
|
-
minDelegatedStake: 0,
|
|
97
|
-
autoStake: false,
|
|
98
|
-
properties: '',
|
|
99
|
-
label: '',
|
|
100
|
-
note: '',
|
|
101
|
-
},
|
|
102
|
-
gatewayAddress: 'addr2',
|
|
103
|
-
observerAddress: 'addr2',
|
|
104
|
-
totalDelegatedStake: 0,
|
|
105
|
-
startTimestamp: 0,
|
|
106
|
-
endTimestamp: 0,
|
|
107
|
-
operatorStake: 0,
|
|
108
|
-
status: 'leaving',
|
|
109
|
-
weights: {
|
|
110
|
-
normalizedCompositeWeight: 0.5,
|
|
111
|
-
stakeWeight: 0.5,
|
|
112
|
-
tenureWeight: 0.5,
|
|
113
|
-
gatewayPerformanceRatio: 0.5,
|
|
114
|
-
observerPerformanceRatio: 0.5,
|
|
115
|
-
compositeWeight: 0.5,
|
|
116
|
-
gatewayRewardRatioWeight: 0.5,
|
|
117
|
-
observerRewardRatioWeight: 0.5,
|
|
118
|
-
},
|
|
119
|
-
stats: {
|
|
120
|
-
passedConsecutiveEpochs: 10,
|
|
121
|
-
failedConsecutiveEpochs: 5,
|
|
122
|
-
totalEpochCount: 15,
|
|
123
|
-
passedEpochCount: 10,
|
|
124
|
-
failedEpochCount: 5,
|
|
125
|
-
observedEpochCount: 15,
|
|
126
|
-
prescribedEpochCount: 20,
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
];
|
|
130
|
-
const mockGatewaysProvider = {
|
|
131
|
-
getGateways: async () => mockGateways,
|
|
132
|
-
};
|
|
133
|
-
it('should prioritize gateway with highest success rate when using successRate weight', async () => {
|
|
134
|
-
const router = new PriorityGatewayRouter({
|
|
135
|
-
gatewaysProvider: mockGatewaysProvider,
|
|
136
|
-
sortBy: 'totalDelegatedStake',
|
|
137
|
-
sortOrder: 'desc',
|
|
138
|
-
limit: 1,
|
|
139
|
-
});
|
|
140
|
-
const result = await router.getTargetGateway();
|
|
141
|
-
assert.deepStrictEqual(result, new URL('http://gateway1.net'));
|
|
142
|
-
});
|
|
143
|
-
it('should prioritize gateway with lowest latency when using latency weight', async () => {
|
|
144
|
-
const router = new PriorityGatewayRouter({
|
|
145
|
-
gatewaysProvider: mockGatewaysProvider,
|
|
146
|
-
sortBy: 'operatorStake',
|
|
147
|
-
sortOrder: 'desc',
|
|
148
|
-
limit: 1,
|
|
149
|
-
});
|
|
150
|
-
const result = await router.getTargetGateway();
|
|
151
|
-
assert.deepStrictEqual(result, new URL('http://gateway2.net'));
|
|
152
|
-
});
|
|
153
|
-
});
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { randomInt } from '../wayfinder.js';
|
|
2
|
-
export class RandomGatewayRouter {
|
|
3
|
-
name = 'random';
|
|
4
|
-
gatewaysProvider;
|
|
5
|
-
blocklist;
|
|
6
|
-
constructor({ gatewaysProvider, blocklist = [], }) {
|
|
7
|
-
this.gatewaysProvider = gatewaysProvider;
|
|
8
|
-
this.blocklist = blocklist;
|
|
9
|
-
}
|
|
10
|
-
async getTargetGateway() {
|
|
11
|
-
const allGateways = await this.gatewaysProvider.getGateways();
|
|
12
|
-
const gateways = allGateways.filter((g) => g.status === 'joined' && !this.blocklist.includes(g.settings.fqdn));
|
|
13
|
-
const targetGateway = gateways[randomInt(0, gateways.length)];
|
|
14
|
-
if (targetGateway === undefined) {
|
|
15
|
-
throw new Error('No target gateway found');
|
|
16
|
-
}
|
|
17
|
-
return new URL(`${targetGateway.settings.protocol}://${targetGateway.settings.fqdn}:${targetGateway.settings.port}`);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from 'node:assert';
|
|
2
|
-
import { describe, it } from 'node:test';
|
|
3
|
-
import { RandomGatewayRouter } from './random.js';
|
|
4
|
-
describe('RandomRouter', () => {
|
|
5
|
-
const mockGateways = [
|
|
6
|
-
{
|
|
7
|
-
settings: {
|
|
8
|
-
fqdn: 'gateway1.net',
|
|
9
|
-
port: 443,
|
|
10
|
-
protocol: 'https',
|
|
11
|
-
allowDelegatedStaking: false,
|
|
12
|
-
delegateRewardShareRatio: 0.5,
|
|
13
|
-
allowedDelegates: [],
|
|
14
|
-
minDelegatedStake: 0,
|
|
15
|
-
autoStake: false,
|
|
16
|
-
properties: '',
|
|
17
|
-
label: '',
|
|
18
|
-
note: '',
|
|
19
|
-
},
|
|
20
|
-
gatewayAddress: 'addr1',
|
|
21
|
-
observerAddress: 'addr1',
|
|
22
|
-
totalDelegatedStake: 1000,
|
|
23
|
-
startTimestamp: 0,
|
|
24
|
-
endTimestamp: 0,
|
|
25
|
-
operatorStake: 100,
|
|
26
|
-
status: 'joined',
|
|
27
|
-
weights: {
|
|
28
|
-
normalizedCompositeWeight: 0.5,
|
|
29
|
-
stakeWeight: 0.5,
|
|
30
|
-
tenureWeight: 0.5,
|
|
31
|
-
gatewayPerformanceRatio: 0.5,
|
|
32
|
-
observerPerformanceRatio: 0.5,
|
|
33
|
-
compositeWeight: 0.5,
|
|
34
|
-
gatewayRewardRatioWeight: 0.5,
|
|
35
|
-
observerRewardRatioWeight: 0.5,
|
|
36
|
-
},
|
|
37
|
-
stats: {
|
|
38
|
-
passedConsecutiveEpochs: 10,
|
|
39
|
-
failedConsecutiveEpochs: 5,
|
|
40
|
-
totalEpochCount: 15,
|
|
41
|
-
passedEpochCount: 10,
|
|
42
|
-
failedEpochCount: 5,
|
|
43
|
-
observedEpochCount: 15,
|
|
44
|
-
prescribedEpochCount: 20,
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
settings: {
|
|
49
|
-
fqdn: 'gateway2.net',
|
|
50
|
-
port: 443,
|
|
51
|
-
protocol: 'https',
|
|
52
|
-
allowDelegatedStaking: false,
|
|
53
|
-
delegateRewardShareRatio: 0.5,
|
|
54
|
-
allowedDelegates: [],
|
|
55
|
-
minDelegatedStake: 0,
|
|
56
|
-
autoStake: false,
|
|
57
|
-
properties: '',
|
|
58
|
-
label: '',
|
|
59
|
-
note: '',
|
|
60
|
-
},
|
|
61
|
-
gatewayAddress: 'addr2',
|
|
62
|
-
observerAddress: 'addr2',
|
|
63
|
-
totalDelegatedStake: 0,
|
|
64
|
-
startTimestamp: 0,
|
|
65
|
-
endTimestamp: 0,
|
|
66
|
-
operatorStake: 0,
|
|
67
|
-
status: 'leaving',
|
|
68
|
-
weights: {
|
|
69
|
-
normalizedCompositeWeight: 0.5,
|
|
70
|
-
stakeWeight: 0.5,
|
|
71
|
-
tenureWeight: 0.5,
|
|
72
|
-
gatewayPerformanceRatio: 0.5,
|
|
73
|
-
observerPerformanceRatio: 0.5,
|
|
74
|
-
compositeWeight: 0.5,
|
|
75
|
-
gatewayRewardRatioWeight: 0.5,
|
|
76
|
-
observerRewardRatioWeight: 0.5,
|
|
77
|
-
},
|
|
78
|
-
stats: {
|
|
79
|
-
passedConsecutiveEpochs: 10,
|
|
80
|
-
failedConsecutiveEpochs: 5,
|
|
81
|
-
totalEpochCount: 15,
|
|
82
|
-
passedEpochCount: 10,
|
|
83
|
-
failedEpochCount: 5,
|
|
84
|
-
observedEpochCount: 15,
|
|
85
|
-
prescribedEpochCount: 20,
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
];
|
|
89
|
-
const mockGatewaysProvider = {
|
|
90
|
-
getGateways: async () => mockGateways,
|
|
91
|
-
};
|
|
92
|
-
it('should only return joined gateways', async () => {
|
|
93
|
-
const router = new RandomGatewayRouter({
|
|
94
|
-
gatewaysProvider: mockGatewaysProvider,
|
|
95
|
-
});
|
|
96
|
-
// Run multiple times to ensure we only get joined gateways
|
|
97
|
-
for (let i = 0; i < 10; i++) {
|
|
98
|
-
const result = await router.getTargetGateway();
|
|
99
|
-
assert.deepStrictEqual(result, new URL('https://gateway1.net'));
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
});
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export class SimpleCacheRouter {
|
|
2
|
-
name;
|
|
3
|
-
lastUpdatedTimestamp;
|
|
4
|
-
ttlSeconds;
|
|
5
|
-
targetGateway;
|
|
6
|
-
router;
|
|
7
|
-
constructor({ router, ttlSeconds = 5 * 60, // 5 minutes
|
|
8
|
-
}) {
|
|
9
|
-
this.router = router;
|
|
10
|
-
this.ttlSeconds = ttlSeconds;
|
|
11
|
-
}
|
|
12
|
-
async getTargetGateway() {
|
|
13
|
-
if (this.targetGateway === undefined ||
|
|
14
|
-
this.lastUpdatedTimestamp + this.ttlSeconds * 1000 < Date.now()) {
|
|
15
|
-
this.targetGateway = await this.router.getTargetGateway();
|
|
16
|
-
this.lastUpdatedTimestamp = Date.now();
|
|
17
|
-
}
|
|
18
|
-
return this.targetGateway;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
// TODO: a router that accepts ario and a router, and adds read through promise cache to ario.getGateways to avoid calling the ARIO contract on every request
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from 'node:assert';
|
|
2
|
-
import { describe, it } from 'node:test';
|
|
3
|
-
import { SimpleCacheRouter } from './simple-cache.js';
|
|
4
|
-
describe('SimpleCacheRouter', () => {
|
|
5
|
-
it('should cache gateway for TTL period even if underlying gateway changes', async () => {
|
|
6
|
-
const gateway1 = new URL('https://gateway1.net');
|
|
7
|
-
const gateway2 = new URL('https://gateway2.net');
|
|
8
|
-
let currentGateway = gateway1;
|
|
9
|
-
const mockRouter = {
|
|
10
|
-
name: 'mock',
|
|
11
|
-
getTargetGateway: async () => currentGateway,
|
|
12
|
-
};
|
|
13
|
-
const router = new SimpleCacheRouter({
|
|
14
|
-
router: mockRouter,
|
|
15
|
-
ttlSeconds: 300, // 5 minutes
|
|
16
|
-
});
|
|
17
|
-
// Get initial gateway which should be cached
|
|
18
|
-
const initial = await router.getTargetGateway();
|
|
19
|
-
assert.deepStrictEqual(initial, gateway1);
|
|
20
|
-
// Change the underlying gateway
|
|
21
|
-
currentGateway = gateway2;
|
|
22
|
-
// Should still return cached gateway1 for multiple calls
|
|
23
|
-
for (let i = 0; i < 5; i++) {
|
|
24
|
-
const result = await router.getTargetGateway();
|
|
25
|
-
assert.deepStrictEqual(result, gateway1);
|
|
26
|
-
}
|
|
27
|
-
// Advance time past TTL
|
|
28
|
-
const originalNow = Date.now;
|
|
29
|
-
try {
|
|
30
|
-
Date.now = () => originalNow() + 300 * 1000 + 1;
|
|
31
|
-
// Should now return the new gateway2
|
|
32
|
-
const result = await router.getTargetGateway();
|
|
33
|
-
assert.deepStrictEqual(result, gateway2);
|
|
34
|
-
}
|
|
35
|
-
finally {
|
|
36
|
-
Date.now = originalNow;
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
});
|