@mainnet-cash/bcmr 2.7.23
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 +1 -0
- package/dist/BCMR-2.7.23.js +1143 -0
- package/dist/index.html +9 -0
- package/dist/module/Bcmr.d.ts +106 -0
- package/dist/module/Bcmr.js +410 -0
- package/dist/module/Bcmr.js.map +1 -0
- package/dist/module/bcmr-v2.schema.d.ts +832 -0
- package/dist/module/bcmr-v2.schema.js +2 -0
- package/dist/module/bcmr-v2.schema.js.map +1 -0
- package/dist/module/index.d.ts +2 -0
- package/dist/module/index.js +3 -0
- package/dist/module/index.js.map +1 -0
- package/dist/tsconfig.browser.tsbuildinfo +1 -0
- package/package.json +29 -0
- package/src/Bcmr.test.headless.js +467 -0
- package/src/Bcmr.test.ts +978 -0
- package/src/Bcmr.ts +560 -0
- package/src/bcmr-v2.schema.ts +893 -0
- package/src/index.ts +2 -0
- package/tsconfig.browser.json +6 -0
- package/tsconfig.json +32 -0
- package/webpack.config.cjs +109 -0
package/src/Bcmr.test.ts
ADDED
|
@@ -0,0 +1,978 @@
|
|
|
1
|
+
import {
|
|
2
|
+
initProviders,
|
|
3
|
+
disconnectProviders,
|
|
4
|
+
setupFetchMock,
|
|
5
|
+
removeFetchMock,
|
|
6
|
+
RegTestWallet,
|
|
7
|
+
OpReturnData,
|
|
8
|
+
SendRequest,
|
|
9
|
+
Network,
|
|
10
|
+
mine,
|
|
11
|
+
NFTCapability,
|
|
12
|
+
ElectrumNetworkProvider,
|
|
13
|
+
} from "mainnet-js";
|
|
14
|
+
import { AuthChain, BCMR } from "./Bcmr.js";
|
|
15
|
+
import { Registry } from "./bcmr-v2.schema.js";
|
|
16
|
+
import { binToHex, hexToBin, sha256, utf8ToBin } from "@bitauth/libauth";
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
await initProviders();
|
|
20
|
+
});
|
|
21
|
+
afterAll(async () => {
|
|
22
|
+
await disconnectProviders();
|
|
23
|
+
});
|
|
24
|
+
afterEach(async () => {
|
|
25
|
+
BCMR.resetRegistries();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe(`Test BCMR support`, () => {
|
|
29
|
+
const registry: Registry = {
|
|
30
|
+
$schema: "https://cashtokens.org/bcmr-v2.schema.json",
|
|
31
|
+
version: {
|
|
32
|
+
major: 0,
|
|
33
|
+
minor: 1,
|
|
34
|
+
patch: 0,
|
|
35
|
+
},
|
|
36
|
+
latestRevision: "2023-01-26T18:51:35.115Z",
|
|
37
|
+
registryIdentity: {
|
|
38
|
+
name: "example bcmr",
|
|
39
|
+
description: "example bcmr for tokens on chipnet",
|
|
40
|
+
},
|
|
41
|
+
identities: {
|
|
42
|
+
"0000000000000000000000000000000000000000000000000000000000000000": {
|
|
43
|
+
"2023-01-26T18:51:35.115Z": {
|
|
44
|
+
name: "test tokens",
|
|
45
|
+
description: "",
|
|
46
|
+
uris: {
|
|
47
|
+
icon: "https://example.com/nft",
|
|
48
|
+
},
|
|
49
|
+
token: {
|
|
50
|
+
category:
|
|
51
|
+
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
52
|
+
symbol: "TOK",
|
|
53
|
+
decimals: 8,
|
|
54
|
+
nfts: {
|
|
55
|
+
description: "",
|
|
56
|
+
parse: {
|
|
57
|
+
bytecode: "00d2",
|
|
58
|
+
types: {
|
|
59
|
+
"00": {
|
|
60
|
+
name: "NFT Item 0",
|
|
61
|
+
description: "NFT Item 0 in the collection",
|
|
62
|
+
uris: {
|
|
63
|
+
icon: "https://example.com/nft/00.jpg",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const registryContent = JSON.stringify(registry, null, 2);
|
|
76
|
+
const registryContentHashBin = sha256.hash(utf8ToBin(registryContent));
|
|
77
|
+
const registryContentHash = binToHex(registryContentHashBin);
|
|
78
|
+
const registryContentHashBinBitcoinByteOrder = registryContentHashBin;
|
|
79
|
+
|
|
80
|
+
test("Add metadata registry and get token info", async () => {
|
|
81
|
+
expect(
|
|
82
|
+
BCMR.getTokenInfo(
|
|
83
|
+
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
84
|
+
)
|
|
85
|
+
).toBe(undefined);
|
|
86
|
+
BCMR.addMetadataRegistry(registry);
|
|
87
|
+
const tokenInfo = BCMR.getTokenInfo(
|
|
88
|
+
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
89
|
+
);
|
|
90
|
+
expect(tokenInfo?.token?.symbol).toBe("TOK");
|
|
91
|
+
expect(tokenInfo?.token?.decimals).toBe(8);
|
|
92
|
+
|
|
93
|
+
// check adding the same registry does not produce a duplicate
|
|
94
|
+
expect(BCMR.metadataRegistries.length).toBe(1);
|
|
95
|
+
BCMR.addMetadataRegistry(registry);
|
|
96
|
+
expect(BCMR.metadataRegistries.length).toBe(1);
|
|
97
|
+
|
|
98
|
+
expect(
|
|
99
|
+
BCMR.getTokenInfo(
|
|
100
|
+
"1111111111111111111111111111111111111111111111111111111111111111"
|
|
101
|
+
)
|
|
102
|
+
).toBe(undefined);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("Add metadata from uri and get token info", async () => {
|
|
106
|
+
setupFetchMock(
|
|
107
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json",
|
|
108
|
+
registry
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
expect(
|
|
112
|
+
BCMR.getTokenInfo(
|
|
113
|
+
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
114
|
+
)
|
|
115
|
+
).toBe(undefined);
|
|
116
|
+
await BCMR.addMetadataRegistryFromUri(
|
|
117
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
|
|
118
|
+
);
|
|
119
|
+
const tokenInfo = BCMR.getTokenInfo(
|
|
120
|
+
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
121
|
+
);
|
|
122
|
+
expect(tokenInfo?.token?.symbol).toBe("TOK");
|
|
123
|
+
expect(tokenInfo?.token?.decimals).toBe(8);
|
|
124
|
+
|
|
125
|
+
// check adding the same registry does not produce a duplicate
|
|
126
|
+
expect(BCMR.metadataRegistries.length).toBe(1);
|
|
127
|
+
await BCMR.addMetadataRegistryFromUri(
|
|
128
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
|
|
129
|
+
);
|
|
130
|
+
expect(BCMR.metadataRegistries.length).toBe(1);
|
|
131
|
+
|
|
132
|
+
removeFetchMock(
|
|
133
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("Add metadata from uri with contenthash and get token info", async () => {
|
|
138
|
+
setupFetchMock(
|
|
139
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json",
|
|
140
|
+
registryContent
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
expect(
|
|
144
|
+
BCMR.getTokenInfo(
|
|
145
|
+
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
146
|
+
)
|
|
147
|
+
).toBe(undefined);
|
|
148
|
+
await expect(
|
|
149
|
+
BCMR.addMetadataRegistryFromUri(
|
|
150
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json",
|
|
151
|
+
"00"
|
|
152
|
+
)
|
|
153
|
+
).rejects.toThrow("mismatch");
|
|
154
|
+
await BCMR.addMetadataRegistryFromUri(
|
|
155
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json",
|
|
156
|
+
registryContentHash
|
|
157
|
+
);
|
|
158
|
+
const tokenInfo = BCMR.getTokenInfo(
|
|
159
|
+
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
160
|
+
);
|
|
161
|
+
expect(tokenInfo?.token?.symbol).toBe("TOK");
|
|
162
|
+
expect(tokenInfo?.token?.decimals).toBe(8);
|
|
163
|
+
|
|
164
|
+
removeFetchMock(
|
|
165
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("Auth chain: invalid transaction", async () => {
|
|
170
|
+
await expect(
|
|
171
|
+
BCMR.buildAuthChain({
|
|
172
|
+
transactionHash:
|
|
173
|
+
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
174
|
+
})
|
|
175
|
+
).rejects.toThrow("Could not decode transaction");
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("Auth chain: no BCMR", async () => {
|
|
179
|
+
const alice = await RegTestWallet.fromId(
|
|
180
|
+
`wif:regtest:${process.env.PRIVATE_WIF!}`
|
|
181
|
+
);
|
|
182
|
+
const bob = await RegTestWallet.newRandom();
|
|
183
|
+
|
|
184
|
+
const chunks = ["Hello"];
|
|
185
|
+
const opreturnData = OpReturnData.fromArray(chunks);
|
|
186
|
+
|
|
187
|
+
const response = await alice.send([
|
|
188
|
+
new SendRequest({ cashaddr: bob.cashaddr!, value: 1000, unit: "sat" }),
|
|
189
|
+
opreturnData,
|
|
190
|
+
]);
|
|
191
|
+
const authChain = await BCMR.buildAuthChain({
|
|
192
|
+
transactionHash: response.txId!,
|
|
193
|
+
network: Network.REGTEST,
|
|
194
|
+
});
|
|
195
|
+
expect(authChain.length).toBe(0);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("Auth chain: BCMR, no hash", async () => {
|
|
199
|
+
const alice = await RegTestWallet.fromId(
|
|
200
|
+
`wif:regtest:${process.env.PRIVATE_WIF!}`
|
|
201
|
+
);
|
|
202
|
+
const bob = await RegTestWallet.newRandom();
|
|
203
|
+
|
|
204
|
+
const chunks = ["BCMR"];
|
|
205
|
+
const opreturnData = OpReturnData.fromArray(chunks);
|
|
206
|
+
|
|
207
|
+
const response = await alice.send([
|
|
208
|
+
new SendRequest({ cashaddr: bob.cashaddr!, value: 1000, unit: "sat" }),
|
|
209
|
+
opreturnData,
|
|
210
|
+
]);
|
|
211
|
+
await expect(
|
|
212
|
+
BCMR.buildAuthChain({
|
|
213
|
+
transactionHash: response.txId!,
|
|
214
|
+
network: Network.REGTEST,
|
|
215
|
+
})
|
|
216
|
+
).rejects.toThrow("Malformed BCMR output");
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("Auth chain: BCMR, ipfs hash", async () => {
|
|
220
|
+
const alice = await RegTestWallet.fromId(
|
|
221
|
+
`wif:regtest:${process.env.PRIVATE_WIF!}`
|
|
222
|
+
);
|
|
223
|
+
const bob = await RegTestWallet.newRandom();
|
|
224
|
+
|
|
225
|
+
const chunks = ["BCMR", "QmbWrG5Asp5iGmUwQHogSJGRX26zuRnuLWPytZfiL75sZv"];
|
|
226
|
+
const opreturnData = OpReturnData.fromArray(chunks);
|
|
227
|
+
|
|
228
|
+
const response = await alice.send([
|
|
229
|
+
new SendRequest({ cashaddr: bob.cashaddr!, value: 1000, unit: "sat" }),
|
|
230
|
+
opreturnData,
|
|
231
|
+
]);
|
|
232
|
+
const chain = await BCMR.buildAuthChain({
|
|
233
|
+
transactionHash: response.txId!,
|
|
234
|
+
network: Network.REGTEST,
|
|
235
|
+
});
|
|
236
|
+
expect(chain.length).toBe(1);
|
|
237
|
+
expect(chain[0].contentHash).toBe(
|
|
238
|
+
"516d62577247354173703569476d557751486f67534a47525832367a75526e754c575079745a66694c3735735a76"
|
|
239
|
+
);
|
|
240
|
+
expect(chain[0].uris[0]).toBe(
|
|
241
|
+
"ipfs://QmbWrG5Asp5iGmUwQHogSJGRX26zuRnuLWPytZfiL75sZv"
|
|
242
|
+
);
|
|
243
|
+
expect(chain[0].httpsUrl).toBe(
|
|
244
|
+
"https://dweb.link/ipfs/QmbWrG5Asp5iGmUwQHogSJGRX26zuRnuLWPytZfiL75sZv"
|
|
245
|
+
);
|
|
246
|
+
expect(chain[0].txHash).toBe(response.txId);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test("Auth chain: BCMR, ipfs hash and uri", async () => {
|
|
250
|
+
const alice = await RegTestWallet.fromId(
|
|
251
|
+
`wif:regtest:${process.env.PRIVATE_WIF!}`
|
|
252
|
+
);
|
|
253
|
+
const bob = await RegTestWallet.newRandom();
|
|
254
|
+
|
|
255
|
+
const chunks = [
|
|
256
|
+
"BCMR",
|
|
257
|
+
sha256.hash(utf8ToBin("registry_contents")),
|
|
258
|
+
"ipfs://bafkreiejafiz23ewtyh6m3dpincmxouohdcimrd33abacrq3h2pacewwjm",
|
|
259
|
+
];
|
|
260
|
+
const opreturnData = OpReturnData.fromArray(chunks);
|
|
261
|
+
|
|
262
|
+
const response = await alice.send([
|
|
263
|
+
new SendRequest({ cashaddr: bob.cashaddr!, value: 1000, unit: "sat" }),
|
|
264
|
+
opreturnData,
|
|
265
|
+
]);
|
|
266
|
+
const chain = await BCMR.buildAuthChain({
|
|
267
|
+
transactionHash: response.txId!,
|
|
268
|
+
network: Network.REGTEST,
|
|
269
|
+
});
|
|
270
|
+
expect(chain.length).toBe(1);
|
|
271
|
+
expect(chain[0].contentHash).toBe(
|
|
272
|
+
"e073b89a80c77c533ad364692db15df01adb9df404592f608d2c0cdd8960ed0e"
|
|
273
|
+
);
|
|
274
|
+
expect(chain[0].uris[0]).toBe(
|
|
275
|
+
"ipfs://bafkreiejafiz23ewtyh6m3dpincmxouohdcimrd33abacrq3h2pacewwjm"
|
|
276
|
+
);
|
|
277
|
+
expect(chain[0].httpsUrl).toBe(
|
|
278
|
+
"https://dweb.link/ipfs/bafkreiejafiz23ewtyh6m3dpincmxouohdcimrd33abacrq3h2pacewwjm"
|
|
279
|
+
);
|
|
280
|
+
expect(chain[0].txHash).toBe(response.txId);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("Auth chain: BCMR, ipfs https url", async () => {
|
|
284
|
+
const alice = await RegTestWallet.fromId(
|
|
285
|
+
`wif:regtest:${process.env.PRIVATE_WIF!}`
|
|
286
|
+
);
|
|
287
|
+
const bob = await RegTestWallet.newRandom();
|
|
288
|
+
|
|
289
|
+
const chunks = [
|
|
290
|
+
"BCMR",
|
|
291
|
+
sha256.hash(utf8ToBin("registry_contents")),
|
|
292
|
+
"bafkreiejafiz23ewtyh6m3dpincmxouohdcimrd33abacrq3h2pacewwjm.ipfs.dweb.link",
|
|
293
|
+
];
|
|
294
|
+
const opreturnData = OpReturnData.fromArray(chunks);
|
|
295
|
+
|
|
296
|
+
const response = await alice.send([
|
|
297
|
+
new SendRequest({ cashaddr: bob.cashaddr!, value: 1000, unit: "sat" }),
|
|
298
|
+
opreturnData,
|
|
299
|
+
]);
|
|
300
|
+
const chain = await BCMR.buildAuthChain({
|
|
301
|
+
transactionHash: response.txId!,
|
|
302
|
+
network: Network.REGTEST,
|
|
303
|
+
});
|
|
304
|
+
expect(chain.length).toBe(1);
|
|
305
|
+
expect(chain[0].contentHash).toBe(
|
|
306
|
+
"e073b89a80c77c533ad364692db15df01adb9df404592f608d2c0cdd8960ed0e"
|
|
307
|
+
);
|
|
308
|
+
expect(chain[0].uris[0]).toBe(
|
|
309
|
+
"bafkreiejafiz23ewtyh6m3dpincmxouohdcimrd33abacrq3h2pacewwjm.ipfs.dweb.link"
|
|
310
|
+
);
|
|
311
|
+
expect(chain[0].httpsUrl).toBe(
|
|
312
|
+
"https://bafkreiejafiz23ewtyh6m3dpincmxouohdcimrd33abacrq3h2pacewwjm.ipfs.dweb.link"
|
|
313
|
+
);
|
|
314
|
+
expect(chain[0].txHash).toBe(response.txId);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test("Auth chain: BCMR, sha256 content hash, uri", async () => {
|
|
318
|
+
const alice = await RegTestWallet.fromId(
|
|
319
|
+
`wif:regtest:${process.env.PRIVATE_WIF!}`
|
|
320
|
+
);
|
|
321
|
+
const bob = await RegTestWallet.newRandom();
|
|
322
|
+
|
|
323
|
+
const contentHashBin = sha256.hash(utf8ToBin("registry_contents"));
|
|
324
|
+
const chunks = ["BCMR", contentHashBin, "mainnet.cash"];
|
|
325
|
+
const opreturnData = OpReturnData.fromArray(chunks);
|
|
326
|
+
|
|
327
|
+
const response = await alice.send([
|
|
328
|
+
new SendRequest({ cashaddr: bob.cashaddr!, value: 1000, unit: "sat" }),
|
|
329
|
+
opreturnData,
|
|
330
|
+
]);
|
|
331
|
+
const chain = await BCMR.buildAuthChain({
|
|
332
|
+
transactionHash: response.txId!,
|
|
333
|
+
network: Network.REGTEST,
|
|
334
|
+
});
|
|
335
|
+
expect(chain.length).toBe(1);
|
|
336
|
+
expect(chain[0].contentHash).toBe(binToHex(contentHashBin));
|
|
337
|
+
expect(chain[0].uris[0]).toBe("mainnet.cash");
|
|
338
|
+
expect(chain[0].httpsUrl).toBe(
|
|
339
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
|
|
340
|
+
);
|
|
341
|
+
expect(chain[0].txHash).toBe(response.txId);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
test("Auth chain: BCMR, sha256 content hash, 2 uris", async () => {
|
|
345
|
+
const alice = await RegTestWallet.fromId(
|
|
346
|
+
`wif:regtest:${process.env.PRIVATE_WIF!}`
|
|
347
|
+
);
|
|
348
|
+
const bob = await RegTestWallet.newRandom();
|
|
349
|
+
|
|
350
|
+
const chunks = [
|
|
351
|
+
"BCMR",
|
|
352
|
+
sha256.hash(utf8ToBin("registry_contents")),
|
|
353
|
+
"mainnet.cash",
|
|
354
|
+
"ipfs://QmbWrG5Asp5iGmUwQHogSJGRX26zuRnuLWPytZfiL75sZv",
|
|
355
|
+
];
|
|
356
|
+
const opreturnData = OpReturnData.fromArray(chunks);
|
|
357
|
+
const response = await alice.send([
|
|
358
|
+
new SendRequest({ cashaddr: bob.cashaddr!, value: 1000, unit: "sat" }),
|
|
359
|
+
opreturnData,
|
|
360
|
+
]);
|
|
361
|
+
const chain = await BCMR.buildAuthChain({
|
|
362
|
+
transactionHash: response.txId!,
|
|
363
|
+
network: Network.REGTEST,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
expect(chain.length).toBe(1);
|
|
367
|
+
expect(chain[0].uris.length).toBe(2);
|
|
368
|
+
expect(chain[0].uris[0]).toBe("mainnet.cash");
|
|
369
|
+
expect(chain[0].uris[1]).toBe(
|
|
370
|
+
"ipfs://QmbWrG5Asp5iGmUwQHogSJGRX26zuRnuLWPytZfiL75sZv"
|
|
371
|
+
);
|
|
372
|
+
expect(chain[0].httpsUrl).toBe(
|
|
373
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
|
|
374
|
+
);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test("Auth chain: all OP_PUSDHDATA encodings", async () => {
|
|
378
|
+
const alice = await RegTestWallet.fromId(
|
|
379
|
+
`wif:regtest:${process.env.PRIVATE_WIF!}`
|
|
380
|
+
);
|
|
381
|
+
const bob = await RegTestWallet.newRandom();
|
|
382
|
+
|
|
383
|
+
const opreturnData = OpReturnData.fromUint8Array(
|
|
384
|
+
hexToBin(
|
|
385
|
+
"6a0442434d524c20e073b89a80c77c533ad364692db15df01adb9df404592f608d2c0cdd8960ed0e4d440068747470733a2f2f6d61696e6e65742e636173682f2e77656c6c2d6b6e6f776e2f626974636f696e2d636173682d6d657461646174612d72656769737472792e6a736f6e"
|
|
386
|
+
)
|
|
387
|
+
);
|
|
388
|
+
const response = await alice.send([
|
|
389
|
+
new SendRequest({ cashaddr: bob.cashaddr!, value: 1000, unit: "sat" }),
|
|
390
|
+
opreturnData,
|
|
391
|
+
]);
|
|
392
|
+
await BCMR.buildAuthChain({
|
|
393
|
+
transactionHash: response.txId!,
|
|
394
|
+
network: Network.REGTEST,
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
test("Auth chain with 1 element, add resolved registry", async () => {
|
|
399
|
+
const alice = await RegTestWallet.fromId(
|
|
400
|
+
`wif:regtest:${process.env.PRIVATE_WIF!}`
|
|
401
|
+
);
|
|
402
|
+
const bob = await RegTestWallet.newRandom();
|
|
403
|
+
|
|
404
|
+
let chunks = [
|
|
405
|
+
"BCMR",
|
|
406
|
+
registryContentHashBinBitcoinByteOrder,
|
|
407
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json",
|
|
408
|
+
];
|
|
409
|
+
const opreturnData = OpReturnData.fromArray(chunks);
|
|
410
|
+
const response = await alice.send([
|
|
411
|
+
new SendRequest({ cashaddr: bob.cashaddr!, value: 10000, unit: "sat" }),
|
|
412
|
+
opreturnData,
|
|
413
|
+
]);
|
|
414
|
+
|
|
415
|
+
setupFetchMock(
|
|
416
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json",
|
|
417
|
+
registry
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
expect(
|
|
421
|
+
BCMR.getTokenInfo(
|
|
422
|
+
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
423
|
+
)
|
|
424
|
+
).toBe(undefined);
|
|
425
|
+
const chain = await BCMR.addMetadataRegistryAuthChain({
|
|
426
|
+
transactionHash: response.txId!,
|
|
427
|
+
network: Network.REGTEST,
|
|
428
|
+
});
|
|
429
|
+
expect(chain.length).toBe(1);
|
|
430
|
+
expect(chain[0].txHash).toBe(response.txId!);
|
|
431
|
+
expect(chain[0].uris[0]).toBe(
|
|
432
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
|
|
433
|
+
);
|
|
434
|
+
expect(chain[0].httpsUrl).toBe(
|
|
435
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
const tokenInfo = BCMR.getTokenInfo(
|
|
439
|
+
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
440
|
+
);
|
|
441
|
+
expect(tokenInfo?.token?.symbol).toBe("TOK");
|
|
442
|
+
expect(tokenInfo?.token?.decimals).toBe(8);
|
|
443
|
+
|
|
444
|
+
// check adding the same registry does not produce a duplicate
|
|
445
|
+
expect(BCMR.metadataRegistries.length).toBe(1);
|
|
446
|
+
const otherChain = await BCMR.addMetadataRegistryAuthChain({
|
|
447
|
+
transactionHash: response.txId!,
|
|
448
|
+
network: Network.REGTEST,
|
|
449
|
+
});
|
|
450
|
+
expect(otherChain.length).toBe(1);
|
|
451
|
+
expect(BCMR.metadataRegistries.length).toBe(1);
|
|
452
|
+
|
|
453
|
+
removeFetchMock(
|
|
454
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
|
|
455
|
+
);
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
test("Auth chain with 3 elements", async () => {
|
|
459
|
+
// tests authchain of 3 elements with all possible confirmed and unconfirmed transaction chains
|
|
460
|
+
// Also change of authchain holding address is assessed
|
|
461
|
+
for (const [index, mineCombo] of [
|
|
462
|
+
[0, 0, 0],
|
|
463
|
+
[0, 0, 1],
|
|
464
|
+
[0, 1, 0],
|
|
465
|
+
[0, 1, 1],
|
|
466
|
+
[1, 0, 0],
|
|
467
|
+
[1, 0, 1],
|
|
468
|
+
[1, 1, 0],
|
|
469
|
+
[1, 1, 1],
|
|
470
|
+
].entries()) {
|
|
471
|
+
const alice = await RegTestWallet.fromId(
|
|
472
|
+
`wif:regtest:${process.env.PRIVATE_WIF!}`
|
|
473
|
+
);
|
|
474
|
+
const bob = await RegTestWallet.newRandom();
|
|
475
|
+
const charlie = await RegTestWallet.newRandom();
|
|
476
|
+
|
|
477
|
+
let chunks = [
|
|
478
|
+
"BCMR",
|
|
479
|
+
registryContentHashBinBitcoinByteOrder,
|
|
480
|
+
"mainnet.cash",
|
|
481
|
+
];
|
|
482
|
+
const opreturnData = OpReturnData.fromArray(chunks);
|
|
483
|
+
const response = await alice.send([
|
|
484
|
+
new SendRequest({ cashaddr: bob.cashaddr!, value: 10000, unit: "sat" }),
|
|
485
|
+
opreturnData,
|
|
486
|
+
]);
|
|
487
|
+
if (mineCombo[0]) await mine({ cashaddr: alice.cashaddr!, blocks: 1 });
|
|
488
|
+
|
|
489
|
+
chunks[2] =
|
|
490
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json";
|
|
491
|
+
const opreturnData2 = OpReturnData.fromArray(chunks);
|
|
492
|
+
const response2 = await bob.send([
|
|
493
|
+
new SendRequest({ cashaddr: bob.cashaddr!, value: 9500, unit: "sat" }),
|
|
494
|
+
opreturnData2,
|
|
495
|
+
]);
|
|
496
|
+
if (mineCombo[1]) await mine({ cashaddr: alice.cashaddr!, blocks: 1 });
|
|
497
|
+
|
|
498
|
+
chunks[2] =
|
|
499
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v3.json";
|
|
500
|
+
const opreturnData3 = OpReturnData.fromArray(chunks);
|
|
501
|
+
const response3 = await bob.send([
|
|
502
|
+
new SendRequest({
|
|
503
|
+
cashaddr: charlie.cashaddr!,
|
|
504
|
+
value: 9000,
|
|
505
|
+
unit: "sat",
|
|
506
|
+
}),
|
|
507
|
+
opreturnData3,
|
|
508
|
+
]);
|
|
509
|
+
if (mineCombo[2]) await mine({ cashaddr: alice.cashaddr!, blocks: 1 });
|
|
510
|
+
|
|
511
|
+
const chain = await BCMR.buildAuthChain({
|
|
512
|
+
transactionHash: response.txId!,
|
|
513
|
+
network: Network.REGTEST,
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
expect(chain.length).toBe(3);
|
|
517
|
+
expect(chain[0].txHash).toBe(response.txId!);
|
|
518
|
+
expect(chain[0].uris[0]).toBe("mainnet.cash");
|
|
519
|
+
expect(chain[0].httpsUrl).toBe(
|
|
520
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
expect(chain[1].txHash).toBe(response2.txId!);
|
|
524
|
+
expect(chain[1].uris[0]).toBe(
|
|
525
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json"
|
|
526
|
+
);
|
|
527
|
+
expect(chain[1].httpsUrl).toBe(
|
|
528
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json"
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
expect(chain[2].txHash).toBe(response3.txId!);
|
|
532
|
+
expect(chain[2].uris[0]).toBe(
|
|
533
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v3.json"
|
|
534
|
+
);
|
|
535
|
+
expect(chain[2].httpsUrl).toBe(
|
|
536
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v3.json"
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
// extra checks for resolving chains not from head
|
|
540
|
+
if (index === 0) {
|
|
541
|
+
const noFollow = await BCMR.buildAuthChain({
|
|
542
|
+
transactionHash: response2.txId!,
|
|
543
|
+
network: Network.REGTEST,
|
|
544
|
+
followToHead: false,
|
|
545
|
+
});
|
|
546
|
+
expect(noFollow.length).toBe(1);
|
|
547
|
+
expect(noFollow[0].txHash).toBe(response2.txId!);
|
|
548
|
+
expect(noFollow[0].uris[0]).toBe(
|
|
549
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json"
|
|
550
|
+
);
|
|
551
|
+
expect(noFollow[0].httpsUrl).toBe(
|
|
552
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json"
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
const follow = await BCMR.buildAuthChain({
|
|
556
|
+
transactionHash: response2.txId!,
|
|
557
|
+
network: Network.REGTEST,
|
|
558
|
+
followToHead: true,
|
|
559
|
+
});
|
|
560
|
+
expect(follow.length).toBe(2);
|
|
561
|
+
|
|
562
|
+
expect(follow[0].txHash).toBe(response2.txId!);
|
|
563
|
+
expect(follow[0].uris[0]).toBe(
|
|
564
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json"
|
|
565
|
+
);
|
|
566
|
+
expect(follow[0].httpsUrl).toBe(
|
|
567
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json"
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
expect(follow[1].txHash).toBe(response3.txId!);
|
|
571
|
+
expect(follow[1].uris[0]).toBe(
|
|
572
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v3.json"
|
|
573
|
+
);
|
|
574
|
+
expect(follow[1].httpsUrl).toBe(
|
|
575
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v3.json"
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
test("Authchain tail resolution info in registry acceleration path", async () => {
|
|
582
|
+
const alice = await RegTestWallet.fromId(
|
|
583
|
+
`wif:regtest:${process.env.PRIVATE_WIF!}`
|
|
584
|
+
);
|
|
585
|
+
const bob = await RegTestWallet.newRandom();
|
|
586
|
+
|
|
587
|
+
const registry_v1 = { ...registry };
|
|
588
|
+
registry_v1.extensions = { authchain: {} };
|
|
589
|
+
const contentHash_v1 = sha256.hash(
|
|
590
|
+
utf8ToBin(JSON.stringify(registry_v1, null, 2))
|
|
591
|
+
);
|
|
592
|
+
setupFetchMock(
|
|
593
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v1.json",
|
|
594
|
+
JSON.stringify(registry_v1, null, 2)
|
|
595
|
+
);
|
|
596
|
+
let chunks = [
|
|
597
|
+
"BCMR",
|
|
598
|
+
contentHash_v1,
|
|
599
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v1.json",
|
|
600
|
+
];
|
|
601
|
+
const opreturnData = OpReturnData.fromArray(chunks);
|
|
602
|
+
const response = await alice.send([
|
|
603
|
+
new SendRequest({ cashaddr: bob.cashaddr!, value: 10000, unit: "sat" }),
|
|
604
|
+
opreturnData,
|
|
605
|
+
]);
|
|
606
|
+
|
|
607
|
+
const registry_v2 = { ...registry };
|
|
608
|
+
registry_v2.extensions = {
|
|
609
|
+
authchain: { 0: await bob.provider!.getRawTransaction(response.txId!) },
|
|
610
|
+
};
|
|
611
|
+
const contentHash_v2 = sha256.hash(
|
|
612
|
+
utf8ToBin(JSON.stringify(registry_v2, null, 2))
|
|
613
|
+
);
|
|
614
|
+
setupFetchMock(
|
|
615
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json",
|
|
616
|
+
JSON.stringify(registry_v2, null, 2)
|
|
617
|
+
);
|
|
618
|
+
chunks = [
|
|
619
|
+
"BCMR",
|
|
620
|
+
contentHash_v2,
|
|
621
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json",
|
|
622
|
+
];
|
|
623
|
+
const opreturnData2 = OpReturnData.fromArray(chunks);
|
|
624
|
+
const response2 = await bob.send([
|
|
625
|
+
new SendRequest({ cashaddr: bob.cashaddr!, value: 9500, unit: "sat" }),
|
|
626
|
+
opreturnData2,
|
|
627
|
+
]);
|
|
628
|
+
|
|
629
|
+
const registry_v3 = { ...registry };
|
|
630
|
+
registry_v3.extensions = {
|
|
631
|
+
authchain: {
|
|
632
|
+
0: await bob.provider!.getRawTransaction(response.txId!),
|
|
633
|
+
1: await bob.provider!.getRawTransaction(response2.txId!),
|
|
634
|
+
},
|
|
635
|
+
};
|
|
636
|
+
const contentHash_v3 = sha256.hash(
|
|
637
|
+
utf8ToBin(JSON.stringify(registry_v3, null, 2))
|
|
638
|
+
);
|
|
639
|
+
setupFetchMock(
|
|
640
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v3.json",
|
|
641
|
+
JSON.stringify(registry_v3, null, 2)
|
|
642
|
+
);
|
|
643
|
+
chunks = [
|
|
644
|
+
"BCMR",
|
|
645
|
+
contentHash_v3,
|
|
646
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v3.json",
|
|
647
|
+
];
|
|
648
|
+
const opreturnData3 = OpReturnData.fromArray(chunks);
|
|
649
|
+
const response3 = await bob.send([
|
|
650
|
+
new SendRequest({ cashaddr: bob.cashaddr!, value: 9000, unit: "sat" }),
|
|
651
|
+
opreturnData3,
|
|
652
|
+
]);
|
|
653
|
+
|
|
654
|
+
const registry_v4 = { ...registry };
|
|
655
|
+
registry_v4.extensions = {};
|
|
656
|
+
const contentHash_v4 = sha256.hash(
|
|
657
|
+
utf8ToBin(JSON.stringify(registry_v4, null, 2))
|
|
658
|
+
);
|
|
659
|
+
setupFetchMock(
|
|
660
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v4.json",
|
|
661
|
+
JSON.stringify(registry_v4, null, 2)
|
|
662
|
+
);
|
|
663
|
+
chunks = [
|
|
664
|
+
"BCMR",
|
|
665
|
+
contentHash_v4,
|
|
666
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v4.json",
|
|
667
|
+
];
|
|
668
|
+
const opreturnData4 = OpReturnData.fromArray(chunks);
|
|
669
|
+
const response4 = await bob.send([
|
|
670
|
+
new SendRequest({ cashaddr: bob.cashaddr!, value: 8500, unit: "sat" }),
|
|
671
|
+
opreturnData4,
|
|
672
|
+
]);
|
|
673
|
+
|
|
674
|
+
let chain = await BCMR.buildAuthChain({
|
|
675
|
+
transactionHash: response.txId!,
|
|
676
|
+
network: Network.REGTEST,
|
|
677
|
+
});
|
|
678
|
+
expect(chain.length).toBe(4);
|
|
679
|
+
|
|
680
|
+
chain = await BCMR.buildAuthChain({
|
|
681
|
+
transactionHash: response.txId!,
|
|
682
|
+
network: Network.REGTEST,
|
|
683
|
+
followToHead: false,
|
|
684
|
+
});
|
|
685
|
+
expect(chain.length).toBe(1);
|
|
686
|
+
|
|
687
|
+
// tail acceleration available, do not follow head
|
|
688
|
+
chain = await BCMR.buildAuthChain({
|
|
689
|
+
transactionHash: response3.txId!,
|
|
690
|
+
network: Network.REGTEST,
|
|
691
|
+
followToHead: false,
|
|
692
|
+
resolveBase: true,
|
|
693
|
+
});
|
|
694
|
+
expect(chain.length).toBe(3);
|
|
695
|
+
|
|
696
|
+
// resolve single element
|
|
697
|
+
chain = await BCMR.buildAuthChain({
|
|
698
|
+
transactionHash: response3.txId!,
|
|
699
|
+
network: Network.REGTEST,
|
|
700
|
+
followToHead: false,
|
|
701
|
+
resolveBase: false,
|
|
702
|
+
});
|
|
703
|
+
expect(chain.length).toBe(1);
|
|
704
|
+
|
|
705
|
+
// no acceleration available, will scan network
|
|
706
|
+
chain = await BCMR.buildAuthChain({
|
|
707
|
+
transactionHash: response4.txId!,
|
|
708
|
+
network: Network.REGTEST,
|
|
709
|
+
resolveBase: true,
|
|
710
|
+
});
|
|
711
|
+
expect(chain.length).toBe(4);
|
|
712
|
+
|
|
713
|
+
expect(chain[0].txHash).toBe(response.txId!);
|
|
714
|
+
expect(chain[0].uris[0]).toBe(
|
|
715
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v1.json"
|
|
716
|
+
);
|
|
717
|
+
expect(chain[0].httpsUrl).toBe(
|
|
718
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v1.json"
|
|
719
|
+
);
|
|
720
|
+
|
|
721
|
+
expect(chain[1].txHash).toBe(response2.txId!);
|
|
722
|
+
expect(chain[1].uris[0]).toBe(
|
|
723
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json"
|
|
724
|
+
);
|
|
725
|
+
expect(chain[1].httpsUrl).toBe(
|
|
726
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json"
|
|
727
|
+
);
|
|
728
|
+
|
|
729
|
+
expect(chain[2].txHash).toBe(response3.txId!);
|
|
730
|
+
expect(chain[2].uris[0]).toBe(
|
|
731
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v3.json"
|
|
732
|
+
);
|
|
733
|
+
expect(chain[2].httpsUrl).toBe(
|
|
734
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v3.json"
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
expect(chain[3].txHash).toBe(response4.txId!);
|
|
738
|
+
expect(chain[3].uris[0]).toBe(
|
|
739
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v4.json"
|
|
740
|
+
);
|
|
741
|
+
expect(chain[3].httpsUrl).toBe(
|
|
742
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v4.json"
|
|
743
|
+
);
|
|
744
|
+
|
|
745
|
+
removeFetchMock(
|
|
746
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v1.json"
|
|
747
|
+
);
|
|
748
|
+
removeFetchMock(
|
|
749
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json"
|
|
750
|
+
);
|
|
751
|
+
removeFetchMock(
|
|
752
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v3.json"
|
|
753
|
+
);
|
|
754
|
+
removeFetchMock(
|
|
755
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v4.json"
|
|
756
|
+
);
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
test("Test NFT cashtoken genesis with BCMR output", async () => {
|
|
760
|
+
const chunks = ["BCMR", "QmbWrG5Asp5iGmUwQHogSJGRX26zuRnuLWPytZfiL75sZv"];
|
|
761
|
+
const opreturnData = OpReturnData.fromArray(chunks);
|
|
762
|
+
|
|
763
|
+
const alice = await RegTestWallet.fromId(process.env.ALICE_ID!);
|
|
764
|
+
const genesisResponse = await alice.tokenGenesis(
|
|
765
|
+
{
|
|
766
|
+
cashaddr: alice.cashaddr!,
|
|
767
|
+
capability: NFTCapability.mutable,
|
|
768
|
+
commitment: "abcd",
|
|
769
|
+
},
|
|
770
|
+
opreturnData
|
|
771
|
+
);
|
|
772
|
+
|
|
773
|
+
const tokenId = genesisResponse.tokenIds![0];
|
|
774
|
+
const tokenBalance = await alice.getTokenBalance(tokenId);
|
|
775
|
+
expect(tokenBalance).toBe(0n);
|
|
776
|
+
const nftTokenBalance = await alice.getNftTokenBalance(tokenId);
|
|
777
|
+
expect(nftTokenBalance).toBe(1);
|
|
778
|
+
const tokenUtxos = await alice.getTokenUtxos(tokenId);
|
|
779
|
+
expect(tokenUtxos.length).toBe(1);
|
|
780
|
+
|
|
781
|
+
const transaction = await (
|
|
782
|
+
alice.provider as ElectrumNetworkProvider
|
|
783
|
+
).getRawTransactionObject(genesisResponse.txId!);
|
|
784
|
+
expect(transaction.vout[0].tokenData?.category).toBe(tokenId);
|
|
785
|
+
expect(transaction.vout[1].scriptPubKey.type).toBe("nulldata");
|
|
786
|
+
|
|
787
|
+
const chain = await BCMR.buildAuthChain({
|
|
788
|
+
transactionHash: genesisResponse.txId!,
|
|
789
|
+
network: Network.REGTEST,
|
|
790
|
+
});
|
|
791
|
+
expect(chain.length).toBe(1);
|
|
792
|
+
expect(chain[0].contentHash).toBe(
|
|
793
|
+
"516d62577247354173703569476d557751486f67534a47525832367a75526e754c575079745a66694c3735735a76"
|
|
794
|
+
);
|
|
795
|
+
expect(chain[0].uris[0]).toBe(
|
|
796
|
+
"ipfs://QmbWrG5Asp5iGmUwQHogSJGRX26zuRnuLWPytZfiL75sZv"
|
|
797
|
+
);
|
|
798
|
+
expect(chain[0].httpsUrl).toBe(
|
|
799
|
+
"https://dweb.link/ipfs/QmbWrG5Asp5iGmUwQHogSJGRX26zuRnuLWPytZfiL75sZv"
|
|
800
|
+
);
|
|
801
|
+
expect(chain[0].txHash).toBe(genesisResponse.txId);
|
|
802
|
+
|
|
803
|
+
const chainByTokenId = await BCMR.buildAuthChain({
|
|
804
|
+
transactionHash: tokenId,
|
|
805
|
+
network: Network.REGTEST,
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
expect(JSON.stringify(chain)).toBe(JSON.stringify(chainByTokenId));
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
test("Auth chain with forwards gaps", async () => {
|
|
812
|
+
const alice = await RegTestWallet.fromId(
|
|
813
|
+
`wif:regtest:${process.env.PRIVATE_WIF!}`
|
|
814
|
+
);
|
|
815
|
+
|
|
816
|
+
const contentHashBin = sha256.hash(utf8ToBin("registry_contents"));
|
|
817
|
+
const chunks = [
|
|
818
|
+
"BCMR",
|
|
819
|
+
contentHashBin,
|
|
820
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json",
|
|
821
|
+
];
|
|
822
|
+
const opreturnData = OpReturnData.fromArray(chunks);
|
|
823
|
+
|
|
824
|
+
const response = await alice.send([
|
|
825
|
+
new SendRequest({ cashaddr: alice.cashaddr!, value: 3000, unit: "sat" }),
|
|
826
|
+
opreturnData,
|
|
827
|
+
]);
|
|
828
|
+
const chain = await BCMR.buildAuthChain({
|
|
829
|
+
transactionHash: response.txId!,
|
|
830
|
+
network: Network.REGTEST,
|
|
831
|
+
});
|
|
832
|
+
expect(chain.length).toBe(1);
|
|
833
|
+
expect(chain[0].contentHash).toBe(binToHex(contentHashBin));
|
|
834
|
+
expect(chain[0].uris[0]).toBe(
|
|
835
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
|
|
836
|
+
);
|
|
837
|
+
expect(chain[0].httpsUrl).toBe(
|
|
838
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
|
|
839
|
+
);
|
|
840
|
+
expect(chain[0].txHash).toBe(response.txId);
|
|
841
|
+
|
|
842
|
+
const gapTxResponse = await alice.send(
|
|
843
|
+
[
|
|
844
|
+
new SendRequest({
|
|
845
|
+
cashaddr: alice.cashaddr!,
|
|
846
|
+
value: 2000,
|
|
847
|
+
unit: "sat",
|
|
848
|
+
}),
|
|
849
|
+
],
|
|
850
|
+
{ utxoIds: [`${response.txId}:0:3000`] }
|
|
851
|
+
);
|
|
852
|
+
|
|
853
|
+
const chainHeadResponse = await alice.send(
|
|
854
|
+
[
|
|
855
|
+
new SendRequest({
|
|
856
|
+
cashaddr: alice.cashaddr!,
|
|
857
|
+
value: 1000,
|
|
858
|
+
unit: "sat",
|
|
859
|
+
}),
|
|
860
|
+
opreturnData,
|
|
861
|
+
],
|
|
862
|
+
{ utxoIds: [`${gapTxResponse.txId}:0:2000`] }
|
|
863
|
+
);
|
|
864
|
+
|
|
865
|
+
const gappedChain = await BCMR.buildAuthChain({
|
|
866
|
+
transactionHash: response.txId!,
|
|
867
|
+
network: Network.REGTEST,
|
|
868
|
+
});
|
|
869
|
+
expect(gappedChain.length).toBe(2);
|
|
870
|
+
expect(gappedChain[0].contentHash).toBe(binToHex(contentHashBin));
|
|
871
|
+
expect(gappedChain[0].uris[0]).toBe(
|
|
872
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
|
|
873
|
+
);
|
|
874
|
+
expect(gappedChain[0].httpsUrl).toBe(
|
|
875
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
|
|
876
|
+
);
|
|
877
|
+
expect(gappedChain[0].txHash).toBe(response.txId);
|
|
878
|
+
|
|
879
|
+
expect(gappedChain[1].contentHash).toBe(binToHex(contentHashBin));
|
|
880
|
+
expect(gappedChain[1].uris[0]).toBe(
|
|
881
|
+
"mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
|
|
882
|
+
);
|
|
883
|
+
expect(gappedChain[1].httpsUrl).toBe(
|
|
884
|
+
"https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
|
|
885
|
+
);
|
|
886
|
+
expect(gappedChain[1].txHash).toBe(chainHeadResponse.txId);
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
test("Test fetching BCMR authchain from chaingraph", async () => {
|
|
890
|
+
setupFetchMock("https://gql.mainnet.cash/v1/graphql", {
|
|
891
|
+
data: {
|
|
892
|
+
transaction: [
|
|
893
|
+
{
|
|
894
|
+
hash: "\\x07275f68d14780c737279898e730cec3a7b189a761caf43b4197b60a7c891a97",
|
|
895
|
+
authchains: [
|
|
896
|
+
{
|
|
897
|
+
authchain_length: 330,
|
|
898
|
+
migrations: [
|
|
899
|
+
{
|
|
900
|
+
transaction: [
|
|
901
|
+
{
|
|
902
|
+
hash: "\\xd5721db8841ecb61ec73daeb2df7df88b180d5029061d4845efc7cb29c42183b",
|
|
903
|
+
inputs: [
|
|
904
|
+
{
|
|
905
|
+
outpoint_index: "0",
|
|
906
|
+
},
|
|
907
|
+
],
|
|
908
|
+
outputs: [
|
|
909
|
+
{
|
|
910
|
+
output_index: "1",
|
|
911
|
+
locking_bytecode:
|
|
912
|
+
"\\x6a0442434d5220107b1719c865e8ab631f9e63f1140b51e710a86606992adc7f901b2291746abe4c506261666b7265696171706d6c727473646635637677676834366d707972696332723434696b717a71677465766e79373471646d726a6335646b78792e697066732e6e667473746f726167652e6c696e6b",
|
|
913
|
+
},
|
|
914
|
+
],
|
|
915
|
+
},
|
|
916
|
+
],
|
|
917
|
+
},
|
|
918
|
+
{
|
|
919
|
+
transaction: [
|
|
920
|
+
{
|
|
921
|
+
hash: "\\x4bdcdd9a347b287e6d26d743ee4404f530a8f35501ff1adb31766edcfb2d20a9",
|
|
922
|
+
inputs: [
|
|
923
|
+
{
|
|
924
|
+
outpoint_index: "0",
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
outpoint_index: "0",
|
|
928
|
+
},
|
|
929
|
+
],
|
|
930
|
+
outputs: [
|
|
931
|
+
{
|
|
932
|
+
output_index: "1",
|
|
933
|
+
locking_bytecode:
|
|
934
|
+
"\\x6a0442434d5240393231666263306665623665666666613639316331346633656636346234333139656138613461643266636637313064303362613661363534633962346661643f697066732e7061742e6d6e2f697066732f516d556e6661524c4356516d4e453567745274705464476b39544d6939364472507a7351554c31505a7874686637",
|
|
935
|
+
},
|
|
936
|
+
],
|
|
937
|
+
},
|
|
938
|
+
],
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
transaction: [
|
|
942
|
+
{
|
|
943
|
+
hash: "\\x0d7a26fcc472d519ef83a1ca9c3a44a394b27423a55a40f7aacd1552c873e2a5",
|
|
944
|
+
inputs: [
|
|
945
|
+
{
|
|
946
|
+
outpoint_index: "0",
|
|
947
|
+
},
|
|
948
|
+
],
|
|
949
|
+
outputs: [
|
|
950
|
+
{
|
|
951
|
+
output_index: "1",
|
|
952
|
+
locking_bytecode:
|
|
953
|
+
"\\x6a0442434d5240323065326630623531343333636566633639393732373765643239616365303438363963326366393136366465653139366538656331333561666630613162343f697066732e7061742e6d6e2f697066732f516d5339687a786a6e42394168416f46584b53626b376a454d7255354577397653726d6e624a593435555932567a",
|
|
954
|
+
},
|
|
955
|
+
],
|
|
956
|
+
},
|
|
957
|
+
],
|
|
958
|
+
},
|
|
959
|
+
],
|
|
960
|
+
},
|
|
961
|
+
],
|
|
962
|
+
},
|
|
963
|
+
],
|
|
964
|
+
},
|
|
965
|
+
});
|
|
966
|
+
|
|
967
|
+
const result: AuthChain = await BCMR.fetchAuthChainFromChaingraph({
|
|
968
|
+
chaingraphUrl: "https://gql.mainnet.cash/v1/graphql",
|
|
969
|
+
transactionHash:
|
|
970
|
+
"07275f68d14780c737279898e730cec3a7b189a761caf43b4197b60a7c891a97",
|
|
971
|
+
});
|
|
972
|
+
expect(result.length).toBe(3);
|
|
973
|
+
expect(result.at(-1)?.uris[0]).toBe(
|
|
974
|
+
"ipfs.pat.mn/ipfs/QmS9hzxjnB9AhAoFXKSbk7jEMrU5Ew9vSrmnbJY45UY2Vz"
|
|
975
|
+
);
|
|
976
|
+
removeFetchMock("https://gql.mainnet.cash/v1/graphql");
|
|
977
|
+
});
|
|
978
|
+
});
|