Skip to content

Commit 01f9bfa

Browse files
committed
private key config
Signed-off-by: [email protected] <[email protected]>
1 parent 33e1082 commit 01f9bfa

File tree

9 files changed

+94
-79
lines changed

9 files changed

+94
-79
lines changed

app/build.gradle

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ dependencies {
1818
implementation "com.sksamuel.hoplite:hoplite-core"
1919

2020
implementation("org.hyperledger.besu.internal:metrics-core")
21+
implementation("tech.pegasys.teku.internal:p2p")
22+
2123

2224
testImplementation(project(":jvm-libs:test-utils"))
2325
testImplementation(testFixtures(project(":core")))

app/src/main/kotlin/maru/app/MaruApp.kt

+26-5
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import org.hyperledger.besu.consensus.common.bft.Gossiper
4545
import org.hyperledger.besu.consensus.common.bft.network.ValidatorMulticaster
4646
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem
4747
import tech.pegasys.teku.infrastructure.async.SafeFuture
48+
import tech.pegasys.teku.networking.p2p.network.config.GeneratingFilePrivateKeySource
4849

4950
class MaruApp(
5051
config: MaruConfig,
@@ -55,21 +56,40 @@ class MaruApp(
5556
) : AutoCloseable {
5657
private val log: Logger = LogManager.getLogger(this::class.java)
5758

58-
val p2pManager =
59-
P2PManager(
60-
privateKeyFile = config.persistence.privateKeyPath,
61-
p2pConfig = config.p2pConfig,
62-
)
59+
private lateinit var privateKeyBytes: ByteArray
6360

6461
init {
62+
if (!config.persistence.privateKeyPath
63+
.toFile()
64+
.exists()
65+
) {
66+
log.info(
67+
"Private key file ${config.persistence.privateKeyPath} does not exist. A new private key will be generated and stored in that location.",
68+
)
69+
} else {
70+
log.info(
71+
"Private key file ${config.persistence.privateKeyPath} already exists. Maru will use the existing private key.",
72+
)
73+
privateKeyBytes =
74+
GeneratingFilePrivateKeySource(config.persistence.privateKeyPath.toString()).privateKeyBytes.toArray()
75+
}
6576
if (config.validator == null) {
6677
log.info("Validator is not defined. Maru is running in follower-only node")
6778
log.error("Follower-only mode is not supported yet! Exiting application.")
6879
exitProcess(1)
6980
}
81+
if (config.p2pConfig == null) {
82+
log.info("P2PManager is not defined.")
83+
}
7084
log.info(config.toString())
7185
}
7286

87+
val p2pManager =
88+
P2PManager(
89+
privateKeyBytes = privateKeyBytes,
90+
p2pConfig = config.p2pConfig,
91+
)
92+
7393
private val ethereumJsonRpcClient =
7494
Helpers.createWeb3jClient(
7595
config.sotNode,
@@ -125,6 +145,7 @@ class MaruApp(
125145
qbftConsensusFactory =
126146
QbftProtocolFactoryWithBeaconChainInitialization(
127147
maruConfig = config,
148+
privateKeyBytes = privateKeyBytes,
128149
metricsSystem = metricsSystem,
129150
finalizationStateProvider = finalizationStateProviderStub,
130151
executionLayerClient = ethereumJsonRpcClient.eth1Web3j,

app/src/main/kotlin/maru/app/QbftProtocolFactoryWithBeaconChainInitialization.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import org.web3j.protocol.core.DefaultBlockParameter
4646

4747
class QbftProtocolFactoryWithBeaconChainInitialization(
4848
private val maruConfig: MaruConfig,
49+
private val privateKeyBytes: ByteArray,
4950
private val metricsSystem: MetricsSystem,
5051
private val finalizationStateProvider: (BeaconBlockBody) -> FinalizationState,
5152
private val executionLayerClient: Web3j,
@@ -82,7 +83,7 @@ class QbftProtocolFactoryWithBeaconChainInitialization(
8283
headerHashFunction = RLPSerializers.DefaultHeaderHashFunction,
8384
)
8485

85-
val initialValidators = setOf(Crypto.privateKeyToValidator(maruConfig.validator!!.privateKey))
86+
val initialValidators = setOf(Crypto.privateKeyToValidator(privateKeyBytes))
8687
val tmpGenesisStateRoot =
8788
BeaconState(
8889
latestBeaconBlockHeader = beaconBlockHeader,
@@ -124,6 +125,7 @@ class QbftProtocolFactoryWithBeaconChainInitialization(
124125
val qbftProtocolFactory =
125126
QbftProtocolFactory(
126127
beaconChain = beaconChain,
128+
privateKeyBytes = privateKeyBytes,
127129
maruConfig = maruConfig,
128130
metricsSystem = metricsSystem,
129131
finalizationStateProvider = finalizationStateProvider,

config/src/main/kotlin/maru.config/Config.kt

+25-14
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,34 @@ data class Persistence(
2828
data class ApiEndpointConfig(
2929
val endpoint: URL,
3030
val jwtSecretPath: String? = null,
31-
)
31+
) {
32+
override fun equals(other: Any?): Boolean {
33+
if (this === other) return true
34+
if (javaClass != other?.javaClass) return false
35+
36+
other as ApiEndpointConfig
37+
38+
if (endpoint != other.endpoint) return false
39+
if (jwtSecretPath != other.jwtSecretPath) return false
40+
41+
return true
42+
}
43+
44+
override fun hashCode(): Int {
45+
var result = endpoint.hashCode()
46+
result = 31 * result + (jwtSecretPath?.hashCode() ?: 0)
47+
return result
48+
}
49+
}
3250

3351
data class FollowersConfig(
3452
val followers: Map<String, ApiEndpointConfig>,
3553
)
3654

3755
data class P2P(
38-
val ipAddress: String = "0.0.0.0",
39-
val port: String = "9000",
40-
val staticPeers: List<String> = emptyList(),
56+
val ipAddress: String,
57+
val port: String,
58+
val staticPeers: List<String>,
4159
) {
4260
override fun equals(other: Any?): Boolean {
4361
if (this === other) return true
@@ -61,25 +79,18 @@ data class P2P(
6179
}
6280

6381
data class Validator(
64-
val privateKey: ByteArray,
6582
val engineApiClient: ApiEndpointConfig,
6683
) {
67-
init {
68-
require(privateKey.size == 32) {
69-
"validator key must be 32 bytes long"
70-
}
71-
}
72-
7384
override fun equals(other: Any?): Boolean {
7485
if (this === other) return true
7586
if (javaClass != other?.javaClass) return false
7687

7788
other as Validator
7889

79-
return privateKey.contentEquals(other.privateKey)
90+
return engineApiClient == other.engineApiClient
8091
}
8192

82-
override fun hashCode(): Int = privateKey.contentHashCode()
93+
override fun hashCode(): Int = engineApiClient.hashCode()
8394
}
8495

8596
data class QbftOptions(
@@ -96,7 +107,7 @@ data class MaruConfig(
96107
val persistence: Persistence,
97108
val sotNode: ApiEndpointConfig,
98109
val qbftOptions: QbftOptions,
99-
val p2pConfig: P2P,
110+
val p2pConfig: P2P?,
100111
val validator: Validator?,
101112
val followers: FollowersConfig,
102113
)

config/src/main/kotlin/maru.config/HopliteFriendly.kt

-4
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,14 @@
1515
*/
1616
package maru.config
1717

18-
import com.sksamuel.hoplite.Masked
1918
import java.net.URL
20-
import maru.extensions.fromHexToByteArray
2119

2220
data class ValidatorDtoToml(
23-
val privateKey: Masked,
2421
val elClientEngineApiEndpoint: URL,
2522
val jwtSecretPath: String? = null,
2623
) {
2724
fun domainFriendly(): Validator =
2825
Validator(
29-
privateKey = privateKey.value.fromHexToByteArray(),
3026
engineApiClient = ApiEndpointDtoToml(elClientEngineApiEndpoint, jwtSecretPath).toDomain(),
3127
)
3228
}

config/src/test/kotlin/maru/config/HopliteFriendlinessTest.kt

+6-11
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,10 @@
1515
*/
1616
package maru.config
1717

18-
import com.sksamuel.hoplite.Secret
1918
import java.net.URI
2019
import kotlin.io.path.Path
2120
import kotlin.time.Duration.Companion.milliseconds
2221
import kotlin.time.Duration.Companion.seconds
23-
import maru.extensions.fromHexToByteArray
2422
import org.assertj.core.api.Assertions.assertThat
2523
import org.junit.jupiter.api.Test
2624

@@ -38,9 +36,10 @@ class HopliteFriendlinessTest {
3836
3937
[p2p-config]
4038
port = 3322
39+
ip-address = "localhost"
40+
static-peers = []
4141
4242
[validator]
43-
private-key = "0x1dd171cec7e2995408b5513004e8207fe88d6820aeff0d82463b3e41df251aae"
4443
jwt-secret-path = "/secret/path"
4544
el-client-engine-api-endpoint = "http://localhost:8555"
4645
""".trimIndent()
@@ -66,11 +65,10 @@ class HopliteFriendlinessTest {
6665
endpoint = URI.create("http://localhost:8545").toURL(),
6766
),
6867
qbftOptions = QbftOptions(100.milliseconds),
69-
p2pConfig = P2P(),
68+
p2pConfig = P2P(ipAddress = "localhost", port = "3322", staticPeers = emptyList()),
7069
validator =
7170
ValidatorDtoToml(
7271
elClientEngineApiEndpoint = URI.create("http://localhost:8555").toURL(),
73-
privateKey = Secret("0x1dd171cec7e2995408b5513004e8207fe88d6820aeff0d82463b3e41df251aae"),
7472
jwtSecretPath = "/secret/path",
7573
),
7674
followerEngineApis =
@@ -100,11 +98,10 @@ class HopliteFriendlinessTest {
10098
endpoint = URI.create("http://localhost:8545").toURL(),
10199
),
102100
qbftOptions = QbftOptions(100.milliseconds),
103-
p2pConfig = P2P(),
101+
p2pConfig = P2P(ipAddress = "localhost", port = "3322", staticPeers = emptyList()),
104102
validator =
105103
ValidatorDtoToml(
106104
elClientEngineApiEndpoint = URI.create("http://localhost:8555").toURL(),
107-
privateKey = Secret("0x1dd171cec7e2995408b5513004e8207fe88d6820aeff0d82463b3e41df251aae"),
108105
jwtSecretPath = "/secret/path",
109106
),
110107
followerEngineApis = null,
@@ -123,11 +120,10 @@ class HopliteFriendlinessTest {
123120
ApiEndpointConfig(
124121
endpoint = URI.create("http://localhost:8545").toURL(),
125122
),
126-
p2pConfig = P2P(),
123+
p2pConfig = P2P(ipAddress = "localhost", port = "3322", staticPeers = emptyList()),
127124
validator =
128125
Validator(
129126
engineApiClient = ApiEndpointConfig(URI.create("http://localhost:8555").toURL()),
130-
privateKey = "0x1dd171cec7e2995408b5513004e8207fe88d6820aeff0d82463b3e41df251aae".fromHexToByteArray(),
131127
),
132128
qbftOptions = QbftOptions(100.milliseconds),
133129
followers =
@@ -154,11 +150,10 @@ class HopliteFriendlinessTest {
154150
endpoint = URI.create("http://localhost:8545").toURL(),
155151
),
156152
qbftOptions = QbftOptions(100.milliseconds),
157-
p2pConfig = P2P(),
153+
p2pConfig = P2P(ipAddress = "localhost", port = "3322", staticPeers = emptyList()),
158154
validator =
159155
Validator(
160156
engineApiClient = ApiEndpointConfig(URI.create("http://localhost:8555").toURL()),
161-
privateKey = "0x1dd171cec7e2995408b5513004e8207fe88d6820aeff0d82463b3e41df251aae".fromHexToByteArray(),
162157
),
163158
followers =
164159
FollowersConfig(

consensus/src/main/kotlin/maru/consensus/qbft/QbftProtocolFactory.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ import org.hyperledger.besu.util.Subscribers
7474

7575
class QbftProtocolFactory(
7676
private val beaconChain: BeaconChain,
77+
private val privateKeyBytes: ByteArray,
7778
private val maruConfig: MaruConfig,
7879
private val metricsSystem: MetricsSystem,
7980
private val finalizationStateProvider: (BeaconBlockBody) -> FinalizationState,
@@ -92,9 +93,8 @@ class QbftProtocolFactory(
9293
"communicationMargin can't be more than blockTimeSeconds"
9394
}
9495

95-
val validatorKey = maruConfig.validator!!.privateKey
9696
val signatureAlgorithm = SignatureAlgorithmFactory.getInstance()
97-
val privateKey = signatureAlgorithm.createPrivateKey(Bytes32.wrap(validatorKey))
97+
val privateKey = signatureAlgorithm.createPrivateKey(Bytes32.wrap(privateKeyBytes))
9898
val keyPair = signatureAlgorithm.createKeyPair(privateKey)
9999
val securityModule = KeyPairSecurityModule(keyPair)
100100
val nodeKey = NodeKey(securityModule)

p2p/src/main/kotlin/maru/p2p/P2PManager.kt

+8-9
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package maru.p2p
1717

1818
import io.libp2p.core.crypto.unmarshalPrivateKey
19-
import java.nio.file.Path
2019
import java.util.Optional
2120
import java.util.concurrent.TimeUnit
2221
import maru.config.P2P
@@ -25,16 +24,16 @@ import org.apache.logging.log4j.Logger
2524
import tech.pegasys.teku.infrastructure.async.SafeFuture
2625
import tech.pegasys.teku.networking.p2p.libp2p.MultiaddrPeerAddress
2726
import tech.pegasys.teku.networking.p2p.libp2p.PeerAlreadyConnectedException
27+
import tech.pegasys.teku.networking.p2p.mock.MockP2PNetwork
2828
import tech.pegasys.teku.networking.p2p.network.P2PNetwork
2929
import tech.pegasys.teku.networking.p2p.network.PeerAddress
30-
import tech.pegasys.teku.networking.p2p.network.config.GeneratingFilePrivateKeySource
3130
import tech.pegasys.teku.networking.p2p.peer.DisconnectReason
3231
import tech.pegasys.teku.networking.p2p.peer.NodeId
3332
import tech.pegasys.teku.networking.p2p.peer.Peer
3433

3534
class P2PManager(
36-
val privateKeyFile: Path,
37-
val p2pConfig: P2P,
35+
private val privateKeyBytes: ByteArray,
36+
private val p2pConfig: P2P?,
3837
) {
3938
companion object {
4039
private const val DEFAULT_RECONNECT_DELAY_MILLI_SECONDS = 5000L // TODO: Do we want to make this configurable?
@@ -50,7 +49,7 @@ class P2PManager(
5049
p2pNetwork
5150
.start()
5251
?.thenApply {
53-
p2pConfig.staticPeers.forEach { peer ->
52+
p2pConfig!!.staticPeers.forEach { peer ->
5453
p2pNetwork.createPeerAddress(peer)?.let { address -> addStaticPeer(address as MultiaddrPeerAddress) }
5554
}
5655
}?.get()
@@ -61,10 +60,10 @@ class P2PManager(
6160
}
6261

6362
private fun buildP2PNetwork(): P2PNetwork<Peer> {
64-
val filePrivateKeySource = GeneratingFilePrivateKeySource(privateKeyFile.toString())
65-
// TODO: reading/generating this key should be done early on, as it is needed for the validator as well
66-
val privKeyBytes = filePrivateKeySource.privateKeyBytes
67-
val privateKey = unmarshalPrivateKey(privKeyBytes.toArrayUnsafe())
63+
if (p2pConfig == null) {
64+
return MockP2PNetwork()
65+
}
66+
val privateKey = unmarshalPrivateKey(privateKeyBytes)
6867

6968
return P2PNetworkFactory.build(
7069
privateKey = privateKey,

0 commit comments

Comments
 (0)