@bsv/dpp-core 0.3.0-beta.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 (86) hide show
  1. package/LICENSE +58 -0
  2. package/README.md +95 -0
  3. package/dist/acceptance.d.ts +100 -0
  4. package/dist/acceptance.d.ts.map +1 -0
  5. package/dist/acceptance.js +202 -0
  6. package/dist/acceptance.js.map +1 -0
  7. package/dist/anchor.d.ts +66 -0
  8. package/dist/anchor.d.ts.map +1 -0
  9. package/dist/anchor.js +145 -0
  10. package/dist/anchor.js.map +1 -0
  11. package/dist/attestation.d.ts +40 -0
  12. package/dist/attestation.d.ts.map +1 -0
  13. package/dist/attestation.js +111 -0
  14. package/dist/attestation.js.map +1 -0
  15. package/dist/blob.d.ts +11 -0
  16. package/dist/blob.d.ts.map +1 -0
  17. package/dist/blob.js +16 -0
  18. package/dist/blob.js.map +1 -0
  19. package/dist/canonical.d.ts +32 -0
  20. package/dist/canonical.d.ts.map +1 -0
  21. package/dist/canonical.js +54 -0
  22. package/dist/canonical.js.map +1 -0
  23. package/dist/canonicalJson.d.ts +22 -0
  24. package/dist/canonicalJson.d.ts.map +1 -0
  25. package/dist/canonicalJson.js +60 -0
  26. package/dist/canonicalJson.js.map +1 -0
  27. package/dist/codec.d.ts +45 -0
  28. package/dist/codec.d.ts.map +1 -0
  29. package/dist/codec.js +459 -0
  30. package/dist/codec.js.map +1 -0
  31. package/dist/constants.d.ts +150 -0
  32. package/dist/constants.d.ts.map +1 -0
  33. package/dist/constants.js +179 -0
  34. package/dist/constants.js.map +1 -0
  35. package/dist/did.d.ts +37 -0
  36. package/dist/did.d.ts.map +1 -0
  37. package/dist/did.js +129 -0
  38. package/dist/did.js.map +1 -0
  39. package/dist/evidence.d.ts +224 -0
  40. package/dist/evidence.d.ts.map +1 -0
  41. package/dist/evidence.js +716 -0
  42. package/dist/evidence.js.map +1 -0
  43. package/dist/evidencePackage.d.ts +114 -0
  44. package/dist/evidencePackage.d.ts.map +1 -0
  45. package/dist/evidencePackage.js +164 -0
  46. package/dist/evidencePackage.js.map +1 -0
  47. package/dist/index.d.ts +31 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +31 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/owner.d.ts +135 -0
  52. package/dist/owner.d.ts.map +1 -0
  53. package/dist/owner.js +181 -0
  54. package/dist/owner.js.map +1 -0
  55. package/dist/publisherPolicy.d.ts +70 -0
  56. package/dist/publisherPolicy.d.ts.map +1 -0
  57. package/dist/publisherPolicy.js +186 -0
  58. package/dist/publisherPolicy.js.map +1 -0
  59. package/dist/signatures.d.ts +84 -0
  60. package/dist/signatures.d.ts.map +1 -0
  61. package/dist/signatures.js +137 -0
  62. package/dist/signatures.js.map +1 -0
  63. package/dist/transition.d.ts +29 -0
  64. package/dist/transition.d.ts.map +1 -0
  65. package/dist/transition.js +117 -0
  66. package/dist/transition.js.map +1 -0
  67. package/dist/types.d.ts +135 -0
  68. package/dist/types.d.ts.map +1 -0
  69. package/dist/types.js +2 -0
  70. package/dist/types.js.map +1 -0
  71. package/dist/verifyChain.d.ts +152 -0
  72. package/dist/verifyChain.d.ts.map +1 -0
  73. package/dist/verifyChain.js +314 -0
  74. package/dist/verifyChain.js.map +1 -0
  75. package/package.json +46 -0
  76. package/schemas/capabilities.schema.json +129 -0
  77. package/schemas/epcis-import.schema.json +212 -0
  78. package/schemas/evidence-export.schema.json +67 -0
  79. package/schemas/evidence-package.schema.json +307 -0
  80. package/schemas/native-evidence-extension.schema.json +66 -0
  81. package/schemas/paginated-history.schema.json +32 -0
  82. package/schemas/passport-projection.schema.json +151 -0
  83. package/schemas/passport-source.schema.json +370 -0
  84. package/schemas/profile-evidence.schema.json +706 -0
  85. package/schemas/publisher-policy.schema.json +173 -0
  86. package/schemas/verification-report.schema.json +397 -0
package/dist/anchor.js ADDED
@@ -0,0 +1,145 @@
1
+ import { CachedKeyDeriver, LockingScript, PublicKey, Signature, Utils } from '@bsv/sdk';
2
+ /**
3
+ * The generic complete-representation anchor, `bsv-attestation-anchor-v1`
4
+ * (`spec/rules.md` §5 and §6). It lives in the core rather than in the overlay
5
+ * package because a reader in a browser verifies anchors without a database or
6
+ * an overlay engine, and `verifyPassportEvidence` (evidence.ts) reports the
7
+ * anchor rail beside the token rail from one package. `@bsv/dpp-overlay-topics`
8
+ * re-exports every name below unchanged.
9
+ */
10
+ export const ATTESTATION_ANCHOR_PREFIX = 'bsv-attestation-anchor-v1';
11
+ export const ATTESTATION_ANCHOR_PROTOCOL = [1, 'bsv attestation anchor v1'];
12
+ export const ATTESTATION_ANCHOR_BASKET = 'attestation-anchor';
13
+ export const ATTESTATION_ANCHOR_FIELD_COUNT = 9;
14
+ const anyone = new CachedKeyDeriver('anyone');
15
+ const bounds = [64, 256, 512, 512, 128, 128, 128, 66];
16
+ function decodeText(bytes, maximum) {
17
+ const value = Utils.toUTF8(bytes);
18
+ if (!value || bytes.length > maximum || /[\u0000-\u001f\u007f-\u009f]/u.test(value) || Utils.toHex(Utils.toArray(value, 'utf8')) !== Utils.toHex(bytes)) {
19
+ throw new Error('anchor field is not bounded printable UTF-8');
20
+ }
21
+ return value;
22
+ }
23
+ function compressedKey(value) {
24
+ if (!/^0[23][0-9a-f]{64}$/.test(value))
25
+ throw new Error('expected a canonical compressed public key');
26
+ const key = PublicKey.fromString(value);
27
+ if (key.toString() !== value)
28
+ throw new Error('non-canonical public key');
29
+ return key;
30
+ }
31
+ export function expectedAttestationLockingKey(anchoredBy, attestationId) {
32
+ compressedKey(anchoredBy);
33
+ decodeText(Utils.toArray(attestationId, 'utf8'), 256);
34
+ return anyone.derivePublicKey(ATTESTATION_ANCHOR_PROTOCOL, attestationId, anchoredBy);
35
+ }
36
+ export function attestationAnchorFields(anchor) {
37
+ const values = [anchor.digest, anchor.attestationId, anchor.issuer, anchor.subject, anchor.attestationType, anchor.representation, anchor.mediaType, anchor.anchoredBy];
38
+ const fields = values.map((value, index) => {
39
+ if (typeof value !== 'string')
40
+ throw new Error('anchor metadata must be text');
41
+ const bytes = Utils.toArray(value, 'utf8');
42
+ if (decodeText(bytes, bounds[index]) !== value)
43
+ throw new Error('invalid UTF-8 metadata');
44
+ return bytes;
45
+ });
46
+ if (!/^[0-9a-f]{64}$/.test(anchor.digest))
47
+ throw new Error('invalid SHA-256 digest');
48
+ compressedKey(anchor.anchoredBy);
49
+ return [Utils.toArray(ATTESTATION_ANCHOR_PREFIX, 'utf8'), ...fields];
50
+ }
51
+ export function attestationAnchorSigningPreimage(fields) {
52
+ if (fields.length !== ATTESTATION_ANCHOR_FIELD_COUNT)
53
+ throw new Error('expected nine signed anchor fields');
54
+ const writer = new Utils.Writer();
55
+ for (const field of fields) {
56
+ writer.writeVarIntNum(field.length);
57
+ writer.write(field);
58
+ }
59
+ return writer.toArray();
60
+ }
61
+ export async function buildAttestationAnchor(anchor, signer) {
62
+ const fields = attestationAnchorFields(anchor);
63
+ const { signature } = await signer.createSignature({
64
+ data: attestationAnchorSigningPreimage(fields), protocolID: ATTESTATION_ANCHOR_PROTOCOL,
65
+ keyID: anchor.attestationId, counterparty: 'anyone',
66
+ });
67
+ const script = new LockingScript();
68
+ script.writeBin(expectedAttestationLockingKey(anchor.anchoredBy, anchor.attestationId).encode(true));
69
+ script.writeOpCode(0xac);
70
+ for (const field of [...fields, signature])
71
+ script.writeBin(field);
72
+ for (let index = 0; index < 5; index++)
73
+ script.writeOpCode(0x6d);
74
+ if (decodeAttestationAnchor(script) === null)
75
+ throw new Error('signer does not match anchoredBy');
76
+ return script;
77
+ }
78
+ export function inspectAttestationAnchor(script) {
79
+ const result = { keyDerivationValid: null, signatureValid: null };
80
+ const chunks = script.chunks;
81
+ if (chunks.length !== 17 || chunks[0].data?.length !== 33 || chunks[1].op !== 0xac) {
82
+ result.failure = 'not a bsv-attestation-anchor-v1 layout: expected a 33-byte key, OP_CHECKSIG, ten fields and five OP_2DROP';
83
+ return result;
84
+ }
85
+ if (!chunks.slice(12).every(chunk => chunk.op === 0x6d && chunk.data === undefined)) {
86
+ result.failure = 'malformed drop tail: exactly five OP_2DROP must end the script';
87
+ return result;
88
+ }
89
+ let fields;
90
+ try {
91
+ fields = chunks.slice(2, 12).map(chunk => {
92
+ if (chunk.data && chunk.data.length > 0)
93
+ return chunk.data;
94
+ if (chunk.op >= 0x51 && chunk.op <= 0x60)
95
+ return [chunk.op - 0x50];
96
+ throw new Error('expected nonempty data push');
97
+ });
98
+ if (decodeText(fields[0], 64) !== ATTESTATION_ANCHOR_PREFIX)
99
+ throw new Error('prefix is not bsv-attestation-anchor-v1');
100
+ const [digest, attestationId, issuer, subject, attestationType, representation, mediaType, anchoredBy] = fields.slice(1, 9).map((field, index) => decodeText(field, bounds[index]));
101
+ const metadata = { digest, attestationId, issuer, subject, attestationType, representation, mediaType, anchoredBy };
102
+ attestationAnchorFields(metadata);
103
+ result.metadata = metadata;
104
+ }
105
+ catch (cause) {
106
+ result.failure = cause instanceof Error ? cause.message : 'anchor fields did not decode';
107
+ return result;
108
+ }
109
+ try {
110
+ result.lockingKey = compressedKey(Utils.toHex(chunks[0].data)).toString();
111
+ }
112
+ catch (cause) {
113
+ result.failure = `locking key: ${cause instanceof Error ? cause.message : 'invalid'}`;
114
+ }
115
+ const derived = expectedAttestationLockingKey(result.metadata.anchoredBy, result.metadata.attestationId);
116
+ result.derivedKey = derived.toString();
117
+ result.keyDerivationValid = result.lockingKey != null && result.lockingKey === result.derivedKey;
118
+ try {
119
+ const signature = Signature.fromDER(fields[9]);
120
+ if (Utils.toHex(signature.toDER()) !== Utils.toHex(fields[9]))
121
+ throw new Error('signature is not canonical DER');
122
+ result.signature = fields[9];
123
+ result.signatureValid = derived.verify(attestationAnchorSigningPreimage(fields.slice(0, 9)), signature);
124
+ }
125
+ catch (cause) {
126
+ result.signatureValid = false;
127
+ result.failure ??= cause instanceof Error ? cause.message : 'signature did not decode';
128
+ }
129
+ return result;
130
+ }
131
+ /** A total strict parser: invalid or legacy outputs return null. No credential proof is implied. */
132
+ export function decodeAttestationAnchor(script) {
133
+ try {
134
+ const inspection = inspectAttestationAnchor(script);
135
+ if (inspection.metadata == null || inspection.lockingKey == null || inspection.signature == null)
136
+ return null;
137
+ if (inspection.keyDerivationValid !== true || inspection.signatureValid !== true)
138
+ return null;
139
+ return { ...inspection.metadata, lockingKey: inspection.lockingKey, signature: inspection.signature };
140
+ }
141
+ catch {
142
+ return null;
143
+ }
144
+ }
145
+ //# sourceMappingURL=anchor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchor.js","sourceRoot":"","sources":["../src/anchor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { type WalletProtocol } from '@bsv/sdk';
2
+ import type { DataSigner } from './signatures.js';
3
+ export declare const LIFECYCLE_CLAIM_FORMAT: "dpp-lifecycle-v1";
4
+ export declare const LIFECYCLE_CLAIM_PROTOCOL: WalletProtocol;
5
+ export declare const LIFECYCLE_REPRESENTATION: "dpp-lifecycle-json-v1";
6
+ export declare const LIFECYCLE_MEDIA_TYPE: "application/json";
7
+ export type LifecycleEventType = 'Origin' | 'Transfer' | 'Transformation' | 'Disposition';
8
+ /** Native token-operation evidence. This is a different format from a VSC SEAL. */
9
+ export interface LifecycleClaim {
10
+ claimFormat: typeof LIFECYCLE_CLAIM_FORMAT;
11
+ passportId: string;
12
+ recordId: string;
13
+ eventType: LifecycleEventType;
14
+ timestamp: string;
15
+ issuer: string;
16
+ issuerKeyId: string;
17
+ issuerKeyDid?: string;
18
+ profile: string;
19
+ profile_version: number;
20
+ }
21
+ export interface SignedLifecycleClaim extends LifecycleClaim {
22
+ signature: string;
23
+ }
24
+ export interface LifecycleClaimVerification {
25
+ signature: 'verified' | 'invalid';
26
+ identityBinding: 'key-identified' | 'requires-resolution';
27
+ issuerKey?: string;
28
+ reason?: string;
29
+ }
30
+ export declare function assertSignedLifecycleClaim(value: unknown): asserts value is SignedLifecycleClaim;
31
+ export declare function signLifecycleClaim(claim: LifecycleClaim, signer: DataSigner): Promise<SignedLifecycleClaim>;
32
+ /** This checks native signature attribution; DID authority and status are separate checks. */
33
+ export declare function verifyLifecycleClaim(value: unknown, expected?: {
34
+ passportId?: string;
35
+ recordId?: string;
36
+ }): LifecycleClaimVerification;
37
+ /** The commitment includes the signature. Signing and anchoring have different preimages. */
38
+ export declare function lifecycleClaimBytes(claim: SignedLifecycleClaim): number[];
39
+ export declare function lifecycleClaimDigest(claim: SignedLifecycleClaim): string;
40
+ //# sourceMappingURL=attestation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attestation.d.ts","sourceRoot":"","sources":["../src/attestation.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4C,KAAK,cAAc,EAAE,MAAM,UAAU,CAAA;AAGxF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,eAAO,MAAM,sBAAsB,EAAG,kBAA2B,CAAA;AACjE,eAAO,MAAM,wBAAwB,EAAE,cAA0C,CAAA;AACjF,eAAO,MAAM,wBAAwB,EAAG,uBAAgC,CAAA;AACxE,eAAO,MAAM,oBAAoB,EAAG,kBAA2B,CAAA;AAC/D,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,UAAU,GAAG,gBAAgB,GAAG,aAAa,CAAA;AAEzF,mFAAmF;AACnF,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,OAAO,sBAAsB,CAAA;IAC1C,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,kBAAkB,CAAA;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,oBAAqB,SAAQ,cAAc;IAAG,SAAS,EAAE,MAAM,CAAA;CAAE;AAElF,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,UAAU,GAAG,SAAS,CAAA;IACjC,eAAe,EAAE,gBAAgB,GAAG,qBAAqB,CAAA;IACzD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAiDD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,oBAAoB,CAEhG;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,oBAAoB,CAAC,CASjH;AAED,8FAA8F;AAC9F,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,EACd,QAAQ,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GACxD,0BAA0B,CAgB5B;AAED,6FAA6F;AAC7F,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAGzE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAExE"}
@@ -0,0 +1,111 @@
1
+ import { CachedKeyDeriver, Hash, Signature, Utils } from '@bsv/sdk';
2
+ import { canonicalBytes } from './canonical.js';
3
+ import { identityKeyFromDidKey } from './did.js';
4
+ export const LIFECYCLE_CLAIM_FORMAT = 'dpp-lifecycle-v1';
5
+ export const LIFECYCLE_CLAIM_PROTOCOL = [1, 'dpp attestation v1'];
6
+ export const LIFECYCLE_REPRESENTATION = 'dpp-lifecycle-json-v1';
7
+ export const LIFECYCLE_MEDIA_TYPE = 'application/json';
8
+ const allowed = new Set([
9
+ 'claimFormat', 'passportId', 'recordId', 'eventType', 'timestamp', 'issuer',
10
+ 'issuerKeyId', 'issuerKeyDid', 'profile', 'profile_version', 'signature',
11
+ ]);
12
+ const types = new Set(['Origin', 'Transfer', 'Transformation', 'Disposition']);
13
+ const anyone = new CachedKeyDeriver('anyone');
14
+ function text(value, field, maximum) {
15
+ if (typeof value !== 'string' || value.length === 0 || /[\u0000-\u001f\u007f-\u009f]/u.test(value)) {
16
+ throw new Error(`${field} must be nonempty printable text`);
17
+ }
18
+ const bytes = Utils.toArray(value, 'utf8');
19
+ if (bytes.length > maximum || Utils.toUTF8(bytes) !== value)
20
+ throw new Error(`${field} exceeds its UTF-8 bound`);
21
+ }
22
+ function validate(value, signed) {
23
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
24
+ throw new Error('claim must be an object');
25
+ const claim = value;
26
+ for (const field of Object.keys(claim)) {
27
+ if (!allowed.has(field) || (!signed && field === 'signature'))
28
+ throw new Error(`unsupported claim field ${field}`);
29
+ }
30
+ if (claim.claimFormat !== LIFECYCLE_CLAIM_FORMAT)
31
+ throw new Error('unsupported claimFormat');
32
+ for (const field of ['passportId', 'recordId', 'issuer'])
33
+ text(claim[field], field, 512);
34
+ for (const field of ['issuerKeyId', 'profile'])
35
+ text(claim[field], field, 256);
36
+ if (typeof claim.eventType !== 'string' || !types.has(claim.eventType))
37
+ throw new Error('unsupported eventType');
38
+ if (!Number.isSafeInteger(claim.profile_version) || claim.profile_version < 1)
39
+ throw new Error('invalid profile_version');
40
+ text(claim.timestamp, 'timestamp', 64);
41
+ const date = /^(\d{4})-(\d{2})-(\d{2})T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.exec(claim.timestamp);
42
+ if (!date || !Number.isFinite(Date.parse(claim.timestamp)))
43
+ throw new Error('timestamp must be an ISO date-time with timezone');
44
+ const days = new Date(Date.UTC(Number(date[1]), Number(date[2]), 0)).getUTCDate();
45
+ if (Number(date[3]) < 1 || Number(date[3]) > days)
46
+ throw new Error('timestamp contains an invalid calendar day');
47
+ if (!/^did:[a-z0-9]+:(?:[A-Za-z0-9._:-]|%[0-9A-Fa-f]{2})*(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})$/.test(claim.issuer))
48
+ throw new Error('issuer must be a DID without a path, query or fragment');
49
+ if (claim.issuer.startsWith('did:key:')) {
50
+ if (claim.issuerKeyDid !== undefined)
51
+ throw new Error('did:key issuer must omit issuerKeyDid');
52
+ identityKeyFromDidKey(claim.issuer);
53
+ }
54
+ else {
55
+ text(claim.issuerKeyDid, 'issuerKeyDid', 256);
56
+ identityKeyFromDidKey(claim.issuerKeyDid);
57
+ }
58
+ if (signed) {
59
+ if (typeof claim.signature !== 'string' || !/^(?:[0-9a-f]{2}){8,72}$/.test(claim.signature))
60
+ throw new Error('signature must be lowercase DER hex');
61
+ const signature = Signature.fromDER(claim.signature, 'hex');
62
+ if (Utils.toHex(signature.toDER()) !== claim.signature)
63
+ throw new Error('signature must use canonical DER');
64
+ }
65
+ canonicalBytes(claim);
66
+ }
67
+ export function assertSignedLifecycleClaim(value) {
68
+ validate(value, true);
69
+ }
70
+ export async function signLifecycleClaim(claim, signer) {
71
+ validate(claim, false);
72
+ const { signature } = await signer.createSignature({
73
+ data: canonicalBytes(claim), protocolID: LIFECYCLE_CLAIM_PROTOCOL,
74
+ keyID: claim.passportId, counterparty: 'anyone',
75
+ });
76
+ const signed = { ...claim, signature: Utils.toHex(signature) };
77
+ if (verifyLifecycleClaim(signed).signature !== 'verified')
78
+ throw new Error('signer does not match the declared issuer key');
79
+ return signed;
80
+ }
81
+ /** This checks native signature attribution; DID authority and status are separate checks. */
82
+ export function verifyLifecycleClaim(value, expected = {}) {
83
+ try {
84
+ assertSignedLifecycleClaim(value);
85
+ if (expected.passportId !== undefined && value.passportId !== expected.passportId)
86
+ throw new Error('passportId does not match the requested subject');
87
+ if (expected.recordId !== undefined && value.recordId !== expected.recordId)
88
+ throw new Error('recordId does not match the requested state');
89
+ const issuerKey = identityKeyFromDidKey(value.issuerKeyDid ?? value.issuer);
90
+ const { signature, ...claim } = value;
91
+ const publicKey = anyone.derivePublicKey(LIFECYCLE_CLAIM_PROTOCOL, value.passportId, issuerKey);
92
+ if (!publicKey.verify(canonicalBytes(claim), Signature.fromDER(signature, 'hex')))
93
+ throw new Error('native claim signature does not verify');
94
+ return {
95
+ signature: 'verified', issuerKey,
96
+ identityBinding: value.issuer.startsWith('did:key:') ? 'key-identified' : 'requires-resolution',
97
+ };
98
+ }
99
+ catch (error) {
100
+ return { signature: 'invalid', identityBinding: 'requires-resolution', reason: error instanceof Error ? error.message : 'invalid claim' };
101
+ }
102
+ }
103
+ /** The commitment includes the signature. Signing and anchoring have different preimages. */
104
+ export function lifecycleClaimBytes(claim) {
105
+ assertSignedLifecycleClaim(claim);
106
+ return canonicalBytes(claim);
107
+ }
108
+ export function lifecycleClaimDigest(claim) {
109
+ return Utils.toHex(Hash.sha256(lifecycleClaimBytes(claim)));
110
+ }
111
+ //# sourceMappingURL=attestation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attestation.js","sourceRoot":"","sources":["../src/attestation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAuB,MAAM,UAAU,CAAA;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAA;AAGhD,MAAM,CAAC,MAAM,sBAAsB,GAAG,kBAA2B,CAAA;AACjE,MAAM,CAAC,MAAM,wBAAwB,GAAmB,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAA;AACjF,MAAM,CAAC,MAAM,wBAAwB,GAAG,uBAAgC,CAAA;AACxE,MAAM,CAAC,MAAM,oBAAoB,GAAG,kBAA2B,CAAA;AA0B/D,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC;IACtB,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ;IAC3E,aAAa,EAAE,cAAc,EAAE,SAAS,EAAE,iBAAiB,EAAE,WAAW;CACzE,CAAC,CAAA;AACF,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAE,aAAa,CAAC,CAAC,CAAA;AAC9E,MAAM,MAAM,GAAG,IAAI,gBAAgB,CAAC,QAAQ,CAAC,CAAA;AAE7C,SAAS,IAAI,CAAC,KAAc,EAAE,KAAa,EAAE,OAAe;IAC1D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,+BAA+B,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACnG,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,kCAAkC,CAAC,CAAA;IAC7D,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAC1C,IAAI,KAAK,CAAC,MAAM,GAAG,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,0BAA0B,CAAC,CAAA;AAClH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc,EAAE,MAAe;IAC/C,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;IACnH,MAAM,KAAK,GAAG,KAAgC,CAAA;IAC9C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACvC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,KAAK,KAAK,WAAW,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,KAAK,EAAE,CAAC,CAAA;IACpH,CAAC;IACD,IAAI,KAAK,CAAC,WAAW,KAAK,sBAAsB;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;IAC5F,KAAK,MAAM,KAAK,IAAI,CAAC,YAAY,EAAE,UAAU,EAAE,QAAQ,CAAU;QAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,CAAA;IACjG,KAAK,MAAM,KAAK,IAAI,CAAC,aAAa,EAAE,SAAS,CAAU;QAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,CAAA;IACvF,IAAI,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAA;IAChH,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,eAAe,CAAC,IAAK,KAAK,CAAC,eAA0B,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;IACrI,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,WAAW,EAAE,EAAE,CAAC,CAAA;IACtC,MAAM,IAAI,GAAG,4EAA4E,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IAC/G,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAA;IAC/H,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAA;IACjF,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;IAChH,IAAI,CAAC,wFAAwF,CAAC,IAAI,CAAC,KAAK,CAAC,MAAgB,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAA;IACrM,IAAK,KAAK,CAAC,MAAiB,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACpD,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAA;QAC9F,qBAAqB,CAAC,KAAK,CAAC,MAAgB,CAAC,CAAA;IAC/C,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,cAAc,EAAE,GAAG,CAAC,CAAA;QAC7C,qBAAqB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;IAC3C,CAAC;IACD,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;QACnJ,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;QAC3D,IAAI,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,EAAc,CAAC,KAAK,KAAK,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAA;IACzH,CAAC;IACD,cAAc,CAAC,KAAK,CAAC,CAAA;AACvB,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,KAAc;IACvD,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAqB,EAAE,MAAkB;IAChF,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;IACtB,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC;QACjD,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,wBAAwB;QACjE,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,YAAY,EAAE,QAAQ;KAChD,CAAC,CAAA;IACF,MAAM,MAAM,GAAG,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAA;IAC9D,IAAI,oBAAoB,CAAC,MAAM,CAAC,CAAC,SAAS,KAAK,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAA;IAC3H,OAAO,MAAM,CAAA;AACf,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,oBAAoB,CAClC,KAAc,EACd,WAAuD,EAAE;IAEzD,IAAI,CAAC;QACH,0BAA0B,CAAC,KAAK,CAAC,CAAA;QACjC,IAAI,QAAQ,CAAC,UAAU,KAAK,SAAS,IAAI,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC,UAAU;YAAE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAA;QACrJ,IAAI,QAAQ,CAAC,QAAQ,KAAK,SAAS,IAAI,KAAK,CAAC,QAAQ,KAAK,QAAQ,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAA;QAC3I,MAAM,SAAS,GAAG,qBAAqB,CAAC,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,MAAM,CAAC,CAAA;QAC3E,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,CAAA;QACrC,MAAM,SAAS,GAAG,MAAM,CAAC,eAAe,CAAC,wBAAwB,EAAE,KAAK,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;QAC/F,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAA;QAC5I,OAAO;YACL,SAAS,EAAE,UAAU,EAAE,SAAS;YAChC,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,qBAAqB;SAChG,CAAA;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAA;IAC3I,CAAC;AACH,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,mBAAmB,CAAC,KAA2B;IAC7D,0BAA0B,CAAC,KAAK,CAAC,CAAA;IACjC,OAAO,cAAc,CAAC,KAAK,CAAC,CAAA;AAC9B,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAA2B;IAC9D,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAC7D,CAAC"}
package/dist/blob.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Owner-tier blob binding (`spec/record-model.md` §7): the encrypted owner-tier
3
+ * blob lives off-chain. Its SHA-256 goes on-chain as payload_owner_hash
4
+ * (field 11). The fetched ciphertext MUST hash to field 11 before any
5
+ * decryption is attempted (§7); mismatch renders "Could not verify."
6
+ */
7
+ /** SHA-256 of the ciphertext blob, hex - the value carried in field 11. */
8
+ export declare function ownerBlobHash(ciphertext: number[]): string;
9
+ /** Check fetched ciphertext against the on-chain payload_owner_hash. */
10
+ export declare function verifyOwnerBlob(ciphertext: number[], payloadOwnerHash: string): boolean;
11
+ //# sourceMappingURL=blob.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob.d.ts","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,CAE1D;AAED,wEAAwE;AACxE,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAAE,EACpB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAET"}
package/dist/blob.js ADDED
@@ -0,0 +1,16 @@
1
+ import { Hash, Utils } from '@bsv/sdk';
2
+ /**
3
+ * Owner-tier blob binding (`spec/record-model.md` §7): the encrypted owner-tier
4
+ * blob lives off-chain. Its SHA-256 goes on-chain as payload_owner_hash
5
+ * (field 11). The fetched ciphertext MUST hash to field 11 before any
6
+ * decryption is attempted (§7); mismatch renders "Could not verify."
7
+ */
8
+ /** SHA-256 of the ciphertext blob, hex - the value carried in field 11. */
9
+ export function ownerBlobHash(ciphertext) {
10
+ return Utils.toHex(Hash.sha256(ciphertext));
11
+ }
12
+ /** Check fetched ciphertext against the on-chain payload_owner_hash. */
13
+ export function verifyOwnerBlob(ciphertext, payloadOwnerHash) {
14
+ return payloadOwnerHash !== '' && ownerBlobHash(ciphertext) === payloadOwnerHash;
15
+ }
16
+ //# sourceMappingURL=blob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob.js","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AAEtC;;;;;GAKG;AAEH,2EAA2E;AAC3E,MAAM,UAAU,aAAa,CAAC,UAAoB;IAChD,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAA;AAC7C,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,eAAe,CAC7B,UAAoB,EACpB,gBAAwB;IAExB,OAAO,gBAAgB,KAAK,EAAE,IAAI,aAAa,CAAC,UAAU,CAAC,KAAK,gBAAgB,CAAA;AAClF,CAAC"}
@@ -0,0 +1,32 @@
1
+ /** Thrown when a value cannot be canonicalised, rather than guessed at. */
2
+ export declare class CanonicalError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ /**
6
+ * The bytes both sides of an attestation sign and hash (`spec/rules.md` §4).
7
+ *
8
+ * **A deliberate subset of RFC 8785, not an implementation of it.** JCS is
9
+ * mostly a specification for the hard cases: number formatting, surrogate
10
+ * pairs, escape selection. This payload has none of them, and a partial JCS
11
+ * that silently mishandled one would be worse than no JCS at all, because two
12
+ * implementations would agree on every value they had tested and differ on the
13
+ * first one they had not. So this canonicalises what it can, refuses everything
14
+ * else, and the refusal is the feature: keys sorted by code point, no
15
+ * whitespace, string values JSON-escaped, integer values only when they are
16
+ * safe integers. Two honest encoders of one claim produce identical bytes or an
17
+ * error, never a second encoding.
18
+ *
19
+ * An absent optional property and a present-but-undefined one are different
20
+ * things here on purpose: `undefined` is refused, not skipped, so a claim built
21
+ * with a property present and empty is a refusal rather than a different
22
+ * digest. Callers spread optional properties conditionally.
23
+ *
24
+ * The parameter is `object` rather than a claim type because more than one
25
+ * signed shape uses this rule, and one canonicaliser serving all of them is the
26
+ * point: a fork is a copy that drifts. `fixtures/anchor-v3.json` pins the output
27
+ * for one complete claim, and every implementing party asserts against it.
28
+ */
29
+ export declare function canonicalBytes(claim: object): number[];
30
+ /** The canonical string, for a fixture or an error message to show a human. */
31
+ export declare function canonicalString(claim: object): string;
32
+ //# sourceMappingURL=canonical.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical.d.ts","sourceRoot":"","sources":["../src/canonical.ts"],"names":[],"mappings":"AAGA,2EAA2E;AAC3E,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBtD;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD"}
@@ -0,0 +1,54 @@
1
+ import { Utils } from '@bsv/sdk';
2
+ import { compareCodePoints } from './canonicalJson.js';
3
+ /** Thrown when a value cannot be canonicalised, rather than guessed at. */
4
+ export class CanonicalError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = 'CanonicalError';
8
+ }
9
+ }
10
+ /**
11
+ * The bytes both sides of an attestation sign and hash (`spec/rules.md` §4).
12
+ *
13
+ * **A deliberate subset of RFC 8785, not an implementation of it.** JCS is
14
+ * mostly a specification for the hard cases: number formatting, surrogate
15
+ * pairs, escape selection. This payload has none of them, and a partial JCS
16
+ * that silently mishandled one would be worse than no JCS at all, because two
17
+ * implementations would agree on every value they had tested and differ on the
18
+ * first one they had not. So this canonicalises what it can, refuses everything
19
+ * else, and the refusal is the feature: keys sorted by code point, no
20
+ * whitespace, string values JSON-escaped, integer values only when they are
21
+ * safe integers. Two honest encoders of one claim produce identical bytes or an
22
+ * error, never a second encoding.
23
+ *
24
+ * An absent optional property and a present-but-undefined one are different
25
+ * things here on purpose: `undefined` is refused, not skipped, so a claim built
26
+ * with a property present and empty is a refusal rather than a different
27
+ * digest. Callers spread optional properties conditionally.
28
+ *
29
+ * The parameter is `object` rather than a claim type because more than one
30
+ * signed shape uses this rule, and one canonicaliser serving all of them is the
31
+ * point: a fork is a copy that drifts. `fixtures/anchor-v3.json` pins the output
32
+ * for one complete claim, and every implementing party asserts against it.
33
+ */
34
+ export function canonicalBytes(claim) {
35
+ const entries = Object.entries(claim).sort(([a], [b]) => compareCodePoints(a, b));
36
+ const parts = [];
37
+ for (const [key, value] of entries) {
38
+ if (typeof value === 'string') {
39
+ parts.push(`${JSON.stringify(key)}:${JSON.stringify(value)}`);
40
+ continue;
41
+ }
42
+ if (typeof value === 'number' && Number.isSafeInteger(value)) {
43
+ parts.push(`${JSON.stringify(key)}:${value}`);
44
+ continue;
45
+ }
46
+ throw new CanonicalError(`${key} is not canonicalisable: only strings and safe integers are, and this is ${typeof value}`);
47
+ }
48
+ return Utils.toArray(`{${parts.join(',')}}`, 'utf8');
49
+ }
50
+ /** The canonical string, for a fixture or an error message to show a human. */
51
+ export function canonicalString(claim) {
52
+ return Utils.toUTF8(canonicalBytes(claim));
53
+ }
54
+ //# sourceMappingURL=canonical.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical.js","sourceRoot":"","sources":["../src/canonical.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAEtD,2EAA2E;AAC3E,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAA;IAC9B,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACjF,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;QACnC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YAC7D,SAAQ;QACV,CAAC;QACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7D,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,KAAK,EAAE,CAAC,CAAA;YAC7C,SAAQ;QACV,CAAC;QACD,MAAM,IAAI,cAAc,CACtB,GAAG,GAAG,4EAA4E,OAAO,KAAK,EAAE,CACjG,CAAA;IACH,CAAC;IACD,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;AACtD,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,OAAO,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAA;AAC5C,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Canonical JSON for nested documents: the publisher policy and the evidence
3
+ * package manifest. Native claims keep the flat restricted form of
4
+ * canonical.ts; this one exists because those documents nest objects and
5
+ * arrays. Object keys are sorted recursively by code point, there is no
6
+ * whitespace, arrays keep their order, `undefined` members are omitted, and
7
+ * only strings, booleans and safe integers are values. Anything else has no
8
+ * canonical form and is refused by name, so two implementations that both
9
+ * accept a document hash the same bytes.
10
+ */
11
+ export declare class CanonicalJsonError extends Error {
12
+ constructor(path: string, detail: string);
13
+ }
14
+ /**
15
+ * Key order by Unicode code point, so two implementations agree on keys that
16
+ * mix astral characters with U+E000 to U+FFFF, where UTF-16 code unit order
17
+ * differs. Every key in the documents this serves is ASCII today; the rule is
18
+ * stated once so a future key does not turn into a divergence.
19
+ */
20
+ export declare function compareCodePoints(a: string, b: string): number;
21
+ export declare function canonicalJson(value: unknown, path?: string): string;
22
+ //# sourceMappingURL=canonicalJson.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonicalJson.d.ts","sourceRoot":"","sources":["../src/canonicalJson.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAIzC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAa9D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,SAAa,GAAG,MAAM,CAcvE"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Canonical JSON for nested documents: the publisher policy and the evidence
3
+ * package manifest. Native claims keep the flat restricted form of
4
+ * canonical.ts; this one exists because those documents nest objects and
5
+ * arrays. Object keys are sorted recursively by code point, there is no
6
+ * whitespace, arrays keep their order, `undefined` members are omitted, and
7
+ * only strings, booleans and safe integers are values. Anything else has no
8
+ * canonical form and is refused by name, so two implementations that both
9
+ * accept a document hash the same bytes.
10
+ */
11
+ export class CanonicalJsonError extends Error {
12
+ constructor(path, detail) {
13
+ super(`${path} is not canonicalisable: ${detail}`);
14
+ this.name = 'CanonicalJsonError';
15
+ }
16
+ }
17
+ /**
18
+ * Key order by Unicode code point, so two implementations agree on keys that
19
+ * mix astral characters with U+E000 to U+FFFF, where UTF-16 code unit order
20
+ * differs. Every key in the documents this serves is ASCII today; the rule is
21
+ * stated once so a future key does not turn into a divergence.
22
+ */
23
+ export function compareCodePoints(a, b) {
24
+ const left = a[Symbol.iterator]();
25
+ const right = b[Symbol.iterator]();
26
+ for (;;) {
27
+ const x = left.next();
28
+ const y = right.next();
29
+ if (x.done && y.done)
30
+ return 0;
31
+ if (x.done)
32
+ return -1;
33
+ if (y.done)
34
+ return 1;
35
+ const cx = x.value.codePointAt(0);
36
+ const cy = y.value.codePointAt(0);
37
+ if (cx !== cy)
38
+ return cx < cy ? -1 : 1;
39
+ }
40
+ }
41
+ export function canonicalJson(value, path = 'document') {
42
+ if (typeof value === 'string')
43
+ return JSON.stringify(value);
44
+ if (typeof value === 'boolean')
45
+ return value ? 'true' : 'false';
46
+ if (typeof value === 'number') {
47
+ if (!Number.isSafeInteger(value))
48
+ throw new CanonicalJsonError(path, 'only safe integers are canonical numbers');
49
+ return String(value);
50
+ }
51
+ if (Array.isArray(value))
52
+ return `[${value.map((v, i) => canonicalJson(v, `${path}[${i}]`)).join(',')}]`;
53
+ if (value != null && typeof value === 'object') {
54
+ const record = value;
55
+ const keys = Object.keys(record).filter((k) => record[k] !== undefined).sort(compareCodePoints);
56
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(record[k], `${path}.${k}`)}`).join(',')}}`;
57
+ }
58
+ throw new CanonicalJsonError(path, `only strings, booleans, safe integers, arrays and objects are, and this is ${value === null ? 'null' : typeof value}`);
59
+ }
60
+ //# sourceMappingURL=canonicalJson.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonicalJson.js","sourceRoot":"","sources":["../src/canonicalJson.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import { LockingScript, PublicKey } from '@bsv/sdk';
2
+ import type { DppState, DppStateData, DppStateDataV2, DppStateV1, DppStateV2, Outpoint } from './types.js';
3
+ /** Thrown when a script does not carry a well-formed DPP output of a version this reader decodes. */
4
+ export declare class DppFormatError extends Error {
5
+ constructor(message: string);
6
+ }
7
+ /** Whether signable content, of either version, is version 2. */
8
+ export declare function isV2Data(d: object): d is DppStateDataV2;
9
+ /** Fields 1–12 (the signable content) of a version 1 state as raw wire bytes, in standard order. */
10
+ export declare function dataFields(d: DppStateData): number[][];
11
+ /** An outpoint as the version 2 fields 12 and 13 carry it: 32 txid bytes in display order, then a big-endian uint32 index. */
12
+ export declare function outpointFieldBytes(outpoint: Outpoint | null): number[];
13
+ /** Fields 1–15 (the signable content) of a version 2 state as raw wire bytes, in standard order. */
14
+ export declare function dataFieldsV2(d: DppStateDataV2): number[][];
15
+ /** All fields of a state as raw wire bytes: 14 for version 1, 17 for version 2. */
16
+ export declare function stateToFields(s: DppState): number[][];
17
+ /**
18
+ * Build the DPP locking script: <33-byte key> OP_CHECKSIG <fields> drops.
19
+ * Custody-neutral (`spec/record-model.md` §2): the locking key may be treasury-derived or user-held.
20
+ * The drop tail is one OP_2DROP per pair of fields and one OP_DROP for an odd
21
+ * field left over: seven OP_2DROP for version 1, eight and an OP_DROP for version 2.
22
+ */
23
+ export declare function buildLockingScript(state: DppState, lockingPublicKey: PublicKey | string): LockingScript;
24
+ /** Validate and type raw version 1 fields per `spec/record-model.md` §2-§3. Throws DppFormatError. */
25
+ export declare function fieldsToState(fields: number[][]): DppStateV1;
26
+ /** Validate and type raw version 2 fields per `spec/record-model-v2.md` §2-§3. Throws DppFormatError. */
27
+ export declare function fieldsToStateV2(fields: number[][]): DppStateV2;
28
+ /**
29
+ * Parse a DPP output script of either version. Throws DppFormatError when the
30
+ * script is not a well-formed DPP output: the field count selects the version's
31
+ * rules (fourteen fields for version 1, seventeen for version 2), the version
32
+ * field must agree with the count, and any other count or version is refused by
33
+ * name so an older reader meeting a newer state fails clearly rather than
34
+ * guessing.
35
+ */
36
+ export declare function parseDppOutput(script: LockingScript): {
37
+ state: DppState;
38
+ lockingPublicKey: PublicKey;
39
+ };
40
+ /** Non-throwing variant of parseDppOutput, for scanning arbitrary outputs. */
41
+ export declare function tryParseDppOutput(script: LockingScript): {
42
+ state: DppState;
43
+ lockingPublicKey: PublicKey;
44
+ } | null;
45
+ //# sourceMappingURL=codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAM,SAAS,EAA2B,MAAM,UAAU,CAAA;AAgBhF,OAAO,KAAK,EAAoB,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAE5H,qGAAqG;AACrG,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAmCD,iEAAiE;AACjE,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,IAAI,cAAc,CAEvD;AAED,oGAAoG;AACpG,wBAAgB,UAAU,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,EAAE,EAAE,CAetD;AAED,8HAA8H;AAC9H,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,GAAG,MAAM,EAAE,CAStE;AAED,oGAAoG;AACpG,wBAAgB,YAAY,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,EAAE,EAAE,CAoB1D;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,EAAE,EAAE,CAGrD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,QAAQ,EACf,gBAAgB,EAAE,SAAS,GAAG,MAAM,GACnC,aAAa,CAmBf;AAsID,sGAAsG;AACtG,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,GAAG,UAAU,CA+D5D;AAED,yGAAyG;AACzG,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,GAAG,UAAU,CAuE9D;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG;IACrD,KAAK,EAAE,QAAQ,CAAA;IACf,gBAAgB,EAAE,SAAS,CAAA;CAC5B,CAyCA;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG;IACxD,KAAK,EAAE,QAAQ,CAAA;IACf,gBAAgB,EAAE,SAAS,CAAA;CAC5B,GAAG,IAAI,CAMP"}