@antseed/node 0.1.0 → 0.1.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.
Files changed (130) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/interfaces/seller-provider.d.ts +13 -1
  6. package/dist/interfaces/seller-provider.d.ts.map +1 -1
  7. package/dist/node.d.ts +13 -3
  8. package/dist/node.d.ts.map +1 -1
  9. package/dist/node.js +123 -15
  10. package/dist/node.js.map +1 -1
  11. package/dist/proxy/proxy-mux.d.ts +3 -1
  12. package/dist/proxy/proxy-mux.d.ts.map +1 -1
  13. package/dist/proxy/proxy-mux.js +9 -5
  14. package/dist/proxy/proxy-mux.js.map +1 -1
  15. package/dist/types/http.d.ts +1 -0
  16. package/dist/types/http.d.ts.map +1 -1
  17. package/dist/types/http.js +1 -1
  18. package/dist/types/http.js.map +1 -1
  19. package/package.json +14 -10
  20. package/contracts/AntseedEscrow.sol +0 -310
  21. package/contracts/MockUSDC.sol +0 -64
  22. package/contracts/README.md +0 -102
  23. package/src/config/encryption.test.ts +0 -49
  24. package/src/config/encryption.ts +0 -53
  25. package/src/config/plugin-config-manager.test.ts +0 -92
  26. package/src/config/plugin-config-manager.ts +0 -153
  27. package/src/config/plugin-loader.ts +0 -90
  28. package/src/discovery/announcer.ts +0 -169
  29. package/src/discovery/bootstrap.ts +0 -57
  30. package/src/discovery/default-metadata-resolver.ts +0 -18
  31. package/src/discovery/dht-health.ts +0 -136
  32. package/src/discovery/dht-node.ts +0 -191
  33. package/src/discovery/http-metadata-resolver.ts +0 -47
  34. package/src/discovery/index.ts +0 -15
  35. package/src/discovery/metadata-codec.ts +0 -453
  36. package/src/discovery/metadata-resolver.ts +0 -7
  37. package/src/discovery/metadata-server.ts +0 -73
  38. package/src/discovery/metadata-validator.ts +0 -172
  39. package/src/discovery/peer-lookup.ts +0 -122
  40. package/src/discovery/peer-metadata.ts +0 -34
  41. package/src/discovery/peer-selector.ts +0 -134
  42. package/src/discovery/profile-manager.ts +0 -131
  43. package/src/discovery/profile-search.ts +0 -100
  44. package/src/discovery/reputation-verifier.ts +0 -54
  45. package/src/index.ts +0 -61
  46. package/src/interfaces/buyer-router.ts +0 -21
  47. package/src/interfaces/plugin.ts +0 -36
  48. package/src/interfaces/seller-provider.ts +0 -81
  49. package/src/metering/index.ts +0 -6
  50. package/src/metering/receipt-generator.ts +0 -105
  51. package/src/metering/receipt-verifier.ts +0 -102
  52. package/src/metering/session-tracker.ts +0 -145
  53. package/src/metering/storage.ts +0 -600
  54. package/src/metering/token-counter.ts +0 -127
  55. package/src/metering/usage-aggregator.ts +0 -236
  56. package/src/node.ts +0 -1698
  57. package/src/p2p/connection-auth.ts +0 -152
  58. package/src/p2p/connection-manager.ts +0 -916
  59. package/src/p2p/handshake.ts +0 -162
  60. package/src/p2p/ice-config.ts +0 -59
  61. package/src/p2p/identity.ts +0 -110
  62. package/src/p2p/index.ts +0 -11
  63. package/src/p2p/keepalive.ts +0 -118
  64. package/src/p2p/message-protocol.ts +0 -171
  65. package/src/p2p/nat-traversal.ts +0 -169
  66. package/src/p2p/payment-codec.ts +0 -165
  67. package/src/p2p/payment-mux.ts +0 -153
  68. package/src/p2p/reconnect.ts +0 -117
  69. package/src/payments/balance-manager.ts +0 -77
  70. package/src/payments/buyer-payment-manager.ts +0 -414
  71. package/src/payments/disputes.ts +0 -72
  72. package/src/payments/evm/escrow-client.ts +0 -263
  73. package/src/payments/evm/keypair.ts +0 -31
  74. package/src/payments/evm/signatures.ts +0 -103
  75. package/src/payments/evm/wallet.ts +0 -42
  76. package/src/payments/index.ts +0 -50
  77. package/src/payments/settlement.ts +0 -40
  78. package/src/payments/types.ts +0 -79
  79. package/src/proxy/index.ts +0 -3
  80. package/src/proxy/provider-detection.ts +0 -78
  81. package/src/proxy/proxy-mux.ts +0 -173
  82. package/src/proxy/request-codec.ts +0 -294
  83. package/src/reputation/index.ts +0 -6
  84. package/src/reputation/rating-manager.ts +0 -118
  85. package/src/reputation/report-manager.ts +0 -91
  86. package/src/reputation/trust-engine.ts +0 -120
  87. package/src/reputation/trust-score.ts +0 -74
  88. package/src/reputation/uptime-tracker.ts +0 -155
  89. package/src/routing/default-router.ts +0 -75
  90. package/src/types/bittorrent-dht.d.ts +0 -19
  91. package/src/types/buyer.ts +0 -37
  92. package/src/types/capability.ts +0 -34
  93. package/src/types/connection.ts +0 -29
  94. package/src/types/http.ts +0 -20
  95. package/src/types/index.ts +0 -14
  96. package/src/types/metering.ts +0 -175
  97. package/src/types/nat-api.d.ts +0 -29
  98. package/src/types/peer-profile.ts +0 -25
  99. package/src/types/peer.ts +0 -62
  100. package/src/types/plugin-config.ts +0 -31
  101. package/src/types/protocol.ts +0 -162
  102. package/src/types/provider.ts +0 -40
  103. package/src/types/rating.ts +0 -23
  104. package/src/types/report.ts +0 -30
  105. package/src/types/seller.ts +0 -38
  106. package/src/types/staking.ts +0 -23
  107. package/src/utils/debug.ts +0 -30
  108. package/src/utils/hex.ts +0 -14
  109. package/tests/balance-manager.test.ts +0 -156
  110. package/tests/bootstrap.test.ts +0 -108
  111. package/tests/buyer-payment-manager.test.ts +0 -358
  112. package/tests/connection-auth.test.ts +0 -87
  113. package/tests/default-router.test.ts +0 -148
  114. package/tests/evm-keypair.test.ts +0 -173
  115. package/tests/identity.test.ts +0 -133
  116. package/tests/message-protocol.test.ts +0 -212
  117. package/tests/metadata-codec.test.ts +0 -165
  118. package/tests/metadata-validator.test.ts +0 -261
  119. package/tests/metering-storage.test.ts +0 -244
  120. package/tests/payment-codec.test.ts +0 -95
  121. package/tests/payment-mux.test.ts +0 -191
  122. package/tests/peer-selector.test.ts +0 -184
  123. package/tests/provider-detection.test.ts +0 -107
  124. package/tests/proxy-mux-security.test.ts +0 -38
  125. package/tests/receipt.test.ts +0 -215
  126. package/tests/reputation-integration.test.ts +0 -195
  127. package/tests/request-codec.test.ts +0 -144
  128. package/tests/token-counter.test.ts +0 -122
  129. package/tsconfig.json +0 -9
  130. package/vitest.config.ts +0 -7
@@ -1,156 +0,0 @@
1
- import { describe, it, expect, afterEach } from 'vitest';
2
- import * as os from 'node:os';
3
- import * as path from 'node:path';
4
- import * as fs from 'node:fs';
5
- import { randomBytes } from 'node:crypto';
6
- import { BalanceManager } from '../src/payments/balance-manager.js';
7
- import type { Transaction } from '../src/payments/types.js';
8
- import type { WalletInfo } from '../src/payments/types.js';
9
-
10
- function tmpDir(): string {
11
- return path.join(os.tmpdir(), `antseed-bm-test-${randomBytes(8).toString('hex')}`);
12
- }
13
-
14
- const dirsToClean: string[] = [];
15
-
16
- afterEach(() => {
17
- for (const dir of dirsToClean) {
18
- try {
19
- fs.rmSync(dir, { recursive: true, force: true });
20
- } catch {}
21
- }
22
- dirsToClean.length = 0;
23
- });
24
-
25
- function makeTx(overrides?: Partial<Transaction>): Transaction {
26
- return {
27
- txId: `tx-${Math.random().toString(36).slice(2)}`,
28
- type: 'escrow_lock',
29
- amountUSD: 10,
30
- from: 'buyer',
31
- to: 'seller',
32
- timestamp: Date.now(),
33
- status: 'confirmed',
34
- ...overrides,
35
- };
36
- }
37
-
38
- describe('BalanceManager.getBalance', () => {
39
- it('should compute unified balance from crypto + escrow', () => {
40
- const bm = new BalanceManager();
41
- const wallet: WalletInfo = { address: '0x1', chainId: 'base-local', balanceETH: '0', balanceUSDC: '50.5' };
42
- const result = bm.getBalance(wallet, 10);
43
-
44
- expect(result.cryptoUSDC).toBeCloseTo(50.5);
45
- expect(result.inEscrowUSDC).toBe(10);
46
- expect(result.totalUSD).toBeCloseTo(60.5);
47
- });
48
-
49
- it('should handle null wallet', () => {
50
- const bm = new BalanceManager();
51
- const result = bm.getBalance(null, 0);
52
- expect(result.cryptoUSDC).toBe(0);
53
- expect(result.totalUSD).toBe(0);
54
- });
55
-
56
- it('should include escrow in total', () => {
57
- const bm = new BalanceManager();
58
- const wallet: WalletInfo = { address: '0x1', chainId: 'base-local', balanceETH: '0', balanceUSDC: '100' };
59
- const result = bm.getBalance(wallet, 5);
60
- expect(result.totalUSD).toBe(105);
61
- });
62
-
63
- it('should handle both null wallet and zero escrow', () => {
64
- const bm = new BalanceManager();
65
- const result = bm.getBalance(null, 0);
66
- expect(result.totalUSD).toBe(0);
67
- });
68
- });
69
-
70
- describe('BalanceManager.recordTransaction / getTransactionHistory', () => {
71
- it('should record and retrieve transactions', () => {
72
- const bm = new BalanceManager();
73
- const tx = makeTx();
74
- bm.recordTransaction(tx);
75
-
76
- const history = bm.getTransactionHistory();
77
- expect(history).toHaveLength(1);
78
- expect(history[0]!.txId).toBe(tx.txId);
79
- });
80
-
81
- it('should filter by type', () => {
82
- const bm = new BalanceManager();
83
- bm.recordTransaction(makeTx({ type: 'escrow_lock' }));
84
- bm.recordTransaction(makeTx({ type: 'escrow_release' }));
85
- bm.recordTransaction(makeTx({ type: 'escrow_lock' }));
86
-
87
- expect(bm.getTransactionHistory('escrow_lock')).toHaveLength(2);
88
- expect(bm.getTransactionHistory('escrow_release')).toHaveLength(1);
89
- });
90
-
91
- it('should support limit and offset', () => {
92
- const bm = new BalanceManager();
93
- for (let i = 0; i < 10; i++) {
94
- bm.recordTransaction(makeTx({ txId: `tx-${i}` }));
95
- }
96
-
97
- const page = bm.getTransactionHistory(undefined, 3, 2);
98
- expect(page).toHaveLength(3);
99
- expect(page[0]!.txId).toBe('tx-2');
100
- });
101
- });
102
-
103
- describe('BalanceManager.getTotalEarnings / getTotalSpending', () => {
104
- it('should sum escrow_release transactions for earnings', () => {
105
- const bm = new BalanceManager();
106
- bm.recordTransaction(makeTx({ type: 'escrow_release', amountUSD: 10 }));
107
- bm.recordTransaction(makeTx({ type: 'escrow_release', amountUSD: 20 }));
108
- bm.recordTransaction(makeTx({ type: 'escrow_lock', amountUSD: 100 }));
109
-
110
- expect(bm.getTotalEarnings()).toBe(30);
111
- });
112
-
113
- it('should sum escrow_lock transactions for spending', () => {
114
- const bm = new BalanceManager();
115
- bm.recordTransaction(makeTx({ type: 'escrow_lock', amountUSD: 5 }));
116
- bm.recordTransaction(makeTx({ type: 'escrow_lock', amountUSD: 15 }));
117
- bm.recordTransaction(makeTx({ type: 'escrow_release', amountUSD: 100 }));
118
-
119
- expect(bm.getTotalSpending()).toBe(20);
120
- });
121
-
122
- it('should filter by since timestamp', () => {
123
- const bm = new BalanceManager();
124
- const now = Date.now();
125
- bm.recordTransaction(makeTx({ type: 'escrow_release', amountUSD: 10, timestamp: now - 2000 }));
126
- bm.recordTransaction(makeTx({ type: 'escrow_release', amountUSD: 20, timestamp: now - 500 }));
127
-
128
- expect(bm.getTotalEarnings(now - 1000)).toBe(20);
129
- expect(bm.getTotalEarnings()).toBe(30);
130
- });
131
- });
132
-
133
- describe('BalanceManager.save / load', () => {
134
- it('should persist and restore transactions', async () => {
135
- const dir = tmpDir();
136
- dirsToClean.push(dir);
137
-
138
- const bm1 = new BalanceManager();
139
- bm1.recordTransaction(makeTx({ txId: 'saved-tx', amountUSD: 42 }));
140
- await bm1.save(dir);
141
-
142
- const bm2 = new BalanceManager();
143
- await bm2.load(dir);
144
-
145
- const history = bm2.getTransactionHistory();
146
- expect(history).toHaveLength(1);
147
- expect(history[0]!.txId).toBe('saved-tx');
148
- expect(history[0]!.amountUSD).toBe(42);
149
- });
150
-
151
- it('should start fresh if load file does not exist', async () => {
152
- const bm = new BalanceManager();
153
- await bm.load('/nonexistent/path');
154
- expect(bm.getTransactionHistory()).toEqual([]);
155
- });
156
- });
@@ -1,108 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import {
3
- OFFICIAL_BOOTSTRAP_NODES,
4
- parseBootstrapList,
5
- mergeBootstrapNodes,
6
- toBootstrapConfig,
7
- type BootstrapNode,
8
- } from '../src/discovery/bootstrap.js';
9
-
10
- describe('OFFICIAL_BOOTSTRAP_NODES', () => {
11
- it('should contain at least one node', () => {
12
- expect(OFFICIAL_BOOTSTRAP_NODES.length).toBeGreaterThan(0);
13
- });
14
-
15
- it('should have valid host and port for every node', () => {
16
- for (const node of OFFICIAL_BOOTSTRAP_NODES) {
17
- expect(node.host).toBeTruthy();
18
- expect(node.port).toBeGreaterThanOrEqual(1);
19
- expect(node.port).toBeLessThanOrEqual(65535);
20
- }
21
- });
22
- });
23
-
24
- describe('parseBootstrapList', () => {
25
- it('should parse valid host:port entries', () => {
26
- const result = parseBootstrapList(['example.com:6881', '10.0.0.1:8080']);
27
- expect(result).toEqual([
28
- { host: 'example.com', port: 6881 },
29
- { host: '10.0.0.1', port: 8080 },
30
- ]);
31
- });
32
-
33
- it('should parse IPv6 addresses with port', () => {
34
- const result = parseBootstrapList(['::1:6881']);
35
- expect(result).toHaveLength(1);
36
- expect(result[0]!.host).toBe('::1');
37
- expect(result[0]!.port).toBe(6881);
38
- });
39
-
40
- it('should throw on missing port', () => {
41
- expect(() => parseBootstrapList(['example.com'])).toThrow('missing port');
42
- });
43
-
44
- it('should throw on invalid port (0)', () => {
45
- expect(() => parseBootstrapList(['example.com:0'])).toThrow('Invalid port');
46
- });
47
-
48
- it('should throw on port > 65535', () => {
49
- expect(() => parseBootstrapList(['example.com:99999'])).toThrow('Invalid port');
50
- });
51
-
52
- it('should throw on non-numeric port', () => {
53
- expect(() => parseBootstrapList(['example.com:abc'])).toThrow('Invalid port');
54
- });
55
-
56
- it('should handle an empty array', () => {
57
- expect(parseBootstrapList([])).toEqual([]);
58
- });
59
- });
60
-
61
- describe('mergeBootstrapNodes', () => {
62
- it('should combine two disjoint lists', () => {
63
- const official: BootstrapNode[] = [{ host: 'a.com', port: 1 }];
64
- const user: BootstrapNode[] = [{ host: 'b.com', port: 2 }];
65
- const result = mergeBootstrapNodes(official, user);
66
- expect(result).toHaveLength(2);
67
- });
68
-
69
- it('should deduplicate by host:port', () => {
70
- const official: BootstrapNode[] = [{ host: 'a.com', port: 1, label: 'Official' }];
71
- const user: BootstrapNode[] = [{ host: 'a.com', port: 1, label: 'User' }];
72
- const result = mergeBootstrapNodes(official, user);
73
- expect(result).toHaveLength(1);
74
- // Official entry comes first and should win
75
- expect(result[0]!.label).toBe('Official');
76
- });
77
-
78
- it('should keep user nodes that have different ports', () => {
79
- const official: BootstrapNode[] = [{ host: 'a.com', port: 1 }];
80
- const user: BootstrapNode[] = [{ host: 'a.com', port: 2 }];
81
- const result = mergeBootstrapNodes(official, user);
82
- expect(result).toHaveLength(2);
83
- });
84
-
85
- it('should return empty when both inputs are empty', () => {
86
- expect(mergeBootstrapNodes([], [])).toEqual([]);
87
- });
88
- });
89
-
90
- describe('toBootstrapConfig', () => {
91
- it('should strip labels and return only host/port', () => {
92
- const nodes: BootstrapNode[] = [
93
- { host: 'a.com', port: 1, label: 'A' },
94
- { host: 'b.com', port: 2 },
95
- ];
96
- const result = toBootstrapConfig(nodes);
97
- expect(result).toEqual([
98
- { host: 'a.com', port: 1 },
99
- { host: 'b.com', port: 2 },
100
- ]);
101
- // Ensure label is not present
102
- expect(result[0]).not.toHaveProperty('label');
103
- });
104
-
105
- it('should return empty array for empty input', () => {
106
- expect(toBootstrapConfig([])).toEqual([]);
107
- });
108
- });
@@ -1,358 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { BuyerPaymentManager, type BuyerPaymentConfig } from '../src/payments/buyer-payment-manager.js';
3
- import type { PaymentMux } from '../src/p2p/payment-mux.js';
4
- import type { Identity } from '../src/p2p/identity.js';
5
- import type {
6
- SessionLockConfirmPayload,
7
- SessionLockRejectPayload,
8
- SellerReceiptPayload,
9
- TopUpRequestPayload,
10
- } from '../src/types/protocol.js';
11
- import * as ed from '@noble/ed25519';
12
-
13
- // --- Helpers ---
14
-
15
- async function createTestIdentity(): Promise<Identity> {
16
- const privateKey = ed.utils.randomPrivateKey();
17
- const publicKey = await ed.getPublicKeyAsync(privateKey);
18
- const peerId = Array.from(publicKey)
19
- .map((b) => b.toString(16).padStart(2, '0'))
20
- .join('');
21
- return { peerId: peerId as any, privateKey, publicKey };
22
- }
23
-
24
- function createMockPaymentMux(): PaymentMux & {
25
- _sentLockAuths: any[];
26
- _sentBuyerAcks: any[];
27
- _sentSessionEnds: any[];
28
- _sentTopUpAuths: any[];
29
- } {
30
- const mux = {
31
- _sentLockAuths: [] as any[],
32
- _sentBuyerAcks: [] as any[],
33
- _sentSessionEnds: [] as any[],
34
- _sentTopUpAuths: [] as any[],
35
- sendSessionLockAuth: vi.fn(function (this: any, payload: any) {
36
- this._sentLockAuths.push(payload);
37
- }),
38
- sendBuyerAck: vi.fn(function (this: any, payload: any) {
39
- this._sentBuyerAcks.push(payload);
40
- }),
41
- sendSessionEnd: vi.fn(function (this: any, payload: any) {
42
- this._sentSessionEnds.push(payload);
43
- }),
44
- sendTopUpAuth: vi.fn(function (this: any, payload: any) {
45
- this._sentTopUpAuths.push(payload);
46
- }),
47
- // Unused but required by type
48
- sendSessionLockConfirm: vi.fn(),
49
- sendSessionLockReject: vi.fn(),
50
- sendSellerReceipt: vi.fn(),
51
- sendTopUpRequest: vi.fn(),
52
- sendDisputeNotify: vi.fn(),
53
- onSessionLockAuth: vi.fn(),
54
- onSessionLockConfirm: vi.fn(),
55
- onSessionLockReject: vi.fn(),
56
- onSellerReceipt: vi.fn(),
57
- onBuyerAck: vi.fn(),
58
- onSessionEnd: vi.fn(),
59
- onTopUpRequest: vi.fn(),
60
- onTopUpAuth: vi.fn(),
61
- onDisputeNotify: vi.fn(),
62
- handleFrame: vi.fn(),
63
- } as unknown as PaymentMux & {
64
- _sentLockAuths: any[];
65
- _sentBuyerAcks: any[];
66
- _sentSessionEnds: any[];
67
- _sentTopUpAuths: any[];
68
- };
69
- return mux;
70
- }
71
-
72
- const DEFAULT_CONFIG: BuyerPaymentConfig = {
73
- defaultLockAmountUSDC: '1000000',
74
- rpcUrl: 'http://127.0.0.1:8545',
75
- contractAddress: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
76
- usdcAddress: '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512',
77
- autoAck: true,
78
- autoTopUp: true,
79
- maxSessionBudgetUSDC: '10000000',
80
- };
81
-
82
- const SELLER_PEER_ID = 'seller-peer-0123456789abcdef';
83
- const SELLER_EVM_ADDRESS = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8';
84
-
85
- // --- Tests ---
86
-
87
- describe('BuyerPaymentManager', () => {
88
- let identity: Identity;
89
- let manager: BuyerPaymentManager;
90
- let mux: ReturnType<typeof createMockPaymentMux>;
91
-
92
- beforeEach(async () => {
93
- identity = await createTestIdentity();
94
- manager = new BuyerPaymentManager(identity, DEFAULT_CONFIG);
95
- mux = createMockPaymentMux();
96
- });
97
-
98
- describe('initiateLock', () => {
99
- it('sends SessionLockAuth with correct session ID and amount', async () => {
100
- const sessionId = await manager.initiateLock(
101
- SELLER_PEER_ID,
102
- SELLER_EVM_ADDRESS,
103
- mux,
104
- );
105
-
106
- expect(sessionId).toMatch(/^0x[0-9a-f]{64}$/);
107
- expect(mux.sendSessionLockAuth).toHaveBeenCalledTimes(1);
108
-
109
- const sentPayload = mux._sentLockAuths[0]!;
110
- expect(sentPayload.sessionId).toBe(sessionId);
111
- expect(sentPayload.lockedAmount).toBe('1000000');
112
- expect(typeof sentPayload.buyerSig).toBe('string');
113
- expect(sentPayload.buyerSig.length).toBeGreaterThan(0);
114
-
115
- // Session should be in pending state
116
- const session = manager.getSession(SELLER_PEER_ID);
117
- expect(session).toBeDefined();
118
- expect(session!.status).toBe('pending');
119
- expect(session!.lockedAmount).toBe(1000000n);
120
- });
121
-
122
- it('uses custom lock amount when specified', async () => {
123
- await manager.initiateLock(
124
- SELLER_PEER_ID,
125
- SELLER_EVM_ADDRESS,
126
- mux,
127
- '5000000',
128
- );
129
-
130
- const sentPayload = mux._sentLockAuths[0]!;
131
- expect(sentPayload.lockedAmount).toBe('5000000');
132
-
133
- const session = manager.getSession(SELLER_PEER_ID);
134
- expect(session!.lockedAmount).toBe(5000000n);
135
- });
136
- });
137
-
138
- describe('handleLockConfirm', () => {
139
- it('marks session as confirmed with tx signature', async () => {
140
- const sessionId = await manager.initiateLock(
141
- SELLER_PEER_ID,
142
- SELLER_EVM_ADDRESS,
143
- mux,
144
- );
145
-
146
- const payload: SessionLockConfirmPayload = {
147
- sessionId,
148
- txSignature: '0xabc123def456',
149
- };
150
-
151
- manager.handleLockConfirm(SELLER_PEER_ID, payload);
152
-
153
- const session = manager.getSession(SELLER_PEER_ID);
154
- expect(session!.status).toBe('confirmed');
155
- expect(session!.txSignature).toBe('0xabc123def456');
156
- expect(manager.isLockConfirmed(SELLER_PEER_ID)).toBe(true);
157
- });
158
-
159
- it('ignores confirmation for unknown seller', () => {
160
- // Should not throw
161
- manager.handleLockConfirm('unknown-peer', {
162
- sessionId: '0x' + 'a'.repeat(64),
163
- txSignature: '0xabc',
164
- });
165
- });
166
- });
167
-
168
- describe('handleLockReject', () => {
169
- it('removes session on rejection', async () => {
170
- const sessionId = await manager.initiateLock(
171
- SELLER_PEER_ID,
172
- SELLER_EVM_ADDRESS,
173
- mux,
174
- );
175
-
176
- const payload: SessionLockRejectPayload = {
177
- sessionId,
178
- reason: 'Insufficient buyer balance',
179
- };
180
-
181
- manager.handleLockReject(SELLER_PEER_ID, payload);
182
-
183
- expect(manager.getSession(SELLER_PEER_ID)).toBeUndefined();
184
- expect(manager.isLockRejected(SELLER_PEER_ID)).toBe(true);
185
- });
186
- });
187
-
188
- describe('handleSellerReceipt (auto-ack)', () => {
189
- it('auto-acknowledges receipt with Ed25519 signature', async () => {
190
- const sessionId = await manager.initiateLock(
191
- SELLER_PEER_ID,
192
- SELLER_EVM_ADDRESS,
193
- mux,
194
- );
195
-
196
- // Confirm the lock first
197
- manager.handleLockConfirm(SELLER_PEER_ID, {
198
- sessionId,
199
- txSignature: '0xabc',
200
- });
201
-
202
- const receipt: SellerReceiptPayload = {
203
- sessionId,
204
- runningTotal: '50000',
205
- requestCount: 1,
206
- responseHash: 'c'.repeat(64),
207
- sellerSig: 'd'.repeat(128),
208
- };
209
-
210
- await manager.handleSellerReceipt(SELLER_PEER_ID, receipt, mux);
211
-
212
- expect(mux.sendBuyerAck).toHaveBeenCalledTimes(1);
213
- const ackPayload = mux._sentBuyerAcks[0]!;
214
- expect(ackPayload.sessionId).toBe(sessionId);
215
- expect(ackPayload.runningTotal).toBe('50000');
216
- expect(ackPayload.requestCount).toBe(1);
217
- expect(typeof ackPayload.buyerSig).toBe('string');
218
- expect(ackPayload.buyerSig.length).toBeGreaterThan(0);
219
-
220
- // Session should update running total
221
- const session = manager.getSession(SELLER_PEER_ID);
222
- expect(session!.lastRunningTotal).toBe(50000n);
223
- expect(session!.lastRequestCount).toBe(1);
224
- expect(session!.status).toBe('active');
225
- });
226
- });
227
-
228
- describe('endSession', () => {
229
- it('sends SessionEnd with ECDSA settlement signature', async () => {
230
- const sessionId = await manager.initiateLock(
231
- SELLER_PEER_ID,
232
- SELLER_EVM_ADDRESS,
233
- mux,
234
- );
235
-
236
- // Confirm and process a receipt
237
- manager.handleLockConfirm(SELLER_PEER_ID, {
238
- sessionId,
239
- txSignature: '0xabc',
240
- });
241
-
242
- await manager.handleSellerReceipt(SELLER_PEER_ID, {
243
- sessionId,
244
- runningTotal: '100000',
245
- requestCount: 3,
246
- responseHash: 'c'.repeat(64),
247
- sellerSig: 'd'.repeat(128),
248
- }, mux);
249
-
250
- await manager.endSession(SELLER_PEER_ID, mux, 90);
251
-
252
- expect(mux.sendSessionEnd).toHaveBeenCalledTimes(1);
253
- const endPayload = mux._sentSessionEnds[0]!;
254
- expect(endPayload.sessionId).toBe(sessionId);
255
- expect(endPayload.runningTotal).toBe('100000');
256
- expect(endPayload.requestCount).toBe(3);
257
- expect(endPayload.score).toBe(90);
258
- expect(typeof endPayload.buyerSig).toBe('string');
259
- expect(endPayload.buyerSig.length).toBeGreaterThan(0);
260
-
261
- // Session should be ended
262
- const session = manager.getSession(SELLER_PEER_ID);
263
- expect(session!.status).toBe('ended');
264
- });
265
- });
266
-
267
- describe('handleTopUpRequest (sufficient balance)', () => {
268
- it('auto-approves top-up when budget allows and balance sufficient', async () => {
269
- const sessionId = await manager.initiateLock(
270
- SELLER_PEER_ID,
271
- SELLER_EVM_ADDRESS,
272
- mux,
273
- );
274
-
275
- manager.handleLockConfirm(SELLER_PEER_ID, {
276
- sessionId,
277
- txSignature: '0xabc',
278
- });
279
-
280
- // Mock the escrow client to return sufficient balance
281
- const mockGetBuyerAccount = vi.fn().mockResolvedValue({
282
- deposited: 20000000n,
283
- committed: 1000000n,
284
- available: 19000000n,
285
- });
286
- (manager as any)._escrowClient = {
287
- ...manager.escrowClient,
288
- getBuyerAccount: mockGetBuyerAccount,
289
- };
290
-
291
- const request: TopUpRequestPayload = {
292
- sessionId,
293
- additionalAmount: '2000000',
294
- currentRunningTotal: '800000',
295
- currentLockedAmount: '1000000',
296
- };
297
-
298
- await manager.handleTopUpRequest(SELLER_PEER_ID, request, mux);
299
-
300
- expect(mux.sendTopUpAuth).toHaveBeenCalledTimes(1);
301
- const authPayload = mux._sentTopUpAuths[0]!;
302
- expect(authPayload.sessionId).toBe(sessionId);
303
- expect(authPayload.additionalAmount).toBe('2000000');
304
- expect(typeof authPayload.buyerSig).toBe('string');
305
-
306
- // Session locked amount should be updated
307
- const session = manager.getSession(SELLER_PEER_ID);
308
- expect(session!.lockedAmount).toBe(3000000n); // 1M + 2M
309
- });
310
- });
311
-
312
- describe('handleTopUpRequest (insufficient balance)', () => {
313
- it('ends session when balance is insufficient for top-up', async () => {
314
- const sessionId = await manager.initiateLock(
315
- SELLER_PEER_ID,
316
- SELLER_EVM_ADDRESS,
317
- mux,
318
- );
319
-
320
- manager.handleLockConfirm(SELLER_PEER_ID, {
321
- sessionId,
322
- txSignature: '0xabc',
323
- });
324
-
325
- // Mock the escrow client to return insufficient balance
326
- const mockGetBuyerAccount = vi.fn().mockResolvedValue({
327
- deposited: 1000000n,
328
- committed: 1000000n,
329
- available: 0n,
330
- });
331
- (manager as any)._escrowClient = {
332
- ...manager.escrowClient,
333
- getBuyerAccount: mockGetBuyerAccount,
334
- };
335
-
336
- const request: TopUpRequestPayload = {
337
- sessionId,
338
- additionalAmount: '2000000',
339
- currentRunningTotal: '800000',
340
- currentLockedAmount: '1000000',
341
- };
342
-
343
- await manager.handleTopUpRequest(SELLER_PEER_ID, request, mux);
344
-
345
- // Should NOT send top-up auth
346
- expect(mux.sendTopUpAuth).not.toHaveBeenCalled();
347
-
348
- // Should end the session instead
349
- expect(mux.sendSessionEnd).toHaveBeenCalledTimes(1);
350
- const endPayload = mux._sentSessionEnds[0]!;
351
- expect(endPayload.sessionId).toBe(sessionId);
352
- expect(endPayload.score).toBe(80); // default score
353
-
354
- const session = manager.getSession(SELLER_PEER_ID);
355
- expect(session!.status).toBe('ended');
356
- });
357
- });
358
- });
@@ -1,87 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import * as ed from '@noble/ed25519';
3
- import { toPeerId } from '../src/types/peer.js';
4
- import { bytesToHex } from '../src/utils/hex.js';
5
- import {
6
- NonceReplayGuard,
7
- buildConnectionAuthEnvelope,
8
- verifyConnectionAuthEnvelope,
9
- } from '../src/p2p/connection-auth.js';
10
-
11
- async function createIdentity(): Promise<{ peerId: string; privateKey: Uint8Array }> {
12
- const privateKey = ed.utils.randomPrivateKey();
13
- const publicKey = await ed.getPublicKeyAsync(privateKey);
14
- const peerId = toPeerId(bytesToHex(publicKey));
15
- return { peerId, privateKey };
16
- }
17
-
18
- describe('connection-auth', () => {
19
- it('accepts valid signed intro auth', async () => {
20
- const { peerId, privateKey } = await createIdentity();
21
- const nowMs = 1_700_000_000_000;
22
- const auth = buildConnectionAuthEnvelope('intro', peerId, privateKey, nowMs);
23
-
24
- const result = verifyConnectionAuthEnvelope({
25
- type: 'intro',
26
- auth,
27
- nowMs,
28
- });
29
-
30
- expect(result.ok).toBe(true);
31
- expect(result.peerId).toBe(peerId);
32
- });
33
-
34
- it('rejects payload type mismatch', async () => {
35
- const { peerId, privateKey } = await createIdentity();
36
- const nowMs = 1_700_000_000_000;
37
- const auth = buildConnectionAuthEnvelope('intro', peerId, privateKey, nowMs);
38
-
39
- const result = verifyConnectionAuthEnvelope({
40
- type: 'hello',
41
- auth,
42
- nowMs,
43
- });
44
-
45
- expect(result.ok).toBe(false);
46
- expect(result.reason).toContain('signature');
47
- });
48
-
49
- it('rejects stale auth timestamps', async () => {
50
- const { peerId, privateKey } = await createIdentity();
51
- const auth = buildConnectionAuthEnvelope('hello', peerId, privateKey, 1_000);
52
-
53
- const result = verifyConnectionAuthEnvelope({
54
- type: 'hello',
55
- auth,
56
- nowMs: 100_000,
57
- maxSkewMs: 30_000,
58
- });
59
-
60
- expect(result.ok).toBe(false);
61
- expect(result.reason).toContain('timestamp');
62
- });
63
-
64
- it('rejects replayed nonces when replay guard is enabled', async () => {
65
- const { peerId, privateKey } = await createIdentity();
66
- const guard = new NonceReplayGuard();
67
- const nowMs = 1_700_000_000_000;
68
- const auth = buildConnectionAuthEnvelope('intro', peerId, privateKey, nowMs);
69
-
70
- const first = verifyConnectionAuthEnvelope({
71
- type: 'intro',
72
- auth,
73
- nowMs,
74
- replayGuard: guard,
75
- });
76
- const second = verifyConnectionAuthEnvelope({
77
- type: 'intro',
78
- auth,
79
- nowMs,
80
- replayGuard: guard,
81
- });
82
-
83
- expect(first.ok).toBe(true);
84
- expect(second.ok).toBe(false);
85
- expect(second.reason).toContain('replayed');
86
- });
87
- });