Skip to content

Commit 1249c3c

Browse files
authored
feat(rust,rust-nodejs,ironfish): Remove old encrypt/decrypt methods (#5377)
* feat(ironfish): Create master key * feat(rust,rust-nodejs,ironfish): Remove old encrypt/decrypt methods * chore(rust): lint * test(ironfish): Fix tests * Fix test * fixtures
1 parent e3a51ce commit 1249c3c

22 files changed

+265
-402
lines changed

ironfish-rust-nodejs/index.d.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,6 @@ export const TRANSACTION_EXPIRATION_LENGTH: number
6464
export const TRANSACTION_FEE_LENGTH: number
6565
export const LATEST_TRANSACTION_VERSION: number
6666
export declare function verifyTransactions(serializedTransactions: Array<Buffer>): boolean
67-
export declare function encrypt(plaintext: Buffer, passphrase: string): Buffer
68-
export declare function decrypt(encryptedBlob: Buffer, passphrase: string): Buffer
6967
export const enum LanguageCode {
7068
English = 0,
7169
ChineseSimplified = 1,

ironfish-rust-nodejs/index.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ if (!nativeBinding) {
252252
throw new Error(`Failed to load native binding`)
253253
}
254254

255-
const { FishHashContext, deserializePublicPackage, deserializeRound2CombinedPublicPackage, KEY_LENGTH, NONCE_LENGTH, BoxKeyPair, randomBytes, boxMessage, unboxMessage, RollingFilter, initSignalHandler, triggerSegfault, ASSET_ID_LENGTH, ASSET_METADATA_LENGTH, ASSET_NAME_LENGTH, ASSET_LENGTH, Asset, NOTE_ENCRYPTION_KEY_LENGTH, MAC_LENGTH, ENCRYPTED_NOTE_PLAINTEXT_LENGTH, ENCRYPTED_NOTE_LENGTH, NoteEncrypted, PUBLIC_ADDRESS_LENGTH, RANDOMNESS_LENGTH, MEMO_LENGTH, AMOUNT_VALUE_LENGTH, DECRYPTED_NOTE_LENGTH, Note, PROOF_LENGTH, TRANSACTION_SIGNATURE_LENGTH, TRANSACTION_PUBLIC_KEY_RANDOMNESS_LENGTH, TRANSACTION_EXPIRATION_LENGTH, TRANSACTION_FEE_LENGTH, LATEST_TRANSACTION_VERSION, TransactionPosted, Transaction, verifyTransactions, UnsignedTransaction, encrypt, decrypt, LanguageCode, generateKey, spendingKeyToWords, wordsToSpendingKey, generatePublicAddressFromIncomingViewKey, generateKeyFromPrivateKey, initializeSapling, FoundBlockResult, ThreadPoolHandler, isValidPublicAddress, CpuCount, getCpuCount, generateRandomizedPublicKey, multisig, xchacha20poly1305 } = nativeBinding
255+
const { FishHashContext, deserializePublicPackage, deserializeRound2CombinedPublicPackage, KEY_LENGTH, NONCE_LENGTH, BoxKeyPair, randomBytes, boxMessage, unboxMessage, RollingFilter, initSignalHandler, triggerSegfault, ASSET_ID_LENGTH, ASSET_METADATA_LENGTH, ASSET_NAME_LENGTH, ASSET_LENGTH, Asset, NOTE_ENCRYPTION_KEY_LENGTH, MAC_LENGTH, ENCRYPTED_NOTE_PLAINTEXT_LENGTH, ENCRYPTED_NOTE_LENGTH, NoteEncrypted, PUBLIC_ADDRESS_LENGTH, RANDOMNESS_LENGTH, MEMO_LENGTH, AMOUNT_VALUE_LENGTH, DECRYPTED_NOTE_LENGTH, Note, PROOF_LENGTH, TRANSACTION_SIGNATURE_LENGTH, TRANSACTION_PUBLIC_KEY_RANDOMNESS_LENGTH, TRANSACTION_EXPIRATION_LENGTH, TRANSACTION_FEE_LENGTH, LATEST_TRANSACTION_VERSION, TransactionPosted, Transaction, verifyTransactions, UnsignedTransaction, LanguageCode, generateKey, spendingKeyToWords, wordsToSpendingKey, generatePublicAddressFromIncomingViewKey, generateKeyFromPrivateKey, initializeSapling, FoundBlockResult, ThreadPoolHandler, isValidPublicAddress, CpuCount, getCpuCount, generateRandomizedPublicKey, multisig, xchacha20poly1305 } = nativeBinding
256256

257257
module.exports.FishHashContext = FishHashContext
258258
module.exports.deserializePublicPackage = deserializePublicPackage
@@ -292,8 +292,6 @@ module.exports.TransactionPosted = TransactionPosted
292292
module.exports.Transaction = Transaction
293293
module.exports.verifyTransactions = verifyTransactions
294294
module.exports.UnsignedTransaction = UnsignedTransaction
295-
module.exports.encrypt = encrypt
296-
module.exports.decrypt = decrypt
297295
module.exports.LanguageCode = LanguageCode
298296
module.exports.generateKey = generateKey
299297
module.exports.spendingKeyToWords = spendingKeyToWords

ironfish-rust-nodejs/src/xchacha20poly1305.rs

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
44

55
use ironfish::xchacha20poly1305::{
6-
self, EncryptOutput, XChaCha20Poly1305Key, KEY_LENGTH as KEY_SIZE, SALT_LENGTH as SALT_SIZE,
6+
XChaCha20Poly1305Key, KEY_LENGTH as KEY_SIZE, SALT_LENGTH as SALT_SIZE,
77
XNONCE_LENGTH as XNONCE_SIZE,
88
};
99
use napi::{bindgen_prelude::*, JsBuffer};
@@ -139,26 +139,3 @@ impl NativeXChaCha20Poly1305Key {
139139
Ok(Buffer::from(&result[..]))
140140
}
141141
}
142-
143-
#[napi]
144-
pub fn encrypt(plaintext: JsBuffer, passphrase: String) -> Result<Buffer> {
145-
let plaintext_bytes = plaintext.into_value()?;
146-
let result = xchacha20poly1305::encrypt(plaintext_bytes.as_ref(), passphrase.as_bytes())
147-
.map_err(to_napi_err)?;
148-
149-
let mut vec: Vec<u8> = vec![];
150-
result.write(&mut vec).map_err(to_napi_err)?;
151-
152-
Ok(Buffer::from(&vec[..]))
153-
}
154-
155-
#[napi]
156-
pub fn decrypt(encrypted_blob: JsBuffer, passphrase: String) -> Result<Buffer> {
157-
let encrypted_bytes = encrypted_blob.into_value()?;
158-
159-
let encrypted_output = EncryptOutput::read(encrypted_bytes.as_ref()).map_err(to_napi_err)?;
160-
let result =
161-
xchacha20poly1305::decrypt(encrypted_output, passphrase.as_bytes()).map_err(to_napi_err)?;
162-
163-
Ok(Buffer::from(&result[..]))
164-
}

ironfish-rust/src/xchacha20poly1305.rs

Lines changed: 1 addition & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
use std::io;
66

7+
use argon2::Argon2;
78
use argon2::RECOMMENDED_SALT_LEN;
8-
use argon2::{password_hash::SaltString, Argon2};
99
use chacha20poly1305::aead::Aead;
1010
use chacha20poly1305::{Key, KeyInit, XChaCha20Poly1305, XNonce};
1111
use hkdf::Hkdf;
@@ -133,109 +133,6 @@ impl XChaCha20Poly1305Key {
133133
}
134134
}
135135

136-
#[derive(Debug)]
137-
pub struct EncryptOutput {
138-
pub salt: Vec<u8>,
139-
140-
pub nonce: [u8; XNONCE_LENGTH],
141-
142-
pub ciphertext: Vec<u8>,
143-
}
144-
145-
impl EncryptOutput {
146-
pub fn write<W: io::Write>(&self, mut writer: W) -> Result<(), IronfishError> {
147-
let salt_len = u32::try_from(self.salt.len())?.to_le_bytes();
148-
writer.write_all(&salt_len)?;
149-
writer.write_all(&self.salt)?;
150-
151-
writer.write_all(&self.nonce)?;
152-
153-
let ciphertext_len = u32::try_from(self.ciphertext.len())?.to_le_bytes();
154-
writer.write_all(&ciphertext_len)?;
155-
writer.write_all(&self.ciphertext)?;
156-
157-
Ok(())
158-
}
159-
160-
pub fn read<R: io::Read>(mut reader: R) -> Result<Self, IronfishError> {
161-
let mut salt_len = [0u8; 4];
162-
reader.read_exact(&mut salt_len)?;
163-
let salt_len = u32::from_le_bytes(salt_len) as usize;
164-
165-
let mut salt = vec![0u8; salt_len];
166-
reader.read_exact(&mut salt)?;
167-
168-
let mut nonce = [0u8; XNONCE_LENGTH];
169-
reader.read_exact(&mut nonce)?;
170-
171-
let mut ciphertext_len = [0u8; 4];
172-
reader.read_exact(&mut ciphertext_len)?;
173-
let ciphertext_len = u32::from_le_bytes(ciphertext_len) as usize;
174-
175-
let mut ciphertext = vec![0u8; ciphertext_len];
176-
reader.read_exact(&mut ciphertext)?;
177-
178-
Ok(EncryptOutput {
179-
salt,
180-
nonce,
181-
ciphertext,
182-
})
183-
}
184-
}
185-
186-
impl PartialEq for EncryptOutput {
187-
fn eq(&self, other: &EncryptOutput) -> bool {
188-
self.salt == other.salt && self.nonce == other.nonce && self.ciphertext == other.ciphertext
189-
}
190-
}
191-
192-
fn derive_key(passphrase: &[u8], salt: &[u8]) -> Result<Key, IronfishError> {
193-
let mut key = [0u8; KEY_LENGTH];
194-
let argon2 = Argon2::default();
195-
196-
argon2
197-
.hash_password_into(passphrase, salt, &mut key)
198-
.map_err(|_| IronfishError::new(IronfishErrorKind::FailedArgon2Hash))?;
199-
200-
Ok(Key::from(key))
201-
}
202-
203-
pub fn encrypt(plaintext: &[u8], passphrase: &[u8]) -> Result<EncryptOutput, IronfishError> {
204-
let salt = SaltString::generate(&mut thread_rng());
205-
let salt_str = salt.to_string();
206-
let salt_bytes = salt_str.as_bytes();
207-
let key = derive_key(passphrase, salt_bytes)?;
208-
209-
let cipher = XChaCha20Poly1305::new(&key);
210-
let mut nonce_bytes = [0u8; XNONCE_LENGTH];
211-
thread_rng().fill_bytes(&mut nonce_bytes);
212-
let nonce = XNonce::from_slice(&nonce_bytes);
213-
214-
let ciphertext = cipher
215-
.encrypt(nonce, plaintext)
216-
.map_err(|_| IronfishError::new(IronfishErrorKind::FailedXChaCha20Poly1305Encryption))?;
217-
218-
Ok(EncryptOutput {
219-
salt: salt_bytes.to_vec(),
220-
nonce: nonce_bytes,
221-
ciphertext,
222-
})
223-
}
224-
225-
pub fn decrypt(
226-
encrypted_output: EncryptOutput,
227-
passphrase: &[u8],
228-
) -> Result<Vec<u8>, IronfishError> {
229-
let nonce = XNonce::from_slice(&encrypted_output.nonce);
230-
231-
let key = derive_key(passphrase, &encrypted_output.salt[..])?;
232-
let cipher = XChaCha20Poly1305::new(&key);
233-
234-
cipher
235-
.decrypt(nonce, encrypted_output.ciphertext.as_ref())
236-
.map_err(|_| IronfishError::new(IronfishErrorKind::FailedXChaCha20Poly1305Decryption))
237-
}
238-
239136
#[cfg(test)]
240137
mod test {
241138
use crate::xchacha20poly1305::XChaCha20Poly1305Key;

ironfish/src/rpc/routes/wallet/create.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,7 @@ routes.register<typeof CreateAccountRequestSchema, CreateAccountResponse>(
3030
)
3131
}
3232

33-
const account = await context.wallet.createAccount(name, {
34-
passphrase: request.data.passphrase,
35-
})
33+
const account = await context.wallet.createAccount(name)
3634
if (context.wallet.nodeClient) {
3735
void context.wallet.scan()
3836
}

ironfish/src/rpc/routes/wallet/createAccount.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { AssertHasRpcContext } from '../rpcContext'
1818
* Hence, we're adding a new createAccount endpoint and will eventually sunset the create endpoint.
1919
*/
2020

21-
export type CreateAccountRequest = { name: string; default?: boolean; passphrase?: string }
21+
export type CreateAccountRequest = { name: string; default?: boolean }
2222
export type CreateAccountResponse = {
2323
name: string
2424
publicAddress: string
@@ -29,7 +29,6 @@ export const CreateAccountRequestSchema: yup.ObjectSchema<CreateAccountRequest>
2929
.object({
3030
name: yup.string().defined(),
3131
default: yup.boolean().optional(),
32-
passphrase: yup.string().optional(),
3332
})
3433
.defined()
3534

@@ -49,9 +48,7 @@ routes.register<typeof CreateAccountRequestSchema, CreateAccountResponse>(
4948

5049
let account
5150
try {
52-
account = await context.wallet.createAccount(request.data.name, {
53-
passphrase: request.data.passphrase,
54-
})
51+
account = await context.wallet.createAccount(request.data.name)
5552
} catch (e) {
5653
if (e instanceof DuplicateAccountNameError) {
5754
throw new RpcValidationError(e.message, 400, RPC_ERROR_CODES.DUPLICATE_ACCOUNT_NAME)

ironfish/src/rpc/routes/wallet/rename.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ routes.register<typeof RenameAccountRequestSchema, RenameAccountResponse>(
2121
AssertHasRpcContext(request, context, 'wallet')
2222

2323
const account = getAccount(context.wallet, request.data.account)
24-
await account.setName(request.data.newName, { passphrase: request.data.passphrase })
24+
await context.wallet.setName(account, request.data.newName)
2525
request.end()
2626
},
2727
)

ironfish/src/rpc/routes/wallet/renameAccount.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ import { routes } from '../router'
77
import { AssertHasRpcContext } from '../rpcContext'
88
import { getAccount } from './utils'
99

10-
export type RenameAccountRequest = { account: string; newName: string; passphrase?: string }
10+
export type RenameAccountRequest = { account: string; newName: string }
1111
export type RenameAccountResponse = undefined
1212

1313
export const RenameAccountRequestSchema: yup.ObjectSchema<RenameAccountRequest> = yup
1414
.object({
1515
account: yup.string().defined(),
1616
newName: yup.string().defined(),
17-
passphrase: yup.string().optional(),
1817
})
1918
.defined()
2019

@@ -29,7 +28,7 @@ routes.register<typeof RenameAccountRequestSchema, RenameAccountResponse>(
2928
AssertHasRpcContext(request, context, 'wallet')
3029

3130
const account = getAccount(context.wallet, request.data.account)
32-
await account.setName(request.data.newName, { passphrase: request.data.passphrase })
31+
await context.wallet.setName(account, request.data.newName)
3332
request.end()
3433
},
3534
)

ironfish/src/rpc/routes/wallet/resetAccount.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export type ResetAccountRequest = {
1111
account: string
1212
resetCreatedAt?: boolean
1313
resetScanningEnabled?: boolean
14-
passphrase?: string
1514
}
1615
export type ResetAccountResponse = undefined
1716

@@ -20,7 +19,6 @@ export const ResetAccountRequestSchema: yup.ObjectSchema<ResetAccountRequest> =
2019
account: yup.string().defined(),
2120
resetCreatedAt: yup.boolean(),
2221
resetScanningEnabled: yup.boolean(),
23-
passphrase: yup.string().optional(),
2422
})
2523
.defined()
2624

@@ -39,7 +37,6 @@ routes.register<typeof ResetAccountRequestSchema, ResetAccountResponse>(
3937
await context.wallet.resetAccount(account, {
4038
resetCreatedAt: request.data.resetCreatedAt,
4139
resetScanningEnabled: request.data.resetScanningEnabled,
42-
passphrase: request.data.passphrase,
4340
})
4441

4542
request.end()

ironfish/src/wallet/account/__fixtures__/account.test.ts.fixture

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5909,13 +5909,13 @@
59095909
"value": {
59105910
"encrypted": false,
59115911
"version": 4,
5912-
"id": "6f0698b4-a99c-46aa-9391-59f0c55cd755",
5912+
"id": "aea047be-8c73-448e-8e8d-22844f49736f",
59135913
"name": "accountA",
5914-
"spendingKey": "89001fcdef6bff7e9fd76d4ae6275bf6786afc2797eded9df094ae4a6894782d",
5915-
"viewKey": "389bc77ae499f3edc0dc445a732add6f36c275b260efe2c791cc515f6c2c0cd75f5b31e9b1f82edb0ee57b9304ece90d48f43c36d78660b04960b9692485d058",
5916-
"incomingViewKey": "fdd70ff012b4e48576bdd71207ebe1e3747811c77fa1a25862c3b869123ce007",
5917-
"outgoingViewKey": "9664b763a0418a476d072c715fcbbba58bcaf60cc951ba017195d75453131f11",
5918-
"publicAddress": "14a9bfb247dcf632f85ff79ebef222cc9ccf364f9b3e3e0ee39b75d68f80782a",
5914+
"spendingKey": "431a4e45c614fd41d7cee2de809e4464e92a125752d6f6839c0af7c706a01f67",
5915+
"viewKey": "4be28bbdca174c4e7499d913ea02642d74136eea9058f4e6eb7586dd5c864e905128a5aa5d25356934e5f4cf36575445b8dc60bfa672e1c135521cb5610e9b4c",
5916+
"incomingViewKey": "78b6e7887d55853b84d5d0b3b80d787379750b8839ddbc738882124453a5c004",
5917+
"outgoingViewKey": "3ce3ee26d7ab6c76838b6920eb6b5cf8fa3aa1ca54940d3b3cbf20532be34e41",
5918+
"publicAddress": "09d7f58ee5ae406b19e714b7fab882b83334e5b566a64c9d9707fcc513426163",
59195919
"createdAt": {
59205920
"hash": {
59215921
"type": "Buffer",
@@ -5924,7 +5924,7 @@
59245924
"sequence": 1
59255925
},
59265926
"scanningEnabled": true,
5927-
"proofAuthorizingKey": "8c11ae2523136f4b11bada56d9bcab2b6591cc99cf7aede8a238c5fefd7fce0d"
5927+
"proofAuthorizingKey": "c95606e98895f390f247f0c9e4beb22a039adfdab5cd11dac28263870dfe4802"
59285928
},
59295929
"head": {
59305930
"hash": {
@@ -5935,18 +5935,18 @@
59355935
}
59365936
}
59375937
],
5938-
"Accounts setName should throw an error if the passphrase is incorrect and the wallet is encrypted": [
5938+
"Accounts setName should throw an error if there is no master key and the wallet is encrypted": [
59395939
{
59405940
"value": {
59415941
"encrypted": false,
59425942
"version": 4,
5943-
"id": "92b122e5-9f14-453b-a364-e20a7d107305",
5943+
"id": "df4836f9-2889-471c-b9c3-c1aaa38fc595",
59445944
"name": "accountA",
5945-
"spendingKey": "3d68ebfd3d600792fc94d583bbab97ab4d02b9f41aa2a6655e151e41d4a33d8d",
5946-
"viewKey": "51e699920432cf221351568ade21fb8af4110c7ad57ad90cc2664340d50d6f207a19eb846feb76588f467e4dce99d0da074ca680aca11d3d8c91bd47ae5d9081",
5947-
"incomingViewKey": "15f8cb20e3a7b494474f4c4737bc0403dbb5de6e532e2c83a4469cd7f95f5b02",
5948-
"outgoingViewKey": "fc1b2e0e85cca6ddaf657baf2c69c76b403afe1adadd73b794c5cb81724d240d",
5949-
"publicAddress": "899ea1e9be7202aedab0a41f6b9cf661ce20e41ba11c1ee9d15ac64d8c96a391",
5945+
"spendingKey": "433f92654fe57940d18e87481c52588fec36fa552f64dca434b1726e022722cd",
5946+
"viewKey": "1d6e8a7faea61bdfd77d6e3c3b63951b3e613a8907bd6786fe0466e3490a8558eec35c9e08b3abbbaa0b596f427801bfc306297d0c3bf1b50ad02aa62cae702f",
5947+
"incomingViewKey": "88328774271cf9b9437e0b76ffc05c5697742d2004b9430a257590cceccbb000",
5948+
"outgoingViewKey": "5cb8a083108e4f3aa7d5db9a6ddce584d9b3a941cf707ee691b016ddf6c0fc87",
5949+
"publicAddress": "534ba54bbf1721cb16e4c8a702f25d96a0c99c2f02c38431be8870a28fc04914",
59505950
"createdAt": {
59515951
"hash": {
59525952
"type": "Buffer",
@@ -5955,7 +5955,7 @@
59555955
"sequence": 1
59565956
},
59575957
"scanningEnabled": true,
5958-
"proofAuthorizingKey": "d0d08a05953a50a0ce8b35e0c410b7dde77e3bf6099aaa4fc413de2096f7ef0a"
5958+
"proofAuthorizingKey": "25079aaa8548a644fc43ba77584bb0cb53adb60e3b2a033865df05569ee44302"
59595959
},
59605960
"head": {
59615961
"hash": {
@@ -5971,13 +5971,13 @@
59715971
"value": {
59725972
"encrypted": false,
59735973
"version": 4,
5974-
"id": "1dd7e196-ffed-4fe0-8dbd-c49f82bf44b7",
5974+
"id": "de02e4f1-ad01-464d-9e5b-9b4e83c57f0c",
59755975
"name": "accountA",
5976-
"spendingKey": "71ec323628c95bd56df353ec444b8ceb3d463603e82a89d4ac3336bdf630993a",
5977-
"viewKey": "5219abc719ecb8954e77d89e60f4fc82588e8f619ba4da38b53ed0471aeccb200cc7cec29ca533c769c96213417f78ccfaf2ba3f12a4b948a0da242805eb044c",
5978-
"incomingViewKey": "d9d70e59490b44c078300a23376e4fc516b47a31231c77538d17740f399e3d00",
5979-
"outgoingViewKey": "e3eca4b31e0d4b48e27458db2eff35b4d713d84b0b07505fdc8d49b2e40ab69d",
5980-
"publicAddress": "fbc47fe75ef9534b28b95fd17288488b89a4b752191a323a3703520095e8c24b",
5976+
"spendingKey": "4c946561f929bfc55faf90cda2f3c8bc6881b0cf96f0a422acb4c2240d5b995b",
5977+
"viewKey": "3120c5db86dd17d6b11f14df5f652878306aba202c74db71aad778e45ae4888843eaa4e8fa18ee78a762c2ed3b024c29d0ccd4163b0e49dba1e9c0b2055fd5b5",
5978+
"incomingViewKey": "2b1bcb2a5821e8f0485740aaec3157d43af552e853c97fa09dedb86442095003",
5979+
"outgoingViewKey": "dff7ee410247d8d2b63cd0da40509bd8fc3e96a4d26c04ec9fdb016da2fbb2ea",
5980+
"publicAddress": "9a3f5703d2cf40fc2899bf25793249b44e187acbd5c1f6f4f20ad4143408e42e",
59815981
"createdAt": {
59825982
"hash": {
59835983
"type": "Buffer",
@@ -5986,7 +5986,7 @@
59865986
"sequence": 1
59875987
},
59885988
"scanningEnabled": true,
5989-
"proofAuthorizingKey": "f335356ed8f77c4facebc2ad9377ab05e6d71ec83aa772df0c23e2733458a10c"
5989+
"proofAuthorizingKey": "8726a9a636670be8e1fd12a0b4b42a1d0e30a526b28cae014afc7b241ad1fb0d"
59905990
},
59915991
"head": {
59925992
"hash": {

0 commit comments

Comments
 (0)