mainnet-js 1.1.18 → 1.1.20

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.
@@ -231,7 +231,10 @@ describe(`Test BCMR support`, () => {
231
231
  expect(chain[0].contentHash).toBe(
232
232
  "516d62577247354173703569476d557751486f67534a47525832367a75526e754c575079745a66694c3735735a76"
233
233
  );
234
- expect(chain[0].uri).toBe(
234
+ expect(chain[0].uris[0]).toBe(
235
+ "ipfs://QmbWrG5Asp5iGmUwQHogSJGRX26zuRnuLWPytZfiL75sZv"
236
+ );
237
+ expect(chain[0].httpsUrl).toBe(
235
238
  "https://dweb.link/ipfs/QmbWrG5Asp5iGmUwQHogSJGRX26zuRnuLWPytZfiL75sZv"
236
239
  );
237
240
  expect(chain[0].txHash).toBe(response.txId);
@@ -247,7 +250,7 @@ describe(`Test BCMR support`, () => {
247
250
  const chunks = [
248
251
  "BCMR",
249
252
  contentHashBin,
250
- "mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json",
253
+ "mainnet.cash",
251
254
  ];
252
255
  const opreturnData = OpReturnData.fromArray(chunks);
253
256
 
@@ -261,13 +264,16 @@ describe(`Test BCMR support`, () => {
261
264
  });
262
265
  expect(chain.length).toBe(1);
263
266
  expect(chain[0].contentHash).toBe(binToHex(contentHashBin));
264
- expect(chain[0].uri).toBe(
267
+ expect(chain[0].uris[0]).toBe(
268
+ "mainnet.cash"
269
+ );
270
+ expect(chain[0].httpsUrl).toBe(
265
271
  "https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
266
272
  );
267
273
  expect(chain[0].txHash).toBe(response.txId);
268
274
  });
269
275
 
270
- test("Auth chain: BCMR, sha256 content hash, uri and another chunk", async () => {
276
+ test("Auth chain: BCMR, sha256 content hash, 2 uris", async () => {
271
277
  const alice = await RegTestWallet.fromId(
272
278
  `wif:regtest:${process.env.PRIVATE_WIF!}`
273
279
  );
@@ -276,20 +282,24 @@ describe(`Test BCMR support`, () => {
276
282
  const chunks = [
277
283
  "BCMR",
278
284
  sha256.hash(utf8ToBin("registry_contents")),
279
- "mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json",
280
- "something else",
285
+ "mainnet.cash",
286
+ "ipfs://QmbWrG5Asp5iGmUwQHogSJGRX26zuRnuLWPytZfiL75sZv",
281
287
  ];
282
288
  const opreturnData = OpReturnData.fromArray(chunks);
283
289
  const response = await alice.send([
284
290
  new SendRequest({ cashaddr: bob.cashaddr!, value: 1000, unit: "sat" }),
285
291
  opreturnData,
286
292
  ]);
287
- await expect(
288
- BCMR.buildAuthChain({
289
- transactionHash: response.txId!,
290
- network: Network.REGTEST,
291
- })
292
- ).rejects.toThrow("Malformed BCMR output");
293
+ const chain = await BCMR.buildAuthChain({
294
+ transactionHash: response.txId!,
295
+ network: Network.REGTEST,
296
+ });
297
+
298
+ expect(chain.length).toBe(1);
299
+ expect(chain[0].uris.length).toBe(2);
300
+ expect(chain[0].uris[0]).toBe("mainnet.cash");
301
+ expect(chain[0].uris[1]).toBe("ipfs://QmbWrG5Asp5iGmUwQHogSJGRX26zuRnuLWPytZfiL75sZv");
302
+ expect(chain[0].httpsUrl).toBe("https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json");
293
303
  });
294
304
 
295
305
  test("Auth chain: all OP_PUSDHDATA encodings", async () => {
@@ -346,7 +356,10 @@ describe(`Test BCMR support`, () => {
346
356
  });
347
357
  expect(chain.length).toBe(1);
348
358
  expect(chain[0].txHash).toBe(response.txId!);
349
- expect(chain[0].uri).toBe(
359
+ expect(chain[0].uris[0]).toBe(
360
+ "mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
361
+ );
362
+ expect(chain[0].httpsUrl).toBe(
350
363
  "https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
351
364
  );
352
365
 
@@ -392,7 +405,7 @@ describe(`Test BCMR support`, () => {
392
405
  let chunks = [
393
406
  "BCMR",
394
407
  registryContentHashBinBitcoinByteOrder,
395
- "mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json",
408
+ "mainnet.cash",
396
409
  ];
397
410
  const opreturnData = OpReturnData.fromArray(chunks);
398
411
  const response = await alice.send([
@@ -430,19 +443,23 @@ describe(`Test BCMR support`, () => {
430
443
 
431
444
  expect(chain.length).toBe(3);
432
445
  expect(chain[0].txHash).toBe(response.txId!);
433
- expect(chain[0].uri).toBe(
434
- "https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
446
+ expect(chain[0].uris[0]).toBe(
447
+ "mainnet.cash"
435
448
  );
449
+ expect(chain[0].httpsUrl).toBe("https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json");
436
450
 
437
451
  expect(chain[1].txHash).toBe(response2.txId!);
438
- expect(chain[1].uri).toBe(
439
- "https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json"
452
+ expect(chain[1].uris[0]).toBe(
453
+ "mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json"
440
454
  );
455
+ expect(chain[1].httpsUrl).toBe("https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json");
456
+
441
457
 
442
458
  expect(chain[2].txHash).toBe(response3.txId!);
443
- expect(chain[2].uri).toBe(
444
- "https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v3.json"
459
+ expect(chain[2].uris[0]).toBe(
460
+ "mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v3.json"
445
461
  );
462
+ expect(chain[2].httpsUrl).toBe("https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v3.json");
446
463
 
447
464
  // extra checks for resolving chains not from head
448
465
  if (index === 0) {
@@ -453,7 +470,10 @@ describe(`Test BCMR support`, () => {
453
470
  });
454
471
  expect(noFollow.length).toBe(1);
455
472
  expect(noFollow[0].txHash).toBe(response2.txId!);
456
- expect(noFollow[0].uri).toBe(
473
+ expect(noFollow[0].uris[0]).toBe(
474
+ "mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json"
475
+ );
476
+ expect(noFollow[0].httpsUrl).toBe(
457
477
  "https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json"
458
478
  );
459
479
 
@@ -465,12 +485,18 @@ describe(`Test BCMR support`, () => {
465
485
  expect(follow.length).toBe(2);
466
486
 
467
487
  expect(follow[0].txHash).toBe(response2.txId!);
468
- expect(follow[0].uri).toBe(
488
+ expect(follow[0].uris[0]).toBe(
489
+ "mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json"
490
+ );
491
+ expect(follow[0].httpsUrl).toBe(
469
492
  "https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json"
470
493
  );
471
494
 
472
495
  expect(follow[1].txHash).toBe(response3.txId!);
473
- expect(follow[1].uri).toBe(
496
+ expect(follow[1].uris[0]).toBe(
497
+ "mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v3.json"
498
+ );
499
+ expect(follow[1].httpsUrl).toBe(
474
500
  "https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v3.json"
475
501
  );
476
502
  }
@@ -610,22 +636,34 @@ describe(`Test BCMR support`, () => {
610
636
  expect(chain.length).toBe(4);
611
637
 
612
638
  expect(chain[0].txHash).toBe(response.txId!);
613
- expect(chain[0].uri).toBe(
639
+ expect(chain[0].uris[0]).toBe(
640
+ "mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v1.json"
641
+ );
642
+ expect(chain[0].httpsUrl).toBe(
614
643
  "https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v1.json"
615
644
  );
616
645
 
617
646
  expect(chain[1].txHash).toBe(response2.txId!);
618
- expect(chain[1].uri).toBe(
647
+ expect(chain[1].uris[0]).toBe(
648
+ "mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json"
649
+ );
650
+ expect(chain[1].httpsUrl).toBe(
619
651
  "https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v2.json"
620
652
  );
621
653
 
622
654
  expect(chain[2].txHash).toBe(response3.txId!);
623
- expect(chain[2].uri).toBe(
655
+ expect(chain[2].uris[0]).toBe(
656
+ "mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v3.json"
657
+ );
658
+ expect(chain[2].httpsUrl).toBe(
624
659
  "https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v3.json"
625
660
  );
626
661
 
627
662
  expect(chain[3].txHash).toBe(response4.txId!);
628
- expect(chain[3].uri).toBe(
663
+ expect(chain[3].uris[0]).toBe(
664
+ "mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v4.json"
665
+ );
666
+ expect(chain[3].httpsUrl).toBe(
629
667
  "https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry_v4.json"
630
668
  );
631
669
 
@@ -679,7 +717,10 @@ describe(`Test BCMR support`, () => {
679
717
  expect(chain[0].contentHash).toBe(
680
718
  "516d62577247354173703569476d557751486f67534a47525832367a75526e754c575079745a66694c3735735a76"
681
719
  );
682
- expect(chain[0].uri).toBe(
720
+ expect(chain[0].uris[0]).toBe(
721
+ "ipfs://QmbWrG5Asp5iGmUwQHogSJGRX26zuRnuLWPytZfiL75sZv"
722
+ );
723
+ expect(chain[0].httpsUrl).toBe(
683
724
  "https://dweb.link/ipfs/QmbWrG5Asp5iGmUwQHogSJGRX26zuRnuLWPytZfiL75sZv"
684
725
  );
685
726
  expect(chain[0].txHash).toBe(genesisResponse.txId);
@@ -715,7 +756,10 @@ describe(`Test BCMR support`, () => {
715
756
  });
716
757
  expect(chain.length).toBe(1);
717
758
  expect(chain[0].contentHash).toBe(binToHex(contentHashBin));
718
- expect(chain[0].uri).toBe(
759
+ expect(chain[0].uris[0]).toBe(
760
+ "mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
761
+ );
762
+ expect(chain[0].httpsUrl).toBe(
719
763
  "https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
720
764
  );
721
765
  expect(chain[0].txHash).toBe(response.txId);
@@ -749,13 +793,19 @@ describe(`Test BCMR support`, () => {
749
793
  });
750
794
  expect(gappedChain.length).toBe(2);
751
795
  expect(gappedChain[0].contentHash).toBe(binToHex(contentHashBin));
752
- expect(gappedChain[0].uri).toBe(
796
+ expect(gappedChain[0].uris[0]).toBe(
797
+ "mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
798
+ );
799
+ expect(gappedChain[0].httpsUrl).toBe(
753
800
  "https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
754
801
  );
755
802
  expect(gappedChain[0].txHash).toBe(response.txId);
756
803
 
757
804
  expect(gappedChain[1].contentHash).toBe(binToHex(contentHashBin));
758
- expect(gappedChain[1].uri).toBe(
805
+ expect(gappedChain[1].uris[0]).toBe(
806
+ "mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
807
+ );
808
+ expect(gappedChain[1].httpsUrl).toBe(
759
809
  "https://mainnet.cash/.well-known/bitcoin-cash-metadata-registry.json"
760
810
  );
761
811
  expect(gappedChain[1].txHash).toBe(chainHeadResponse.txId);
@@ -14,11 +14,13 @@ import ElectrumNetworkProvider from "../network/ElectrumNetworkProvider.js";
14
14
  import { ElectrumRawTransaction } from "../network/interface.js";
15
15
  import { IdentitySnapshot, Registry } from "./bcmr-v2.schema.js";
16
16
  import { initProvider } from "../network/Connection.js";
17
+ import { OpReturnData } from "./model.js";
17
18
 
18
19
  export interface AuthChainElement {
19
20
  txHash: string;
20
21
  contentHash: string;
21
- uri: string;
22
+ uris: string[];
23
+ httpsUrl: string;
22
24
  }
23
25
 
24
26
  export type AuthChain = AuthChainElement[];
@@ -236,58 +238,62 @@ export class BCMR {
236
238
  return {
237
239
  txHash: hash,
238
240
  contentHash: "",
239
- uri: "",
241
+ uris: [],
242
+ httpsUrl: "",
240
243
  };
241
244
  }
242
245
 
243
246
  const opReturnHex = opReturns[0];
244
- const opReturn = hexToBin(opReturnHex);
245
- const chunks: Uint8Array[] = [];
246
- let position = 1;
247
-
248
- // handle direct push, OP_PUSHDATA1, OP_PUSHDATA2;
249
- // OP_PUSHDATA4 is not supported in OP_RETURNs by consensus
250
- while (opReturn[position]) {
251
- let length = 0;
252
- if (opReturn[position] === 0x4c) {
253
- length = opReturn[position + 1];
254
- position += 2;
255
- } else if (opReturn[position] === 0x4d) {
256
- length = binToNumberUint16LE(
257
- opReturn.slice(position + 1, position + 3)
258
- );
259
- position += 3;
260
- } else {
261
- length = opReturn[position];
262
- position += 1;
263
- }
264
-
265
- chunks.push(opReturn.slice(position, position + length));
266
- position += length;
267
- }
268
-
269
- if (chunks.length < 2 || chunks.length > 3) {
247
+ const chunks = OpReturnData.parseBinary(hexToBin(opReturnHex));
248
+ if (chunks.length < 2) {
270
249
  throw new Error(`Malformed BCMR output: ${opReturnHex}`);
271
250
  }
272
251
 
273
252
  const result: AuthChainElement = {
274
253
  txHash: hash,
275
254
  contentHash: "",
276
- uri: "",
255
+ uris: [],
256
+ httpsUrl: "",
277
257
  };
278
258
 
279
259
  if (chunks.length === 2) {
280
260
  // IPFS Publication Output
281
261
  result.contentHash = binToHex(chunks[1]);
282
262
  const ipfsCid = binToUtf8(chunks[1]);
283
- result.uri = `https://dweb.link/ipfs/${ipfsCid}`;
263
+ result.uris = [`ipfs://${ipfsCid}`];
264
+ result.httpsUrl = `https://dweb.link/ipfs/${ipfsCid}`;
284
265
  } else {
285
- // HTTPS Publication Output
266
+ // URI Publication Output
286
267
  // content hash is in OP_SHA256 byte order per spec
287
268
  result.contentHash = binToHex(chunks[1].slice());
288
- result.uri = binToUtf8(chunks[2]);
289
- if (result.uri.indexOf("https://") < 0) {
290
- result.uri = `https://${result.uri}`;
269
+
270
+ const uris = chunks.slice(2);
271
+
272
+ for (const uri of uris) {
273
+ const uriString = binToUtf8(uri);
274
+ result.uris.push(uriString);
275
+
276
+ if (result.httpsUrl) {
277
+ continue;
278
+ }
279
+
280
+ if (uriString.indexOf("https://") === 0) {
281
+ result.httpsUrl = uriString;
282
+ } else if (uriString.indexOf("https://") === -1) {
283
+ result.httpsUrl = uriString;
284
+
285
+ // case for domain name specifier, like example.com
286
+ if (uriString.indexOf("/") === -1) {
287
+ result.httpsUrl = `${result.httpsUrl}/.well-known/bitcoin-cash-metadata-registry.json`
288
+ }
289
+
290
+ result.httpsUrl = `https://${result.httpsUrl}`;
291
+ } else if (uriString.indexOf("ipfs://") === 0 ) {
292
+ const ipfsCid = uriString.replace("ipfs://", "");
293
+ result.httpsUrl = `https://dweb.link/ipfs/${ipfsCid}`
294
+ } else {
295
+ throw new Error(`Unsupported uri type: ${uriString}`)
296
+ }
291
297
  }
292
298
  }
293
299
  return result;
@@ -317,7 +323,7 @@ export class BCMR {
317
323
  if (options.resolveBase) {
318
324
  // check for accelerated path if "authchain" extension is in registry
319
325
  const registry: Registry = await this.fetchMetadataRegistry(
320
- element.uri,
326
+ element.httpsUrl,
321
327
  element.contentHash
322
328
  );
323
329
  if (
@@ -379,13 +385,13 @@ export class BCMR {
379
385
 
380
386
  // combine the authchain element with the rest obtained
381
387
  return [...chainBase, element, ...chainHead].filter(
382
- (val) => val.uri.length
388
+ (val) => val.httpsUrl.length
383
389
  );
384
390
  }
385
391
  }
386
392
 
387
393
  // return the last chain element (or the only found in an edge case)
388
- return [...chainBase, element].filter((val) => val.uri.length);
394
+ return [...chainBase, element].filter((val) => val.httpsUrl.length);
389
395
  }
390
396
 
391
397
  /**
@@ -420,7 +426,7 @@ export class BCMR {
420
426
  }
421
427
 
422
428
  const registry = await this.fetchMetadataRegistry(
423
- authChain.reverse()[0].uri
429
+ authChain.reverse()[0].httpsUrl
424
430
  );
425
431
 
426
432
  this.addMetadataRegistry(registry);