@agirails/sdk 2.0.0-beta
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 +183 -0
- package/dist/ACTPClient.d.ts +52 -0
- package/dist/ACTPClient.d.ts.map +1 -0
- package/dist/ACTPClient.js +120 -0
- package/dist/ACTPClient.js.map +1 -0
- package/dist/abi/ACTPKernel.json +1340 -0
- package/dist/abi/ERC20.json +38 -0
- package/dist/abi/EscrowVault.json +64 -0
- package/dist/builders/DeliveryProofBuilder.d.ts +37 -0
- package/dist/builders/DeliveryProofBuilder.d.ts.map +1 -0
- package/dist/builders/DeliveryProofBuilder.js +165 -0
- package/dist/builders/DeliveryProofBuilder.js.map +1 -0
- package/dist/builders/QuoteBuilder.d.ts +68 -0
- package/dist/builders/QuoteBuilder.d.ts.map +1 -0
- package/dist/builders/QuoteBuilder.js +255 -0
- package/dist/builders/QuoteBuilder.js.map +1 -0
- package/dist/builders/index.d.ts +3 -0
- package/dist/builders/index.d.ts.map +1 -0
- package/dist/builders/index.js +10 -0
- package/dist/builders/index.js.map +1 -0
- package/dist/config/networks.d.ts +27 -0
- package/dist/config/networks.d.ts.map +1 -0
- package/dist/config/networks.js +103 -0
- package/dist/config/networks.js.map +1 -0
- package/dist/errors/index.d.ts +38 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +87 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/ACTPKernel.d.ts +30 -0
- package/dist/protocol/ACTPKernel.d.ts.map +1 -0
- package/dist/protocol/ACTPKernel.js +261 -0
- package/dist/protocol/ACTPKernel.js.map +1 -0
- package/dist/protocol/EASHelper.d.ts +23 -0
- package/dist/protocol/EASHelper.d.ts.map +1 -0
- package/dist/protocol/EASHelper.js +106 -0
- package/dist/protocol/EASHelper.js.map +1 -0
- package/dist/protocol/EscrowVault.d.ts +24 -0
- package/dist/protocol/EscrowVault.d.ts.map +1 -0
- package/dist/protocol/EscrowVault.js +114 -0
- package/dist/protocol/EscrowVault.js.map +1 -0
- package/dist/protocol/EventMonitor.d.ts +18 -0
- package/dist/protocol/EventMonitor.d.ts.map +1 -0
- package/dist/protocol/EventMonitor.js +92 -0
- package/dist/protocol/EventMonitor.js.map +1 -0
- package/dist/protocol/MessageSigner.d.ts +23 -0
- package/dist/protocol/MessageSigner.d.ts.map +1 -0
- package/dist/protocol/MessageSigner.js +178 -0
- package/dist/protocol/MessageSigner.js.map +1 -0
- package/dist/protocol/ProofGenerator.d.ts +22 -0
- package/dist/protocol/ProofGenerator.d.ts.map +1 -0
- package/dist/protocol/ProofGenerator.js +64 -0
- package/dist/protocol/ProofGenerator.js.map +1 -0
- package/dist/protocol/QuoteBuilder.d.ts +2 -0
- package/dist/protocol/QuoteBuilder.d.ts.map +1 -0
- package/dist/protocol/QuoteBuilder.js +7 -0
- package/dist/protocol/QuoteBuilder.js.map +1 -0
- package/dist/types/eip712.d.ts +106 -0
- package/dist/types/eip712.d.ts.map +1 -0
- package/dist/types/eip712.js +84 -0
- package/dist/types/eip712.js.map +1 -0
- package/dist/types/escrow.d.ts +18 -0
- package/dist/types/escrow.d.ts.map +1 -0
- package/dist/types/escrow.js +3 -0
- package/dist/types/escrow.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +22 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/message.d.ts +109 -0
- package/dist/types/message.d.ts.map +1 -0
- package/dist/types/message.js +3 -0
- package/dist/types/message.js.map +1 -0
- package/dist/types/state.d.ts +19 -0
- package/dist/types/state.d.ts.map +1 -0
- package/dist/types/state.js +49 -0
- package/dist/types/state.js.map +1 -0
- package/dist/types/transaction.d.ts +36 -0
- package/dist/types/transaction.d.ts.map +1 -0
- package/dist/types/transaction.js +3 -0
- package/dist/types/transaction.js.map +1 -0
- package/dist/utils/IPFSClient.d.ts +37 -0
- package/dist/utils/IPFSClient.d.ts.map +1 -0
- package/dist/utils/IPFSClient.js +128 -0
- package/dist/utils/IPFSClient.js.map +1 -0
- package/dist/utils/NonceManager.d.ts +34 -0
- package/dist/utils/NonceManager.d.ts.map +1 -0
- package/dist/utils/NonceManager.js +114 -0
- package/dist/utils/NonceManager.js.map +1 -0
- package/dist/utils/ReceivedNonceTracker.d.ts +35 -0
- package/dist/utils/ReceivedNonceTracker.d.ts.map +1 -0
- package/dist/utils/ReceivedNonceTracker.js +196 -0
- package/dist/utils/ReceivedNonceTracker.js.map +1 -0
- package/dist/utils/canonicalJson.d.ts +4 -0
- package/dist/utils/canonicalJson.d.ts.map +1 -0
- package/dist/utils/canonicalJson.js +21 -0
- package/dist/utils/canonicalJson.js.map +1 -0
- package/dist/utils/computeTypeHash.d.ts +3 -0
- package/dist/utils/computeTypeHash.d.ts.map +1 -0
- package/dist/utils/computeTypeHash.js +30 -0
- package/dist/utils/computeTypeHash.js.map +1 -0
- package/dist/utils/validation.d.ts +6 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +46 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +73 -0
- package/src/ACTPClient.ts +276 -0
- package/src/__tests__/ProofGenerator.test.ts +124 -0
- package/src/__tests__/QuoteBuilder.test.ts +516 -0
- package/src/__tests__/StateMachine.test.ts +82 -0
- package/src/__tests__/builders/DeliveryProofBuilder.test.ts +581 -0
- package/src/__tests__/integration/ACTPClient.test.ts +263 -0
- package/src/__tests__/integration.test.ts +289 -0
- package/src/__tests__/protocol/EASHelper.test.ts +472 -0
- package/src/__tests__/protocol/EventMonitor.test.ts +382 -0
- package/src/__tests__/security/ACTPKernel.security.test.ts +1167 -0
- package/src/__tests__/security/EscrowVault.security.test.ts +570 -0
- package/src/__tests__/security/MessageSigner.security.test.ts +286 -0
- package/src/__tests__/security/NonceReplay.security.test.ts +501 -0
- package/src/__tests__/security/validation.security.test.ts +376 -0
- package/src/__tests__/utils/IPFSClient.test.ts +262 -0
- package/src/__tests__/utils/NonceManager.test.ts +205 -0
- package/src/__tests__/utils/canonicalJson.test.ts +153 -0
- package/src/abi/ACTPKernel.json +1340 -0
- package/src/abi/ERC20.json +40 -0
- package/src/abi/EscrowVault.json +66 -0
- package/src/builders/DeliveryProofBuilder.ts +326 -0
- package/src/builders/QuoteBuilder.ts +483 -0
- package/src/builders/index.ts +17 -0
- package/src/config/networks.ts +165 -0
- package/src/errors/index.ts +130 -0
- package/src/index.ts +108 -0
- package/src/protocol/ACTPKernel.ts +625 -0
- package/src/protocol/EASHelper.ts +197 -0
- package/src/protocol/EscrowVault.ts +237 -0
- package/src/protocol/EventMonitor.ts +161 -0
- package/src/protocol/MessageSigner.ts +336 -0
- package/src/protocol/ProofGenerator.ts +119 -0
- package/src/protocol/QuoteBuilder.ts +15 -0
- package/src/types/eip712.ts +175 -0
- package/src/types/escrow.ts +26 -0
- package/src/types/index.ts +10 -0
- package/src/types/message.ts +145 -0
- package/src/types/state.ts +77 -0
- package/src/types/transaction.ts +54 -0
- package/src/utils/IPFSClient.ts +248 -0
- package/src/utils/NonceManager.ts +293 -0
- package/src/utils/ReceivedNonceTracker.ts +397 -0
- package/src/utils/canonicalJson.ts +38 -0
- package/src/utils/computeTypeHash.ts +50 -0
- package/src/utils/validation.ts +82 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Nonce Manager
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
InMemoryNonceManager,
|
|
7
|
+
DIDScopedNonceManager,
|
|
8
|
+
createNonceManager
|
|
9
|
+
} from '../../utils/NonceManager';
|
|
10
|
+
|
|
11
|
+
describe('InMemoryNonceManager', () => {
|
|
12
|
+
let manager: InMemoryNonceManager;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
manager = new InMemoryNonceManager();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('getNextNonce', () => {
|
|
19
|
+
it('should return 1 for first nonce', () => {
|
|
20
|
+
expect(manager.getNextNonce('agirails.delivery.v1')).toBe(1);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should increment for multiple calls', () => {
|
|
24
|
+
expect(manager.getNextNonce('agirails.delivery.v1')).toBe(1);
|
|
25
|
+
manager.recordNonce('agirails.delivery.v1', 1);
|
|
26
|
+
expect(manager.getNextNonce('agirails.delivery.v1')).toBe(2);
|
|
27
|
+
manager.recordNonce('agirails.delivery.v1', 2);
|
|
28
|
+
expect(manager.getNextNonce('agirails.delivery.v1')).toBe(3);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should track nonces separately per message type', () => {
|
|
32
|
+
manager.recordNonce('agirails.delivery.v1', 5);
|
|
33
|
+
manager.recordNonce('agirails.quote.v1', 10);
|
|
34
|
+
|
|
35
|
+
expect(manager.getNextNonce('agirails.delivery.v1')).toBe(6);
|
|
36
|
+
expect(manager.getNextNonce('agirails.quote.v1')).toBe(11);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('recordNonce', () => {
|
|
41
|
+
it('should update current nonce', () => {
|
|
42
|
+
manager.recordNonce('agirails.delivery.v1', 5);
|
|
43
|
+
expect(manager.getCurrentNonce('agirails.delivery.v1')).toBe(5);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should throw if nonce is not strictly increasing', () => {
|
|
47
|
+
manager.recordNonce('agirails.delivery.v1', 5);
|
|
48
|
+
|
|
49
|
+
expect(() => manager.recordNonce('agirails.delivery.v1', 5))
|
|
50
|
+
.toThrow('Nonce must be strictly increasing');
|
|
51
|
+
|
|
52
|
+
expect(() => manager.recordNonce('agirails.delivery.v1', 3))
|
|
53
|
+
.toThrow('Nonce must be strictly increasing');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should allow gaps in nonces', () => {
|
|
57
|
+
manager.recordNonce('agirails.delivery.v1', 1);
|
|
58
|
+
manager.recordNonce('agirails.delivery.v1', 10);
|
|
59
|
+
|
|
60
|
+
expect(manager.getCurrentNonce('agirails.delivery.v1')).toBe(10);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('getCurrentNonce', () => {
|
|
65
|
+
it('should return 0 for unused message type', () => {
|
|
66
|
+
expect(manager.getCurrentNonce('agirails.unknown.v1')).toBe(0);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should return last recorded nonce', () => {
|
|
70
|
+
manager.recordNonce('agirails.delivery.v1', 42);
|
|
71
|
+
expect(manager.getCurrentNonce('agirails.delivery.v1')).toBe(42);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('resetNonce', () => {
|
|
76
|
+
it('should reset nonce to 0', () => {
|
|
77
|
+
manager.recordNonce('agirails.delivery.v1', 10);
|
|
78
|
+
manager.resetNonce('agirails.delivery.v1');
|
|
79
|
+
|
|
80
|
+
expect(manager.getCurrentNonce('agirails.delivery.v1')).toBe(0);
|
|
81
|
+
expect(manager.getNextNonce('agirails.delivery.v1')).toBe(1);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('getAllNonces', () => {
|
|
86
|
+
it('should return all nonces as object', () => {
|
|
87
|
+
manager.recordNonce('agirails.delivery.v1', 5);
|
|
88
|
+
manager.recordNonce('agirails.quote.v1', 10);
|
|
89
|
+
|
|
90
|
+
const nonces = manager.getAllNonces();
|
|
91
|
+
|
|
92
|
+
expect(nonces).toEqual({
|
|
93
|
+
'agirails.delivery.v1': 5,
|
|
94
|
+
'agirails.quote.v1': 10
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should be empty for new manager', () => {
|
|
99
|
+
expect(manager.getAllNonces()).toEqual({});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('constructor with initialNonces', () => {
|
|
104
|
+
it('should initialize with provided nonces', () => {
|
|
105
|
+
const manager = new InMemoryNonceManager({
|
|
106
|
+
'agirails.delivery.v1': 100,
|
|
107
|
+
'agirails.quote.v1': 200
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(manager.getCurrentNonce('agirails.delivery.v1')).toBe(100);
|
|
111
|
+
expect(manager.getCurrentNonce('agirails.quote.v1')).toBe(200);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('DIDScopedNonceManager', () => {
|
|
117
|
+
const did1 = 'did:ethr:84532:0x1111111111111111111111111111111111111111';
|
|
118
|
+
const did2 = 'did:ethr:84532:0x2222222222222222222222222222222222222222';
|
|
119
|
+
|
|
120
|
+
let manager: DIDScopedNonceManager;
|
|
121
|
+
|
|
122
|
+
beforeEach(() => {
|
|
123
|
+
manager = new DIDScopedNonceManager(did1);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('DID scoping', () => {
|
|
127
|
+
it('should track nonces per DID', () => {
|
|
128
|
+
manager.recordNonceForDID(did1, 'agirails.delivery.v1', 5);
|
|
129
|
+
manager.recordNonceForDID(did2, 'agirails.delivery.v1', 10);
|
|
130
|
+
|
|
131
|
+
expect(manager.getNextNonceForDID(did1, 'agirails.delivery.v1')).toBe(6);
|
|
132
|
+
expect(manager.getNextNonceForDID(did2, 'agirails.delivery.v1')).toBe(11);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should use current DID for simple methods', () => {
|
|
136
|
+
manager.recordNonce('agirails.delivery.v1', 5);
|
|
137
|
+
|
|
138
|
+
expect(manager.getCurrentNonce('agirails.delivery.v1')).toBe(5);
|
|
139
|
+
expect(manager.getNextNonce('agirails.delivery.v1')).toBe(6);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should allow switching DID context', () => {
|
|
143
|
+
manager.recordNonce('agirails.delivery.v1', 5);
|
|
144
|
+
|
|
145
|
+
manager.switchDID(did2);
|
|
146
|
+
expect(manager.getCurrentNonce('agirails.delivery.v1')).toBe(0);
|
|
147
|
+
|
|
148
|
+
manager.recordNonce('agirails.delivery.v1', 10);
|
|
149
|
+
|
|
150
|
+
manager.switchDID(did1);
|
|
151
|
+
expect(manager.getCurrentNonce('agirails.delivery.v1')).toBe(5);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe('getAllNonces', () => {
|
|
156
|
+
it('should return nested structure', () => {
|
|
157
|
+
manager.recordNonceForDID(did1, 'agirails.delivery.v1', 5);
|
|
158
|
+
manager.recordNonceForDID(did1, 'agirails.quote.v1', 3);
|
|
159
|
+
manager.recordNonceForDID(did2, 'agirails.delivery.v1', 10);
|
|
160
|
+
|
|
161
|
+
const nonces = manager.getAllNonces();
|
|
162
|
+
|
|
163
|
+
expect(nonces).toEqual({
|
|
164
|
+
[did1]: {
|
|
165
|
+
'agirails.delivery.v1': 5,
|
|
166
|
+
'agirails.quote.v1': 3
|
|
167
|
+
},
|
|
168
|
+
[did2]: {
|
|
169
|
+
'agirails.delivery.v1': 10
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe('constructor with initialNonces', () => {
|
|
176
|
+
it('should initialize DID with provided nonces', () => {
|
|
177
|
+
const manager = new DIDScopedNonceManager(did1, {
|
|
178
|
+
'agirails.delivery.v1': 100
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
expect(manager.getCurrentNonce('agirails.delivery.v1')).toBe(100);
|
|
182
|
+
expect(manager.getNextNonce('agirails.delivery.v1')).toBe(101);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe('createNonceManager factory', () => {
|
|
188
|
+
it('should create InMemoryNonceManager when no DID provided', () => {
|
|
189
|
+
const manager = createNonceManager();
|
|
190
|
+
expect(manager).toBeInstanceOf(InMemoryNonceManager);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should create DIDScopedNonceManager when DID provided', () => {
|
|
194
|
+
const manager = createNonceManager('did:ethr:84532:0x...');
|
|
195
|
+
expect(manager).toBeInstanceOf(DIDScopedNonceManager);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('should pass initialNonces to manager', () => {
|
|
199
|
+
const manager = createNonceManager(undefined, {
|
|
200
|
+
'agirails.delivery.v1': 50
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
expect(manager.getCurrentNonce('agirails.delivery.v1')).toBe(50);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Canonical JSON Utilities (AIP-4 Β§3.6)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { canonicalJsonStringify, computeCanonicalHash, computeResultHash } from '../../utils/canonicalJson';
|
|
6
|
+
import { keccak256, toUtf8Bytes } from 'ethers';
|
|
7
|
+
|
|
8
|
+
describe('Canonical JSON Utilities', () => {
|
|
9
|
+
describe('canonicalJsonStringify', () => {
|
|
10
|
+
it('should sort object keys alphabetically', () => {
|
|
11
|
+
const obj = { z: 1, a: 2, m: 3 };
|
|
12
|
+
const result = canonicalJsonStringify(obj);
|
|
13
|
+
expect(result).toBe('{"a":2,"m":3,"z":1}');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should handle nested objects', () => {
|
|
17
|
+
const obj = {
|
|
18
|
+
b: { y: 1, x: 2 },
|
|
19
|
+
a: { z: 3, w: 4 }
|
|
20
|
+
};
|
|
21
|
+
const result = canonicalJsonStringify(obj);
|
|
22
|
+
expect(result).toBe('{"a":{"w":4,"z":3},"b":{"x":2,"y":1}}');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should handle arrays', () => {
|
|
26
|
+
const obj = { items: [3, 1, 2], name: 'test' };
|
|
27
|
+
const result = canonicalJsonStringify(obj);
|
|
28
|
+
expect(result).toBe('{"items":[3,1,2],"name":"test"}');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should produce deterministic output', () => {
|
|
32
|
+
const obj1 = { a: 1, b: 2, c: 3 };
|
|
33
|
+
const obj2 = { c: 3, b: 2, a: 1 };
|
|
34
|
+
|
|
35
|
+
expect(canonicalJsonStringify(obj1)).toBe(canonicalJsonStringify(obj2));
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should handle special characters', () => {
|
|
39
|
+
const obj = { text: 'hello "world"', emoji: 'π' };
|
|
40
|
+
const result = canonicalJsonStringify(obj);
|
|
41
|
+
expect(result).toContain('hello \\"world\\"');
|
|
42
|
+
expect(result).toContain('π');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should handle null and boolean values', () => {
|
|
46
|
+
const obj = { isTrue: true, isFalse: false, nothing: null };
|
|
47
|
+
const result = canonicalJsonStringify(obj);
|
|
48
|
+
expect(result).toBe('{"isFalse":false,"isTrue":true,"nothing":null}');
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('computeCanonicalHash', () => {
|
|
53
|
+
it('should compute keccak256 hash of canonical JSON', () => {
|
|
54
|
+
const obj = { a: 1, b: 2 };
|
|
55
|
+
const hash = computeCanonicalHash(obj);
|
|
56
|
+
|
|
57
|
+
// Hash should be 0x-prefixed, 66 chars (0x + 64 hex)
|
|
58
|
+
expect(hash).toMatch(/^0x[a-f0-9]{64}$/);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should produce same hash for semantically equal objects', () => {
|
|
62
|
+
const obj1 = { a: 1, b: 2, c: 3 };
|
|
63
|
+
const obj2 = { c: 3, b: 2, a: 1 }; // Different order
|
|
64
|
+
|
|
65
|
+
const hash1 = computeCanonicalHash(obj1);
|
|
66
|
+
const hash2 = computeCanonicalHash(obj2);
|
|
67
|
+
|
|
68
|
+
expect(hash1).toBe(hash2);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should produce different hashes for different objects', () => {
|
|
72
|
+
const obj1 = { a: 1, b: 2 };
|
|
73
|
+
const obj2 = { a: 1, b: 3 };
|
|
74
|
+
|
|
75
|
+
const hash1 = computeCanonicalHash(obj1);
|
|
76
|
+
const hash2 = computeCanonicalHash(obj2);
|
|
77
|
+
|
|
78
|
+
expect(hash1).not.toBe(hash2);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('computeResultHash', () => {
|
|
83
|
+
it('should alias computeCanonicalHash', () => {
|
|
84
|
+
const obj = { txId: '0xabc', result: 'test' };
|
|
85
|
+
|
|
86
|
+
const hash1 = computeResultHash(obj);
|
|
87
|
+
const hash2 = computeCanonicalHash(obj);
|
|
88
|
+
|
|
89
|
+
expect(hash1).toBe(hash2);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('AIP-4 Test Vector (Β§10.1)', () => {
|
|
94
|
+
it('should match AIP-4 canonical JSON test vector', () => {
|
|
95
|
+
const input = {
|
|
96
|
+
txId: '0x7d87c3b8e23a5c9d1f4e6b2a8c5d9e3f1a7b4c6d8e2f5a3b9c1d7e4f6a8b2c5d',
|
|
97
|
+
serviceType: 'ocr',
|
|
98
|
+
result: { text: 'test', confidence: 1.0 },
|
|
99
|
+
createdAt: 1700000000,
|
|
100
|
+
provider: 'did:ethr:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const canonical = canonicalJsonStringify(input);
|
|
104
|
+
|
|
105
|
+
// Expected output from AIP-4 Β§10.1
|
|
106
|
+
const expected = '{"createdAt":1700000000,"provider":"did:ethr:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb","result":{"confidence":1,"text":"test"},"serviceType":"ocr","txId":"0x7d87c3b8e23a5c9d1f4e6b2a8c5d9e3f1a7b4c6d8e2f5a3b9c1d7e4f6a8b2c5d"}';
|
|
107
|
+
|
|
108
|
+
expect(canonical).toBe(expected);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should compute correct hash for test vector', () => {
|
|
112
|
+
const input = {
|
|
113
|
+
txId: '0x7d87c3b8e23a5c9d1f4e6b2a8c5d9e3f1a7b4c6d8e2f5a3b9c1d7e4f6a8b2c5d',
|
|
114
|
+
serviceType: 'ocr',
|
|
115
|
+
result: { text: 'test', confidence: 1.0 },
|
|
116
|
+
createdAt: 1700000000,
|
|
117
|
+
provider: 'did:ethr:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const hash = computeResultHash(input);
|
|
121
|
+
|
|
122
|
+
// Compute expected hash manually
|
|
123
|
+
const canonical = '{"createdAt":1700000000,"provider":"did:ethr:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb","result":{"confidence":1,"text":"test"},"serviceType":"ocr","txId":"0x7d87c3b8e23a5c9d1f4e6b2a8c5d9e3f1a7b4c6d8e2f5a3b9c1d7e4f6a8b2c5d"}';
|
|
124
|
+
const expectedHash = keccak256(toUtf8Bytes(canonical));
|
|
125
|
+
|
|
126
|
+
expect(hash).toBe(expectedHash);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('Cross-language compatibility', () => {
|
|
131
|
+
it('should handle numbers without decimals', () => {
|
|
132
|
+
const obj = { value: 1.0 };
|
|
133
|
+
const result = canonicalJsonStringify(obj);
|
|
134
|
+
|
|
135
|
+
// JavaScript serializes 1.0 as 1 (no decimal)
|
|
136
|
+
expect(result).toBe('{"value":1}');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should handle large numbers', () => {
|
|
140
|
+
const obj = { timestamp: 1700000000, amount: 1000000000000 };
|
|
141
|
+
const result = canonicalJsonStringify(obj);
|
|
142
|
+
|
|
143
|
+
expect(result).toBe('{"amount":1000000000000,"timestamp":1700000000}');
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should handle unicode strings', () => {
|
|
147
|
+
const obj = { text: 'Hello δΈη π' };
|
|
148
|
+
const result = canonicalJsonStringify(obj);
|
|
149
|
+
|
|
150
|
+
expect(result).toContain('Hello δΈη π');
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|