Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[PM-3144, PM-3106]: Checkpointing app main (#75)
* PM-3144: Skeleton for the main entry point. * PM-3144: Configuration and parser. * PM-3144: Default application config. * PM-3144: Local network config. * PM-3144: Test parsing the config. * PM-3144: ECDSA key generator entry point. * PM-3144: Example of running key key generation and the service. * PM-3144: Remove unused imports. * PM-3144: Allow private-key to be a path. * PM-3144: Composition of network components. * PM-3144: Checkpointing tracers. * PM-3144: Network splitter. * PM-3144: Database connection. * PM-3144: Remove the -n CLI option. * PM-3144: Storages. * PM-3144: Block pruning. * PM-3144: Checkpointing Service. * PM-3144: Merge network tracers. * PM-3144: Wire in the HotStuffService. * PM-3144: Signing that accepts a genesis block. * PM-3144: Better comments in BlockPruning. * PM-3144: Update spec docstring. * PM-3144: Explicit return values for composition functions. * PM-3144: Alternative way of picking the new root. * PM-3144: Fix configuration: keys need to be different.
- Loading branch information
Showing
33 changed files
with
1,848 additions
and
116 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
metronome { | ||
checkpointing { | ||
# A name for the node that we can use to distinguish | ||
# if we run multiple instances on the same machine. | ||
name = service | ||
|
||
federation { | ||
# Public address of this federation member; required. | ||
self { | ||
host = null | ||
port = 9080 | ||
# Private ECDSA key of this federation member in hexadecimal format; required. | ||
# It can either the the key itself, or a path to a file which contains the key. | ||
# The public key will be derived from the private key. | ||
private-key = null | ||
} | ||
|
||
# List of other federation members; records of {host, port, public-key}. | ||
others = [] | ||
|
||
# The maximum number of tolerated Byzantine nodes; optional. | ||
# At most (n-1)/3, but can be lower to require smaller quorum. | ||
maxFaulty = null | ||
} | ||
|
||
consensus { | ||
# Minimum time to allow for a HotStuff round. | ||
min-timeout = 5s | ||
# Maximum time to allow for a HotStuff round, after numerous timeouts. | ||
max-timeout = 15s | ||
# Increment factor to apply on the timeout after a failed round. | ||
timeout-factor = 1.2 | ||
} | ||
|
||
# Network configuration to accept connections from remote federation nodes. | ||
remote { | ||
# Bind address for the checkpointing service remote interface. | ||
listen { | ||
host = 0.0.0.0 | ||
port = ${metronome.checkpointing.federation.self.port} | ||
} | ||
# Request roundtrip timeout. | ||
timeout = 3s | ||
} | ||
|
||
# Network configuration to accept connection from the local interpreter. | ||
local { | ||
# Bind address for the checkpointing service local interface. | ||
listen { | ||
host = 127.0.0.1 | ||
port = 9081 | ||
} | ||
# Node of the PoW Interpreter. | ||
interpreter { | ||
host = 127.0.0.1 | ||
port = 9082 | ||
# ECDSA key used by the interpreter to secure the connection; required. | ||
public-key = null | ||
} | ||
# Request roundtrip timeout. | ||
timeout = 3s | ||
# Whether we should expect the Interpreter to send us notifications about | ||
# the arrival of a checkpoint height, or check in every time we have to | ||
# create a block. Depends on how the Interpreter is implemented, it's an | ||
# optimisation to save unnecessary round trips. | ||
expect-checkpoint-candidate-notifications = false | ||
} | ||
|
||
database { | ||
# Storage location for RocksDB. | ||
path = ${user.home}"/.metronome/checkpointing/db/"${metronome.checkpointing.name} | ||
# Size of the ring buffer for the checkpointing ledger. | ||
state-history-size = 100 | ||
# Number of blocks to keep before pruning. | ||
block-history-size = 100 | ||
# Time to wait before pruning a block from history. | ||
prune-interval = 60s | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<configuration> | ||
|
||
<property name="encoder.pattern" value="%d{HH:mm:ss.SSS} %-5level %logger{36} %msg%n" /> | ||
<property name="log.file.dir" value="${user.home}/.metronome/checkpointing/logs" /> | ||
|
||
<!-- Properties `log.file.name` and `log.console.level` are expected to be set programmatically at startup using `System.setProperty`. --> | ||
|
||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> | ||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> | ||
<level>${log.console.level}</level> | ||
</filter> | ||
<encoder> | ||
<pattern>${encoder.pattern}</pattern> | ||
</encoder> | ||
</appender> | ||
|
||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> | ||
<file>${log.file.dir}/${log.file.name}.log</file> | ||
<append>true</append> | ||
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> | ||
<fileNamePattern>${log.file.dir}/${log.file.name}.%i.log.zip</fileNamePattern> | ||
<minIndex>1</minIndex> | ||
<maxIndex>10</maxIndex> | ||
</rollingPolicy> | ||
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> | ||
<maxFileSize>10MB</maxFileSize> | ||
</triggeringPolicy> | ||
<encoder> | ||
<pattern>${encoder.pattern}</pattern> | ||
</encoder> | ||
</appender> | ||
|
||
<logger name="io.netty" level="OFF"/> | ||
<logger name="io.iohk.scalanet.peergroup" level="OFF"/> | ||
|
||
<root level="DEBUG"> | ||
<appender-ref ref="CONSOLE"/> | ||
<appender-ref ref="FILE"/> | ||
</root> | ||
|
||
</configuration> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Extend the defaults. | ||
# The leading "/"" works when a file refers to the default module included in the resources. | ||
# Here we could use "application" or "application.conf" without it, but when executed with | ||
# `java -Dconfig.file=example.conf` only the one that begins with "/" seems to work. | ||
include "/application" | ||
|
||
metronome { | ||
checkpointing { | ||
federation { | ||
self { | ||
host = localhost | ||
port = 40000 | ||
private-key = cd2a249a76d8e9fd0e538e651b9e97c3fc5efcceeb10fc98dd57fbdd156457e6 | ||
} | ||
|
||
others = [ | ||
{host = localhost, port = 40001, public-key = ff7849206b7faef9557cf53333739ecd947698d76ba11ffabf2587435322b9a8b4f063faf97e5aace2a75b8f6714e5bd3d483cad6e830ae3036afcc4ff1b5369, private-key = 15cc92810f61bc705f939432197fee100bcc1a99d6cc66c7c28fa158d4144f84} | ||
{host = localhost, port = 40002, public-key = cb020251d396614a35038dd2ff88fd2f1a5fd74c8bcad4b353fa605405c8b1b8c80ee12d2a10b1fca59424b16890c8115fbc94a68026369acc3c2603595e6387, private-key = a4769d076bb7eefeb1aba8aa97520d8f7f8bcd65049a128c3040f9dd5d3eeae6} | ||
{host = localhost, port = 40003, public-key = 23fcab42e8f1078880b27aab4849092489bfa8d3e3b0faa54c9db89e89223c783ec7a3b2f8e6461b27778f78cea261a2272abe31c5601173b2964ef14af897dc, private-key = 9441f3e96104a11405cb0e03ceb693f889770dd2c155dab7573023e00e878ace} | ||
] | ||
} | ||
local { | ||
interpreter { | ||
public-key = 65e2f6da1bb1e7f0b07f5b892c568acb5429833e30af3974eedd2137ebc9f1fb8b0c462d4ca558dda64c5da8cf10280a1f579556ac8a611bd2fa7199f5a2c69a | ||
} | ||
} | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
.../specs/src/io/iohk/metronome/checkpointing/app/config/CheckpointingConfigParserSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package io.iohk.metronome.checkpointing.app.config | ||
|
||
import com.typesafe.config.ConfigFactory | ||
import org.scalatest.flatspec.AnyFlatSpec | ||
import org.scalatest.Inside | ||
import org.scalatest.matchers.should.Matchers | ||
import java.nio.file.Path | ||
|
||
class CheckpointingConfigParserSpec | ||
extends AnyFlatSpec | ||
with Inside | ||
with Matchers { | ||
|
||
behavior of "CheckpointingConfigParser" | ||
|
||
it should "not parse the default configuration due to missing data" in { | ||
inside(CheckpointingConfigParser.parse(ConfigFactory.load())) { | ||
case Left(_) => | ||
succeed | ||
} | ||
} | ||
|
||
it should "parse the with the test overrides" in { | ||
inside( | ||
CheckpointingConfigParser.parse(ConfigFactory.load("test.conf")) | ||
) { case Right(config) => | ||
config.remote.listen.port shouldBe config.federation.self.port | ||
config.federation.self.privateKey.isLeft shouldBe true | ||
} | ||
} | ||
|
||
it should "parse when the private key is a path" in { | ||
inside( | ||
CheckpointingConfigParser.parse { | ||
ConfigFactory.parseString( | ||
"metronome.checkpointing.federation.self.private-key=./node.key" | ||
) withFallback ConfigFactory.load("test.conf") | ||
} | ||
) { case Right(config) => | ||
config.federation.self.privateKey shouldBe Right(Path.of("./node.key")) | ||
} | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
metronome/checkpointing/app/src/io/iohk/metronome/checkpointing/app/CheckpointingApp.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package io.iohk.metronome.checkpointing.app | ||
|
||
import cats.effect.ExitCode | ||
import com.typesafe.config.ConfigFactory | ||
import monix.eval.{Task, TaskApp} | ||
import io.iohk.metronome.checkpointing.app.config.{ | ||
CheckpointingConfigParser, | ||
CheckpointingOptions | ||
} | ||
|
||
object CheckpointingApp extends TaskApp { | ||
override def run(args: List[String]): Task[ExitCode] = { | ||
CheckpointingOptions.parse(args) match { | ||
case None => | ||
Task.pure(ExitCode.Error) | ||
|
||
case Some(opts) => | ||
run(opts) | ||
} | ||
} | ||
|
||
def run(opts: CheckpointingOptions): Task[ExitCode] = | ||
opts.mode match { | ||
case CheckpointingOptions.KeyGen => | ||
setLogProperties(opts, "keygen") >> | ||
// Not parsing the configuration for this as it may be incomplete without the keys. | ||
CheckpointingKeyGen.generateAndPrint.as(ExitCode.Success) | ||
|
||
case CheckpointingOptions.Service => | ||
CheckpointingConfigParser.parse(ConfigFactory.load()) match { | ||
case Left(error) => | ||
Task | ||
.delay(println(s"Error parsing configuration: $error")) | ||
.as(ExitCode.Error) | ||
|
||
case Right(config) => | ||
setLogProperties(opts, config.name) >> | ||
CheckpointingComposition | ||
.compose(config) | ||
.use(_ => Task.never.as(ExitCode.Success)) | ||
} | ||
} | ||
|
||
/** Set dynamic system properties expected by `logback.xml` before any logging module is loaded. */ | ||
def setLogProperties( | ||
opts: CheckpointingOptions, | ||
name: String | ||
): Task[Unit] = Task { | ||
// Separate log file for each node. | ||
System.setProperty("log.file.name", name) | ||
// Control how much logging goes on the console. | ||
System.setProperty("log.console.level", opts.logLevel.toString) | ||
}.void | ||
} |
Oops, something went wrong.