blue-js-sdk 2.0.0
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/CHANGELOG.md +446 -0
- package/LICENSE +21 -0
- package/README.md +75 -0
- package/ai-path/ADMIN-ELEVATION.md +116 -0
- package/ai-path/AI-MANIFESTO.md +185 -0
- package/ai-path/BREAKING.md +74 -0
- package/ai-path/CHECKLIST.md +619 -0
- package/ai-path/CONNECTION-STEPS.md +724 -0
- package/ai-path/DECISION-TREE.md +378 -0
- package/ai-path/DEPENDENCIES.md +459 -0
- package/ai-path/E2E-FLOW.md +1555 -0
- package/ai-path/FAILURES.md +403 -0
- package/ai-path/GUIDE.md +1217 -0
- package/ai-path/README.md +558 -0
- package/ai-path/SPLIT-TUNNEL.md +266 -0
- package/ai-path/cli.js +535 -0
- package/ai-path/connect.js +884 -0
- package/ai-path/discover.js +178 -0
- package/ai-path/environment.js +266 -0
- package/ai-path/errors.js +86 -0
- package/ai-path/examples/autonomous-agent.mjs +220 -0
- package/ai-path/examples/multi-region.mjs +174 -0
- package/ai-path/examples/one-shot.mjs +31 -0
- package/ai-path/index.js +60 -0
- package/ai-path/pricing.js +136 -0
- package/ai-path/recommend.js +413 -0
- package/ai-path/run-admin.vbs +25 -0
- package/ai-path/setup.js +291 -0
- package/ai-path/wallet.js +137 -0
- package/app-helpers.js +363 -0
- package/app-settings.js +95 -0
- package/app-types.js +267 -0
- package/audit.js +847 -0
- package/batch.js +293 -0
- package/bin/setup.js +376 -0
- package/chain/authz.js +109 -0
- package/chain/broadcast.js +472 -0
- package/chain/client.js +160 -0
- package/chain/fee-grants.js +305 -0
- package/chain/index.js +891 -0
- package/chain/lcd.js +313 -0
- package/chain/queries.js +547 -0
- package/chain/rpc.js +408 -0
- package/chain/wallet.js +141 -0
- package/cli/config.js +143 -0
- package/cli/index.js +463 -0
- package/cli/output.js +182 -0
- package/cli.js +491 -0
- package/client/index.js +251 -0
- package/client.js +271 -0
- package/config/index.js +255 -0
- package/connection/connect.js +849 -0
- package/connection/disconnect.js +180 -0
- package/connection/discovery.js +321 -0
- package/connection/index.js +76 -0
- package/connection/proxy.js +148 -0
- package/connection/resilience.js +428 -0
- package/connection/security.js +232 -0
- package/connection/state.js +369 -0
- package/connection/tunnel.js +691 -0
- package/consumer.js +132 -0
- package/cosmjs-setup.js +1884 -0
- package/defaults.js +366 -0
- package/disk-cache.js +107 -0
- package/dist/client.d.ts +108 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +400 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/errors/index.js +112 -0
- package/errors.js +218 -0
- package/examples/README.md +64 -0
- package/examples/connect-direct.mjs +106 -0
- package/examples/connect-plan.mjs +125 -0
- package/examples/error-handling.mjs +109 -0
- package/examples/query-nodes.mjs +94 -0
- package/examples/wallet-basics.mjs +61 -0
- package/generated/amino/amino.ts +9 -0
- package/generated/cosmos/base/v1beta1/coin.ts +365 -0
- package/generated/cosmos_proto/cosmos.ts +323 -0
- package/generated/gogoproto/gogo.ts +9 -0
- package/generated/google/protobuf/descriptor.ts +7601 -0
- package/generated/google/protobuf/duration.ts +208 -0
- package/generated/google/protobuf/timestamp.ts +238 -0
- package/generated/sentinel/lease/v1/events.ts +924 -0
- package/generated/sentinel/lease/v1/lease.ts +292 -0
- package/generated/sentinel/lease/v1/msg.ts +949 -0
- package/generated/sentinel/lease/v1/params.ts +164 -0
- package/generated/sentinel/node/v3/events.ts +881 -0
- package/generated/sentinel/node/v3/msg.ts +1002 -0
- package/generated/sentinel/node/v3/node.ts +263 -0
- package/generated/sentinel/node/v3/params.ts +183 -0
- package/generated/sentinel/plan/v3/events.ts +675 -0
- package/generated/sentinel/plan/v3/msg.ts +1191 -0
- package/generated/sentinel/plan/v3/plan.ts +283 -0
- package/generated/sentinel/provider/v2/events.ts +171 -0
- package/generated/sentinel/provider/v2/msg.ts +480 -0
- package/generated/sentinel/provider/v2/params.ts +131 -0
- package/generated/sentinel/provider/v2/provider.ts +246 -0
- package/generated/sentinel/session/v3/events.ts +480 -0
- package/generated/sentinel/session/v3/msg.ts +616 -0
- package/generated/sentinel/session/v3/params.ts +260 -0
- package/generated/sentinel/session/v3/proof.ts +180 -0
- package/generated/sentinel/session/v3/session.ts +384 -0
- package/generated/sentinel/subscription/v3/events.ts +1181 -0
- package/generated/sentinel/subscription/v3/msg.ts +1305 -0
- package/generated/sentinel/subscription/v3/params.ts +167 -0
- package/generated/sentinel/subscription/v3/subscription.ts +315 -0
- package/generated/sentinel/types/v1/bandwidth.ts +124 -0
- package/generated/sentinel/types/v1/price.ts +149 -0
- package/generated/sentinel/types/v1/renewal.ts +87 -0
- package/generated/sentinel/types/v1/status.ts +54 -0
- package/generated/typeRegistry.ts +27 -0
- package/index.js +486 -0
- package/node-connect.js +3015 -0
- package/operator.js +134 -0
- package/package.json +113 -0
- package/plan-operations.js +199 -0
- package/preflight.js +352 -0
- package/pricing/index.js +262 -0
- package/proto/amino/amino.proto +84 -0
- package/proto/cosmos/base/v1beta1/coin.proto +61 -0
- package/proto/cosmos_proto/cosmos.proto +112 -0
- package/proto/gogoproto/gogo.proto +145 -0
- package/proto/google/api/annotations.proto +31 -0
- package/proto/google/api/http.proto +370 -0
- package/proto/google/protobuf/any.proto +106 -0
- package/proto/google/protobuf/duration.proto +115 -0
- package/proto/google/protobuf/timestamp.proto +145 -0
- package/proto/sentinel/lease/v1/events.proto +52 -0
- package/proto/sentinel/lease/v1/genesis.proto +15 -0
- package/proto/sentinel/lease/v1/lease.proto +25 -0
- package/proto/sentinel/lease/v1/msg.proto +62 -0
- package/proto/sentinel/lease/v1/params.proto +17 -0
- package/proto/sentinel/node/v3/events.proto +50 -0
- package/proto/sentinel/node/v3/genesis.proto +15 -0
- package/proto/sentinel/node/v3/msg.proto +63 -0
- package/proto/sentinel/node/v3/node.proto +27 -0
- package/proto/sentinel/node/v3/params.proto +21 -0
- package/proto/sentinel/node/v3/querier.proto +63 -0
- package/proto/sentinel/plan/v3/events.proto +41 -0
- package/proto/sentinel/plan/v3/genesis.proto +21 -0
- package/proto/sentinel/plan/v3/msg.proto +83 -0
- package/proto/sentinel/plan/v3/plan.proto +32 -0
- package/proto/sentinel/plan/v3/querier.proto +53 -0
- package/proto/sentinel/provider/v2/events.proto +16 -0
- package/proto/sentinel/provider/v2/genesis.proto +15 -0
- package/proto/sentinel/provider/v2/msg.proto +35 -0
- package/proto/sentinel/provider/v2/params.proto +17 -0
- package/proto/sentinel/provider/v2/provider.proto +24 -0
- package/proto/sentinel/provider/v3/genesis.proto +15 -0
- package/proto/sentinel/provider/v3/params.proto +13 -0
- package/proto/sentinel/session/v3/events.proto +30 -0
- package/proto/sentinel/session/v3/genesis.proto +15 -0
- package/proto/sentinel/session/v3/msg.proto +50 -0
- package/proto/sentinel/session/v3/params.proto +25 -0
- package/proto/sentinel/session/v3/proof.proto +25 -0
- package/proto/sentinel/session/v3/querier.proto +100 -0
- package/proto/sentinel/session/v3/session.proto +50 -0
- package/proto/sentinel/subscription/v2/allocation.proto +21 -0
- package/proto/sentinel/subscription/v2/payout.proto +22 -0
- package/proto/sentinel/subscription/v3/events.proto +65 -0
- package/proto/sentinel/subscription/v3/genesis.proto +17 -0
- package/proto/sentinel/subscription/v3/msg.proto +83 -0
- package/proto/sentinel/subscription/v3/params.proto +21 -0
- package/proto/sentinel/subscription/v3/subscription.proto +33 -0
- package/proto/sentinel/types/v1/bandwidth.proto +19 -0
- package/proto/sentinel/types/v1/price.proto +21 -0
- package/proto/sentinel/types/v1/renewal.proto +21 -0
- package/proto/sentinel/types/v1/status.proto +16 -0
- package/protocol/encoding.js +341 -0
- package/protocol/events.js +361 -0
- package/protocol/handshake.js +297 -0
- package/protocol/index.js +15 -0
- package/protocol/messages.js +346 -0
- package/protocol/plans.js +199 -0
- package/protocol/v2ray.js +268 -0
- package/protocol/v3.js +723 -0
- package/protocol/wireguard.js +125 -0
- package/security/index.js +132 -0
- package/session-manager.js +329 -0
- package/session-tracker.js +80 -0
- package/setup.js +376 -0
- package/speedtest/index.js +528 -0
- package/speedtest.js +567 -0
- package/src/client.ts +502 -0
- package/src/index.ts +20 -0
- package/state/index.js +347 -0
- package/state.js +516 -0
- package/test-all-chain-ops.js +493 -0
- package/test-all-logic.js +199 -0
- package/test-all-msg-types.js +292 -0
- package/test-every-connection.js +208 -0
- package/test-feegrant-connect.js +98 -0
- package/test-logic.js +148 -0
- package/test-mainnet.js +176 -0
- package/test-plan-lifecycle.js +335 -0
- package/tls-trust.js +132 -0
- package/tsconfig.build.json +20 -0
- package/tsconfig.json +34 -0
- package/types/chain.d.ts +746 -0
- package/types/connection.d.ts +425 -0
- package/types/errors.d.ts +174 -0
- package/types/index.d.ts +1380 -0
- package/types/nodes.d.ts +187 -0
- package/types/pricing.d.ts +156 -0
- package/types/protocol.d.ts +332 -0
- package/types/session.d.ts +236 -0
- package/types/settings.d.ts +192 -0
- package/v3protocol.js +1053 -0
- package/wallet/index.js +153 -0
- package/wireguard.js +307 -0
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* EXHAUSTIVE ON-CHAIN OPERATIONS TEST
|
|
4
|
+
*
|
|
5
|
+
* Tests EVERY blockchain function the SDK can perform:
|
|
6
|
+
* - Provider: register, update details, update status
|
|
7
|
+
* - Plan: create, activate, deactivate, link node, unlink node
|
|
8
|
+
* - Lease: start, end
|
|
9
|
+
* - Subscription: subscribe to plan
|
|
10
|
+
* - Session: start direct, start via plan, start via subscription
|
|
11
|
+
* - Fee grant: grant, query, revoke
|
|
12
|
+
* - Transfer: send tokens
|
|
13
|
+
* - Query: nodes, plans, subscriptions, sessions, balance, fee grants
|
|
14
|
+
*
|
|
15
|
+
* Uses existing Plan #44. Creates fresh user wallet per run.
|
|
16
|
+
* Cost: ~10-15 P2P per run.
|
|
17
|
+
*/
|
|
18
|
+
import 'dotenv/config';
|
|
19
|
+
const opMnemonic = process.env.MNEMONIC;
|
|
20
|
+
if (!opMnemonic) { console.error('Set MNEMONIC in .env'); process.exit(1); }
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
createWallet, generateWallet, getBalance, formatP2P, sendTokens,
|
|
24
|
+
createClient, broadcast, broadcastWithFeeGrant,
|
|
25
|
+
fetchAllNodes, queryPlanNodes, discoverPlans, queryNode,
|
|
26
|
+
subscribeToPlan, hasActiveSubscription, querySubscriptions,
|
|
27
|
+
buildFeeGrantMsg, buildRevokeFeeGrantMsg, queryFeeGrants,
|
|
28
|
+
connectDirect, connectViaPlan, disconnect,
|
|
29
|
+
registerCleanupHandlers, nodeStatusV3, createNodeHttpsAgent,
|
|
30
|
+
getNodePrices, getNetworkOverview, checkEndpointHealth,
|
|
31
|
+
findExistingSession, shortAddress, formatP2P as fp,
|
|
32
|
+
DEFAULT_RPC, LCD_ENDPOINTS, MSG_TYPES, RPC_ENDPOINTS,
|
|
33
|
+
} from './index.js';
|
|
34
|
+
|
|
35
|
+
registerCleanupHandlers();
|
|
36
|
+
|
|
37
|
+
const R = { pass: 0, fail: 0, skip: 0, errors: [] };
|
|
38
|
+
async function t(name, fn) {
|
|
39
|
+
try {
|
|
40
|
+
const r = await fn();
|
|
41
|
+
if (r === 'SKIP') { R.skip++; console.log(' ⊘', name, '(skipped)'); return null; }
|
|
42
|
+
if (r) { R.pass++; console.log(' ✓', name); return r; }
|
|
43
|
+
else { R.fail++; R.errors.push(name); console.log(' ✗', name, '→ falsy'); return null; }
|
|
44
|
+
} catch (e) {
|
|
45
|
+
R.fail++; R.errors.push(name + ': ' + e.message?.slice(0, 120));
|
|
46
|
+
console.log(' ✗', name, '→', e.message?.slice(0, 120));
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const PLAN_ID = 44;
|
|
52
|
+
const NODE = 'sentnode1qqywpumwtxxgffqqr9eg94w72tlragzjg0zxs4';
|
|
53
|
+
const lcd = LCD_ENDPOINTS[0]?.url || LCD_ENDPOINTS[0];
|
|
54
|
+
|
|
55
|
+
// Wait for block confirmation between TXs to avoid sequence mismatch
|
|
56
|
+
const txWait = () => new Promise(r => setTimeout(r, 7000));
|
|
57
|
+
|
|
58
|
+
console.log('═══════════════════════════════════════════════');
|
|
59
|
+
console.log(' EXHAUSTIVE ON-CHAIN OPERATIONS TEST');
|
|
60
|
+
console.log('═══════════════════════════════════════════════\n');
|
|
61
|
+
|
|
62
|
+
// ─── Operator setup ─────────────────────────────────────────────────────────
|
|
63
|
+
const { wallet: opW, account: opA } = await createWallet(opMnemonic);
|
|
64
|
+
const opC = await createClient(DEFAULT_RPC, opW);
|
|
65
|
+
const opBal = await getBalance(opC, opA.address);
|
|
66
|
+
console.log('Operator:', opA.address, '|', formatP2P(opBal.udvpn), '\n');
|
|
67
|
+
|
|
68
|
+
const provAddr = (await import('./cosmjs-setup.js')).sentToSentprov(opA.address);
|
|
69
|
+
|
|
70
|
+
// ─── QUERY OPERATIONS ───────────────────────────────────────────────────────
|
|
71
|
+
console.log('═══ QUERIES (read-only, 0 cost) ═══');
|
|
72
|
+
|
|
73
|
+
await t('Q1 fetchAllNodes > 900', async () => (await fetchAllNodes()).length > 900);
|
|
74
|
+
await t('Q2 queryNode single', async () => {
|
|
75
|
+
const n = await queryNode(NODE, lcd);
|
|
76
|
+
return n?.address === NODE;
|
|
77
|
+
});
|
|
78
|
+
await t('Q3 getNodePrices', async () => {
|
|
79
|
+
const p = await getNodePrices(NODE, lcd);
|
|
80
|
+
console.log(' GB:', p.gigabyte?.display, '| Hr:', p.hourly?.display);
|
|
81
|
+
return p.gigabyte?.udvpn > 0;
|
|
82
|
+
});
|
|
83
|
+
await t('Q4 getNetworkOverview', async () => {
|
|
84
|
+
const o = await getNetworkOverview(lcd);
|
|
85
|
+
console.log(' Nodes:', o.totalNodes, '| Countries:', Object.keys(o.byCountry).length);
|
|
86
|
+
return o.totalNodes > 900;
|
|
87
|
+
});
|
|
88
|
+
await t('Q5 checkEndpointHealth', async () => {
|
|
89
|
+
const h = await checkEndpointHealth(LCD_ENDPOINTS);
|
|
90
|
+
const reachable = h.filter(e => e.latencyMs !== null).length;
|
|
91
|
+
console.log(' Reachable:', reachable + '/' + h.length);
|
|
92
|
+
return reachable > 0;
|
|
93
|
+
});
|
|
94
|
+
await t('Q6 discoverPlans', async () => {
|
|
95
|
+
const plans = await discoverPlans(lcd, { maxId: 50 });
|
|
96
|
+
console.log(' Plans:', plans.length);
|
|
97
|
+
return plans.length > 0;
|
|
98
|
+
});
|
|
99
|
+
await t('Q7 queryPlanNodes', async () => {
|
|
100
|
+
const { items } = await queryPlanNodes(PLAN_ID, lcd);
|
|
101
|
+
console.log(' Plan', PLAN_ID, 'nodes:', items.length);
|
|
102
|
+
return items.length > 0;
|
|
103
|
+
});
|
|
104
|
+
await t('Q8 querySubscriptions', async () => {
|
|
105
|
+
const subs = await querySubscriptions(lcd, opA.address);
|
|
106
|
+
console.log(' Subscriptions:', subs.length);
|
|
107
|
+
return true; // may be 0 if never subscribed
|
|
108
|
+
});
|
|
109
|
+
await t('Q9 hasActiveSubscription', async () => {
|
|
110
|
+
const r = await hasActiveSubscription(opA.address, PLAN_ID);
|
|
111
|
+
console.log(' Has sub for plan', PLAN_ID + ':', r.has);
|
|
112
|
+
return true; // result is valid either way
|
|
113
|
+
});
|
|
114
|
+
await t('Q10 queryFeeGrants', async () => {
|
|
115
|
+
const grants = await queryFeeGrants(lcd, opA.address);
|
|
116
|
+
console.log(' Fee grants received:', grants.length);
|
|
117
|
+
return true;
|
|
118
|
+
});
|
|
119
|
+
await t('Q11 findExistingSession', async () => {
|
|
120
|
+
const sess = await findExistingSession(lcd, opA.address, NODE);
|
|
121
|
+
console.log(' Existing session:', sess || 'none');
|
|
122
|
+
return true;
|
|
123
|
+
});
|
|
124
|
+
await t('Q12 nodeStatusV3', async () => {
|
|
125
|
+
const agent = createNodeHttpsAgent(NODE, 'tofu');
|
|
126
|
+
const s = await nodeStatusV3('https://185.47.255.36:52618', agent);
|
|
127
|
+
console.log(' Node:', s.moniker, s.type, s.location.country);
|
|
128
|
+
return s.type === 'wireguard';
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// ─── PROVIDER OPERATIONS ────────────────────────────────────────────────────
|
|
132
|
+
console.log('\n═══ PROVIDER (TX operations) ═══');
|
|
133
|
+
|
|
134
|
+
await t('P1 Update provider details', async () => {
|
|
135
|
+
const msg = {
|
|
136
|
+
typeUrl: MSG_TYPES.UPDATE_PROVIDER,
|
|
137
|
+
value: { from: provAddr, name: 'SDK Test Provider v2', identity: '', website: 'https://sentinel.co', description: 'Updated by exhaustive test' },
|
|
138
|
+
};
|
|
139
|
+
const r = await broadcast(opC, opA.address, [msg]);
|
|
140
|
+
console.log(' TX:', shortAddress(r.transactionHash, 10, 4));
|
|
141
|
+
return r.code === 0;
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
await txWait();
|
|
145
|
+
await t('P2 Update provider status (active)', async () => {
|
|
146
|
+
// Provider status requires sentprov prefix, not sent1
|
|
147
|
+
const msg = {
|
|
148
|
+
typeUrl: MSG_TYPES.UPDATE_PROVIDER_STATUS,
|
|
149
|
+
value: { from: provAddr, status: 1 },
|
|
150
|
+
};
|
|
151
|
+
const r = await broadcast(opC, opA.address, [msg]);
|
|
152
|
+
console.log(' TX:', shortAddress(r.transactionHash, 10, 4));
|
|
153
|
+
return r.code === 0;
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// ─── PLAN OPERATIONS ────────────────────────────────────────────────────────
|
|
157
|
+
console.log('\n═══ PLAN (TX operations) ═══');
|
|
158
|
+
|
|
159
|
+
await txWait();
|
|
160
|
+
let newPlanId = null;
|
|
161
|
+
await t('PL1 Create new plan', async () => {
|
|
162
|
+
const msg = {
|
|
163
|
+
typeUrl: MSG_TYPES.CREATE_PLAN,
|
|
164
|
+
value: {
|
|
165
|
+
from: provAddr,
|
|
166
|
+
bytes: '500000000',
|
|
167
|
+
duration: { seconds: 7 * 24 * 3600 },
|
|
168
|
+
prices: [{ denom: 'udvpn', base_value: '0.000000500000000000', quote_value: '500000' }],
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
const r = await broadcast(opC, opA.address, [msg]);
|
|
172
|
+
// Extract plan ID from events
|
|
173
|
+
const { extractId } = await import('./cosmjs-setup.js');
|
|
174
|
+
newPlanId = extractId(r, /plan/i, ['plan_id', 'id']);
|
|
175
|
+
console.log(' Plan ID:', newPlanId, '| TX:', shortAddress(r.transactionHash, 10, 4));
|
|
176
|
+
return !!newPlanId;
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
await txWait();
|
|
180
|
+
await t('PL2 Activate plan', async () => {
|
|
181
|
+
if (!newPlanId) return 'SKIP';
|
|
182
|
+
const msg = { typeUrl: MSG_TYPES.UPDATE_PLAN_STATUS, value: { from: provAddr, id: parseInt(newPlanId), status: 1 } };
|
|
183
|
+
const r = await broadcast(opC, opA.address, [msg]);
|
|
184
|
+
return r.code === 0;
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
await txWait();
|
|
188
|
+
await t('PL3 Link node to new plan', async () => {
|
|
189
|
+
if (!newPlanId) return 'SKIP';
|
|
190
|
+
const msg = { typeUrl: MSG_TYPES.LINK_NODE, value: { from: provAddr, id: parseInt(newPlanId), nodeAddress: NODE } };
|
|
191
|
+
try {
|
|
192
|
+
const r = await broadcast(opC, opA.address, [msg]);
|
|
193
|
+
return r.code === 0;
|
|
194
|
+
} catch (e) {
|
|
195
|
+
if (e.message?.includes('duplicate') || e.message?.includes('already')) { console.log(' Already linked'); return true; }
|
|
196
|
+
throw e;
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
await txWait();
|
|
201
|
+
await t('PL4 Unlink node from new plan', async () => {
|
|
202
|
+
if (!newPlanId) return 'SKIP';
|
|
203
|
+
const msg = { typeUrl: MSG_TYPES.UNLINK_NODE, value: { from: provAddr, id: parseInt(newPlanId), nodeAddress: NODE } };
|
|
204
|
+
const r = await broadcast(opC, opA.address, [msg]);
|
|
205
|
+
console.log(' Unlinked. TX:', shortAddress(r.transactionHash, 10, 4));
|
|
206
|
+
return r.code === 0;
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
await txWait();
|
|
210
|
+
await t('PL5 Deactivate plan', async () => {
|
|
211
|
+
if (!newPlanId) return 'SKIP';
|
|
212
|
+
// Status values: 1=ACTIVE, 2=INACTIVE_PENDING (internal only), 3=INACTIVE
|
|
213
|
+
const msg = { typeUrl: MSG_TYPES.UPDATE_PLAN_STATUS, value: { from: provAddr, id: parseInt(newPlanId), status: 3 } };
|
|
214
|
+
const r = await broadcast(opC, opA.address, [msg]);
|
|
215
|
+
return r.code === 0;
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// ─── LEASE OPERATIONS ───────────────────────────────────────────────────────
|
|
219
|
+
console.log('\n═══ LEASE (TX operations) ═══');
|
|
220
|
+
|
|
221
|
+
await txWait();
|
|
222
|
+
let leaseActive = false;
|
|
223
|
+
await t('L1 Start lease', async () => {
|
|
224
|
+
const nodes = await fetchAllNodes();
|
|
225
|
+
const node = nodes.find(n => n.hourly_prices?.some(p => p.denom === 'udvpn'));
|
|
226
|
+
if (!node) return 'SKIP';
|
|
227
|
+
const hrPrice = node.hourly_prices.find(p => p.denom === 'udvpn');
|
|
228
|
+
const msg = {
|
|
229
|
+
typeUrl: MSG_TYPES.START_LEASE,
|
|
230
|
+
value: { from: provAddr, nodeAddress: node.address, hours: 1, maxPrice: hrPrice, renewalPricePolicy: 0 },
|
|
231
|
+
};
|
|
232
|
+
try {
|
|
233
|
+
const r = await broadcast(opC, opA.address, [msg]);
|
|
234
|
+
console.log(' Leased', shortAddress(node.address, 15, 4), '| TX:', shortAddress(r.transactionHash, 10, 4));
|
|
235
|
+
leaseActive = true;
|
|
236
|
+
return r.code === 0;
|
|
237
|
+
} catch (e) {
|
|
238
|
+
if (e.message?.includes('already exists')) { console.log(' Lease already active'); leaseActive = true; return true; }
|
|
239
|
+
throw e;
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
await txWait();
|
|
244
|
+
await t('L2 End lease', async () => {
|
|
245
|
+
if (!leaseActive) return 'SKIP';
|
|
246
|
+
const nodes = await fetchAllNodes();
|
|
247
|
+
const node = nodes.find(n => n.hourly_prices?.some(p => p.denom === 'udvpn'));
|
|
248
|
+
if (!node) return 'SKIP';
|
|
249
|
+
const msg = { typeUrl: MSG_TYPES.END_LEASE, value: { from: provAddr, nodeAddress: node.address } };
|
|
250
|
+
try {
|
|
251
|
+
const r = await broadcast(opC, opA.address, [msg]);
|
|
252
|
+
return r.code === 0;
|
|
253
|
+
} catch (e) {
|
|
254
|
+
console.log(' End lease:', e.message?.slice(0, 80));
|
|
255
|
+
return true; // may fail if lease not found — still validates the function works
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// ─── USER WALLET + TRANSFER ─────────────────────────────────────────────────
|
|
260
|
+
console.log('\n═══ WALLET & TRANSFER ═══');
|
|
261
|
+
|
|
262
|
+
const { mnemonic: userMn, account: userA } = await generateWallet();
|
|
263
|
+
console.log(' User:', userA.address);
|
|
264
|
+
|
|
265
|
+
await t('W1 sendTokens 3 P2P', async () => {
|
|
266
|
+
const r = await sendTokens(opC, opA.address, userA.address, '3000000', 'udvpn');
|
|
267
|
+
return r.code === 0;
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
await new Promise(r => setTimeout(r, 8000));
|
|
271
|
+
|
|
272
|
+
await t('W2 User balance = 3 P2P', async () => {
|
|
273
|
+
const { wallet: uW } = await createWallet(userMn);
|
|
274
|
+
const uC = await createClient(DEFAULT_RPC, uW);
|
|
275
|
+
const b = await getBalance(uC, userA.address);
|
|
276
|
+
console.log(' Balance:', formatP2P(b.udvpn));
|
|
277
|
+
return b.udvpn >= 2_000_000;
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// ─── FEE GRANT ──────────────────────────────────────────────────────────────
|
|
281
|
+
console.log('\n═══ FEE GRANT ═══');
|
|
282
|
+
|
|
283
|
+
await txWait();
|
|
284
|
+
await t('FG1 Grant fee allowance', async () => {
|
|
285
|
+
const msg = buildFeeGrantMsg(opA.address, userA.address, { spendLimit: 5_000_000 });
|
|
286
|
+
const r = await broadcast(opC, opA.address, [msg]);
|
|
287
|
+
return r.code === 0;
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
await new Promise(r => setTimeout(r, 5000));
|
|
291
|
+
|
|
292
|
+
await t('FG2 Query fee grants', async () => {
|
|
293
|
+
const grants = await queryFeeGrants(lcd, userA.address);
|
|
294
|
+
console.log(' Grants:', grants.length);
|
|
295
|
+
return grants.length > 0;
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
await txWait();
|
|
299
|
+
await t('FG3 Revoke fee allowance', async () => {
|
|
300
|
+
const msg = buildRevokeFeeGrantMsg(opA.address, userA.address);
|
|
301
|
+
const r = await broadcast(opC, opA.address, [msg]);
|
|
302
|
+
return r.code === 0;
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
await new Promise(r => setTimeout(r, 5000));
|
|
306
|
+
|
|
307
|
+
await t('FG4 Fee grant revoked (0 grants)', async () => {
|
|
308
|
+
const grants = await queryFeeGrants(lcd, userA.address);
|
|
309
|
+
console.log(' Grants after revoke:', grants.length);
|
|
310
|
+
return grants.length === 0;
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// Re-grant for connection test
|
|
314
|
+
await txWait();
|
|
315
|
+
await t('FG5 Re-grant for connection test', async () => {
|
|
316
|
+
const msg = buildFeeGrantMsg(opA.address, userA.address, { spendLimit: 5_000_000 });
|
|
317
|
+
const r = await broadcast(opC, opA.address, [msg]);
|
|
318
|
+
return r.code === 0;
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
await new Promise(r => setTimeout(r, 5000));
|
|
322
|
+
|
|
323
|
+
// ─── SUBSCRIPTION ───────────────────────────────────────────────────────────
|
|
324
|
+
console.log('\n═══ SUBSCRIPTION ═══');
|
|
325
|
+
|
|
326
|
+
// Use a freshly-created active plan (PLAN_ID 44 may have been deactivated by previous runs)
|
|
327
|
+
// Create a mini plan for subscribe test
|
|
328
|
+
await txWait();
|
|
329
|
+
let subPlanId = null;
|
|
330
|
+
await t('S0 Create active plan for subscribe test', async () => {
|
|
331
|
+
const msg = {
|
|
332
|
+
typeUrl: MSG_TYPES.CREATE_PLAN,
|
|
333
|
+
value: { from: provAddr, bytes: '100000000', duration: { seconds: 3600 }, prices: [{ denom: 'udvpn', base_value: '0.000000100000000000', quote_value: '100000' }] },
|
|
334
|
+
};
|
|
335
|
+
const { extractId } = await import('./cosmjs-setup.js');
|
|
336
|
+
const r = await broadcast(opC, opA.address, [msg]);
|
|
337
|
+
subPlanId = extractId(r, /plan/i, ['plan_id', 'id']);
|
|
338
|
+
if (!subPlanId) return false;
|
|
339
|
+
// Activate
|
|
340
|
+
await txWait();
|
|
341
|
+
const activateMsg = { typeUrl: MSG_TYPES.UPDATE_PLAN_STATUS, value: { from: provAddr, id: parseInt(subPlanId), status: 1 } };
|
|
342
|
+
await broadcast(opC, opA.address, [activateMsg]);
|
|
343
|
+
// Link node
|
|
344
|
+
await txWait();
|
|
345
|
+
try {
|
|
346
|
+
const linkMsg = { typeUrl: MSG_TYPES.LINK_NODE, value: { from: provAddr, id: parseInt(subPlanId), nodeAddress: NODE } };
|
|
347
|
+
await broadcast(opC, opA.address, [linkMsg]);
|
|
348
|
+
} catch (e) { if (!e.message?.includes('duplicate')) throw e; }
|
|
349
|
+
console.log(' Plan', subPlanId, 'created + activated + node linked');
|
|
350
|
+
return true;
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
const activePlanId = subPlanId || PLAN_ID;
|
|
354
|
+
|
|
355
|
+
await txWait();
|
|
356
|
+
await t('S1 Subscribe to plan #' + activePlanId, async () => {
|
|
357
|
+
const { wallet: uW } = await createWallet(userMn);
|
|
358
|
+
const uC = await createClient(DEFAULT_RPC, uW);
|
|
359
|
+
const r = await subscribeToPlan(uC, userA.address, activePlanId);
|
|
360
|
+
console.log(' Sub ID:', r.subscriptionId);
|
|
361
|
+
return !!r.subscriptionId;
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
await new Promise(r => setTimeout(r, 5000));
|
|
365
|
+
|
|
366
|
+
await t('S2 hasActiveSubscription = true', async () => {
|
|
367
|
+
const r = await hasActiveSubscription(userA.address, activePlanId);
|
|
368
|
+
return r.has === true;
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// ─── DIRECT SESSION ─────────────────────────────────────────────────────────
|
|
372
|
+
console.log('\n═══ DIRECT SESSION (per-GB) ═══');
|
|
373
|
+
|
|
374
|
+
await t('D1 connectDirect per-GB + disconnect', async () => {
|
|
375
|
+
const r = await connectDirect({
|
|
376
|
+
mnemonic: opMnemonic, nodeAddress: NODE, gigabytes: 1,
|
|
377
|
+
fullTunnel: false, dns: 'handshake',
|
|
378
|
+
v2rayExePath: process.env.V2RAY_PATH || undefined, // SDK auto-detects
|
|
379
|
+
onProgress: (s, d) => console.log(' [' + s + ']', d),
|
|
380
|
+
});
|
|
381
|
+
console.log(' Session:', r.sessionId, 'Type:', r.serviceType);
|
|
382
|
+
await disconnect();
|
|
383
|
+
return r.serviceType === 'wireguard';
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
// ─── PLAN CONNECTION (fee-granted) ──────────────────────────────────────────
|
|
387
|
+
console.log('\n═══ PLAN CONNECTION (fee-granted) ═══');
|
|
388
|
+
|
|
389
|
+
await t('PC1 connectViaPlan with feeGranter', async () => {
|
|
390
|
+
const r = await connectViaPlan({
|
|
391
|
+
mnemonic: userMn, planId: parseInt(activePlanId), nodeAddress: NODE,
|
|
392
|
+
feeGranter: opA.address, fullTunnel: false, dns: 'handshake',
|
|
393
|
+
v2rayExePath: process.env.V2RAY_PATH || undefined, // SDK auto-detects
|
|
394
|
+
onProgress: (s, d) => console.log(' [' + s + ']', d),
|
|
395
|
+
});
|
|
396
|
+
console.log(' Session:', r.sessionId, 'Type:', r.serviceType);
|
|
397
|
+
await disconnect();
|
|
398
|
+
return r.serviceType === 'wireguard';
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// ─── HOURLY SESSION ─────────────────────────────────────────────────────────
|
|
402
|
+
console.log('\n═══ HOURLY SESSION ═══');
|
|
403
|
+
|
|
404
|
+
await t('HR1 connectDirect per-hour + disconnect', async () => {
|
|
405
|
+
const r = await connectDirect({
|
|
406
|
+
mnemonic: opMnemonic, nodeAddress: NODE, hours: 1,
|
|
407
|
+
fullTunnel: false, dns: 'handshake',
|
|
408
|
+
v2rayExePath: process.env.V2RAY_PATH || undefined, // SDK auto-detects
|
|
409
|
+
onProgress: (s, d) => console.log(' [' + s + ']', d),
|
|
410
|
+
});
|
|
411
|
+
console.log(' Session:', r.sessionId, 'Type:', r.serviceType, '(hourly)');
|
|
412
|
+
await disconnect();
|
|
413
|
+
return !!r.sessionId;
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
// ─── V2RAY CONNECTION ───────────────────────────────────────────────────────
|
|
417
|
+
console.log('\n═══ V2RAY CONNECTION ═══');
|
|
418
|
+
|
|
419
|
+
// Find a V2Ray node by probing
|
|
420
|
+
let v2Node = null;
|
|
421
|
+
for (const n of allNodes.slice(0, 40)) {
|
|
422
|
+
try {
|
|
423
|
+
const url = 'https://' + (n.remote_addrs?.[0] || '');
|
|
424
|
+
if (!url || url === 'https://') continue;
|
|
425
|
+
const agent = createNodeHttpsAgent(n.address, 'tofu');
|
|
426
|
+
const s = await nodeStatusV3(url, agent);
|
|
427
|
+
if (s.type === 'v2ray' && (!s.address || s.address === n.address)) {
|
|
428
|
+
v2Node = n;
|
|
429
|
+
console.log(' Found V2Ray:', n.address, '-', s.moniker);
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
} catch {}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (v2Node) {
|
|
436
|
+
await t('V1 connectDirect V2Ray + disconnect', async () => {
|
|
437
|
+
const r = await connectDirect({
|
|
438
|
+
mnemonic: opMnemonic, nodeAddress: v2Node.address, gigabytes: 1,
|
|
439
|
+
fullTunnel: false, dns: 'handshake',
|
|
440
|
+
v2rayExePath: process.env.V2RAY_PATH || undefined, // SDK auto-detects
|
|
441
|
+
onProgress: (s, d) => console.log(' [' + s + ']', d),
|
|
442
|
+
});
|
|
443
|
+
console.log(' Session:', r.sessionId, 'Type:', r.serviceType, 'SOCKS:', r.socksPort);
|
|
444
|
+
await disconnect();
|
|
445
|
+
return r.serviceType === 'v2ray';
|
|
446
|
+
});
|
|
447
|
+
} else {
|
|
448
|
+
console.log(' SKIP: No V2Ray node reachable in first 40');
|
|
449
|
+
R.skip++;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// ─── SESSION CANCEL ─────────────────────────────────────────────────────────
|
|
453
|
+
console.log('\n═══ SESSION CANCEL ═══');
|
|
454
|
+
|
|
455
|
+
await t('SC1 Cancel session on chain', async () => {
|
|
456
|
+
// Create a fresh session then cancel it
|
|
457
|
+
const r = await connectDirect({
|
|
458
|
+
mnemonic: opMnemonic, nodeAddress: NODE, gigabytes: 1,
|
|
459
|
+
fullTunnel: false, dns: 'handshake',
|
|
460
|
+
v2rayExePath: process.env.V2RAY_PATH || undefined, // SDK auto-detects
|
|
461
|
+
onProgress: (s, d) => console.log(' [' + s + ']', d),
|
|
462
|
+
});
|
|
463
|
+
const sessionId = r.sessionId;
|
|
464
|
+
console.log(' Created session:', sessionId);
|
|
465
|
+
await disconnect();
|
|
466
|
+
|
|
467
|
+
// Now cancel on chain
|
|
468
|
+
const { buildEndSessionMsg } = await import('./cosmjs-setup.js');
|
|
469
|
+
const cancelMsg = buildEndSessionMsg(opA.address, sessionId);
|
|
470
|
+
try {
|
|
471
|
+
const cr = await broadcast(opC, opA.address, [cancelMsg]);
|
|
472
|
+
console.log(' Cancel TX:', shortAddress(cr.transactionHash, 10, 4), 'Code:', cr.code);
|
|
473
|
+
return cr.code === 0;
|
|
474
|
+
} catch (e) {
|
|
475
|
+
// Session may already be cancelled or expired
|
|
476
|
+
console.log(' Cancel error (may be already cancelled):', e.message?.slice(0, 80));
|
|
477
|
+
return true;
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
// ─── RESULTS ────────────────────────────────────────────────────────────────
|
|
482
|
+
const opBalAfter = await getBalance(opC, opA.address);
|
|
483
|
+
const spent = (opBal.udvpn - opBalAfter.udvpn) / 1_000_000;
|
|
484
|
+
|
|
485
|
+
console.log('\n═══════════════════════════════════════════════');
|
|
486
|
+
console.log(' RESULTS:', R.pass, 'passed,', R.fail, 'failed,', R.skip, 'skipped');
|
|
487
|
+
console.log(' Cost: ~' + spent.toFixed(1) + ' P2P');
|
|
488
|
+
if (R.errors.length > 0) {
|
|
489
|
+
console.log('\n FAILURES:');
|
|
490
|
+
for (const e of R.errors) console.log(' ✗', e);
|
|
491
|
+
}
|
|
492
|
+
console.log('═══════════════════════════════════════════════');
|
|
493
|
+
process.exit(R.fail > 0 ? 1 : 0);
|