1
+ import fs from "node:fs" ;
1
2
import path from "node:path" ;
2
3
import bls from "@chainsafe/bls" ;
4
+ import { Keystore } from "@chainsafe/bls-keystore" ;
3
5
import { SignerLocal , SignerType } from "@lodestar/validator" ;
4
6
import { LogLevel , Logger } from "@lodestar/utils" ;
5
7
import { lockFilepath , unlockFilepath } from "../../../util/lockfile.js" ;
6
8
import { LocalKeystoreDefinition } from "./interface.js" ;
7
9
import { clearKeystoreCache , loadKeystoreCache , writeKeystoreCache } from "./keystoreCache.js" ;
8
10
import { DecryptKeystoresThreadPool } from "./decryptKeystores/index.js" ;
9
11
10
- type KeystoreDecryptOptions = {
12
+ export type KeystoreDecryptOptions = {
11
13
ignoreLockFile ?: boolean ;
12
14
onDecrypt ?: ( index : number ) => void ;
13
15
// Try to use the cache file if it exists
14
16
cacheFilePath ?: string ;
17
+ /** Use main thread to decrypt keystores */
18
+ disableThreadPool ?: boolean ;
15
19
logger : Pick < Logger , LogLevel . info | LogLevel . warn | LogLevel . debug > ;
16
20
signal : AbortSignal ;
17
21
} ;
@@ -57,14 +61,50 @@ export async function decryptKeystoreDefinitions(
57
61
const signers = new Array < SignerLocal > ( keystoreCount ) ;
58
62
const passwords = new Array < string > ( keystoreCount ) ;
59
63
const errors : KeystoreDecryptError [ ] = [ ] ;
60
- const decryptKeystores = new DecryptKeystoresThreadPool ( keystoreCount , opts . signal ) ;
61
64
62
- for ( const [ index , definition ] of keystoreDefinitions . entries ( ) ) {
63
- lockKeystore ( definition . keystorePath , opts ) ;
65
+ if ( ! opts . disableThreadPool ) {
66
+ const decryptKeystores = new DecryptKeystoresThreadPool ( keystoreCount , opts . signal ) ;
67
+
68
+ for ( const [ index , definition ] of keystoreDefinitions . entries ( ) ) {
69
+ lockKeystore ( definition . keystorePath , opts ) ;
70
+
71
+ decryptKeystores . queue (
72
+ definition ,
73
+ ( secretKeyBytes : Uint8Array ) => {
74
+ const signer : SignerLocal = {
75
+ type : SignerType . Local ,
76
+ secretKey : bls . SecretKey . fromBytes ( secretKeyBytes ) ,
77
+ } ;
78
+
79
+ signers [ index ] = signer ;
80
+ passwords [ index ] = definition . password ;
81
+
82
+ if ( opts ?. onDecrypt ) {
83
+ opts ?. onDecrypt ( index ) ;
84
+ }
85
+ } ,
86
+ ( error : Error ) => {
87
+ // In-progress tasks can't be canceled, so there's a chance that multiple errors may be caught
88
+ // add to the list of errors
89
+ errors . push ( { keystoreFile : path . basename ( definition . keystorePath ) , error} ) ;
90
+ // cancel all pending tasks, no need to continue decrypting after we hit one error
91
+ decryptKeystores . cancel ( ) ;
92
+ }
93
+ ) ;
94
+ }
95
+
96
+ await decryptKeystores . completed ( ) ;
97
+ } else {
98
+ // Decrypt keystores in main thread
99
+ for ( const [ index , definition ] of keystoreDefinitions . entries ( ) ) {
100
+ lockKeystore ( definition . keystorePath , opts ) ;
101
+
102
+ try {
103
+ const keystore = Keystore . parse ( fs . readFileSync ( definition . keystorePath , "utf8" ) ) ;
104
+
105
+ // Memory-hogging function
106
+ const secretKeyBytes = await keystore . decrypt ( definition . password ) ;
64
107
65
- decryptKeystores . queue (
66
- definition ,
67
- ( secretKeyBytes : Uint8Array ) => {
68
108
const signer : SignerLocal = {
69
109
type : SignerType . Local ,
70
110
secretKey : bls . SecretKey . fromBytes ( secretKeyBytes ) ,
@@ -76,19 +116,14 @@ export async function decryptKeystoreDefinitions(
76
116
if ( opts ?. onDecrypt ) {
77
117
opts ?. onDecrypt ( index ) ;
78
118
}
79
- } ,
80
- ( error : Error ) => {
81
- // In-progress tasks can't be canceled, so there's a chance that multiple errors may be caught
82
- // add to the list of errors
83
- errors . push ( { keystoreFile : path . basename ( definition . keystorePath ) , error} ) ;
84
- // cancel all pending tasks, no need to continue decrypting after we hit one error
85
- decryptKeystores . cancel ( ) ;
119
+ } catch ( e ) {
120
+ errors . push ( { keystoreFile : path . basename ( definition . keystorePath ) , error : e as Error } ) ;
121
+ // stop processing, no need to continue decrypting after we hit one error
122
+ break ;
86
123
}
87
- ) ;
124
+ }
88
125
}
89
126
90
- await decryptKeystores . completed ( ) ;
91
-
92
127
if ( errors . length > 0 ) {
93
128
// If an error occurs, the program isn't going to be running,
94
129
// so we should unlock all lockfiles we created
0 commit comments