@otskit/client 0.6.1 → 0.7.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.
- package/dist/browser.d.ts +6 -0
- package/dist/browser.js +68 -1
- package/dist/index.cjs +26 -8
- package/dist/index.js +26 -8
- package/package.json +1 -1
package/dist/browser.d.ts
CHANGED
|
@@ -31,6 +31,12 @@ declare class OpenTimestampsBrowserClient {
|
|
|
31
31
|
constructor(options?: BrowserClientOptions);
|
|
32
32
|
/** hash: 32-byte Uint8Array or 64-char hex. Returns the pending .ots as Uint8Array. */
|
|
33
33
|
stamp(hash: Uint8Array | string): Promise<Uint8Array>;
|
|
34
|
+
/**
|
|
35
|
+
* proof: a pending .ots as bytes. Queries the calendars it references (allowlisted) and returns
|
|
36
|
+
* the upgraded .ots. Throws UpgradeError if no calendar has confirmed the timestamp yet, which is
|
|
37
|
+
* the normal state until Bitcoin mines the commitment (typically an hour or more after stamping).
|
|
38
|
+
*/
|
|
39
|
+
upgrade(proof: Uint8Array): Promise<Uint8Array>;
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
export { BROWSER_CALENDARS, type BrowserClientOptions, OpenTimestampsBrowserClient, bytesToHex, hashBlob, hashBytes };
|
package/dist/browser.js
CHANGED
|
@@ -20,6 +20,8 @@ var StampError = class extends OpenTimestampsClientError {
|
|
|
20
20
|
this.failedSubmissions = failed;
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
|
+
var UpgradeError = class extends OpenTimestampsClientError {
|
|
24
|
+
};
|
|
23
25
|
var NetworkError = class extends OpenTimestampsClientError {
|
|
24
26
|
/** HTTP status code when the failure originates from an HTTP response. */
|
|
25
27
|
status;
|
|
@@ -498,7 +500,7 @@ function parseWhitelistPattern(raw) {
|
|
|
498
500
|
return void 0;
|
|
499
501
|
}
|
|
500
502
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return void 0;
|
|
501
|
-
const hostname = parsed.hostname.toLowerCase();
|
|
503
|
+
const hostname = parsed.hostname.toLowerCase().replaceAll("%2a", "*");
|
|
502
504
|
const wildcardSuffix = hostname.startsWith("*.") ? hostname.slice(2) : void 0;
|
|
503
505
|
if (hostname.includes("*") && wildcardSuffix === void 0) return void 0;
|
|
504
506
|
if (wildcardSuffix !== void 0 && (wildcardSuffix.length === 0 || wildcardSuffix.includes("*"))) return void 0;
|
|
@@ -653,6 +655,63 @@ async function orchestrateStamp(hash, calendars, networkLayer, validateCalendarU
|
|
|
653
655
|
return detached.serializeToBytes();
|
|
654
656
|
}
|
|
655
657
|
|
|
658
|
+
// src/core/upgrade-core.ts
|
|
659
|
+
import { DetachedTimestampFile as DetachedTimestampFile2 } from "@otskit/core";
|
|
660
|
+
function bytesEqual(a, b) {
|
|
661
|
+
if (a.length !== b.length) return false;
|
|
662
|
+
let diff = 0;
|
|
663
|
+
for (let i = 0; i < a.length; i++) {
|
|
664
|
+
diff |= a[i] ^ b[i];
|
|
665
|
+
}
|
|
666
|
+
return diff === 0;
|
|
667
|
+
}
|
|
668
|
+
async function upgradeProofBytes(incompleteProof, networkLayer, logger, signal) {
|
|
669
|
+
let detached;
|
|
670
|
+
try {
|
|
671
|
+
detached = DetachedTimestampFile2.deserialize(incompleteProof);
|
|
672
|
+
} catch (error) {
|
|
673
|
+
throw new ValidationError("Invalid .ots proof format", {
|
|
674
|
+
/* v8 ignore next */
|
|
675
|
+
...error instanceof Error ? { cause: error } : {}
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
if (detached.timestamp.isTimestampComplete()) {
|
|
679
|
+
logger?.info("Proof already complete; nothing to upgrade");
|
|
680
|
+
return incompleteProof;
|
|
681
|
+
}
|
|
682
|
+
const before = detached.serializeToBytes();
|
|
683
|
+
for (const subStamp of detached.timestamp.directlyVerified()) {
|
|
684
|
+
if (subStamp.isTimestampComplete()) continue;
|
|
685
|
+
for (const att of subStamp.attestations) {
|
|
686
|
+
if (att.kind !== "pending") continue;
|
|
687
|
+
if (!DEFAULT_CALENDAR_WHITELIST.contains(att.uri)) {
|
|
688
|
+
logger?.warn(`Ignoring attestation from non-whitelisted calendar ${att.uri}`);
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
try {
|
|
692
|
+
const upgraded = await new CalendarClient(att.uri, networkLayer, logger).getTimestamp(
|
|
693
|
+
subStamp.getDigest(),
|
|
694
|
+
signal
|
|
695
|
+
);
|
|
696
|
+
subStamp.merge(upgraded);
|
|
697
|
+
} catch (err) {
|
|
698
|
+
if (err instanceof CommitmentNotFoundError) {
|
|
699
|
+
logger?.debug(`Calendar ${att.uri} has not confirmed yet`);
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
logger?.warn(
|
|
703
|
+
`Failed to query ${att.uri}: ${err instanceof Error ? err.message : String(err)}`
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
const after = detached.serializeToBytes();
|
|
709
|
+
if (bytesEqual(before, after)) {
|
|
710
|
+
throw new UpgradeError("No calendar has confirmed the timestamp yet (Bitcoin not yet mined)");
|
|
711
|
+
}
|
|
712
|
+
return after;
|
|
713
|
+
}
|
|
714
|
+
|
|
656
715
|
// src/security/ssrf-web.ts
|
|
657
716
|
async function assertSafeCalendarUrlStructural(url) {
|
|
658
717
|
let parsed;
|
|
@@ -732,6 +791,14 @@ var OpenTimestampsBrowserClient = class {
|
|
|
732
791
|
this.minSubs
|
|
733
792
|
);
|
|
734
793
|
}
|
|
794
|
+
/**
|
|
795
|
+
* proof: a pending .ots as bytes. Queries the calendars it references (allowlisted) and returns
|
|
796
|
+
* the upgraded .ots. Throws UpgradeError if no calendar has confirmed the timestamp yet, which is
|
|
797
|
+
* the normal state until Bitcoin mines the commitment (typically an hour or more after stamping).
|
|
798
|
+
*/
|
|
799
|
+
upgrade(proof) {
|
|
800
|
+
return upgradeProofBytes(proof, this.layer, this.logger);
|
|
801
|
+
}
|
|
735
802
|
};
|
|
736
803
|
export {
|
|
737
804
|
BROWSER_CALENDARS,
|
package/dist/index.cjs
CHANGED
|
@@ -583,7 +583,7 @@ function parseWhitelistPattern(raw) {
|
|
|
583
583
|
return void 0;
|
|
584
584
|
}
|
|
585
585
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return void 0;
|
|
586
|
-
const hostname = parsed.hostname.toLowerCase();
|
|
586
|
+
const hostname = parsed.hostname.toLowerCase().replaceAll("%2a", "*");
|
|
587
587
|
const wildcardSuffix = hostname.startsWith("*.") ? hostname.slice(2) : void 0;
|
|
588
588
|
if (hostname.includes("*") && wildcardSuffix === void 0) return void 0;
|
|
589
589
|
if (wildcardSuffix !== void 0 && (wildcardSuffix.length === 0 || wildcardSuffix.includes("*"))) return void 0;
|
|
@@ -738,13 +738,20 @@ async function orchestrateStamp(hash, calendars, networkLayer, validateCalendarU
|
|
|
738
738
|
return detached.serializeToBytes();
|
|
739
739
|
}
|
|
740
740
|
|
|
741
|
-
// src/core/upgrade.ts
|
|
741
|
+
// src/core/upgrade-core.ts
|
|
742
742
|
var import_core4 = require("@otskit/core");
|
|
743
|
-
|
|
744
|
-
|
|
743
|
+
function bytesEqual(a, b) {
|
|
744
|
+
if (a.length !== b.length) return false;
|
|
745
|
+
let diff = 0;
|
|
746
|
+
for (let i = 0; i < a.length; i++) {
|
|
747
|
+
diff |= a[i] ^ b[i];
|
|
748
|
+
}
|
|
749
|
+
return diff === 0;
|
|
750
|
+
}
|
|
751
|
+
async function upgradeProofBytes(incompleteProof, networkLayer, logger, signal) {
|
|
745
752
|
let detached;
|
|
746
753
|
try {
|
|
747
|
-
detached = import_core4.DetachedTimestampFile.deserialize(
|
|
754
|
+
detached = import_core4.DetachedTimestampFile.deserialize(incompleteProof);
|
|
748
755
|
} catch (error) {
|
|
749
756
|
throw new ValidationError("Invalid .ots proof format", {
|
|
750
757
|
/* v8 ignore next */
|
|
@@ -753,7 +760,7 @@ async function orchestrateUpgrade(incompleteProof, _calendars, networkLayer, log
|
|
|
753
760
|
}
|
|
754
761
|
if (detached.timestamp.isTimestampComplete()) {
|
|
755
762
|
logger?.info("Proof already complete; nothing to upgrade");
|
|
756
|
-
return
|
|
763
|
+
return incompleteProof;
|
|
757
764
|
}
|
|
758
765
|
const before = detached.serializeToBytes();
|
|
759
766
|
for (const subStamp of detached.timestamp.directlyVerified()) {
|
|
@@ -782,10 +789,21 @@ async function orchestrateUpgrade(incompleteProof, _calendars, networkLayer, log
|
|
|
782
789
|
}
|
|
783
790
|
}
|
|
784
791
|
const after = detached.serializeToBytes();
|
|
785
|
-
if (
|
|
792
|
+
if (bytesEqual(before, after)) {
|
|
786
793
|
throw new UpgradeError("No calendar has confirmed the timestamp yet (Bitcoin not yet mined)");
|
|
787
794
|
}
|
|
788
|
-
return
|
|
795
|
+
return after;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// src/core/upgrade.ts
|
|
799
|
+
async function orchestrateUpgrade(incompleteProof, _calendars, networkLayer, logger, signal) {
|
|
800
|
+
const upgraded = await upgradeProofBytes(
|
|
801
|
+
new Uint8Array(incompleteProof),
|
|
802
|
+
networkLayer,
|
|
803
|
+
logger,
|
|
804
|
+
signal
|
|
805
|
+
);
|
|
806
|
+
return Buffer.from(upgraded);
|
|
789
807
|
}
|
|
790
808
|
|
|
791
809
|
// src/core/verify.ts
|
package/dist/index.js
CHANGED
|
@@ -533,7 +533,7 @@ function parseWhitelistPattern(raw) {
|
|
|
533
533
|
return void 0;
|
|
534
534
|
}
|
|
535
535
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return void 0;
|
|
536
|
-
const hostname = parsed.hostname.toLowerCase();
|
|
536
|
+
const hostname = parsed.hostname.toLowerCase().replaceAll("%2a", "*");
|
|
537
537
|
const wildcardSuffix = hostname.startsWith("*.") ? hostname.slice(2) : void 0;
|
|
538
538
|
if (hostname.includes("*") && wildcardSuffix === void 0) return void 0;
|
|
539
539
|
if (wildcardSuffix !== void 0 && (wildcardSuffix.length === 0 || wildcardSuffix.includes("*"))) return void 0;
|
|
@@ -688,13 +688,20 @@ async function orchestrateStamp(hash, calendars, networkLayer, validateCalendarU
|
|
|
688
688
|
return detached.serializeToBytes();
|
|
689
689
|
}
|
|
690
690
|
|
|
691
|
-
// src/core/upgrade.ts
|
|
691
|
+
// src/core/upgrade-core.ts
|
|
692
692
|
import { DetachedTimestampFile as DetachedTimestampFile2 } from "@otskit/core";
|
|
693
|
-
|
|
694
|
-
|
|
693
|
+
function bytesEqual(a, b) {
|
|
694
|
+
if (a.length !== b.length) return false;
|
|
695
|
+
let diff = 0;
|
|
696
|
+
for (let i = 0; i < a.length; i++) {
|
|
697
|
+
diff |= a[i] ^ b[i];
|
|
698
|
+
}
|
|
699
|
+
return diff === 0;
|
|
700
|
+
}
|
|
701
|
+
async function upgradeProofBytes(incompleteProof, networkLayer, logger, signal) {
|
|
695
702
|
let detached;
|
|
696
703
|
try {
|
|
697
|
-
detached = DetachedTimestampFile2.deserialize(
|
|
704
|
+
detached = DetachedTimestampFile2.deserialize(incompleteProof);
|
|
698
705
|
} catch (error) {
|
|
699
706
|
throw new ValidationError("Invalid .ots proof format", {
|
|
700
707
|
/* v8 ignore next */
|
|
@@ -703,7 +710,7 @@ async function orchestrateUpgrade(incompleteProof, _calendars, networkLayer, log
|
|
|
703
710
|
}
|
|
704
711
|
if (detached.timestamp.isTimestampComplete()) {
|
|
705
712
|
logger?.info("Proof already complete; nothing to upgrade");
|
|
706
|
-
return
|
|
713
|
+
return incompleteProof;
|
|
707
714
|
}
|
|
708
715
|
const before = detached.serializeToBytes();
|
|
709
716
|
for (const subStamp of detached.timestamp.directlyVerified()) {
|
|
@@ -732,10 +739,21 @@ async function orchestrateUpgrade(incompleteProof, _calendars, networkLayer, log
|
|
|
732
739
|
}
|
|
733
740
|
}
|
|
734
741
|
const after = detached.serializeToBytes();
|
|
735
|
-
if (
|
|
742
|
+
if (bytesEqual(before, after)) {
|
|
736
743
|
throw new UpgradeError("No calendar has confirmed the timestamp yet (Bitcoin not yet mined)");
|
|
737
744
|
}
|
|
738
|
-
return
|
|
745
|
+
return after;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// src/core/upgrade.ts
|
|
749
|
+
async function orchestrateUpgrade(incompleteProof, _calendars, networkLayer, logger, signal) {
|
|
750
|
+
const upgraded = await upgradeProofBytes(
|
|
751
|
+
new Uint8Array(incompleteProof),
|
|
752
|
+
networkLayer,
|
|
753
|
+
logger,
|
|
754
|
+
signal
|
|
755
|
+
);
|
|
756
|
+
return Buffer.from(upgraded);
|
|
739
757
|
}
|
|
740
758
|
|
|
741
759
|
// src/core/verify.ts
|