@aztec/foundation 0.70.0 → 0.72.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/dest/abi/decoder.d.ts +1 -1
- package/dest/abi/decoder.d.ts.map +1 -1
- package/dest/abi/decoder.js +11 -2
- package/dest/abi/encoder.d.ts.map +1 -1
- package/dest/abi/encoder.js +12 -2
- package/dest/abi/event_selector.d.ts +1 -2
- package/dest/abi/event_selector.d.ts.map +1 -1
- package/dest/abi/event_selector.js +2 -3
- package/dest/abi/function_selector.d.ts +1 -2
- package/dest/abi/function_selector.d.ts.map +1 -1
- package/dest/abi/function_selector.js +2 -3
- package/dest/abi/index.d.ts +1 -0
- package/dest/abi/index.d.ts.map +1 -1
- package/dest/abi/index.js +2 -1
- package/dest/abi/u128.d.ts +13 -0
- package/dest/abi/u128.d.ts.map +1 -0
- package/dest/abi/u128.js +58 -0
- package/dest/abi/utils.d.ts +5 -0
- package/dest/abi/utils.d.ts.map +1 -1
- package/dest/abi/utils.js +8 -1
- package/dest/array/array.d.ts +8 -0
- package/dest/array/array.d.ts.map +1 -1
- package/dest/array/array.js +13 -1
- package/dest/aztec-address/index.d.ts +3 -3
- package/dest/aztec-address/index.d.ts.map +1 -1
- package/dest/aztec-address/index.js +5 -5
- package/dest/collection/array.d.ts +7 -0
- package/dest/collection/array.d.ts.map +1 -1
- package/dest/collection/array.js +13 -1
- package/dest/config/env_var.d.ts +1 -1
- package/dest/config/env_var.d.ts.map +1 -1
- package/dest/config/index.d.ts +3 -1
- package/dest/config/index.d.ts.map +1 -1
- package/dest/config/index.js +6 -2
- package/dest/fields/fields.d.ts +10 -1
- package/dest/fields/fields.d.ts.map +1 -1
- package/dest/fields/fields.js +19 -10
- package/dest/fields/point.d.ts +4 -4
- package/dest/fields/point.d.ts.map +1 -1
- package/dest/fields/point.js +5 -5
- package/dest/json-rpc/client/fetch.d.ts +2 -2
- package/dest/json-rpc/client/fetch.d.ts.map +1 -1
- package/dest/json-rpc/client/fetch.js +7 -7
- package/dest/json-rpc/client/safe_json_rpc_client.d.ts.map +1 -1
- package/dest/json-rpc/client/safe_json_rpc_client.js +1 -1
- package/dest/json-rpc/server/safe_json_rpc_server.d.ts +17 -5
- package/dest/json-rpc/server/safe_json_rpc_server.d.ts.map +1 -1
- package/dest/json-rpc/server/safe_json_rpc_server.js +16 -9
- package/dest/json-rpc/server/telemetry.d.ts +2 -0
- package/dest/json-rpc/server/telemetry.d.ts.map +1 -0
- package/dest/json-rpc/server/telemetry.js +2 -0
- package/dest/log/gcloud-logger-config.d.ts +14 -0
- package/dest/log/gcloud-logger-config.d.ts.map +1 -0
- package/dest/log/gcloud-logger-config.js +64 -0
- package/dest/log/pino-logger.d.ts.map +1 -1
- package/dest/log/pino-logger.js +14 -40
- package/dest/noir/noir_package_config.d.ts +2 -2
- package/dest/testing/files/index.d.ts +1 -1
- package/dest/testing/files/index.d.ts.map +1 -1
- package/dest/testing/files/index.js +3 -3
- package/dest/timer/timeout.js +2 -2
- package/package.json +3 -3
- package/src/abi/decoder.ts +11 -2
- package/src/abi/encoder.ts +11 -1
- package/src/abi/event_selector.ts +1 -2
- package/src/abi/function_selector.ts +1 -2
- package/src/abi/index.ts +1 -0
- package/src/abi/u128.ts +71 -0
- package/src/abi/utils.ts +8 -0
- package/src/array/array.ts +15 -0
- package/src/aztec-address/index.ts +5 -5
- package/src/collection/array.ts +15 -0
- package/src/config/env_var.ts +3 -2
- package/src/config/index.ts +7 -2
- package/src/fields/fields.ts +19 -10
- package/src/fields/point.ts +6 -6
- package/src/json-rpc/client/fetch.ts +14 -6
- package/src/json-rpc/client/safe_json_rpc_client.ts +0 -1
- package/src/json-rpc/server/safe_json_rpc_server.ts +27 -11
- package/src/json-rpc/server/telemetry.ts +0 -0
- package/src/log/gcloud-logger-config.ts +71 -0
- package/src/log/pino-logger.ts +13 -42
- package/src/testing/files/index.ts +2 -2
- package/src/timer/timeout.ts +1 -1
package/src/abi/encoder.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Fr } from '../fields/index.js';
|
|
2
2
|
import { type AbiType, type FunctionAbi } from './abi.js';
|
|
3
|
-
import {
|
|
3
|
+
import { U128 } from './u128.js';
|
|
4
|
+
import { isAddressStruct, isFunctionSelectorStruct, isU128Struct, isWrappedFieldStruct } from './utils.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Encodes arguments for a function call.
|
|
@@ -105,6 +106,15 @@ class ArgumentEncoder {
|
|
|
105
106
|
this.encodeArgument({ kind: 'integer', sign: 'unsigned', width: 32 }, arg.value ?? arg, `${name}.inner`);
|
|
106
107
|
break;
|
|
107
108
|
}
|
|
109
|
+
if (isU128Struct(abiType)) {
|
|
110
|
+
// U128 struct has low and high limbs - so we first convert the value to the 2 limbs and then we encode them
|
|
111
|
+
const value = new U128(arg);
|
|
112
|
+
const limbs = value.toFields();
|
|
113
|
+
const limbNames = U128.getLimbNames();
|
|
114
|
+
this.encodeArgument({ kind: 'field' }, limbs[0], `${name}.${limbNames[0]}`);
|
|
115
|
+
this.encodeArgument({ kind: 'field' }, limbs[1], `${name}.${limbNames[1]}`);
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
108
118
|
if (isWrappedFieldStruct(abiType)) {
|
|
109
119
|
this.encodeArgument({ kind: 'field' }, arg.inner ?? arg, `${name}.inner`);
|
|
110
120
|
break;
|
|
@@ -53,11 +53,10 @@ export class EventSelector extends Selector {
|
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
55
|
* Create a Selector instance from a hex-encoded string.
|
|
56
|
-
* The input 'address' should be prefixed with '0x' or not, and have exactly 64 hex characters.
|
|
57
|
-
* Throws an error if the input length is invalid or address value is out of range.
|
|
58
56
|
*
|
|
59
57
|
* @param selector - The hex-encoded string representing the Selector.
|
|
60
58
|
* @returns An Selector instance.
|
|
59
|
+
* @throws If the selector length is invalid.
|
|
61
60
|
*/
|
|
62
61
|
static fromString(selector: string) {
|
|
63
62
|
const buf = fromHex(selector);
|
|
@@ -82,11 +82,10 @@ export class FunctionSelector extends Selector {
|
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
84
|
* Create a Selector instance from a hex-encoded string.
|
|
85
|
-
* The input 'address' should be prefixed with '0x' or not, and have exactly 64 hex characters.
|
|
86
|
-
* Throws an error if the input length is invalid or address value is out of range.
|
|
87
85
|
*
|
|
88
86
|
* @param selector - The hex-encoded string representing the Selector.
|
|
89
87
|
* @returns An Selector instance.
|
|
88
|
+
* @throws If the selector length is invalid.
|
|
90
89
|
*/
|
|
91
90
|
static fromString(selector: string) {
|
|
92
91
|
const buf = fromHex(selector);
|
package/src/abi/index.ts
CHANGED
package/src/abi/u128.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Fr } from '../fields/fields.js';
|
|
2
|
+
|
|
3
|
+
// A typescript version of noir::std::U128
|
|
4
|
+
export class U128 {
|
|
5
|
+
private readonly value: bigint;
|
|
6
|
+
|
|
7
|
+
constructor(value: bigint | number) {
|
|
8
|
+
if (typeof value === 'number') {
|
|
9
|
+
value = BigInt(value);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Check value is within 128 bits
|
|
13
|
+
if (value < 0n || value >= 2n ** 128n) {
|
|
14
|
+
throw new Error(`Value ${value} is not within 128 bits and hence cannot be converted to U128.`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
this.value = value;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static fromU64sLE(lo: bigint, hi: bigint): U128 {
|
|
21
|
+
// Validate limbs are within valid ranges
|
|
22
|
+
if (lo < 0n || lo >= 2n ** 64n) {
|
|
23
|
+
throw new Error(`Lower limb ${lo} is not within valid range (0 to 2^64-1)`);
|
|
24
|
+
}
|
|
25
|
+
if (hi < 0n || hi >= 2n ** 64n) {
|
|
26
|
+
throw new Error(`Higher limb ${hi} is not within valid range (0 to 2^64-1)`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Combine limbs into full value and create new instance
|
|
30
|
+
const value = (hi << 64n) | lo;
|
|
31
|
+
return new U128(value);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get lo(): bigint {
|
|
35
|
+
return this.value & 0xffffffffffffffffn;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get hi(): bigint {
|
|
39
|
+
return this.value >> 64n;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
toInteger(): bigint {
|
|
43
|
+
return this.value;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// We use little-endian ordering to match the order in which U128 defines its limbs.
|
|
47
|
+
// This is necessary because of how Noir handles serialization:
|
|
48
|
+
// - When calling a contract function from TypeScript, the serialization below gets used and then Noir
|
|
49
|
+
// deserializes using its intrinsic serialization logic (based on the limb order in the struct).
|
|
50
|
+
// - When calling a contract function from another function, the `serialize` method is invoked
|
|
51
|
+
// on the type first.
|
|
52
|
+
// For this reason if we didn't use the ordering of U128 limbs here and in the implementation of Serialize
|
|
53
|
+
// trait for U128 we would get an arguments hash mismatch.
|
|
54
|
+
toFields(): Fr[] {
|
|
55
|
+
return [new Fr(this.lo), new Fr(this.hi)];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Has to follow ordering of `toFields()`
|
|
59
|
+
static fromFields(fields: Fr[]): U128 {
|
|
60
|
+
if (fields.length !== 2) {
|
|
61
|
+
throw new Error(`Expected 2 fields for U128, got ${fields.length}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return U128.fromU64sLE(fields[0].toBigInt(), fields[1].toBigInt());
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Has to follow ordering of `toFields()`
|
|
68
|
+
static getLimbNames(): string[] {
|
|
69
|
+
return ['lo', 'hi'];
|
|
70
|
+
}
|
|
71
|
+
}
|
package/src/abi/utils.ts
CHANGED
|
@@ -36,6 +36,14 @@ export function isFunctionSelectorStruct(abiType: AbiType) {
|
|
|
36
36
|
return abiType.kind === 'struct' && abiType.path.endsWith('types::abis::function_selector::FunctionSelector');
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Returns whether the ABI type is the U128 defined in noir::std.
|
|
41
|
+
* @param abiType - Type to check.
|
|
42
|
+
*/
|
|
43
|
+
export function isU128Struct(abiType: AbiType) {
|
|
44
|
+
return abiType.kind === 'struct' && abiType.path.endsWith('U128');
|
|
45
|
+
}
|
|
46
|
+
|
|
39
47
|
/**
|
|
40
48
|
* Returns whether the ABI type is a struct with a single `inner` field.
|
|
41
49
|
* @param abiType - Type to check.
|
package/src/array/array.ts
CHANGED
|
@@ -27,6 +27,21 @@ export function makeTuple<T, N extends number>(length: N, fn: (i: number) => T,
|
|
|
27
27
|
return Array.from({ length }, (_: any, i: number) => fn(i + offset)) as Tuple<T, N>;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Create an array over an integer range, filled with a function 'fn'.
|
|
32
|
+
* This is used over e.g. lodash because it resolved to a tuple type, needed for our fixed array type safety.
|
|
33
|
+
* @param n - The number of integers.
|
|
34
|
+
* @param fn - The generator function.
|
|
35
|
+
* @returns The array of numbers.
|
|
36
|
+
*/
|
|
37
|
+
export async function makeTupleAsync<T, N extends number>(length: N, fn: (i: number) => Promise<T>, offset = 0) {
|
|
38
|
+
return (await Promise.all(
|
|
39
|
+
Array(length)
|
|
40
|
+
.fill(0)
|
|
41
|
+
.map(async (_: any, i: number) => await fn(i + offset)),
|
|
42
|
+
)) as Tuple<T, N>;
|
|
43
|
+
}
|
|
44
|
+
|
|
30
45
|
/**
|
|
31
46
|
* Create an array over an integer range, filled with a function 'fn'. However, the latter half of the array are set to zeros.
|
|
32
47
|
* see `makeTuple` above.
|
|
@@ -75,11 +75,11 @@ export class AztecAddress {
|
|
|
75
75
|
/**
|
|
76
76
|
* @returns a random valid address (i.e. one that can be encrypted to).
|
|
77
77
|
*/
|
|
78
|
-
static random() {
|
|
78
|
+
static async random() {
|
|
79
79
|
// About half of random field elements result in invalid addresses, so we loop until we get a valid one.
|
|
80
80
|
while (true) {
|
|
81
81
|
const candidate = new AztecAddress(Fr.random());
|
|
82
|
-
if (candidate.isValid()) {
|
|
82
|
+
if (await candidate.isValid()) {
|
|
83
83
|
return candidate;
|
|
84
84
|
}
|
|
85
85
|
}
|
|
@@ -100,20 +100,20 @@ export class AztecAddress {
|
|
|
100
100
|
/**
|
|
101
101
|
* @returns true if the address is valid. Invalid addresses cannot receive encrypted messages.
|
|
102
102
|
*/
|
|
103
|
-
isValid() {
|
|
103
|
+
async isValid() {
|
|
104
104
|
// An address is a field value (Fr), which for some purposes is assumed to be the x coordinate of a point in the
|
|
105
105
|
// Grumpkin curve (notably in order to encrypt to it). An address that is not the x coordinate of such a point is
|
|
106
106
|
// called an 'invalid' address.
|
|
107
107
|
//
|
|
108
108
|
// For Grumpkin, y^2 = x^3 − 17 . There exist values x ∈ Fr for which no y satisfies this equation. This means that
|
|
109
109
|
// given such an x and t = x^3 − 17, then sqrt(t) does not exist in Fr.
|
|
110
|
-
return Point.YFromX(this.xCoord) !== null;
|
|
110
|
+
return (await Point.YFromX(this.xCoord)) !== null;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/**
|
|
114
114
|
* @returns the Point from which the address is derived. Throws if the address is invalid.
|
|
115
115
|
*/
|
|
116
|
-
toAddressPoint() {
|
|
116
|
+
toAddressPoint(): Promise<Point> {
|
|
117
117
|
return Point.fromXAndSign(this.xCoord, true);
|
|
118
118
|
}
|
|
119
119
|
|
package/src/collection/array.ts
CHANGED
|
@@ -89,6 +89,21 @@ export async function timesAsync<T>(n: number, fn: (i: number) => Promise<T>): P
|
|
|
89
89
|
return results;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Executes the given async function n times in parallel and returns the results in an array.
|
|
94
|
+
* @param n - How many times to repeat.
|
|
95
|
+
* @param fn - Mapper from index to value.
|
|
96
|
+
* @returns The array with the result from all executions.
|
|
97
|
+
*/
|
|
98
|
+
export async function timesParallel<T>(n: number, fn: (i: number) => Promise<T>): Promise<T[]> {
|
|
99
|
+
const results: T[] = await Promise.all(
|
|
100
|
+
Array(n)
|
|
101
|
+
.fill(0)
|
|
102
|
+
.map((_, i) => fn(i)),
|
|
103
|
+
);
|
|
104
|
+
return results;
|
|
105
|
+
}
|
|
106
|
+
|
|
92
107
|
/**
|
|
93
108
|
* Returns the serialized size of all non-empty items in an array.
|
|
94
109
|
* @param arr - Array
|
package/src/config/env_var.ts
CHANGED
|
@@ -42,7 +42,6 @@ export type EnvVar =
|
|
|
42
42
|
| 'DEBUG'
|
|
43
43
|
| 'DEPLOY_AZTEC_CONTRACTS_SALT'
|
|
44
44
|
| 'DEPLOY_AZTEC_CONTRACTS'
|
|
45
|
-
| 'ENABLE_GAS'
|
|
46
45
|
| 'ENFORCE_FEES'
|
|
47
46
|
| 'ETHEREUM_HOST'
|
|
48
47
|
| 'FEE_JUICE_CONTRACT_ADDRESS'
|
|
@@ -70,6 +69,7 @@ export type EnvVar =
|
|
|
70
69
|
| 'OTEL_EXPORTER_OTLP_LOGS_ENDPOINT'
|
|
71
70
|
| 'OTEL_SERVICE_NAME'
|
|
72
71
|
| 'OTEL_COLLECT_INTERVAL_MS'
|
|
72
|
+
| 'OTEL_EXCLUDE_METRICS'
|
|
73
73
|
| 'OTEL_EXPORT_TIMEOUT_MS'
|
|
74
74
|
| 'OUTBOX_CONTRACT_ADDRESS'
|
|
75
75
|
| 'P2P_BLOCK_CHECK_INTERVAL_MS'
|
|
@@ -100,6 +100,7 @@ export type EnvVar =
|
|
|
100
100
|
| 'P2P_TX_PROTOCOL'
|
|
101
101
|
| 'P2P_UDP_ANNOUNCE_ADDR'
|
|
102
102
|
| 'P2P_UDP_LISTEN_ADDR'
|
|
103
|
+
| 'P2P_ARCHIVED_TX_LIMIT'
|
|
103
104
|
| 'PEER_ID_PRIVATE_KEY'
|
|
104
105
|
| 'PROVER_BLOB_SINK_URL'
|
|
105
106
|
| 'PROOF_VERIFIER_L1_START_BLOCK'
|
|
@@ -116,6 +117,7 @@ export type EnvVar =
|
|
|
116
117
|
| 'PROVER_BROKER_JOB_MAX_RETRIES'
|
|
117
118
|
| 'PROVER_COORDINATION_NODE_URL'
|
|
118
119
|
| 'PROVER_DISABLED'
|
|
120
|
+
| 'PROVER_FAILED_PROOF_STORE'
|
|
119
121
|
| 'PROVER_ID'
|
|
120
122
|
| 'PROVER_JOB_POLL_INTERVAL_MS'
|
|
121
123
|
| 'PROVER_JOB_TIMEOUT_MS'
|
|
@@ -160,7 +162,6 @@ export type EnvVar =
|
|
|
160
162
|
| 'TX_GOSSIP_VERSION'
|
|
161
163
|
| 'TXE_PORT'
|
|
162
164
|
| 'VALIDATOR_ATTESTATIONS_POLLING_INTERVAL_MS'
|
|
163
|
-
| 'VALIDATOR_ATTESTATIONS_WAIT_TIMEOUT_MS'
|
|
164
165
|
| 'VALIDATOR_DISABLED'
|
|
165
166
|
| 'VALIDATOR_PRIVATE_KEY'
|
|
166
167
|
| 'VALIDATOR_REEXECUTE'
|
package/src/config/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type EnvVar } from './env_var.js';
|
|
2
2
|
|
|
3
|
-
export { EnvVar } from './env_var.js';
|
|
3
|
+
export { type EnvVar } from './env_var.js';
|
|
4
4
|
|
|
5
5
|
export interface ConfigMapping {
|
|
6
6
|
env?: EnvVar;
|
|
@@ -102,7 +102,7 @@ export function optionalNumberConfigHelper(): Pick<ConfigMapping, 'parseEnv'> {
|
|
|
102
102
|
export function booleanConfigHelper(
|
|
103
103
|
defaultVal = false,
|
|
104
104
|
): Required<Pick<ConfigMapping, 'parseEnv' | 'defaultValue' | 'isBoolean'> & { parseVal: (val: string) => boolean }> {
|
|
105
|
-
const parse = (val: string | boolean) => (typeof val === 'boolean' ? val :
|
|
105
|
+
const parse = (val: string | boolean) => (typeof val === 'boolean' ? val : parseBooleanEnv(val));
|
|
106
106
|
return {
|
|
107
107
|
parseEnv: parse,
|
|
108
108
|
parseVal: parse,
|
|
@@ -111,6 +111,11 @@ export function booleanConfigHelper(
|
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
/** Parses an env var as boolean. Returns true only if value is 1, true, or TRUE. */
|
|
115
|
+
export function parseBooleanEnv(val: string | undefined): boolean {
|
|
116
|
+
return val !== undefined && ['1', 'true', 'TRUE'].includes(val);
|
|
117
|
+
}
|
|
118
|
+
|
|
114
119
|
/**
|
|
115
120
|
* Safely parses a number from a string.
|
|
116
121
|
* If the value is not a number or is not a safe integer, the default value is returned.
|
package/src/fields/fields.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BarretenbergLazy } from '@aztec/bb.js';
|
|
2
2
|
|
|
3
3
|
import { inspect } from 'util';
|
|
4
4
|
|
|
@@ -99,6 +99,10 @@ abstract class BaseField {
|
|
|
99
99
|
return Boolean(this.toBigInt());
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Converts this field to a number.
|
|
104
|
+
* Throws if the underlying value is greater than MAX_SAFE_INTEGER.
|
|
105
|
+
*/
|
|
102
106
|
toNumber(): number {
|
|
103
107
|
const value = this.toBigInt();
|
|
104
108
|
if (value > Number.MAX_SAFE_INTEGER) {
|
|
@@ -107,6 +111,15 @@ abstract class BaseField {
|
|
|
107
111
|
return Number(value);
|
|
108
112
|
}
|
|
109
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Converts this field to a number.
|
|
116
|
+
* May cause loss of precision if the underlying value is greater than MAX_SAFE_INTEGER.
|
|
117
|
+
*/
|
|
118
|
+
toNumberUnsafe(): number {
|
|
119
|
+
const value = this.toBigInt();
|
|
120
|
+
return Number(value);
|
|
121
|
+
}
|
|
122
|
+
|
|
110
123
|
toShortString(): string {
|
|
111
124
|
const str = this.toString();
|
|
112
125
|
return `${str.slice(0, 10)}...${str.slice(-4)}`;
|
|
@@ -305,19 +318,15 @@ export class Fr extends BaseField {
|
|
|
305
318
|
* Computes a square root of the field element.
|
|
306
319
|
* @returns A square root of the field element (null if it does not exist).
|
|
307
320
|
*/
|
|
308
|
-
sqrt(): Fr | null {
|
|
309
|
-
const wasm =
|
|
310
|
-
wasm.
|
|
311
|
-
|
|
312
|
-
const isSqrtBuf = Buffer.from(wasm.getMemorySlice(Fr.SIZE_IN_BYTES, Fr.SIZE_IN_BYTES + 1));
|
|
313
|
-
const isSqrt = isSqrtBuf[0] === 1;
|
|
321
|
+
async sqrt(): Promise<Fr | null> {
|
|
322
|
+
const wasm = (await BarretenbergLazy.getSingleton()).getWasm();
|
|
323
|
+
const [buf] = await wasm.callWasmExport('bn254_fr_sqrt', [this.toBuffer()], [Fr.SIZE_IN_BYTES + 1]);
|
|
324
|
+
const isSqrt = buf[0] === 1;
|
|
314
325
|
if (!isSqrt) {
|
|
315
326
|
// Field element is not a quadratic residue mod p so it has no square root.
|
|
316
327
|
return null;
|
|
317
328
|
}
|
|
318
|
-
|
|
319
|
-
const rootBuf = Buffer.from(wasm.getMemorySlice(Fr.SIZE_IN_BYTES + 1, Fr.SIZE_IN_BYTES * 2 + 1));
|
|
320
|
-
return Fr.fromBuffer(rootBuf);
|
|
329
|
+
return new Fr(Buffer.from(buf.slice(1)));
|
|
321
330
|
}
|
|
322
331
|
|
|
323
332
|
toJSON() {
|
package/src/fields/point.ts
CHANGED
|
@@ -50,10 +50,10 @@ export class Point {
|
|
|
50
50
|
*
|
|
51
51
|
* @returns A randomly generated Point instance.
|
|
52
52
|
*/
|
|
53
|
-
static random() {
|
|
53
|
+
static async random() {
|
|
54
54
|
while (true) {
|
|
55
55
|
try {
|
|
56
|
-
return Point.fromXAndSign(Fr.random(), randomBoolean());
|
|
56
|
+
return await Point.fromXAndSign(Fr.random(), randomBoolean());
|
|
57
57
|
} catch (e: any) {
|
|
58
58
|
if (!(e instanceof NotOnCurveError)) {
|
|
59
59
|
throw e;
|
|
@@ -83,7 +83,7 @@ export class Point {
|
|
|
83
83
|
* @param buffer - The buffer containing the x coordinate and the sign of the y coordinate.
|
|
84
84
|
* @returns A Point instance.
|
|
85
85
|
*/
|
|
86
|
-
static fromCompressedBuffer(buffer: Buffer | BufferReader) {
|
|
86
|
+
static fromCompressedBuffer(buffer: Buffer | BufferReader): Promise<Point> {
|
|
87
87
|
const reader = BufferReader.asReader(buffer);
|
|
88
88
|
const value = toBigIntBE(reader.readBytes(Point.COMPRESSED_SIZE_IN_BYTES));
|
|
89
89
|
|
|
@@ -127,8 +127,8 @@ export class Point {
|
|
|
127
127
|
* Instead it is a boolean flag that determines whether the y coordinate is <= (Fr.MODULUS - 1) / 2
|
|
128
128
|
* @returns The point as an array of 2 fields
|
|
129
129
|
*/
|
|
130
|
-
static fromXAndSign(x: Fr, sign: boolean) {
|
|
131
|
-
const y = Point.YFromX(x);
|
|
130
|
+
static async fromXAndSign(x: Fr, sign: boolean) {
|
|
131
|
+
const y = await Point.YFromX(x);
|
|
132
132
|
if (y == null) {
|
|
133
133
|
throw new NotOnCurveError(x);
|
|
134
134
|
}
|
|
@@ -146,7 +146,7 @@ export class Point {
|
|
|
146
146
|
/**
|
|
147
147
|
* @returns
|
|
148
148
|
*/
|
|
149
|
-
static YFromX(x: Fr): Fr | null {
|
|
149
|
+
static YFromX(x: Fr): Promise<Fr | null> {
|
|
150
150
|
// Calculate y^2 = x^3 - 17 (i.e. the Grumpkin curve equation)
|
|
151
151
|
const ySquared = x.square().mul(x).sub(new Fr(17));
|
|
152
152
|
|
|
@@ -21,6 +21,7 @@ export async function defaultFetch(
|
|
|
21
21
|
rpcMethod: string,
|
|
22
22
|
body: any,
|
|
23
23
|
useApiEndpoints: boolean,
|
|
24
|
+
extraHeaders: Record<string, string> = {},
|
|
24
25
|
noRetry = false,
|
|
25
26
|
) {
|
|
26
27
|
log.debug(format(`JsonRpcClient.fetch`, host, rpcMethod, '->', body));
|
|
@@ -30,13 +31,13 @@ export async function defaultFetch(
|
|
|
30
31
|
resp = await fetch(`${host}/${rpcMethod}`, {
|
|
31
32
|
method: 'POST',
|
|
32
33
|
body: jsonStringify(body),
|
|
33
|
-
headers: { 'content-type': 'application/json' },
|
|
34
|
+
headers: { 'content-type': 'application/json', ...extraHeaders },
|
|
34
35
|
});
|
|
35
36
|
} else {
|
|
36
37
|
resp = await fetch(host, {
|
|
37
38
|
method: 'POST',
|
|
38
39
|
body: jsonStringify({ ...body, method: rpcMethod }),
|
|
39
|
-
headers: { 'content-type': 'application/json' },
|
|
40
|
+
headers: { 'content-type': 'application/json', ...extraHeaders },
|
|
40
41
|
});
|
|
41
42
|
}
|
|
42
43
|
} catch (err) {
|
|
@@ -55,7 +56,7 @@ export async function defaultFetch(
|
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
if (!resp.ok) {
|
|
58
|
-
const errorMessage = `
|
|
59
|
+
const errorMessage = `Error ${resp.status} from server ${host} on ${rpcMethod}: ${responseJson.error.message}`;
|
|
59
60
|
if (noRetry || (resp.status >= 400 && resp.status < 500)) {
|
|
60
61
|
throw new NoRetryError(errorMessage);
|
|
61
62
|
} else {
|
|
@@ -73,10 +74,17 @@ export async function defaultFetch(
|
|
|
73
74
|
* @param log - Optional logger for logging attempts.
|
|
74
75
|
* @returns A fetch function.
|
|
75
76
|
*/
|
|
76
|
-
export function makeFetch(retries: number[], defaultNoRetry: boolean, log?: Logger) {
|
|
77
|
-
return async (
|
|
77
|
+
export function makeFetch(retries: number[], defaultNoRetry: boolean, log?: Logger): typeof defaultFetch {
|
|
78
|
+
return async (
|
|
79
|
+
host: string,
|
|
80
|
+
rpcMethod: string,
|
|
81
|
+
body: any,
|
|
82
|
+
useApiEndpoints: boolean,
|
|
83
|
+
extraHeaders: Record<string, string> = {},
|
|
84
|
+
noRetry?: boolean,
|
|
85
|
+
) => {
|
|
78
86
|
return await retry(
|
|
79
|
-
() => defaultFetch(host, rpcMethod, body, useApiEndpoints, noRetry ?? defaultNoRetry),
|
|
87
|
+
() => defaultFetch(host, rpcMethod, body, useApiEndpoints, extraHeaders, noRetry ?? defaultNoRetry),
|
|
80
88
|
`JsonRpcClient request ${rpcMethod} to ${host}`,
|
|
81
89
|
makeBackoff(retries),
|
|
82
90
|
log,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import cors from '@koa/cors';
|
|
2
2
|
import http from 'http';
|
|
3
|
-
import Koa from 'koa';
|
|
3
|
+
import { type default as Application, default as Koa } from 'koa';
|
|
4
4
|
import bodyParser from 'koa-bodyparser';
|
|
5
5
|
import compress from 'koa-compress';
|
|
6
6
|
import Router from 'koa-router';
|
|
@@ -14,6 +14,15 @@ import { type ApiSchema, type ApiSchemaFor, parseWithOptionals, schemaHasMethod
|
|
|
14
14
|
import { jsonStringify } from '../convert.js';
|
|
15
15
|
import { assert } from '../js_utils.js';
|
|
16
16
|
|
|
17
|
+
export type DiagnosticsData = {
|
|
18
|
+
id: number | string | null;
|
|
19
|
+
method: string;
|
|
20
|
+
params: any[];
|
|
21
|
+
headers: http.IncomingHttpHeaders;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type DiagnosticsMiddleware = (ctx: DiagnosticsData, next: () => Promise<void>) => Promise<void>;
|
|
25
|
+
|
|
17
26
|
export class SafeJsonRpcServer {
|
|
18
27
|
/**
|
|
19
28
|
* The HTTP server accepting remote requests.
|
|
@@ -31,6 +40,8 @@ export class SafeJsonRpcServer {
|
|
|
31
40
|
private http200OnError = false,
|
|
32
41
|
/** Health check function */
|
|
33
42
|
private readonly healthCheck: StatusCheckFn = () => true,
|
|
43
|
+
/** Additional middlewares */
|
|
44
|
+
private extraMiddlewares: Application.Middleware[] = [],
|
|
34
45
|
/** Logger */
|
|
35
46
|
private log = createLogger('json-rpc:server'),
|
|
36
47
|
) {}
|
|
@@ -90,8 +101,11 @@ export class SafeJsonRpcServer {
|
|
|
90
101
|
this.log.error(`Error on API handler: ${error}`);
|
|
91
102
|
});
|
|
92
103
|
|
|
93
|
-
app.use(compress({ br: false }
|
|
104
|
+
app.use(compress({ br: false }));
|
|
94
105
|
app.use(jsonResponse);
|
|
106
|
+
for (const middleware of this.extraMiddlewares) {
|
|
107
|
+
app.use(middleware);
|
|
108
|
+
}
|
|
95
109
|
app.use(exceptionHandler);
|
|
96
110
|
app.use(bodyParser({ jsonLimit: '50mb', enableTypes: ['json'], detectJSON: () => true }));
|
|
97
111
|
app.use(cors());
|
|
@@ -114,7 +128,9 @@ export class SafeJsonRpcServer {
|
|
|
114
128
|
// Fail if not a registered function in the proxy
|
|
115
129
|
if (typeof method !== 'string' || method === 'constructor' || !this.proxy.hasMethod(method)) {
|
|
116
130
|
ctx.status = 400;
|
|
117
|
-
|
|
131
|
+
const code = -32601;
|
|
132
|
+
const message = `Method not found: ${method}`;
|
|
133
|
+
ctx.body = { jsonrpc, id, error: { code, message } };
|
|
118
134
|
} else {
|
|
119
135
|
ctx.status = 200;
|
|
120
136
|
const result = await this.proxy.call(method, params);
|
|
@@ -263,10 +279,11 @@ function makeAggregateHealthcheck(namedHandlers: NamespacedApiHandlers, log?: Lo
|
|
|
263
279
|
};
|
|
264
280
|
}
|
|
265
281
|
|
|
266
|
-
type SafeJsonRpcServerOptions = {
|
|
282
|
+
export type SafeJsonRpcServerOptions = {
|
|
267
283
|
http200OnError: boolean;
|
|
268
284
|
healthCheck?: StatusCheckFn;
|
|
269
285
|
log?: Logger;
|
|
286
|
+
middlewares?: Application.Middleware[];
|
|
270
287
|
};
|
|
271
288
|
|
|
272
289
|
/**
|
|
@@ -276,25 +293,24 @@ type SafeJsonRpcServerOptions = {
|
|
|
276
293
|
*/
|
|
277
294
|
export function createNamespacedSafeJsonRpcServer(
|
|
278
295
|
handlers: NamespacedApiHandlers,
|
|
279
|
-
options: Omit<SafeJsonRpcServerOptions, 'healthcheck'
|
|
280
|
-
http200OnError: false,
|
|
296
|
+
options: Partial<Omit<SafeJsonRpcServerOptions, 'healthcheck'>> = {
|
|
281
297
|
log: createLogger('json-rpc:server'),
|
|
282
298
|
},
|
|
283
299
|
): SafeJsonRpcServer {
|
|
284
|
-
const { http200OnError, log } = options;
|
|
300
|
+
const { middlewares, http200OnError, log } = options;
|
|
285
301
|
const proxy = new NamespacedSafeJsonProxy(handlers);
|
|
286
302
|
const healthCheck = makeAggregateHealthcheck(handlers, log);
|
|
287
|
-
return new SafeJsonRpcServer(proxy, http200OnError, healthCheck, log);
|
|
303
|
+
return new SafeJsonRpcServer(proxy, http200OnError, healthCheck, middlewares, log);
|
|
288
304
|
}
|
|
289
305
|
|
|
290
306
|
export function createSafeJsonRpcServer<T extends object = any>(
|
|
291
307
|
handler: T,
|
|
292
308
|
schema: ApiSchemaFor<T>,
|
|
293
|
-
options: SafeJsonRpcServerOptions = {
|
|
309
|
+
options: Partial<SafeJsonRpcServerOptions> = {},
|
|
294
310
|
) {
|
|
295
|
-
const { http200OnError, log, healthCheck } = options;
|
|
311
|
+
const { http200OnError, log, healthCheck, middlewares: extraMiddlewares } = options;
|
|
296
312
|
const proxy = new SafeJsonProxy(handler, schema);
|
|
297
|
-
return new SafeJsonRpcServer(proxy, http200OnError, healthCheck, log);
|
|
313
|
+
return new SafeJsonRpcServer(proxy, http200OnError, healthCheck, extraMiddlewares, log);
|
|
298
314
|
}
|
|
299
315
|
|
|
300
316
|
/**
|
|
File without changes
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { type pino } from 'pino';
|
|
2
|
+
|
|
3
|
+
/* eslint-disable camelcase */
|
|
4
|
+
|
|
5
|
+
const GOOGLE_CLOUD_TRACE_ID = 'logging.googleapis.com/trace';
|
|
6
|
+
const GOOGLE_CLOUD_SPAN_ID = 'logging.googleapis.com/spanId';
|
|
7
|
+
const GOOGLE_CLOUD_TRACE_SAMPLED = 'logging.googleapis.com/trace_sampled';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Pino configuration for google cloud observability. Tweaks message and timestamp,
|
|
11
|
+
* adds trace context attributes, and injects severity level.
|
|
12
|
+
* Adapted from https://cloud.google.com/trace/docs/setup/nodejs-ot#config-structured-logging.
|
|
13
|
+
*/
|
|
14
|
+
export const GoogleCloudLoggerConfig = {
|
|
15
|
+
messageKey: 'message',
|
|
16
|
+
// Same as pino.stdTimeFunctions.isoTime but uses "timestamp" key instead of "time"
|
|
17
|
+
timestamp(): string {
|
|
18
|
+
return `,"timestamp":"${new Date(Date.now()).toISOString()}"`;
|
|
19
|
+
},
|
|
20
|
+
formatters: {
|
|
21
|
+
log(object: Record<string, unknown>): Record<string, unknown> {
|
|
22
|
+
// Add trace context attributes following Cloud Logging structured log format described
|
|
23
|
+
// in https://cloud.google.com/logging/docs/structured-logging#special-payload-fields
|
|
24
|
+
const { trace_id, span_id, trace_flags, ...rest } = object;
|
|
25
|
+
|
|
26
|
+
if (trace_id && span_id) {
|
|
27
|
+
return {
|
|
28
|
+
[GOOGLE_CLOUD_TRACE_ID]: trace_id,
|
|
29
|
+
[GOOGLE_CLOUD_SPAN_ID]: span_id,
|
|
30
|
+
[GOOGLE_CLOUD_TRACE_SAMPLED]: trace_flags ? trace_flags === '01' : undefined,
|
|
31
|
+
trace_flags, // Keep the original trace_flags for otel-pino-stream
|
|
32
|
+
...rest,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return object;
|
|
36
|
+
},
|
|
37
|
+
level(label: string, level: number): object {
|
|
38
|
+
// Inspired by https://github.com/pinojs/pino/issues/726#issuecomment-605814879
|
|
39
|
+
// Severity labels https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity
|
|
40
|
+
let severity: string;
|
|
41
|
+
|
|
42
|
+
switch (label as pino.Level | keyof typeof customLevels) {
|
|
43
|
+
case 'trace':
|
|
44
|
+
case 'debug':
|
|
45
|
+
severity = 'DEBUG';
|
|
46
|
+
break;
|
|
47
|
+
case 'verbose':
|
|
48
|
+
case 'info':
|
|
49
|
+
severity = 'INFO';
|
|
50
|
+
break;
|
|
51
|
+
case 'warn':
|
|
52
|
+
severity = 'WARNING';
|
|
53
|
+
break;
|
|
54
|
+
case 'error':
|
|
55
|
+
severity = 'ERROR';
|
|
56
|
+
break;
|
|
57
|
+
case 'fatal':
|
|
58
|
+
severity = 'CRITICAL';
|
|
59
|
+
break;
|
|
60
|
+
default:
|
|
61
|
+
severity = 'DEFAULT';
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return { severity, level };
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
} satisfies pino.LoggerOptions;
|
|
69
|
+
|
|
70
|
+
// Define custom logging levels for pino. Duplicate from pino-logger.ts.
|
|
71
|
+
const customLevels = { verbose: 25 };
|