@jamscript/client 0.1.0-rc.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/LICENSE +201 -0
- package/README.md +126 -0
- package/dist/abi.d.ts +101 -0
- package/dist/abi.js +18 -0
- package/dist/client.d.ts +69 -0
- package/dist/client.js +339 -0
- package/dist/codec.d.ts +8 -0
- package/dist/codec.js +314 -0
- package/dist/crypto.d.ts +65 -0
- package/dist/crypto.js +233 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/matrix.d.ts +23 -0
- package/dist/matrix.js +31 -0
- package/dist/proof.d.ts +1 -0
- package/dist/proof.js +125 -0
- package/dist/rpc.d.ts +115 -0
- package/dist/rpc.js +83 -0
- package/dist/runtime.d.ts +53 -0
- package/dist/runtime.js +405 -0
- package/dist/signer.d.ts +27 -0
- package/dist/signer.js +33 -0
- package/dist/state-provider.d.ts +51 -0
- package/dist/state-provider.js +173 -0
- package/package.json +55 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { verifyManagedStateProof } from "./proof.js";
|
|
2
|
+
import { parseHex } from "./crypto.js";
|
|
3
|
+
export class StateProviderError extends Error {
|
|
4
|
+
kind;
|
|
5
|
+
cause;
|
|
6
|
+
constructor(kind, message, cause) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.kind = kind;
|
|
9
|
+
this.cause = cause;
|
|
10
|
+
this.name = "StateProviderError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/** Legacy explicit-root proof provider retained for compatibility. */
|
|
14
|
+
export class RpcStateProvider {
|
|
15
|
+
transport;
|
|
16
|
+
constructor(transport) {
|
|
17
|
+
this.transport = transport;
|
|
18
|
+
}
|
|
19
|
+
async get(request) {
|
|
20
|
+
let response;
|
|
21
|
+
try {
|
|
22
|
+
response = await this.transport.call("minijam_getManagedStateV1", {
|
|
23
|
+
serviceId: request.serviceId,
|
|
24
|
+
stateRoot: request.stateRoot,
|
|
25
|
+
keyBase64: toBase64(request.key),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
const detail = error instanceof Error ? ": " + error.message : "";
|
|
30
|
+
throw new StateProviderError(classifyRpcFailure(error), "managed-state provider request failed" + detail, error);
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
return {
|
|
34
|
+
serviceId: response.serviceId,
|
|
35
|
+
stateRoot: response.managedStateRoot ?? response.stateRoot ?? "",
|
|
36
|
+
key: fromBase64(response.keyBase64),
|
|
37
|
+
value: response.valueBase64 === null ? null : fromBase64(response.valueBase64),
|
|
38
|
+
proof: response.proofBase64.map(fromBase64),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
throw new StateProviderError("MalformedResponse", "managed-state provider response is malformed", error);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The v0.1 default provider. The backend owns canonical-root discovery and
|
|
48
|
+
* returns only the trusted value; proof validation remains available through
|
|
49
|
+
* RpcStateProvider when an application explicitly opts into proof mode.
|
|
50
|
+
*/
|
|
51
|
+
export class TrustedStateProvider {
|
|
52
|
+
transport;
|
|
53
|
+
constructor(transport) {
|
|
54
|
+
this.transport = transport;
|
|
55
|
+
}
|
|
56
|
+
async get(request) {
|
|
57
|
+
let response;
|
|
58
|
+
try {
|
|
59
|
+
response = await this.transport.call("jamscript_getStateV1", {
|
|
60
|
+
serviceId: request.serviceId,
|
|
61
|
+
keyBase64: toBase64(request.key),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const detail = error instanceof Error ? ": " + error.message : "";
|
|
66
|
+
throw new StateProviderError(classifyRpcFailure(error), "trusted managed-state request failed" + detail, error);
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
return {
|
|
70
|
+
serviceId: response.serviceId,
|
|
71
|
+
stateRoot: response.managedStateRoot ?? response.stateRoot ?? "",
|
|
72
|
+
key: fromBase64(response.keyBase64),
|
|
73
|
+
value: response.valueBase64 === null ? null : fromBase64(response.valueBase64),
|
|
74
|
+
proof: [],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
throw new StateProviderError("MalformedResponse", "trusted managed-state response is malformed", error);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** Requests the v1 proof endpoint and leaves verification to the client. */
|
|
83
|
+
export class ProofStateProvider {
|
|
84
|
+
transport;
|
|
85
|
+
constructor(transport) {
|
|
86
|
+
this.transport = transport;
|
|
87
|
+
}
|
|
88
|
+
async get(request) {
|
|
89
|
+
let response;
|
|
90
|
+
try {
|
|
91
|
+
response = await this.transport.call("jamscript_getStateProofV1", {
|
|
92
|
+
serviceId: request.serviceId,
|
|
93
|
+
keyBase64: toBase64(request.key),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
const detail = error instanceof Error ? ": " + error.message : "";
|
|
98
|
+
throw new StateProviderError(classifyRpcFailure(error), "proof managed-state request failed" + detail, error);
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
return {
|
|
102
|
+
serviceId: response.serviceId,
|
|
103
|
+
stateRoot: response.managedStateRoot ?? response.stateRoot ?? "",
|
|
104
|
+
key: fromBase64(response.keyBase64),
|
|
105
|
+
value: response.valueBase64 === null ? null : fromBase64(response.valueBase64),
|
|
106
|
+
proof: response.proofBase64.map(fromBase64),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
throw new StateProviderError("MalformedResponse", "proof managed-state response is malformed", error);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/** Tries providers in order and only accepts a response with a valid proof. */
|
|
115
|
+
export class FallbackStateProvider {
|
|
116
|
+
providers;
|
|
117
|
+
constructor(providers) {
|
|
118
|
+
this.providers = providers;
|
|
119
|
+
if (providers.length === 0)
|
|
120
|
+
throw new Error("at least one state provider is required");
|
|
121
|
+
}
|
|
122
|
+
async get(request) {
|
|
123
|
+
let lastError;
|
|
124
|
+
for (const provider of this.providers) {
|
|
125
|
+
try {
|
|
126
|
+
const response = await provider.get(request);
|
|
127
|
+
assertResponseIdentity(request, response);
|
|
128
|
+
verifyManagedStateProof(parseHex(request.stateRoot, 32), request.key, response.value, response.proof);
|
|
129
|
+
return response;
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
lastError = error instanceof StateProviderError
|
|
133
|
+
? error
|
|
134
|
+
: new StateProviderError("InvalidProof", "state provider proof verification failed", error);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
throw lastError ?? new StateProviderError("Unavailable", "no state provider is available");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function assertResponseIdentity(request, response) {
|
|
141
|
+
if (response.serviceId !== request.serviceId
|
|
142
|
+
|| !sameHex(response.stateRoot, request.stateRoot)
|
|
143
|
+
|| !sameBytes(response.key, request.key)) {
|
|
144
|
+
throw new StateProviderError("InconsistentResponse", "managed-state provider response does not match the request");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function classifyRpcFailure(error) {
|
|
148
|
+
const code = typeof error === "object" && error !== null && "code" in error
|
|
149
|
+
? error.code
|
|
150
|
+
: undefined;
|
|
151
|
+
if (code === -32030 || code === -32601)
|
|
152
|
+
return "RootUnavailable";
|
|
153
|
+
return "Unavailable";
|
|
154
|
+
}
|
|
155
|
+
function sameHex(left, right) {
|
|
156
|
+
return left.toLowerCase().replace(/^0x/, "") === right.toLowerCase().replace(/^0x/, "");
|
|
157
|
+
}
|
|
158
|
+
function sameBytes(left, right) {
|
|
159
|
+
return left.length === right.length && left.every((byte, index) => byte === right[index]);
|
|
160
|
+
}
|
|
161
|
+
function toBase64(bytes) {
|
|
162
|
+
let binary = "";
|
|
163
|
+
for (const byte of bytes)
|
|
164
|
+
binary += String.fromCharCode(byte);
|
|
165
|
+
return btoa(binary);
|
|
166
|
+
}
|
|
167
|
+
function fromBase64(value) {
|
|
168
|
+
const binary = atob(value);
|
|
169
|
+
const output = new Uint8Array(binary.length);
|
|
170
|
+
for (let index = 0; index < binary.length; index += 1)
|
|
171
|
+
output[index] = binary.charCodeAt(index);
|
|
172
|
+
return output;
|
|
173
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jamscript/client",
|
|
3
|
+
"version": "0.1.0-rc.1",
|
|
4
|
+
"description": "TypeScript client for JamScript services, managed state and Ownership.",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc -p tsconfig.json",
|
|
22
|
+
"test": "node --test test/*.test.mjs",
|
|
23
|
+
"test:network": "node tests/minijam-network.e2e.mjs",
|
|
24
|
+
"test:transaction-closure": "node tests/minijam-transaction-closure.e2e.mjs",
|
|
25
|
+
"prepack": "npm run build"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@polkadot-api/substrate-bindings": "^0.21.0",
|
|
29
|
+
"@polkadot/util": "^14.0.1",
|
|
30
|
+
"@polkadot/util-crypto": "^14.0.1"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"typescript": "^5.9.2"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/ArcheLabs/JamScript.git",
|
|
38
|
+
"directory": "packages/client"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/ArcheLabs/JamScript",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/ArcheLabs/JamScript/issues"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"jamscript",
|
|
46
|
+
"jam",
|
|
47
|
+
"ownership",
|
|
48
|
+
"polkadot"
|
|
49
|
+
],
|
|
50
|
+
"license": "Apache-2.0",
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public",
|
|
53
|
+
"registry": "https://registry.npmjs.org/"
|
|
54
|
+
}
|
|
55
|
+
}
|