@midnightntwrk/wallet-sdk-prover-client 1.2.3
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/README.md +77 -0
- package/dist/effect/HttpProverClient.d.ts +12 -0
- package/dist/effect/HttpProverClient.js +83 -0
- package/dist/effect/ProverClient.d.ts +32 -0
- package/dist/effect/ProverClient.js +16 -0
- package/dist/effect/WasmProver.d.ts +47 -0
- package/dist/effect/WasmProver.js +200 -0
- package/dist/effect/index.d.ts +3 -0
- package/dist/effect/index.js +15 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +43 -0
- package/dist/proof-worker.d.ts +1 -0
- package/dist/proof-worker.js +77 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# @midnightntwrk/wallet-sdk-prover-client
|
|
2
|
+
|
|
3
|
+
Client for interacting with the Midnight ZK proof generation service.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @midnightntwrk/wallet-sdk-prover-client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
This package provides a client for submitting transactions to a Proof Server that generates zero-knowledge proofs. It is
|
|
14
|
+
used to finalize shielded transactions by converting unproven transactions into proven ones.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
### Basic Usage
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { HttpProverClient } from '@midnightntwrk/wallet-sdk-prover-client';
|
|
22
|
+
|
|
23
|
+
// Initialize the client with the Proof Server URL
|
|
24
|
+
const proverClient = new HttpProverClient({
|
|
25
|
+
serverUrl: 'http://localhost:6300',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Prove an unproven transaction
|
|
29
|
+
const provenTransaction = await proverClient.proveTransaction(unprovenTransaction);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### With Custom Cost Model
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
const provenTransaction = await proverClient.proveTransaction(unprovenTransaction, customCostModel);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## API
|
|
39
|
+
|
|
40
|
+
### HttpProverClient
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
class HttpProverClient {
|
|
44
|
+
constructor(config: { serverUrl: string });
|
|
45
|
+
|
|
46
|
+
proveTransaction<S extends Signaturish, B extends Bindingish>(
|
|
47
|
+
transaction: Transaction<S, PreProof, B>,
|
|
48
|
+
costModel?: CostModel,
|
|
49
|
+
): Promise<Transaction<S, Proof, B>>;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Exports
|
|
54
|
+
|
|
55
|
+
### Default Export
|
|
56
|
+
|
|
57
|
+
- `HttpProverClient` - HTTP client for the Proof Server
|
|
58
|
+
|
|
59
|
+
### Effect Submodule (`/effect`)
|
|
60
|
+
|
|
61
|
+
Effect.ts-based implementation:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { ProverClient, HttpProverClient } from '@midnightntwrk/wallet-sdk-prover-client/effect';
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Error Handling
|
|
68
|
+
|
|
69
|
+
The client may throw the following errors:
|
|
70
|
+
|
|
71
|
+
- `ClientError` - Issues with the provided transaction or connection problems
|
|
72
|
+
- `ServerError` - Internal server errors or connection failures
|
|
73
|
+
- `InvalidProtocolSchemeError` - Invalid URL scheme in configuration
|
|
74
|
+
|
|
75
|
+
## License
|
|
76
|
+
|
|
77
|
+
Apache-2.0
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Effect, Layer } from 'effect';
|
|
2
|
+
import { ProverClient } from './ProverClient.js';
|
|
3
|
+
import { type InvalidProtocolSchemeError } from '@midnightntwrk/wallet-sdk-utilities/networking';
|
|
4
|
+
/**
|
|
5
|
+
* Creates a layer for a {@link ProverClient} that sends requests to a Proof Server over HTTP.
|
|
6
|
+
*
|
|
7
|
+
* @param config The server configuration to use when configuring the HTTP elements of the layer.
|
|
8
|
+
* @returns A `Layer` for {@link ProverClient} that sends requests to a configured Proof Server over HTTP. The layer can
|
|
9
|
+
* fail with an `InvalidProtocolSchemeError` if `config` is invalid for a HTTP context.
|
|
10
|
+
*/
|
|
11
|
+
export declare const layer: (config: ProverClient.ServerConfig) => Layer.Layer<ProverClient, InvalidProtocolSchemeError>;
|
|
12
|
+
export declare const create: (config: ProverClient.ServerConfig) => Effect.Effect<ProverClient.Service, InvalidProtocolSchemeError>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
import { Chunk, Duration, Effect, Either, Layer, pipe, Schedule, Stream } from 'effect';
|
|
14
|
+
import { FetchHttpClient, HttpBody, HttpClient, HttpClientRequest } from '@effect/platform';
|
|
15
|
+
import { ProverClient } from './ProverClient.js';
|
|
16
|
+
import { ClientError, HttpURL, ServerError, } from '@midnightntwrk/wallet-sdk-utilities/networking';
|
|
17
|
+
import { BlobOps, EitherOps } from '@midnightntwrk/wallet-sdk-utilities';
|
|
18
|
+
import * as ledger from '@midnight-ntwrk/ledger-v8';
|
|
19
|
+
const PROVE_TX_PATH = '/prove';
|
|
20
|
+
const CHECK_TX_PATH = '/check';
|
|
21
|
+
/**
|
|
22
|
+
* Creates a layer for a {@link ProverClient} that sends requests to a Proof Server over HTTP.
|
|
23
|
+
*
|
|
24
|
+
* @param config The server configuration to use when configuring the HTTP elements of the layer.
|
|
25
|
+
* @returns A `Layer` for {@link ProverClient} that sends requests to a configured Proof Server over HTTP. The layer can
|
|
26
|
+
* fail with an `InvalidProtocolSchemeError` if `config` is invalid for a HTTP context.
|
|
27
|
+
*/
|
|
28
|
+
export const layer = (config) => Layer.effect(ProverClient, HttpURL.make(new URL(config.url)).pipe(Either.map((baseUrl) => new HttpProverClientImpl(baseUrl)), Either.match({
|
|
29
|
+
onLeft: (l) => Effect.fail(l),
|
|
30
|
+
onRight: (r) => Effect.succeed(r),
|
|
31
|
+
})));
|
|
32
|
+
export const create = (config) => {
|
|
33
|
+
return pipe(HttpURL.make(new URL(config.url)), Either.map((baseUrl) => new HttpProverClientImpl(baseUrl)), EitherOps.toEffect, Effect.map((client) => ProverClient.of(client)));
|
|
34
|
+
};
|
|
35
|
+
class HttpProverClientImpl {
|
|
36
|
+
constructor(baseUrl) {
|
|
37
|
+
this.baseUrl = baseUrl;
|
|
38
|
+
}
|
|
39
|
+
baseUrl;
|
|
40
|
+
request(path, payload, failurePrefix) {
|
|
41
|
+
const concatBytes = (chunks) => Effect.promise(() => BlobOps.getBytes(new Blob(chunks.map((chunk) => new Uint8Array(chunk)))));
|
|
42
|
+
const receiveBody = (response) => pipe(response.stream, Stream.runCollect, Effect.flatMap((chunks) => concatBytes(Chunk.toArray(chunks))));
|
|
43
|
+
// Build endpoint URL from the already validated base URL
|
|
44
|
+
const url = HttpURL.HttpURL(new URL(path, this.baseUrl));
|
|
45
|
+
const proveTxRequest = pipe(Effect.succeed(payload), Effect.map((body) => HttpClientRequest.post(url).pipe(
|
|
46
|
+
// A raw body must be used instead of `bodyUint8Array` so that no explicit `content-length`
|
|
47
|
+
// header is attached: `fetch` derives the length from the body itself, and an explicit
|
|
48
|
+
// header is rejected as malformed by undici >= 8.2.0 when it is installed as the global
|
|
49
|
+
// dispatcher (e.g. transitively via testcontainers or @effect/platform-node).
|
|
50
|
+
HttpClientRequest.setBody(HttpBody.raw(body, { contentType: 'application/octet-stream' })))), Effect.flatMap(HttpClient.execute), Effect.flatMap((response) => Effect.gen(function* () {
|
|
51
|
+
if (response.status !== 200) {
|
|
52
|
+
const text = yield* response.text;
|
|
53
|
+
return yield* new ClientError({ message: `${failurePrefix}: ${text}` });
|
|
54
|
+
}
|
|
55
|
+
return yield* receiveBody(response);
|
|
56
|
+
})), Effect.retry({
|
|
57
|
+
times: 3,
|
|
58
|
+
while: (error) =>
|
|
59
|
+
// Retry if we get a Bad Gateway, Service Unavailable, or Gateway Timeout error.
|
|
60
|
+
error._tag === 'ResponseError' && error.response.status >= 502 && error.response.status <= 504,
|
|
61
|
+
schedule: Schedule.exponential(Duration.seconds(2), 2),
|
|
62
|
+
}));
|
|
63
|
+
return proveTxRequest.pipe(Effect.catchTags({
|
|
64
|
+
RequestError: (err) => new ClientError({ message: `Failed to connect to Proof Server: ${err.message}` }),
|
|
65
|
+
ResponseError: (err) => Effect.orElseSucceed(err.response.text, () => 'Unknown server error').pipe(Effect.flatMap((message) => new ServerError({ message }))),
|
|
66
|
+
}), Effect.provide(FetchHttpClient.layer));
|
|
67
|
+
}
|
|
68
|
+
serverProverProvider = () => ({
|
|
69
|
+
check: async (serializedPreimage, _keyLocation) => pipe(Effect.succeed(ledger.createCheckPayload(serializedPreimage)), Effect.flatMap((tx) => this.request(CHECK_TX_PATH, tx, 'Failed to check')), Effect.map((response) => ledger.parseCheckResult(response)), Effect.runPromise),
|
|
70
|
+
prove: async (serializedPreimage, _keyLocation, overwriteBindingInput) => pipe(Effect.succeed(ledger.createProvingPayload(serializedPreimage, overwriteBindingInput)), Effect.flatMap((tx) => this.request(PROVE_TX_PATH, tx, 'Failed to prove')), Effect.runPromise),
|
|
71
|
+
});
|
|
72
|
+
proveTransaction(transaction, costModel) {
|
|
73
|
+
return pipe(Effect.succeed(this.serverProverProvider()), Effect.flatMap((provider) => Effect.tryPromise({
|
|
74
|
+
try: () => transaction.prove(provider, costModel),
|
|
75
|
+
catch: (error) => error instanceof ClientError || error instanceof ServerError
|
|
76
|
+
? error
|
|
77
|
+
: new ClientError({ message: 'Failed to prove transaction', cause: error }),
|
|
78
|
+
})));
|
|
79
|
+
}
|
|
80
|
+
asProvingProvider() {
|
|
81
|
+
return this.serverProverProvider();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Effect, Context } from 'effect';
|
|
2
|
+
import type * as ledger from '@midnight-ntwrk/ledger-v8';
|
|
3
|
+
import { type ClientError, type ServerError } from '@midnightntwrk/wallet-sdk-utilities/networking';
|
|
4
|
+
import type { KeyMaterialProvider } from '@midnight-ntwrk/zkir-v2';
|
|
5
|
+
declare const ProverClient_base: Context.TagClass<ProverClient, "@midnight-ntwrk/prover-client#ProverClient", ProverClient.Service>;
|
|
6
|
+
/** A client that provides proof services for unproven transactions. */
|
|
7
|
+
export declare class ProverClient extends ProverClient_base {
|
|
8
|
+
}
|
|
9
|
+
export declare namespace ProverClient {
|
|
10
|
+
/** Provides server-related configuration for {@link ProverClient} implementations. */
|
|
11
|
+
interface ServerConfig {
|
|
12
|
+
/** The base URL to the Proof Server. */
|
|
13
|
+
readonly url: URL | string;
|
|
14
|
+
}
|
|
15
|
+
/** Provides wasm-related configuration for {@link KeyMaterialProvider} implementations. */
|
|
16
|
+
interface WasmConfig {
|
|
17
|
+
/** The Key Material Provider. */
|
|
18
|
+
readonly keyMaterialProvider: KeyMaterialProvider;
|
|
19
|
+
}
|
|
20
|
+
interface Service {
|
|
21
|
+
/**
|
|
22
|
+
* Proves an unproven transaction by submitting it to an associated Proof Server.
|
|
23
|
+
*
|
|
24
|
+
* @param transaction A serialized unproven transaction.
|
|
25
|
+
* @returns An `Effect` that yields with a serialized transaction representing the proven version of `transaction`;
|
|
26
|
+
* or fails with a client or server side error.
|
|
27
|
+
*/
|
|
28
|
+
proveTransaction<S extends ledger.Signaturish, B extends ledger.Bindingish>(tx: ledger.Transaction<S, ledger.PreProof, B>, costModel?: ledger.CostModel): Effect.Effect<ledger.Transaction<S, ledger.Proof, B>, ClientError | ServerError>;
|
|
29
|
+
asProvingProvider(): ledger.ProvingProvider;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
import { Context } from 'effect';
|
|
14
|
+
/** A client that provides proof services for unproven transactions. */
|
|
15
|
+
export class ProverClient extends Context.Tag('@midnight-ntwrk/prover-client#ProverClient')() {
|
|
16
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Effect, Layer, Schema } from 'effect';
|
|
2
|
+
import { type InvalidProtocolSchemeError } from '@midnightntwrk/wallet-sdk-utilities/networking';
|
|
3
|
+
import { type KeyMaterialProvider } from '@midnight-ntwrk/zkir-v2';
|
|
4
|
+
import { ProverClient } from './ProverClient.js';
|
|
5
|
+
/**
|
|
6
|
+
* Creates a layer for a {@link ProverClient} that sends requests to a Wasm Prover.
|
|
7
|
+
*
|
|
8
|
+
* @param config The Key Material Provider to use when configuring the prover's elements of the layer.
|
|
9
|
+
* @returns A `Layer` for {@link ProverClient} that sends requests to a configured Wasm Prover.
|
|
10
|
+
*/
|
|
11
|
+
export declare const layer: (config: ProverClient.WasmConfig) => Layer.Layer<ProverClient, InvalidProtocolSchemeError>;
|
|
12
|
+
export declare const create: (config: ProverClient.WasmConfig) => Effect.Effect<ProverClient.Service, InvalidProtocolSchemeError>;
|
|
13
|
+
export declare const CheckOperationSchema: Schema.Struct<{
|
|
14
|
+
op: Schema.Literal<["check"]>;
|
|
15
|
+
args: Schema.Tuple<[typeof Schema.Uint8Array]>;
|
|
16
|
+
}>;
|
|
17
|
+
export declare const ProveOperationSchema: Schema.Struct<{
|
|
18
|
+
op: Schema.Literal<["prove"]>;
|
|
19
|
+
args: Schema.Tuple2<typeof Schema.Uint8Array, Schema.Union<[typeof Schema.BigIntFromSelf, typeof Schema.Undefined]>>;
|
|
20
|
+
}>;
|
|
21
|
+
export declare const LookupKeyRequestSchema: Schema.Struct<{
|
|
22
|
+
op: Schema.Literal<["lookupKey"]>;
|
|
23
|
+
keyLocation: typeof Schema.String;
|
|
24
|
+
}>;
|
|
25
|
+
export declare const GetParamsRequestSchema: Schema.Struct<{
|
|
26
|
+
op: Schema.Literal<["getParams"]>;
|
|
27
|
+
k: typeof Schema.Number;
|
|
28
|
+
}>;
|
|
29
|
+
export declare const ResponseFromWorkerSchema: Schema.Struct<{
|
|
30
|
+
op: Schema.Literal<["result"]>;
|
|
31
|
+
value: Schema.Union<[typeof Schema.Uint8Array, Schema.Array$<Schema.Union<[typeof Schema.BigIntFromSelf, typeof Schema.Undefined]>>]>;
|
|
32
|
+
}>;
|
|
33
|
+
export declare const LookupKeyOperationResultSchema: Schema.Struct<{
|
|
34
|
+
op: Schema.Literal<["lookupKey"]>;
|
|
35
|
+
keyLocation: typeof Schema.String;
|
|
36
|
+
result: Schema.optional<Schema.Struct<{
|
|
37
|
+
proverKey: typeof Schema.Uint8Array;
|
|
38
|
+
verifierKey: typeof Schema.Uint8Array;
|
|
39
|
+
ir: typeof Schema.Uint8Array;
|
|
40
|
+
}>>;
|
|
41
|
+
}>;
|
|
42
|
+
export declare const GetParamsOperationResultSchema: Schema.Struct<{
|
|
43
|
+
op: Schema.Literal<["getParams"]>;
|
|
44
|
+
k: typeof Schema.Number;
|
|
45
|
+
result: typeof Schema.Uint8Array;
|
|
46
|
+
}>;
|
|
47
|
+
export declare const makeDefaultKeyMaterialProvider: () => KeyMaterialProvider;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
import Worker from 'web-worker';
|
|
14
|
+
import { Effect, Layer, Schema, pipe } from 'effect';
|
|
15
|
+
import { ClientError } from '@midnightntwrk/wallet-sdk-utilities/networking';
|
|
16
|
+
import { ProverClient } from './ProverClient.js';
|
|
17
|
+
/**
|
|
18
|
+
* Creates a layer for a {@link ProverClient} that sends requests to a Wasm Prover.
|
|
19
|
+
*
|
|
20
|
+
* @param config The Key Material Provider to use when configuring the prover's elements of the layer.
|
|
21
|
+
* @returns A `Layer` for {@link ProverClient} that sends requests to a configured Wasm Prover.
|
|
22
|
+
*/
|
|
23
|
+
export const layer = (config) => Layer.effect(ProverClient, Effect.succeed(new WasmProverImpl(config.keyMaterialProvider)));
|
|
24
|
+
export const create = (config) => {
|
|
25
|
+
return Effect.succeed(new WasmProverImpl(config.keyMaterialProvider));
|
|
26
|
+
};
|
|
27
|
+
const MAX_TIME_TO_PROCESS = 10 * 60 * 1000;
|
|
28
|
+
export const CheckOperationSchema = Schema.Struct({
|
|
29
|
+
op: Schema.Literal('check'),
|
|
30
|
+
args: Schema.Tuple(Schema.Uint8Array),
|
|
31
|
+
});
|
|
32
|
+
export const ProveOperationSchema = Schema.Struct({
|
|
33
|
+
op: Schema.Literal('prove'),
|
|
34
|
+
args: Schema.Tuple(Schema.Uint8Array, Schema.Union(Schema.BigIntFromSelf, Schema.Undefined)),
|
|
35
|
+
});
|
|
36
|
+
export const LookupKeyRequestSchema = Schema.Struct({
|
|
37
|
+
op: Schema.Literal('lookupKey'),
|
|
38
|
+
keyLocation: Schema.String,
|
|
39
|
+
});
|
|
40
|
+
export const GetParamsRequestSchema = Schema.Struct({
|
|
41
|
+
op: Schema.Literal('getParams'),
|
|
42
|
+
k: Schema.Number,
|
|
43
|
+
});
|
|
44
|
+
export const ResponseFromWorkerSchema = Schema.Struct({
|
|
45
|
+
op: Schema.Literal('result'),
|
|
46
|
+
value: Schema.Union(Schema.Uint8Array, Schema.Array(Schema.Union(Schema.BigIntFromSelf, Schema.Undefined))),
|
|
47
|
+
});
|
|
48
|
+
const ProvingKeyMaterialSchema = Schema.Struct({
|
|
49
|
+
proverKey: Schema.Uint8Array,
|
|
50
|
+
verifierKey: Schema.Uint8Array,
|
|
51
|
+
ir: Schema.Uint8Array,
|
|
52
|
+
});
|
|
53
|
+
export const LookupKeyOperationResultSchema = Schema.Struct({
|
|
54
|
+
op: Schema.Literal('lookupKey'),
|
|
55
|
+
keyLocation: Schema.String,
|
|
56
|
+
result: Schema.optional(ProvingKeyMaterialSchema),
|
|
57
|
+
});
|
|
58
|
+
export const GetParamsOperationResultSchema = Schema.Struct({
|
|
59
|
+
op: Schema.Literal('getParams'),
|
|
60
|
+
k: Schema.Number,
|
|
61
|
+
result: Schema.Uint8Array,
|
|
62
|
+
});
|
|
63
|
+
const MessageDataSchema = Schema.Union(LookupKeyRequestSchema, GetParamsRequestSchema, ResponseFromWorkerSchema);
|
|
64
|
+
const callProverWorker = ({ kmProvider, op, args }) => {
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
const currentFile = import.meta.url;
|
|
67
|
+
const worker = new Worker(new URL(`../../dist/proof-worker.js`, currentFile), { type: 'module' });
|
|
68
|
+
// initialize worker
|
|
69
|
+
worker.postMessage(op === 'check'
|
|
70
|
+
? Schema.encodeSync(CheckOperationSchema)({ op, args: [args[0]] })
|
|
71
|
+
: Schema.encodeSync(ProveOperationSchema)({ op, args: [args[0], args[1]] }));
|
|
72
|
+
// a message from the worker
|
|
73
|
+
worker.addEventListener('message', ({ data }) => {
|
|
74
|
+
const decoded = Schema.decodeUnknownSync(MessageDataSchema)(data);
|
|
75
|
+
const { op } = decoded;
|
|
76
|
+
if (op === 'lookupKey') {
|
|
77
|
+
const { keyLocation } = decoded;
|
|
78
|
+
kmProvider
|
|
79
|
+
.lookupKey(keyLocation)
|
|
80
|
+
.then((result) => {
|
|
81
|
+
worker.postMessage(Schema.encodeSync(LookupKeyOperationResultSchema)({ op, keyLocation, result }));
|
|
82
|
+
})
|
|
83
|
+
.catch((e) => {
|
|
84
|
+
worker.terminate();
|
|
85
|
+
reject(e);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
else if (op === 'getParams') {
|
|
89
|
+
const { k } = decoded;
|
|
90
|
+
kmProvider
|
|
91
|
+
.getParams(k)
|
|
92
|
+
.then((result) => {
|
|
93
|
+
worker.postMessage(Schema.encodeSync(GetParamsOperationResultSchema)({ op, k, result }));
|
|
94
|
+
})
|
|
95
|
+
.catch((e) => {
|
|
96
|
+
worker.terminate();
|
|
97
|
+
reject(e);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
else if (op === 'result') {
|
|
101
|
+
const { value } = decoded;
|
|
102
|
+
worker.terminate();
|
|
103
|
+
resolve(value);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
worker.addEventListener('error', (e) => {
|
|
107
|
+
worker.terminate();
|
|
108
|
+
reject(Error(e.message));
|
|
109
|
+
});
|
|
110
|
+
setTimeout(() => {
|
|
111
|
+
worker.terminate();
|
|
112
|
+
reject(new Error(`${op} action timed out`));
|
|
113
|
+
}, MAX_TIME_TO_PROCESS);
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
class WasmProverImpl {
|
|
117
|
+
constructor(keyMaterialProvider) {
|
|
118
|
+
this.keyMaterialProvider = keyMaterialProvider;
|
|
119
|
+
}
|
|
120
|
+
keyMaterialProvider;
|
|
121
|
+
wasmProverProvider = (keyMaterialProvider) => ({
|
|
122
|
+
check: async (serializedPreimage, _keyLocation) => callProverWorker({
|
|
123
|
+
kmProvider: keyMaterialProvider ?? this.keyMaterialProvider,
|
|
124
|
+
op: 'check',
|
|
125
|
+
args: [serializedPreimage],
|
|
126
|
+
}),
|
|
127
|
+
prove: async (serializedPreimage, _keyLocation, overwriteBindingInput) => callProverWorker({
|
|
128
|
+
kmProvider: keyMaterialProvider ?? this.keyMaterialProvider,
|
|
129
|
+
op: 'prove',
|
|
130
|
+
args: [serializedPreimage, overwriteBindingInput],
|
|
131
|
+
}),
|
|
132
|
+
});
|
|
133
|
+
proveTransaction(transaction, costModel, keyMaterialProvider) {
|
|
134
|
+
return pipe(Effect.succeed(this.wasmProverProvider(keyMaterialProvider)), Effect.flatMap((provider) => Effect.tryPromise({
|
|
135
|
+
try: () => transaction.prove(provider, costModel),
|
|
136
|
+
catch: (error) => error instanceof ClientError
|
|
137
|
+
? error
|
|
138
|
+
: new ClientError({ message: 'Failed to prove transaction', cause: error }),
|
|
139
|
+
})));
|
|
140
|
+
}
|
|
141
|
+
asProvingProvider() {
|
|
142
|
+
return this.wasmProverProvider();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
export const makeDefaultKeyMaterialProvider = () => {
|
|
146
|
+
const cache = new Map();
|
|
147
|
+
const s3 = 'https://midnight-s3-fileshare-dev-eu-west-1.s3.eu-west-1.amazonaws.com';
|
|
148
|
+
const ver = 9;
|
|
149
|
+
const fetchWithRetry = async (url, retries = 5) => {
|
|
150
|
+
for (let i = 0; i < retries; i++) {
|
|
151
|
+
try {
|
|
152
|
+
return await fetch(url);
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
// eslint-disable-next-line no-console
|
|
156
|
+
console.error('Failed to fetch at attempt', i + 1, url, e);
|
|
157
|
+
// cooldown a bit before retrying
|
|
158
|
+
await new Promise((resolve) => setTimeout(resolve, 1000 * Math.pow(2, i + 1)));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
throw new Error(`Failed to fetch ${url} after ${retries} attempts`);
|
|
162
|
+
};
|
|
163
|
+
const keyMaterialProvider = {
|
|
164
|
+
lookupKey: async (keyLocation) => {
|
|
165
|
+
const pth = {
|
|
166
|
+
'midnight/zswap/spend': `zswap/${ver}/spend`,
|
|
167
|
+
'midnight/zswap/output': `zswap/${ver}/output`,
|
|
168
|
+
'midnight/zswap/sign': `zswap/${ver}/sign`,
|
|
169
|
+
'midnight/dust/spend': `dust/${ver}/spend`,
|
|
170
|
+
}[keyLocation];
|
|
171
|
+
if (pth === undefined) {
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
if (cache.has(pth)) {
|
|
175
|
+
return cache.get(pth);
|
|
176
|
+
}
|
|
177
|
+
const pk = await fetchWithRetry(`${s3}/${pth}.prover`);
|
|
178
|
+
const vk = await fetchWithRetry(`${s3}/${pth}.verifier`);
|
|
179
|
+
const ir = await fetchWithRetry(`${s3}/${pth}.bzkir`);
|
|
180
|
+
const result = {
|
|
181
|
+
proverKey: new Uint8Array(await pk.arrayBuffer()),
|
|
182
|
+
verifierKey: new Uint8Array(await vk.arrayBuffer()),
|
|
183
|
+
ir: new Uint8Array(await ir.arrayBuffer()),
|
|
184
|
+
};
|
|
185
|
+
cache.set(pth, result);
|
|
186
|
+
return result;
|
|
187
|
+
},
|
|
188
|
+
getParams: async (k) => {
|
|
189
|
+
const cacheKey = `params-${k}`;
|
|
190
|
+
if (cache.has(cacheKey)) {
|
|
191
|
+
return cache.get(cacheKey);
|
|
192
|
+
}
|
|
193
|
+
const data = await fetchWithRetry(`${s3}/bls_midnight_2p${k}`);
|
|
194
|
+
const result = new Uint8Array(await data.arrayBuffer());
|
|
195
|
+
cache.set(cacheKey, result);
|
|
196
|
+
return result;
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
return keyMaterialProvider;
|
|
200
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
export * as ProverClient from './ProverClient.js';
|
|
14
|
+
export * as HttpProverClient from './HttpProverClient.js';
|
|
15
|
+
export * as WasmProver from './WasmProver.js';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ProverClient } from './effect/index.js';
|
|
2
|
+
import type * as ledger from '@midnight-ntwrk/ledger-v8';
|
|
3
|
+
/** Sends serialized unproven transactions to a Proof Server over HTTP. */
|
|
4
|
+
export declare class HttpProverClient {
|
|
5
|
+
#private;
|
|
6
|
+
/**
|
|
7
|
+
* Initializes a new {@link HttpProverClient}.
|
|
8
|
+
*
|
|
9
|
+
* @param config The server configuration to use when configuring the HTTP elements of the Proof Server.
|
|
10
|
+
* @throws {@link ProverClient.InvalidProtocolSchemeError} The `config` is invalid for a HTTP context. E.g., expecting
|
|
11
|
+
* 'http:' or 'https:' URLs but something other was provided.
|
|
12
|
+
*/
|
|
13
|
+
constructor(config: ProverClient.ProverClient.ServerConfig);
|
|
14
|
+
/**
|
|
15
|
+
* Proves an unproven transaction by submitting it over HTTP to an associated Proof Server.
|
|
16
|
+
*
|
|
17
|
+
* @param transaction An unproven ledger transaction.
|
|
18
|
+
* @returns A `Promise` that resolves with a proven transaction or fails with a client or server side error.
|
|
19
|
+
* @throws {@link ClientError} There was an issue with the provided `transaction`, or a connection with the configured
|
|
20
|
+
* Proof Server could not be initiated.
|
|
21
|
+
* @throws {@link ServerError} Unable to establish a connection with the configured Proof Server, or there was an
|
|
22
|
+
* internal error that prevented the proof request from being executed.
|
|
23
|
+
*/
|
|
24
|
+
proveTransaction<S extends ledger.Signaturish, B extends ledger.Bindingish>(transaction: ledger.Transaction<S, ledger.PreProof, B>, costModel?: ledger.CostModel): Promise<ledger.Transaction<S, ledger.Proof, B>>;
|
|
25
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
import { Effect } from 'effect';
|
|
14
|
+
import { ProverClient, HttpProverClient as _HttpProverClient } from './effect/index.js';
|
|
15
|
+
/** Sends serialized unproven transactions to a Proof Server over HTTP. */
|
|
16
|
+
export class HttpProverClient {
|
|
17
|
+
#innerClient;
|
|
18
|
+
/**
|
|
19
|
+
* Initializes a new {@link HttpProverClient}.
|
|
20
|
+
*
|
|
21
|
+
* @param config The server configuration to use when configuring the HTTP elements of the Proof Server.
|
|
22
|
+
* @throws {@link ProverClient.InvalidProtocolSchemeError} The `config` is invalid for a HTTP context. E.g., expecting
|
|
23
|
+
* 'http:' or 'https:' URLs but something other was provided.
|
|
24
|
+
*/
|
|
25
|
+
constructor(config) {
|
|
26
|
+
this.#innerClient = Effect.gen(function* () {
|
|
27
|
+
return yield* ProverClient.ProverClient;
|
|
28
|
+
}).pipe(Effect.provide(_HttpProverClient.layer(config)), Effect.runSync);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Proves an unproven transaction by submitting it over HTTP to an associated Proof Server.
|
|
32
|
+
*
|
|
33
|
+
* @param transaction An unproven ledger transaction.
|
|
34
|
+
* @returns A `Promise` that resolves with a proven transaction or fails with a client or server side error.
|
|
35
|
+
* @throws {@link ClientError} There was an issue with the provided `transaction`, or a connection with the configured
|
|
36
|
+
* Proof Server could not be initiated.
|
|
37
|
+
* @throws {@link ServerError} Unable to establish a connection with the configured Proof Server, or there was an
|
|
38
|
+
* internal error that prevented the proof request from being executed.
|
|
39
|
+
*/
|
|
40
|
+
proveTransaction(transaction, costModel) {
|
|
41
|
+
return this.#innerClient.proveTransaction(transaction, costModel).pipe(Effect.runPromise);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
import { Schema } from 'effect';
|
|
14
|
+
import { check, prove } from '@midnight-ntwrk/zkir-v2';
|
|
15
|
+
import { GetParamsOperationResultSchema, CheckOperationSchema, ProveOperationSchema, LookupKeyOperationResultSchema, ResponseFromWorkerSchema, LookupKeyRequestSchema, GetParamsRequestSchema, } from './effect/WasmProver.js';
|
|
16
|
+
const MAX_TIME_TO_PROCESS = 10 * 60 * 1000;
|
|
17
|
+
const keyMaterialProvider = {
|
|
18
|
+
lookupKey(keyLocation) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
postMessage(Schema.encodeSync(LookupKeyRequestSchema)({ op: 'lookupKey', keyLocation }));
|
|
21
|
+
const subscription = ({ data }) => {
|
|
22
|
+
const decoded = Schema.decodeUnknownSync(MessageDataSchema)(data);
|
|
23
|
+
if (decoded.op === 'lookupKey' && decoded.keyLocation === keyLocation) {
|
|
24
|
+
removeEventListener('message', subscription);
|
|
25
|
+
resolve(decoded.result);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
addEventListener('message', subscription);
|
|
29
|
+
setTimeout(() => reject(new Error(`Promise timed out for lookupKey: ${keyLocation}`)), MAX_TIME_TO_PROCESS);
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
getParams(k) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
postMessage(Schema.encodeSync(GetParamsRequestSchema)({ op: 'getParams', k }));
|
|
35
|
+
const subscription = ({ data }) => {
|
|
36
|
+
const decoded = Schema.decodeUnknownSync(MessageDataSchema)(data);
|
|
37
|
+
if (decoded.op === 'getParams' && decoded.k === k) {
|
|
38
|
+
removeEventListener('message', subscription);
|
|
39
|
+
resolve(decoded.result);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
addEventListener('message', subscription);
|
|
43
|
+
setTimeout(() => reject(new Error(`Promise timed out for getParams: ${k}`)), MAX_TIME_TO_PROCESS);
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
const MessageDataSchema = Schema.Union(CheckOperationSchema, ProveOperationSchema, LookupKeyOperationResultSchema, GetParamsOperationResultSchema);
|
|
48
|
+
addEventListener('message', ({ data }) => {
|
|
49
|
+
const decoded = Schema.decodeUnknownSync(MessageDataSchema)(data);
|
|
50
|
+
const { op } = decoded;
|
|
51
|
+
if (op === 'check') {
|
|
52
|
+
const [a] = decoded.args;
|
|
53
|
+
check(a, keyMaterialProvider)
|
|
54
|
+
.then((result) => {
|
|
55
|
+
postMessage(Schema.encodeSync(ResponseFromWorkerSchema)({
|
|
56
|
+
op: 'result',
|
|
57
|
+
value: result,
|
|
58
|
+
}));
|
|
59
|
+
})
|
|
60
|
+
.catch((e) => {
|
|
61
|
+
throw e;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
else if (op === 'prove') {
|
|
65
|
+
const [a, b] = decoded.args;
|
|
66
|
+
prove(a, keyMaterialProvider, b)
|
|
67
|
+
.then((result) => {
|
|
68
|
+
postMessage(Schema.encodeSync(ResponseFromWorkerSchema)({
|
|
69
|
+
op: 'result',
|
|
70
|
+
value: result,
|
|
71
|
+
}));
|
|
72
|
+
})
|
|
73
|
+
.catch((e) => {
|
|
74
|
+
throw e;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@midnightntwrk/wallet-sdk-prover-client",
|
|
3
|
+
"version": "1.2.3",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"author": "Midnight Foundation",
|
|
9
|
+
"license": "Apache-2.0",
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"registry": "https://registry.npmjs.org/",
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist/"
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/midnightntwrk/midnight-wallet.git",
|
|
20
|
+
"directory": "packages/prover-client"
|
|
21
|
+
},
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"import": "./dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./effect": {
|
|
28
|
+
"types": "./dist/effect/index.d.ts",
|
|
29
|
+
"import": "./dist/effect/index.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@effect/platform": "^0.96.0",
|
|
34
|
+
"@midnight-ntwrk/ledger-v8": "^8.1.0",
|
|
35
|
+
"@midnight-ntwrk/zkir-v2": "^2.1.0",
|
|
36
|
+
"@midnightntwrk/wallet-sdk-utilities": "^1.2.0",
|
|
37
|
+
"effect": "^3.19.19",
|
|
38
|
+
"web-worker": "^1.5.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"typecheck": "tsc -b ./tsconfig.json --noEmit --force",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"lint": "eslint --max-warnings 0",
|
|
44
|
+
"format": "prettier --write \"**/*.{ts,js,json,yaml,yml,md}\"",
|
|
45
|
+
"format:check": "prettier --check \"**/*.{ts,js,json,yaml,yml,md}\"",
|
|
46
|
+
"dist": "tsc -b ./tsconfig.build.json",
|
|
47
|
+
"dist:publish": "tsc -b ./tsconfig.publish.json",
|
|
48
|
+
"clean": "rimraf --glob dist 'tsconfig.*.tsbuildinfo' && date +%s > .clean-timestamp",
|
|
49
|
+
"publint": "publint --strict"
|
|
50
|
+
}
|
|
51
|
+
}
|