@algovoi/substrate 0.2.0 → 0.3.0
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/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/transactional.d.ts +103 -0
- package/dist/transactional.d.ts.map +1 -0
- package/dist/transactional.js +161 -0
- package/dist/transactional.js.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +12 -0
- package/src/transactional.ts +241 -0
package/dist/index.d.ts
CHANGED
|
@@ -21,4 +21,5 @@ export { ActionRefError, type ActionRefInput, type ActionRefObject, actionRef, a
|
|
|
21
21
|
export { CompositeTrustQueryError, type EmitterRow, compositeTrustQueryHash, } from './composite-trust-query.js';
|
|
22
22
|
export { SCREEN_RESULTS, type ScreenResult, ComplianceReceiptError, type ComplianceReceipt, type BuildComplianceReceiptInput, buildComplianceReceipt, } from './compliance-receipt.js';
|
|
23
23
|
export { AuditChainError, type AuditChainRow, appendToChain, verifyAuditChain, } from './audit-chain.js';
|
|
24
|
+
export { TransactionalError, type TransitionPreimage, type TransitionInput, type TransitionRecord, type TransactionalChain, type TransactionalChainInput, transitionPreimage, transitionHash, buildTransactionalActionChain, } from './transactional.js';
|
|
24
25
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,SAAS,GACV,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,SAAS,EACT,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,wBAAwB,EACxB,KAAK,UAAU,EACf,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,cAAc,EACd,KAAK,YAAY,EACjB,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,2BAA2B,EAChC,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,EACf,KAAK,aAAa,EAClB,aAAa,EACb,gBAAgB,GACjB,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,SAAS,GACV,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,SAAS,EACT,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,wBAAwB,EACxB,KAAK,UAAU,EACf,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,cAAc,EACd,KAAK,YAAY,EACjB,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,2BAA2B,EAChC,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,EACf,KAAK,aAAa,EAClB,aAAa,EACb,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,kBAAkB,EAClB,cAAc,EACd,6BAA6B,GAC9B,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -21,4 +21,5 @@ export { ActionRefError, actionRef, actionRefObject, } from './action-ref.js';
|
|
|
21
21
|
export { CompositeTrustQueryError, compositeTrustQueryHash, } from './composite-trust-query.js';
|
|
22
22
|
export { SCREEN_RESULTS, ComplianceReceiptError, buildComplianceReceipt, } from './compliance-receipt.js';
|
|
23
23
|
export { AuditChainError, appendToChain, verifyAuditChain, } from './audit-chain.js';
|
|
24
|
+
export { TransactionalError, transitionPreimage, transitionHash, buildTransactionalActionChain, } from './transactional.js';
|
|
24
25
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,SAAS,GACV,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EAGd,SAAS,EACT,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,wBAAwB,EAExB,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,cAAc,EAEd,sBAAsB,EAGtB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,EAEf,aAAa,EACb,gBAAgB,GACjB,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,SAAS,GACV,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EAGd,SAAS,EACT,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,wBAAwB,EAExB,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,cAAc,EAEd,sBAAsB,EAGtB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,EAEf,aAAa,EACb,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,kBAAkB,EAMlB,kBAAkB,EAClB,cAAc,EACd,6BAA6B,GAC9B,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transactional action_ref lifecycle (non-normative substrate extension).
|
|
3
|
+
*
|
|
4
|
+
* For actions that traverse multiple state transitions
|
|
5
|
+
* (authorisation -> settlement -> refund; issuance -> execution ->
|
|
6
|
+
* revocation; admission -> review -> close), the action_ref four-field
|
|
7
|
+
* preimage is the STABLE identity anchor across the full lifecycle, and
|
|
8
|
+
* per-transition lifecycle metadata sits OUTSIDE the action_ref preimage.
|
|
9
|
+
*
|
|
10
|
+
* Invariants:
|
|
11
|
+
*
|
|
12
|
+
* 1. action_ref is byte-stable across every transition of the same action.
|
|
13
|
+
* 2. transition_hash is byte-stable for identical preimage tuples.
|
|
14
|
+
* 3. transition_hash differs per state for the same action_ref + timestamps.
|
|
15
|
+
* 4. action_ref and any transition_hash are byte-distinct.
|
|
16
|
+
* 5. All timestamp fields MUST be epoch-millisecond integers per
|
|
17
|
+
* Substrate Rule 2. RFC 3339 string forms are rejected.
|
|
18
|
+
*
|
|
19
|
+
* State value is a non-empty string with no closed enum at the
|
|
20
|
+
* canonicalisation layer, consistent with the scope-field treatment.
|
|
21
|
+
*/
|
|
22
|
+
export declare class TransactionalError extends Error {
|
|
23
|
+
constructor(message: string);
|
|
24
|
+
}
|
|
25
|
+
export interface TransitionPreimage {
|
|
26
|
+
action_ref: string;
|
|
27
|
+
state: string;
|
|
28
|
+
transition_timestamp_ms: number;
|
|
29
|
+
authority_verified_at_ms: number;
|
|
30
|
+
revocation_check_at_ms: number;
|
|
31
|
+
}
|
|
32
|
+
export interface TransitionInput {
|
|
33
|
+
state: string;
|
|
34
|
+
transition_timestamp_ms: number;
|
|
35
|
+
authority_verified_at_ms: number;
|
|
36
|
+
revocation_check_at_ms: number;
|
|
37
|
+
}
|
|
38
|
+
export interface TransitionRecord extends TransitionInput {
|
|
39
|
+
transition_hash: string;
|
|
40
|
+
}
|
|
41
|
+
export interface TransactionalChain {
|
|
42
|
+
action_ref: string;
|
|
43
|
+
transitions: TransitionRecord[];
|
|
44
|
+
}
|
|
45
|
+
export interface TransactionalChainInput {
|
|
46
|
+
agent_id: string;
|
|
47
|
+
action_type: string;
|
|
48
|
+
scope: string;
|
|
49
|
+
timestamp_ms: number;
|
|
50
|
+
transitions: TransitionInput[];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Return the validated preimage object for a single transition.
|
|
54
|
+
*
|
|
55
|
+
* The five-field shape is fixed at the substrate layer; the action_ref
|
|
56
|
+
* field cryptographically binds the transition to its action identity.
|
|
57
|
+
*/
|
|
58
|
+
export declare function transitionPreimage(input: {
|
|
59
|
+
action_ref: string;
|
|
60
|
+
state: string;
|
|
61
|
+
transition_timestamp_ms: number;
|
|
62
|
+
authority_verified_at_ms: number;
|
|
63
|
+
revocation_check_at_ms: number;
|
|
64
|
+
}): TransitionPreimage;
|
|
65
|
+
/**
|
|
66
|
+
* Return the lowercase hex SHA-256 transition_hash.
|
|
67
|
+
*
|
|
68
|
+
* transition_hash = SHA-256(JCS({action_ref, state,
|
|
69
|
+
* transition_timestamp_ms,
|
|
70
|
+
* authority_verified_at_ms,
|
|
71
|
+
* revocation_check_at_ms}))
|
|
72
|
+
*/
|
|
73
|
+
export declare function transitionHash(input: {
|
|
74
|
+
action_ref: string;
|
|
75
|
+
state: string;
|
|
76
|
+
transition_timestamp_ms: number;
|
|
77
|
+
authority_verified_at_ms: number;
|
|
78
|
+
revocation_check_at_ms: number;
|
|
79
|
+
}): string;
|
|
80
|
+
/**
|
|
81
|
+
* Build a transactional action chain.
|
|
82
|
+
*
|
|
83
|
+
* Returns the chain shape:
|
|
84
|
+
* {
|
|
85
|
+
* action_ref: "<lowercase 64-char hex>",
|
|
86
|
+
* transitions: [
|
|
87
|
+
* {
|
|
88
|
+
* state, transition_timestamp_ms,
|
|
89
|
+
* authority_verified_at_ms, revocation_check_at_ms,
|
|
90
|
+
* transition_hash
|
|
91
|
+
* },
|
|
92
|
+
* ...
|
|
93
|
+
* ]
|
|
94
|
+
* }
|
|
95
|
+
*
|
|
96
|
+
* The action_ref is computed once and is byte-stable across every
|
|
97
|
+
* transition. Each transition_hash is bound to the action_ref by
|
|
98
|
+
* including it in the transition preimage. Transitions are processed
|
|
99
|
+
* in the order supplied; the substrate verifies determinism, not
|
|
100
|
+
* lifecycle semantics.
|
|
101
|
+
*/
|
|
102
|
+
export declare function buildTransactionalActionChain(input: TransactionalChainInput): TransactionalChain;
|
|
103
|
+
//# sourceMappingURL=transactional.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactional.d.ts","sourceRoot":"","sources":["../src/transactional.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAOH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB,EAAE,MAAM,CAAC;IAChC,wBAAwB,EAAE,MAAM,CAAC;IACjC,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB,EAAE,MAAM,CAAC;IAChC,wBAAwB,EAAE,MAAM,CAAC;IACjC,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACvD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,gBAAgB,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,eAAe,EAAE,CAAC;CAChC;AAgDD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB,EAAE,MAAM,CAAC;IAChC,wBAAwB,EAAE,MAAM,CAAC;IACjC,sBAAsB,EAAE,MAAM,CAAC;CAChC,GAAG,kBAAkB,CAiBrB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB,EAAE,MAAM,CAAC;IAChC,wBAAwB,EAAE,MAAM,CAAC;IACjC,sBAAsB,EAAE,MAAM,CAAC;CAChC,GAAG,MAAM,CAGT;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,uBAAuB,GAC7B,kBAAkB,CAsDpB"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transactional action_ref lifecycle (non-normative substrate extension).
|
|
3
|
+
*
|
|
4
|
+
* For actions that traverse multiple state transitions
|
|
5
|
+
* (authorisation -> settlement -> refund; issuance -> execution ->
|
|
6
|
+
* revocation; admission -> review -> close), the action_ref four-field
|
|
7
|
+
* preimage is the STABLE identity anchor across the full lifecycle, and
|
|
8
|
+
* per-transition lifecycle metadata sits OUTSIDE the action_ref preimage.
|
|
9
|
+
*
|
|
10
|
+
* Invariants:
|
|
11
|
+
*
|
|
12
|
+
* 1. action_ref is byte-stable across every transition of the same action.
|
|
13
|
+
* 2. transition_hash is byte-stable for identical preimage tuples.
|
|
14
|
+
* 3. transition_hash differs per state for the same action_ref + timestamps.
|
|
15
|
+
* 4. action_ref and any transition_hash are byte-distinct.
|
|
16
|
+
* 5. All timestamp fields MUST be epoch-millisecond integers per
|
|
17
|
+
* Substrate Rule 2. RFC 3339 string forms are rejected.
|
|
18
|
+
*
|
|
19
|
+
* State value is a non-empty string with no closed enum at the
|
|
20
|
+
* canonicalisation layer, consistent with the scope-field treatment.
|
|
21
|
+
*/
|
|
22
|
+
import { actionRef } from './action-ref.js';
|
|
23
|
+
import { sha256Jcs } from './canonicalize.js';
|
|
24
|
+
const HEX64_RE = /^[0-9a-f]{64}$/;
|
|
25
|
+
export class TransactionalError extends Error {
|
|
26
|
+
constructor(message) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = 'TransactionalError';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function requireActionRefHex(value) {
|
|
32
|
+
if (typeof value !== 'string') {
|
|
33
|
+
throw new TransactionalError(`action_ref must be string, got ${typeof value}`);
|
|
34
|
+
}
|
|
35
|
+
if (!HEX64_RE.test(value)) {
|
|
36
|
+
throw new TransactionalError('action_ref must be a lowercase 64-character SHA-256 hex string');
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
function requireState(value) {
|
|
41
|
+
if (typeof value !== 'string') {
|
|
42
|
+
throw new TransactionalError(`state must be string, got ${typeof value}`);
|
|
43
|
+
}
|
|
44
|
+
if (value.length === 0) {
|
|
45
|
+
throw new TransactionalError('state must be a non-empty string');
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
function requireIntMs(field, value) {
|
|
50
|
+
if (typeof value !== 'number') {
|
|
51
|
+
throw new TransactionalError(`${field} must be epoch-millisecond integer (Substrate Rule 2), got ${typeof value}`);
|
|
52
|
+
}
|
|
53
|
+
if (!Number.isFinite(value)) {
|
|
54
|
+
throw new TransactionalError(`${field} must be a finite number, got ${value}`);
|
|
55
|
+
}
|
|
56
|
+
if (!Number.isInteger(value)) {
|
|
57
|
+
throw new TransactionalError(`${field} must be epoch-millisecond integer (Substrate Rule 2), got float ${value}`);
|
|
58
|
+
}
|
|
59
|
+
if (value < 0) {
|
|
60
|
+
throw new TransactionalError(`${field} must be non-negative, got ${value}`);
|
|
61
|
+
}
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Return the validated preimage object for a single transition.
|
|
66
|
+
*
|
|
67
|
+
* The five-field shape is fixed at the substrate layer; the action_ref
|
|
68
|
+
* field cryptographically binds the transition to its action identity.
|
|
69
|
+
*/
|
|
70
|
+
export function transitionPreimage(input) {
|
|
71
|
+
return {
|
|
72
|
+
action_ref: requireActionRefHex(input.action_ref),
|
|
73
|
+
state: requireState(input.state),
|
|
74
|
+
transition_timestamp_ms: requireIntMs('transition_timestamp_ms', input.transition_timestamp_ms),
|
|
75
|
+
authority_verified_at_ms: requireIntMs('authority_verified_at_ms', input.authority_verified_at_ms),
|
|
76
|
+
revocation_check_at_ms: requireIntMs('revocation_check_at_ms', input.revocation_check_at_ms),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Return the lowercase hex SHA-256 transition_hash.
|
|
81
|
+
*
|
|
82
|
+
* transition_hash = SHA-256(JCS({action_ref, state,
|
|
83
|
+
* transition_timestamp_ms,
|
|
84
|
+
* authority_verified_at_ms,
|
|
85
|
+
* revocation_check_at_ms}))
|
|
86
|
+
*/
|
|
87
|
+
export function transitionHash(input) {
|
|
88
|
+
const obj = transitionPreimage(input);
|
|
89
|
+
return sha256Jcs(obj);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Build a transactional action chain.
|
|
93
|
+
*
|
|
94
|
+
* Returns the chain shape:
|
|
95
|
+
* {
|
|
96
|
+
* action_ref: "<lowercase 64-char hex>",
|
|
97
|
+
* transitions: [
|
|
98
|
+
* {
|
|
99
|
+
* state, transition_timestamp_ms,
|
|
100
|
+
* authority_verified_at_ms, revocation_check_at_ms,
|
|
101
|
+
* transition_hash
|
|
102
|
+
* },
|
|
103
|
+
* ...
|
|
104
|
+
* ]
|
|
105
|
+
* }
|
|
106
|
+
*
|
|
107
|
+
* The action_ref is computed once and is byte-stable across every
|
|
108
|
+
* transition. Each transition_hash is bound to the action_ref by
|
|
109
|
+
* including it in the transition preimage. Transitions are processed
|
|
110
|
+
* in the order supplied; the substrate verifies determinism, not
|
|
111
|
+
* lifecycle semantics.
|
|
112
|
+
*/
|
|
113
|
+
export function buildTransactionalActionChain(input) {
|
|
114
|
+
if (!Array.isArray(input.transitions)) {
|
|
115
|
+
throw new TransactionalError('transitions must be an array');
|
|
116
|
+
}
|
|
117
|
+
if (input.transitions.length === 0) {
|
|
118
|
+
throw new TransactionalError('transitions array must be non-empty');
|
|
119
|
+
}
|
|
120
|
+
const ar = actionRef({
|
|
121
|
+
agent_id: input.agent_id,
|
|
122
|
+
action_type: input.action_type,
|
|
123
|
+
scope: input.scope,
|
|
124
|
+
timestamp_ms: input.timestamp_ms,
|
|
125
|
+
});
|
|
126
|
+
const records = input.transitions.map((t, i) => {
|
|
127
|
+
if (t === null || typeof t !== 'object') {
|
|
128
|
+
throw new TransactionalError(`transitions[${i}] must be object, got ${typeof t}`);
|
|
129
|
+
}
|
|
130
|
+
const required = [
|
|
131
|
+
'state',
|
|
132
|
+
'transition_timestamp_ms',
|
|
133
|
+
'authority_verified_at_ms',
|
|
134
|
+
'revocation_check_at_ms',
|
|
135
|
+
];
|
|
136
|
+
for (const k of required) {
|
|
137
|
+
if (!(k in t)) {
|
|
138
|
+
throw new TransactionalError(`transitions[${i}] missing required field '${k}'`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const th = transitionHash({
|
|
142
|
+
action_ref: ar,
|
|
143
|
+
state: t.state,
|
|
144
|
+
transition_timestamp_ms: t.transition_timestamp_ms,
|
|
145
|
+
authority_verified_at_ms: t.authority_verified_at_ms,
|
|
146
|
+
revocation_check_at_ms: t.revocation_check_at_ms,
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
state: t.state,
|
|
150
|
+
transition_timestamp_ms: t.transition_timestamp_ms,
|
|
151
|
+
authority_verified_at_ms: t.authority_verified_at_ms,
|
|
152
|
+
revocation_check_at_ms: t.revocation_check_at_ms,
|
|
153
|
+
transition_hash: th,
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
return {
|
|
157
|
+
action_ref: ar,
|
|
158
|
+
transitions: records,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=transactional.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactional.js","sourceRoot":"","sources":["../src/transactional.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,QAAQ,GAAG,gBAAgB,CAAC;AAElC,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAkCD,SAAS,mBAAmB,CAAC,KAAc;IACzC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,kBAAkB,CAC1B,kCAAkC,OAAO,KAAK,EAAE,CACjD,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,kBAAkB,CAC1B,gEAAgE,CACjE,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,kBAAkB,CAAC,6BAA6B,OAAO,KAAK,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,kBAAkB,CAAC,kCAAkC,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,YAAY,CAAC,KAAa,EAAE,KAAc;IACjD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,kBAAkB,CAC1B,GAAG,KAAK,8DAA8D,OAAO,KAAK,EAAE,CACrF,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,kBAAkB,CAC1B,GAAG,KAAK,iCAAiC,KAAK,EAAE,CACjD,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,kBAAkB,CAC1B,GAAG,KAAK,oEAAoE,KAAK,EAAE,CACpF,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,MAAM,IAAI,kBAAkB,CAAC,GAAG,KAAK,8BAA8B,KAAK,EAAE,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAMlC;IACC,OAAO;QACL,UAAU,EAAE,mBAAmB,CAAC,KAAK,CAAC,UAAU,CAAC;QACjD,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC;QAChC,uBAAuB,EAAE,YAAY,CACnC,yBAAyB,EACzB,KAAK,CAAC,uBAAuB,CAC9B;QACD,wBAAwB,EAAE,YAAY,CACpC,0BAA0B,EAC1B,KAAK,CAAC,wBAAwB,CAC/B;QACD,sBAAsB,EAAE,YAAY,CAClC,wBAAwB,EACxB,KAAK,CAAC,sBAAsB,CAC7B;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,KAM9B;IACC,MAAM,GAAG,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACtC,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC;AACxB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,6BAA6B,CAC3C,KAA8B;IAE9B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,kBAAkB,CAAC,8BAA8B,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,kBAAkB,CAAC,qCAAqC,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,EAAE,GAAG,SAAS,CAAC;QACnB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,YAAY,EAAE,KAAK,CAAC,YAAY;KACjC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAuB,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACjE,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YACxC,MAAM,IAAI,kBAAkB,CAC1B,eAAe,CAAC,yBAAyB,OAAO,CAAC,EAAE,CACpD,CAAC;QACJ,CAAC;QACD,MAAM,QAAQ,GAAG;YACf,OAAO;YACP,yBAAyB;YACzB,0BAA0B;YAC1B,wBAAwB;SAChB,CAAC;QACX,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;gBACd,MAAM,IAAI,kBAAkB,CAC1B,eAAe,CAAC,6BAA6B,CAAC,GAAG,CAClD,CAAC;YACJ,CAAC;QACH,CAAC;QACD,MAAM,EAAE,GAAG,cAAc,CAAC;YACxB,UAAU,EAAE,EAAE;YACd,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,uBAAuB,EAAE,CAAC,CAAC,uBAAuB;YAClD,wBAAwB,EAAE,CAAC,CAAC,wBAAwB;YACpD,sBAAsB,EAAE,CAAC,CAAC,sBAAsB;SACjD,CAAC,CAAC;QACH,OAAO;YACL,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,uBAAuB,EAAE,CAAC,CAAC,uBAAuB;YAClD,wBAAwB,EAAE,CAAC,CAAC,wBAAwB;YACpD,sBAAsB,EAAE,CAAC,CAAC,sBAAsB;YAChD,eAAe,EAAE,EAAE;SACpB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,UAAU,EAAE,EAAE;QACd,WAAW,EAAE,OAAO;KACrB,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -54,3 +54,15 @@ export {
|
|
|
54
54
|
appendToChain,
|
|
55
55
|
verifyAuditChain,
|
|
56
56
|
} from './audit-chain.js';
|
|
57
|
+
|
|
58
|
+
export {
|
|
59
|
+
TransactionalError,
|
|
60
|
+
type TransitionPreimage,
|
|
61
|
+
type TransitionInput,
|
|
62
|
+
type TransitionRecord,
|
|
63
|
+
type TransactionalChain,
|
|
64
|
+
type TransactionalChainInput,
|
|
65
|
+
transitionPreimage,
|
|
66
|
+
transitionHash,
|
|
67
|
+
buildTransactionalActionChain,
|
|
68
|
+
} from './transactional.js';
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transactional action_ref lifecycle (non-normative substrate extension).
|
|
3
|
+
*
|
|
4
|
+
* For actions that traverse multiple state transitions
|
|
5
|
+
* (authorisation -> settlement -> refund; issuance -> execution ->
|
|
6
|
+
* revocation; admission -> review -> close), the action_ref four-field
|
|
7
|
+
* preimage is the STABLE identity anchor across the full lifecycle, and
|
|
8
|
+
* per-transition lifecycle metadata sits OUTSIDE the action_ref preimage.
|
|
9
|
+
*
|
|
10
|
+
* Invariants:
|
|
11
|
+
*
|
|
12
|
+
* 1. action_ref is byte-stable across every transition of the same action.
|
|
13
|
+
* 2. transition_hash is byte-stable for identical preimage tuples.
|
|
14
|
+
* 3. transition_hash differs per state for the same action_ref + timestamps.
|
|
15
|
+
* 4. action_ref and any transition_hash are byte-distinct.
|
|
16
|
+
* 5. All timestamp fields MUST be epoch-millisecond integers per
|
|
17
|
+
* Substrate Rule 2. RFC 3339 string forms are rejected.
|
|
18
|
+
*
|
|
19
|
+
* State value is a non-empty string with no closed enum at the
|
|
20
|
+
* canonicalisation layer, consistent with the scope-field treatment.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { actionRef } from './action-ref.js';
|
|
24
|
+
import { sha256Jcs } from './canonicalize.js';
|
|
25
|
+
|
|
26
|
+
const HEX64_RE = /^[0-9a-f]{64}$/;
|
|
27
|
+
|
|
28
|
+
export class TransactionalError extends Error {
|
|
29
|
+
constructor(message: string) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.name = 'TransactionalError';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface TransitionPreimage {
|
|
36
|
+
action_ref: string;
|
|
37
|
+
state: string;
|
|
38
|
+
transition_timestamp_ms: number;
|
|
39
|
+
authority_verified_at_ms: number;
|
|
40
|
+
revocation_check_at_ms: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface TransitionInput {
|
|
44
|
+
state: string;
|
|
45
|
+
transition_timestamp_ms: number;
|
|
46
|
+
authority_verified_at_ms: number;
|
|
47
|
+
revocation_check_at_ms: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface TransitionRecord extends TransitionInput {
|
|
51
|
+
transition_hash: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface TransactionalChain {
|
|
55
|
+
action_ref: string;
|
|
56
|
+
transitions: TransitionRecord[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface TransactionalChainInput {
|
|
60
|
+
agent_id: string;
|
|
61
|
+
action_type: string;
|
|
62
|
+
scope: string;
|
|
63
|
+
timestamp_ms: number;
|
|
64
|
+
transitions: TransitionInput[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function requireActionRefHex(value: unknown): string {
|
|
68
|
+
if (typeof value !== 'string') {
|
|
69
|
+
throw new TransactionalError(
|
|
70
|
+
`action_ref must be string, got ${typeof value}`,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
if (!HEX64_RE.test(value)) {
|
|
74
|
+
throw new TransactionalError(
|
|
75
|
+
'action_ref must be a lowercase 64-character SHA-256 hex string',
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function requireState(value: unknown): string {
|
|
82
|
+
if (typeof value !== 'string') {
|
|
83
|
+
throw new TransactionalError(`state must be string, got ${typeof value}`);
|
|
84
|
+
}
|
|
85
|
+
if (value.length === 0) {
|
|
86
|
+
throw new TransactionalError('state must be a non-empty string');
|
|
87
|
+
}
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function requireIntMs(field: string, value: unknown): number {
|
|
92
|
+
if (typeof value !== 'number') {
|
|
93
|
+
throw new TransactionalError(
|
|
94
|
+
`${field} must be epoch-millisecond integer (Substrate Rule 2), got ${typeof value}`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
if (!Number.isFinite(value)) {
|
|
98
|
+
throw new TransactionalError(
|
|
99
|
+
`${field} must be a finite number, got ${value}`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (!Number.isInteger(value)) {
|
|
103
|
+
throw new TransactionalError(
|
|
104
|
+
`${field} must be epoch-millisecond integer (Substrate Rule 2), got float ${value}`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
if (value < 0) {
|
|
108
|
+
throw new TransactionalError(`${field} must be non-negative, got ${value}`);
|
|
109
|
+
}
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Return the validated preimage object for a single transition.
|
|
115
|
+
*
|
|
116
|
+
* The five-field shape is fixed at the substrate layer; the action_ref
|
|
117
|
+
* field cryptographically binds the transition to its action identity.
|
|
118
|
+
*/
|
|
119
|
+
export function transitionPreimage(input: {
|
|
120
|
+
action_ref: string;
|
|
121
|
+
state: string;
|
|
122
|
+
transition_timestamp_ms: number;
|
|
123
|
+
authority_verified_at_ms: number;
|
|
124
|
+
revocation_check_at_ms: number;
|
|
125
|
+
}): TransitionPreimage {
|
|
126
|
+
return {
|
|
127
|
+
action_ref: requireActionRefHex(input.action_ref),
|
|
128
|
+
state: requireState(input.state),
|
|
129
|
+
transition_timestamp_ms: requireIntMs(
|
|
130
|
+
'transition_timestamp_ms',
|
|
131
|
+
input.transition_timestamp_ms,
|
|
132
|
+
),
|
|
133
|
+
authority_verified_at_ms: requireIntMs(
|
|
134
|
+
'authority_verified_at_ms',
|
|
135
|
+
input.authority_verified_at_ms,
|
|
136
|
+
),
|
|
137
|
+
revocation_check_at_ms: requireIntMs(
|
|
138
|
+
'revocation_check_at_ms',
|
|
139
|
+
input.revocation_check_at_ms,
|
|
140
|
+
),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Return the lowercase hex SHA-256 transition_hash.
|
|
146
|
+
*
|
|
147
|
+
* transition_hash = SHA-256(JCS({action_ref, state,
|
|
148
|
+
* transition_timestamp_ms,
|
|
149
|
+
* authority_verified_at_ms,
|
|
150
|
+
* revocation_check_at_ms}))
|
|
151
|
+
*/
|
|
152
|
+
export function transitionHash(input: {
|
|
153
|
+
action_ref: string;
|
|
154
|
+
state: string;
|
|
155
|
+
transition_timestamp_ms: number;
|
|
156
|
+
authority_verified_at_ms: number;
|
|
157
|
+
revocation_check_at_ms: number;
|
|
158
|
+
}): string {
|
|
159
|
+
const obj = transitionPreimage(input);
|
|
160
|
+
return sha256Jcs(obj);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Build a transactional action chain.
|
|
165
|
+
*
|
|
166
|
+
* Returns the chain shape:
|
|
167
|
+
* {
|
|
168
|
+
* action_ref: "<lowercase 64-char hex>",
|
|
169
|
+
* transitions: [
|
|
170
|
+
* {
|
|
171
|
+
* state, transition_timestamp_ms,
|
|
172
|
+
* authority_verified_at_ms, revocation_check_at_ms,
|
|
173
|
+
* transition_hash
|
|
174
|
+
* },
|
|
175
|
+
* ...
|
|
176
|
+
* ]
|
|
177
|
+
* }
|
|
178
|
+
*
|
|
179
|
+
* The action_ref is computed once and is byte-stable across every
|
|
180
|
+
* transition. Each transition_hash is bound to the action_ref by
|
|
181
|
+
* including it in the transition preimage. Transitions are processed
|
|
182
|
+
* in the order supplied; the substrate verifies determinism, not
|
|
183
|
+
* lifecycle semantics.
|
|
184
|
+
*/
|
|
185
|
+
export function buildTransactionalActionChain(
|
|
186
|
+
input: TransactionalChainInput,
|
|
187
|
+
): TransactionalChain {
|
|
188
|
+
if (!Array.isArray(input.transitions)) {
|
|
189
|
+
throw new TransactionalError('transitions must be an array');
|
|
190
|
+
}
|
|
191
|
+
if (input.transitions.length === 0) {
|
|
192
|
+
throw new TransactionalError('transitions array must be non-empty');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const ar = actionRef({
|
|
196
|
+
agent_id: input.agent_id,
|
|
197
|
+
action_type: input.action_type,
|
|
198
|
+
scope: input.scope,
|
|
199
|
+
timestamp_ms: input.timestamp_ms,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const records: TransitionRecord[] = input.transitions.map((t, i) => {
|
|
203
|
+
if (t === null || typeof t !== 'object') {
|
|
204
|
+
throw new TransactionalError(
|
|
205
|
+
`transitions[${i}] must be object, got ${typeof t}`,
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
const required = [
|
|
209
|
+
'state',
|
|
210
|
+
'transition_timestamp_ms',
|
|
211
|
+
'authority_verified_at_ms',
|
|
212
|
+
'revocation_check_at_ms',
|
|
213
|
+
] as const;
|
|
214
|
+
for (const k of required) {
|
|
215
|
+
if (!(k in t)) {
|
|
216
|
+
throw new TransactionalError(
|
|
217
|
+
`transitions[${i}] missing required field '${k}'`,
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const th = transitionHash({
|
|
222
|
+
action_ref: ar,
|
|
223
|
+
state: t.state,
|
|
224
|
+
transition_timestamp_ms: t.transition_timestamp_ms,
|
|
225
|
+
authority_verified_at_ms: t.authority_verified_at_ms,
|
|
226
|
+
revocation_check_at_ms: t.revocation_check_at_ms,
|
|
227
|
+
});
|
|
228
|
+
return {
|
|
229
|
+
state: t.state,
|
|
230
|
+
transition_timestamp_ms: t.transition_timestamp_ms,
|
|
231
|
+
authority_verified_at_ms: t.authority_verified_at_ms,
|
|
232
|
+
revocation_check_at_ms: t.revocation_check_at_ms,
|
|
233
|
+
transition_hash: th,
|
|
234
|
+
};
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
action_ref: ar,
|
|
239
|
+
transitions: records,
|
|
240
|
+
};
|
|
241
|
+
}
|