Repository: input-output-hk/mithril
Pull request #470 (2022-09-01 09:11:10)

Move GCP Aggregator to 'preview' network
This PR upgrades the Aggregator hosted on GCP cloud so that it uses the `preview` Cardano network instead of the legacy `testnet`:

- [x] Upgrade Cardano node to `1.35.3`
- [x] Parametrize the `docker-compose.yaml` file so that it can be used with different network
- [x] Update the `poolIds` of the Signer nodes (which `poolIds` are selected among the SPOs on the `preview` network) and reduce their number to `2`
- [x] Use `preview` network with `NETWORK=preview` in the `aggregator.tf` file
- [x] Update documentation

After merge, manually duplicate stores data so that we don't need to wait 2 epochs before signing

Relates to #457 


Patch:
diff --git a/.envrc b/.envrc
new file mode 100644
index 000000000..1d953f4bd
--- /dev/null
+++ b/.envrc
@@ -0,0 +1,1 @@
+use nix
diff --git a/Cargo.lock b/Cargo.lock
index 5b306071b..528a4ab47 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1835,7 +1835,6 @@ dependencies = [
  "config",
  "hex",
  "httpmock",
- "mithril",
  "mithril-common",
  "mockall",
  "rand_chacha 0.3.1",
diff --git a/demo/protocol-demo/src/demonstrator.rs b/demo/protocol-demo/src/demonstrator.rs
index b11c32084..54476af3a 100644
--- a/demo/protocol-demo/src/demonstrator.rs
+++ b/demo/protocol-demo/src/demonstrator.rs
@@ -107,7 +107,7 @@ impl Party {
         let seed = [0u8; 32];
         let mut rng = ChaCha20Rng::from_seed(seed);
         let p = ProtocolInitializer::setup(self.params.unwrap(), self.stake, &mut rng);
-        self.signer = Some(p.new_signer(closed_reg).unwrap());
+        self.signer = Some(p.new_signer(closed_reg));
         self.clerk = Some(ProtocolClerk::from_signer(self.signer.as_ref().unwrap()));
     }
 
diff --git a/docs/root/manual/getting-started/bootstrap-cardano-node.md b/docs/root/manual/getting-started/bootstrap-cardano-node.md
index f5e39ad74..8f6288b64 100644
--- a/docs/root/manual/getting-started/bootstrap-cardano-node.md
+++ b/docs/root/manual/getting-started/bootstrap-cardano-node.md
@@ -10,6 +10,18 @@ Thanks to a **Mithril Client** connected to a **Mithril Aggregator**, you will r
 
 :::
 
+:::tip
+
+The Mithril test networks are:
+
+* `preview`: Test network with magic id `2`, implemented on the IOG hosted Mitril Aggregator
+* `preprod`: Test network with magic id `1`, not implemented yet on the IOG hosted Mithril Aggregator
+* `testnet`: Legacy test network with magic id `1097911063`, used to be on the IOG hosted Mitril Aggregator, now deprecated
+
+In this documentation, we use the generic `testnet` identifier, but you need to replace it with the identifier of the network that runs on your Cardano node
+
+:::
+
 ## Download source
 
 Download from Github (HTTPS)
diff --git a/docs/root/manual/getting-started/run-mithril-devnet.md b/docs/root/manual/getting-started/run-mithril-devnet.md
index f58524a65..db5978c85 100644
--- a/docs/root/manual/getting-started/run-mithril-devnet.md
+++ b/docs/root/manual/getting-started/run-mithril-devnet.md
@@ -486,8 +486,5 @@ to /home/mithril/data/devnet /85f09b39b0b5a13cec9d8fe7ffb82b5e5f236f02ae896f4e47
 
 Restore a Cardano Node with:
 
-docker run -v cardano-node-ipc:/ipc -v cardano-node-data:/data \
-  --mount type=bind,source="./data/devnet/85f09b39b0b5a13cec9d8fe7ffb82b5e5f236f02ae896f4e47b77e5cd1f2a917/db",target=/data/db/ \
-  -e NETWORK=devnet \
-  inputoutput/cardano-node
+docker run -v cardano-node-ipc:/ipc -v cardano-node-data:/data --mount type=bind,source="./data/devnet /85f09b39b0b5a13cec9d8fe7ffb82b5e5f236f02ae896f4e47b77e5cd1f2a917/db",target=/data/db/ -e NETWORK=devnet  inputoutput/cardano-node
 ```
diff --git a/docs/root/manual/getting-started/run-signer-node.md b/docs/root/manual/getting-started/run-signer-node.md
index 2b9211207..327839a2d 100644
--- a/docs/root/manual/getting-started/run-signer-node.md
+++ b/docs/root/manual/getting-started/run-signer-node.md
@@ -10,6 +10,18 @@ In this guide, you will learn how to setup a **Mithril Signer** on top of a **Ca
 
 :::
 
+:::tip
+
+The Mithril test networks are:
+
+* `preview`: Test network with magic id `2`, implemented on the IOG hosted Mitril Aggregator
+* `preprod`: Test network with magic id `1`, not implemented yet on the IOG hosted Mithril Aggregator
+* `testnet`: Legacy test network with magic id `1097911063`, used to be on the IOG hosted Mitril Aggregator, now deprecated
+
+In this documentation, we use the generic `testnet` identifier, but you need to replace it with the identifier of the network that runs on your Cardano node
+
+:::
+
 :::danger
 
 This guide is working only on a Linux machine.
@@ -131,7 +143,7 @@ Replace this value with the correct user. We assume that the user used to run th
   * `DB_DIRECTORY=/cardano/db`: replace `/cardano/db` with the path to the database folder of the **Cardano Node** (the one in `--database-path`)
   * `CARDANO_NODE_SOCKET_PATH=/cardano/ipc/node.socket`: replace with the path to the IPC file (`CARDANO_NODE_SOCKET_PATH` env var)
   * `CARDANO_CLI_PATH=/app/bin/cardano-cli`: replace with the path to the `cardano-cli` executable
-  * `DATA_STORES_DIRECTORY=/opt/mithril/mithril-signer/stores`: replace with the path to a folder where the **Mithril Signer** will store its data (`/opt/mithril/mithril-signer/stores` e.g.)
+  * `STAKE_STORE_DIRECTORY=/opt/mithril/mithril-signer/store/stake_db`: replace with the path to a folder where the **Mithril Signer** will store its data (`/opt/mithril/mithril-signer/store` e.g.)
 
 :::
 
@@ -141,12 +153,11 @@ First create an env file that will be used by the service
 sudo cat > /opt/mithril/mithril-signer.env << EOF
 PARTY_ID=YOUR_POOL_ID_BECH32
 NETWORK=testnet
-NETWORK_MAGIC=1097911063
 AGGREGATOR_ENDPOINT=https://aggregator.api.mithril.network/aggregator
 RUN_INTERVAL=60000 DB_DIRECTORY=/cardano/db
 CARDANO_NODE_SOCKET_PATH=/cardano/ipc/node.socket
 CARDANO_CLI_PATH=/app/bin/cardano-cli
-DATA_STORES_DIRECTORY=/opt/mithril/mithril-signer/stores
+STAKE_STORE_DIRECTORY=/opt/mithril/mithril-signer/store/stake_db
 EOF
 ```
 
diff --git a/mithril-aggregator/config/preview.json b/mithril-aggregator/config/preview.json
new file mode 100644
index 000000000..ac76edbe4
--- /dev/null
+++ b/mithril-aggregator/config/preview.json
@@ -0,0 +1,15 @@
+{
+    "cardano_cli_path": "cardano-cli",
+    "cardano_cli_socket_path": "/tmp/cardano.sock",
+    "network": "preview",
+    "run_interval": 60000,
+    "protocol_parameters": {
+        "k": 5,
+        "m": 100,
+        "phi_f": 0.65
+    },
+    "url_snapshot_manifest": "https://storage.googleapis.com/cardano-preview/snapshots.json",
+    "snapshot_store_type": "local",
+    "snapshot_uploader_type": "gcp",
+    "stores_directory": "./mithril-aggregator/stores"
+}
\ No newline at end of file
diff --git a/mithril-aggregator/src/entities.rs b/mithril-aggregator/src/entities.rs
index 440b5b3b7..cdb90780d 100644
--- a/mithril-aggregator/src/entities.rs
+++ b/mithril-aggregator/src/entities.rs
@@ -104,30 +104,7 @@ impl Config {
     }
 
     pub fn get_network(&self) -> Result<CardanoNetwork, ConfigError> {
-        match self.network.to_lowercase().as_str() {
-            "mainnet" => Ok(CardanoNetwork::MainNet),
-            "testnet" => {
-                if let Some(magic) = self.network_magic {
-                    Ok(CardanoNetwork::TestNet(magic))
-                } else {
-                    Err(ConfigError::Message(
-                        "no NETWORK MAGIC number given for testnet network".to_string(),
-                    ))
-                }
-            }
-            "devnet" => {
-                if let Some(magic) = self.network_magic {
-                    Ok(CardanoNetwork::DevNet(magic))
-                } else {
-                    Err(ConfigError::Message(
-                        "no NETWORK MAGIC number given for devnet network".to_string(),
-                    ))
-                }
-            }
-            what => Err(ConfigError::Message(format!(
-                "could not parse network '{}', the only recognized networks are: mainnet, devnet, testnet",
-                what
-            ))),
-        }
+        CardanoNetwork::from_code(self.network.clone(), self.network_magic)
+            .map_err(|e| ConfigError::Message(e.to_string()))
     }
 }
diff --git a/mithril-aggregator/src/lib.rs b/mithril-aggregator/src/lib.rs
index 0a771f2df..52caa6fdc 100644
--- a/mithril-aggregator/src/lib.rs
+++ b/mithril-aggregator/src/lib.rs
@@ -20,8 +20,9 @@ pub use snapshot_uploaders::{
 };
 pub use snapshotter::{DumbSnapshotter, GzipSnapshotter, SnapshotError, Snapshotter};
 pub use store::{
-    CertificatePendingStore, CertificateStore, ProtocolParametersStore, ProtocolParametersStorer,
-    SingleSignatureStore, VerificationKeyStore, VerificationKeyStorer,
+    CertificatePendingStore, CertificateStore, ProtocolParametersStore,
+    ProtocolParametersStoreError, ProtocolParametersStorer, SingleSignatureStore,
+    VerificationKeyStore, VerificationKeyStoreError, VerificationKeyStorer,
 };
 
 #[cfg(test)]
diff --git a/mithril-aggregator/src/multi_signer.rs b/mithril-aggregator/src/multi_signer.rs
index f8b553549..11e48f961 100644
--- a/mithril-aggregator/src/multi_signer.rs
+++ b/mithril-aggregator/src/multi_signer.rs
@@ -13,7 +13,7 @@ use mithril_common::crypto_helper::{
     PROTOCOL_VERSION,
 };
 use mithril_common::entities::{self, SignerWithStake};
-use mithril_common::store::{StakeStorer, StoreError};
+use mithril_common::store::{StakeStoreError, StakeStorer};
 use mithril_common::{
     NEXT_SIGNER_EPOCH_RETRIEVAL_OFFSET, SIGNER_EPOCH_RECORDING_OFFSET,
     SIGNER_EPOCH_RETRIEVAL_OFFSET,
@@ -23,8 +23,11 @@ use crate::dependency::{
     ProtocolParametersStoreWrapper, SingleSignatureStoreWrapper, StakeStoreWrapper,
     VerificationKeyStoreWrapper,
 };
-use crate::store::{SingleSignatureStorer, VerificationKeyStorer};
-use crate::ProtocolParametersStorer;
+use crate::store::{
+    SingleSignatureStoreError, SingleSignatureStorer, VerificationKeyStoreError,
+    VerificationKeyStorer,
+};
+use crate::{ProtocolParametersStoreError, ProtocolParametersStorer};
 
 #[cfg(test)]
 use mockall::automock;
@@ -55,8 +58,17 @@ pub enum ProtocolError {
     #[error("no beacon available")]
     UnavailableBeacon(),
 
-    #[error("store error: {0}")]
-    StoreError(#[from] StoreError),
+    #[error("verification key store error: '{0}'")]
+    VerificationKeyStore(#[from] VerificationKeyStoreError),
+
+    #[error("stake store error: '{0}'")]
+    StakeStore(#[from] StakeStoreError),
+
+    #[error("single signature store error: '{0}'")]
+    SingleSignatureStore(#[from] SingleSignatureStoreError),
+
+    #[error("protocol parameters store error: '{0}'")]
+    ProtocolParametersStore(#[from] ProtocolParametersStoreError),
 
     #[error("beacon error: '{0}'")]
     Beacon(#[from] entities::EpochError),
diff --git a/mithril-aggregator/src/runtime/error.rs b/mithril-aggregator/src/runtime/error.rs
index a7bad7429..6d1ede430 100644
--- a/mithril-aggregator/src/runtime/error.rs
+++ b/mithril-aggregator/src/runtime/error.rs
@@ -1,10 +1,11 @@
 use crate::snapshot_stores::SnapshotStoreError;
-use crate::{ProtocolError, SnapshotError};
+use crate::store::StoreError;
+use crate::{ProtocolError, ProtocolParametersStoreError, SnapshotError};
 
 use mithril_common::chain_observer::ChainObserverError;
 use mithril_common::digesters::{ImmutableDigesterError, ImmutableFileListingError};
 use mithril_common::entities::Epoch;
-use mithril_common::store::StoreError;
+use mithril_common::store::StakeStoreError;
 use mithril_common::BeaconProviderError;
 use std::error::Error as StdError;
 use std::io;
@@ -21,6 +22,15 @@ pub enum RuntimeError {
     #[error("digester error: {0}")]
     Digester(#[from] ImmutableDigesterError),
 
+    #[error("snapshot store error: {0}")]
+    SnapshotStore(#[from] SnapshotStoreError),
+
+    #[error("stake store error: {0}")]
+    StakeStore(#[from] StakeStoreError),
+
+    #[error("protocol parameters store error: {0}")]
+    ProtocolParametersStore(#[from] ProtocolParametersStoreError),
+
     #[error("store error: {0}")]
     StoreError(#[from] StoreError),
 
@@ -42,9 +52,6 @@ pub enum RuntimeError {
     #[error("certificate chain gap error: {0} vs {1}")]
     CertificateChainEpochGap(Epoch, Epoch),
 
-    #[error("snapshot store error: {0}")]
-    SnapshotStore(#[from] SnapshotStoreError),
-
     #[error("general error: {0}")]
     General(Box<dyn StdError + Sync + Send>),
 }
diff --git a/mithril-aggregator/src/runtime/runner.rs b/mithril-aggregator/src/runtime/runner.rs
index 26c76f9ae..eb26a5989 100644
--- a/mithril-aggregator/src/runtime/runner.rs
+++ b/mithril-aggregator/src/runtime/runner.rs
@@ -484,7 +484,7 @@ pub mod tests {
 
         // old beacon means the current beacon is newer
         let beacon = Beacon {
-            network: "testnet".to_string(),
+            network: "private".to_string(),
             epoch: Epoch(0),
             immutable_file_number: 0,
         };
diff --git a/mithril-aggregator/src/store/certificate_store.rs b/mithril-aggregator/src/store/certificate_store.rs
index 208e04043..1b2dfa26d 100644
--- a/mithril-aggregator/src/store/certificate_store.rs
+++ b/mithril-aggregator/src/store/certificate_store.rs
@@ -1,7 +1,8 @@
+use super::StoreError;
 use tokio::sync::RwLock;
 
 use mithril_common::entities::{Beacon, Certificate};
-use mithril_common::store::{adapter::StoreAdapter, StoreError};
+use mithril_common::store::adapter::StoreAdapter;
 
 type Adapter = Box<dyn StoreAdapter<Key = String, Record = Certificate>>;
 
diff --git a/mithril-aggregator/src/store/error.rs b/mithril-aggregator/src/store/error.rs
new file mode 100644
index 000000000..9903841f7
--- /dev/null
+++ b/mithril-aggregator/src/store/error.rs
@@ -0,0 +1,8 @@
+use mithril_common::store::adapter::AdapterError;
+use thiserror::Error;
+
+#[derive(Debug, Error)]
+pub enum StoreError {
+    #[error("physical adapter returned an error: {0}")]
+    AdapterError(#[from] AdapterError),
+}
diff --git a/mithril-aggregator/src/store/mod.rs b/mithril-aggregator/src/store/mod.rs
index 601151670..67ba6d56a 100644
--- a/mithril-aggregator/src/store/mod.rs
+++ b/mithril-aggregator/src/store/mod.rs
@@ -1,11 +1,19 @@
 mod certificate_store;
+mod error;
 mod pending_certificate_store;
 mod protocol_parameters_store;
 mod single_signature_store;
 mod verification_key_store;
 
 pub use certificate_store::CertificateStore;
+pub use error::StoreError;
 pub use pending_certificate_store::CertificatePendingStore;
-pub use protocol_parameters_store::{ProtocolParametersStore, ProtocolParametersStorer};
-pub use single_signature_store::{SingleSignatureStore, SingleSignatureStorer};
-pub use verification_key_store::{VerificationKeyStore, VerificationKeyStorer};
+pub use protocol_parameters_store::{
+    ProtocolParametersStore, ProtocolParametersStoreError, ProtocolParametersStorer,
+};
+pub use single_signature_store::{
+    SingleSignatureStore, SingleSignatureStoreError, SingleSignatureStorer,
+};
+pub use verification_key_store::{
+    VerificationKeyStore, VerificationKeyStoreError, VerificationKeyStorer,
+};
diff --git a/mithril-aggregator/src/store/pending_certificate_store.rs b/mithril-aggregator/src/store/pending_certificate_store.rs
index c77d1a71f..268c16a8d 100644
--- a/mithril-aggregator/src/store/pending_certificate_store.rs
+++ b/mithril-aggregator/src/store/pending_certificate_store.rs
@@ -1,7 +1,8 @@
+use super::StoreError;
 use tokio::sync::RwLock;
 
 use mithril_common::entities::CertificatePending;
-use mithril_common::store::{adapter::StoreAdapter, StoreError};
+use mithril_common::store::adapter::StoreAdapter;
 
 type Adapter = Box<dyn StoreAdapter<Key = String, Record = CertificatePending>>;
 
diff --git a/mithril-aggregator/src/store/protocol_parameters_store.rs b/mithril-aggregator/src/store/protocol_parameters_store.rs
index bb870aad4..234d73a1c 100644
--- a/mithril-aggregator/src/store/protocol_parameters_store.rs
+++ b/mithril-aggregator/src/store/protocol_parameters_store.rs
@@ -1,23 +1,30 @@
 use async_trait::async_trait;
+use thiserror::Error;
 use tokio::sync::RwLock;
 
 use mithril_common::entities::{Epoch, ProtocolParameters};
-use mithril_common::store::{adapter::StoreAdapter, StoreError};
+use mithril_common::store::adapter::{AdapterError, StoreAdapter};
 
 type Adapter = Box<dyn StoreAdapter<Key = Epoch, Record = ProtocolParameters>>;
 
+#[derive(Debug, Error)]
+pub enum ProtocolParametersStoreError {
+    #[error("adapter error {0}")]
+    AdapterError(#[from] AdapterError),
+}
+
 #[async_trait]
 pub trait ProtocolParametersStorer {
     async fn save_protocol_parameters(
         &self,
         epoch: Epoch,
         protocol_parameters: ProtocolParameters,
-    ) -> Result<Option<ProtocolParameters>, StoreError>;
+    ) -> Result<Option<ProtocolParameters>, ProtocolParametersStoreError>;
 
     async fn get_protocol_parameters(
         &self,
         epoch: Epoch,
-    ) -> Result<Option<ProtocolParameters>, StoreError>;
+    ) -> Result<Option<ProtocolParameters>, ProtocolParametersStoreError>;
 }
 pub struct ProtocolParametersStore {
     adapter: RwLock<Adapter>,
@@ -37,7 +44,7 @@ impl ProtocolParametersStorer for ProtocolParametersStore {
         &self,
         epoch: Epoch,
         protocol_parameters: ProtocolParameters,
-    ) -> Result<Option<ProtocolParameters>, StoreError> {
+    ) -> Result<Option<ProtocolParameters>, ProtocolParametersStoreError> {
         let previous_protocol_parameters = self.adapter.read().await.get_record(&epoch).await?;
         self.adapter
             .write()
@@ -51,9 +58,8 @@ impl ProtocolParametersStorer for ProtocolParametersStore {
     async fn get_protocol_parameters(
         &self,
         epoch: Epoch,
-    ) -> Result<Option<ProtocolParameters>, StoreError> {
+    ) -> Result<Option<ProtocolParameters>, ProtocolParametersStoreError> {
         let record = self.adapter.read().await.get_record(&epoch).await?;
-
         Ok(record)
     }
 }
diff --git a/mithril-aggregator/src/store/single_signature_store.rs b/mithril-aggregator/src/store/single_signature_store.rs
index d4564e0e1..603c0c28b 100644
--- a/mithril-aggregator/src/store/single_signature_store.rs
+++ b/mithril-aggregator/src/store/single_signature_store.rs
@@ -1,24 +1,31 @@
 use async_trait::async_trait;
 use std::collections::HashMap;
+use thiserror::Error;
 use tokio::sync::RwLock;
 
 use mithril_common::entities::{Beacon, PartyId, SingleSignatures};
-use mithril_common::store::{adapter::StoreAdapter, StoreError};
+use mithril_common::store::adapter::{AdapterError, StoreAdapter};
 
 type Adapter = Box<dyn StoreAdapter<Key = Beacon, Record = HashMap<PartyId, SingleSignatures>>>;
 
+#[derive(Debug, Error)]
+pub enum SingleSignatureStoreError {
+    #[error("adapter error {0}")]
+    AdapterError(#[from] AdapterError),
+}
+
 #[async_trait]
 pub trait SingleSignatureStorer {
     async fn save_single_signatures(
         &self,
         beacon: &Beacon,
         single_signature: &SingleSignatures,
-    ) -> Result<Option<SingleSignatures>, StoreError>;
+    ) -> Result<Option<SingleSignatures>, SingleSignatureStoreError>;
 
     async fn get_single_signatures(
         &self,
         beacon: &Beacon,
-    ) -> Result<Option<HashMap<PartyId, SingleSignatures>>, StoreError>;
+    ) -> Result<Option<HashMap<PartyId, SingleSignatures>>, SingleSignatureStoreError>;
 }
 
 pub struct SingleSignatureStore {
@@ -39,7 +46,7 @@ impl SingleSignatureStorer for SingleSignatureStore {
         &self,
         beacon: &Beacon,
         single_signatures: &SingleSignatures,
-    ) -> Result<Option<SingleSignatures>, StoreError> {
+    ) -> Result<Option<SingleSignatures>, SingleSignatureStoreError> {
         let mut single_signatures_per_party_id = self
             .adapter
             .read()
@@ -64,7 +71,7 @@ impl SingleSignatureStorer for SingleSignatureStore {
     async fn get_single_signatures(
         &self,
         beacon: &Beacon,
-    ) -> Result<Option<HashMap<PartyId, SingleSignatures>>, StoreError> {
+    ) -> Result<Option<HashMap<PartyId, SingleSignatures>>, SingleSignatureStoreError> {
         let record = self.adapter.read().await.get_record(beacon).await?;
         Ok(record)
     }
diff --git a/mithril-aggregator/src/store/verification_key_store.rs b/mithril-aggregator/src/store/verification_key_store.rs
index 93afa4457..f8947ed3c 100644
--- a/mithril-aggregator/src/store/verification_key_store.rs
+++ b/mithril-aggregator/src/store/verification_key_store.rs
@@ -1,24 +1,31 @@
 use async_trait::async_trait;
 use std::collections::HashMap;
+use thiserror::Error;
 use tokio::sync::RwLock;
 
 use mithril_common::entities::{Epoch, PartyId, Signer};
-use mithril_common::store::{adapter::StoreAdapter, StoreError};
+use mithril_common::store::adapter::{AdapterError, StoreAdapter};
 
 type Adapter = Box<dyn StoreAdapter<Key = Epoch, Record = HashMap<PartyId, Signer>>>;
 
+#[derive(Debug, Error)]
+pub enum VerificationKeyStoreError {
+    #[error("adapter error {0}")]
+    AdapterError(#[from] AdapterError),
+}
+
 #[async_trait]
 pub trait VerificationKeyStorer {
     async fn save_verification_key(
         &self,
         epoch: Epoch,
         signer: Signer,
-    ) -> Result<Option<Signer>, StoreError>;
+    ) -> Result<Option<Signer>, VerificationKeyStoreError>;
 
     async fn get_verification_keys(
         &self,
         epoch: Epoch,
-    ) -> Result<Option<HashMap<PartyId, Signer>>, StoreError>;
+    ) -> Result<Option<HashMap<PartyId, Signer>>, VerificationKeyStoreError>;
 }
 pub struct VerificationKeyStore {
     adapter: RwLock<Adapter>,
@@ -38,7 +45,7 @@ impl VerificationKeyStorer for VerificationKeyStore {
         &self,
         epoch: Epoch,
         signer: Signer,
-    ) -> Result<Option<Signer>, StoreError> {
+    ) -> Result<Option<Signer>, VerificationKeyStoreError> {
         let mut signers = match self.adapter.read().await.get_record(&epoch).await? {
             Some(s) => s,
             None => HashMap::new(),
@@ -56,7 +63,7 @@ impl VerificationKeyStorer for VerificationKeyStore {
     async fn get_verification_keys(
         &self,
         epoch: Epoch,
-    ) -> Result<Option<HashMap<PartyId, Signer>>, StoreError> {
+    ) -> Result<Option<HashMap<PartyId, Signer>>, VerificationKeyStoreError> {
         let record = self.adapter.read().await.get_record(&epoch).await?;
         Ok(record)
     }
diff --git a/mithril-client/config/preview.json b/mithril-client/config/preview.json
new file mode 100644
index 000000000..8b9629540
--- /dev/null
+++ b/mithril-client/config/preview.json
@@ -0,0 +1,4 @@
+{
+    "network": "preview",
+    "aggregator_endpoint": "https://aggregator.api.mithril.network/aggregator"
+}
\ No newline at end of file
diff --git a/mithril-client/src/main.rs b/mithril-client/src/main.rs
index a8f50f3a3..0eab544d4 100644
--- a/mithril-client/src/main.rs
+++ b/mithril-client/src/main.rs
@@ -154,10 +154,13 @@ to {}
 
 Restore a Cardano Node with:
 
-docker run -v cardano-node-ipc:/ipc -v cardano-node-data:/data --mount type=bind,source="{}",target=/data/db/ -e NETWORK=testnet inputoutput/cardano-node
+docker run -v cardano-node-ipc:/ipc -v cardano-node-data:/data --mount type=bind,source="{}",target=/data/db/ -e NETWORK={} inputoutput/cardano-node
 
 "###,
-                    digest, to, to
+                    digest,
+                    to,
+                    to,
+                    config.network.clone()
                 );
                 Ok(())
             }
diff --git a/mithril-common/src/entities/cardano_network.rs b/mithril-common/src/entities/cardano_network.rs
index fe4a9249a..e7fadcdfc 100644
--- a/mithril-common/src/entities/cardano_network.rs
+++ b/mithril-common/src/entities/cardano_network.rs
@@ -1,27 +1,81 @@
-use crate::MagicId;
 use serde::{Deserialize, Serialize};
 use std::fmt::Display;
+use thiserror::Error;
+
+use crate::MagicId;
+
+const TESTNET_MAGIC_ID: MagicId = 1097911063;
+const PREPROD_MAGIC_ID: MagicId = 1;
+const PREVIEW_MAGIC_ID: MagicId = 2;
+
+#[derive(Error, Debug)]
+pub enum CardanoNetworkError {
+    #[error("parse from code error: '{0}'")]
+    ParseFromCode(String),
+}
 
 /// The Cardano Network that is being targeted
 #[allow(clippy::enum_variant_names)]
 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, Hash, Eq, PartialOrd)]
 pub enum CardanoNetwork {
-    /// The Cardano mainnet
+    /// The Cardano mainnet network
     MainNet,
 
-    /// The Cardano testnet
+    /// A Cardano test network (testnet, preview, or preprod)
     TestNet(MagicId),
 
     /// A Cardano private devnet
     DevNet(MagicId),
 }
 
+impl CardanoNetwork {
+    /// Instantiates a CardanoNetwork from its code and magic id
+    pub fn from_code(
+        network_code: String,
+        network_magic: Option<u64>,
+    ) -> Result<CardanoNetwork, CardanoNetworkError> {
+        match network_code.to_lowercase().as_str() {
+            "mainnet" => Ok(CardanoNetwork::MainNet),
+            "testnet" => Ok(CardanoNetwork::TestNet(TESTNET_MAGIC_ID)),
+            "preview" => Ok(CardanoNetwork::TestNet(PREVIEW_MAGIC_ID)),
+            "preprod" => Ok(CardanoNetwork::TestNet(PREPROD_MAGIC_ID)),
+            "private" => {
+                if let Some(magic) = network_magic {
+                    Ok(CardanoNetwork::TestNet(magic))
+                } else {
+                    Err(CardanoNetworkError::ParseFromCode(
+                        "no NETWORK MAGIC number given for test network".to_string(),
+                    ))
+                }
+            }
+            "devnet" => {
+                if let Some(magic) = network_magic {
+                    Ok(CardanoNetwork::DevNet(magic))
+                } else {
+                    Err(CardanoNetworkError::ParseFromCode(
+                        "no NETWORK MAGIC number given for devnet network".to_string(),
+                    ))
+                }
+            }
+            what => Err(CardanoNetworkError::ParseFromCode(format!(
+                "could not parse network '{}', the only recognized networks are: mainnet, devnet, testnet, preview, preprod and private",
+                what
+            ))),
+        }
+    }
+}
+
 impl Display for CardanoNetwork {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match *self {
             CardanoNetwork::MainNet => write!(f, "mainnet"),
             CardanoNetwork::DevNet(_) => write!(f, "devnet"),
-            CardanoNetwork::TestNet(_) => write!(f, "testnet"),
+            CardanoNetwork::TestNet(magic_id) => match magic_id {
+                TESTNET_MAGIC_ID => write!(f, "testnet"),
+                PREVIEW_MAGIC_ID => write!(f, "preview"),
+                PREPROD_MAGIC_ID => write!(f, "preprod"),
+                _ => write!(f, "private"),
+            },
         }
     }
 }
diff --git a/mithril-common/src/store/error.rs b/mithril-common/src/store/error.rs
deleted file mode 100644
index 638595e63..000000000
--- a/mithril-common/src/store/error.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-use thiserror::Error;
-
-use super::adapter::AdapterError;
-
-/// Generic error type for stores.
-#[derive(Error, Debug)]
-pub enum StoreError {
-    /// Error raised when the underlying [adapter][StoreAdapter] raise an error.
-    #[error("Store adapter raised an error: {0}")]
-    AdapterError(#[from] AdapterError),
-}
diff --git a/mithril-common/src/store/mod.rs b/mithril-common/src/store/mod.rs
index 18c3c032d..1ec7a27d4 100644
--- a/mithril-common/src/store/mod.rs
+++ b/mithril-common/src/store/mod.rs
@@ -2,8 +2,6 @@
 //! to store stakes.
 
 pub mod adapter;
-mod error;
 mod stake_store;
 
-pub use error::StoreError;
-pub use stake_store::{StakeStore, StakeStorer};
+pub use stake_store::{StakeStore, StakeStoreError, StakeStorer};
diff --git a/mithril-common/src/store/stake_store.rs b/mithril-common/src/store/stake_store.rs
index 4926fab42..372f0fda8 100644
--- a/mithril-common/src/store/stake_store.rs
+++ b/mithril-common/src/store/stake_store.rs
@@ -1,12 +1,21 @@
 use async_trait::async_trait;
+use thiserror::Error;
 use tokio::sync::RwLock;
 
 use crate::entities::{Epoch, StakeDistribution};
 
-use super::{adapter::StoreAdapter, StoreError};
+use super::adapter::{AdapterError, StoreAdapter};
 
 type Adapter = Box<dyn StoreAdapter<Key = Epoch, Record = StakeDistribution>>;
 
+/// [StakeStorer] related errors.
+#[derive(Debug, Error)]
+pub enum StakeStoreError {
+    /// Error raised when the underlying [adapter][StoreAdapter] raise an error.
+    #[error("adapter error {0}")]
+    AdapterError(#[from] AdapterError),
+}
+
 /// Represent a way to store the stake of mithril party members.
 #[async_trait]
 pub trait StakeStorer {
@@ -15,17 +24,17 @@ pub trait StakeStorer {
         &self,
         epoch: Epoch,
         stakes: StakeDistribution,
-    ) -> Result<Option<StakeDistribution>, StoreError>;
+    ) -> Result<Option<StakeDistribution>, StakeStoreError>;
 
     /// Get the stakes of all party at a given `epoch`.
-    async fn get_stakes(&self, epoch: Epoch) -> Result<Option<StakeDistribution>, StoreError>;
+    async fn get_stakes(&self, epoch: Epoch) -> Result<Option<StakeDistribution>, StakeStoreError>;
 
     /// Return the last stakes recorded in the store
     /// This is mainly used for testing right now
     async fn get_last_stakes(
         &self,
         last: usize,
-    ) -> Result<Vec<(Epoch, StakeDistribution)>, StoreError>;
+    ) -> Result<Vec<(Epoch, StakeDistribution)>, StakeStoreError>;
 }
 
 /// A [StakeStorer] that use a [StoreAdapter] to store data.
@@ -48,7 +57,7 @@ impl StakeStorer for StakeStore {
         &self,
         epoch: Epoch,
         stakes: StakeDistribution,
-    ) -> Result<Option<StakeDistribution>, StoreError> {
+    ) -> Result<Option<StakeDistribution>, StakeStoreError> {
         let mut adapter = self.adapter.write().await;
         let signers = adapter.get_record(&epoch).await?;
         adapter.store_record(&epoch, &stakes).await?;
@@ -56,14 +65,14 @@ impl StakeStorer for StakeStore {
         Ok(signers)
     }
 
-    async fn get_stakes(&self, epoch: Epoch) -> Result<Option<StakeDistribution>, StoreError> {
+    async fn get_stakes(&self, epoch: Epoch) -> Result<Option<StakeDistribution>, StakeStoreError> {
         Ok(self.adapter.read().await.get_record(&epoch).await?)
     }
 
     async fn get_last_stakes(
         &self,
         last: usize,
-    ) -> Result<Vec<(Epoch, StakeDistribution)>, StoreError> {
+    ) -> Result<Vec<(Epoch, StakeDistribution)>, StakeStoreError> {
         let results = self.adapter.read().await.get_last_n_records(last).await?;
 
         Ok(results)
diff --git a/mithril-core/benches/size_benches.rs b/mithril-core/benches/size_benches.rs
index 00d7a223d..c6b4a6c9f 100644
--- a/mithril-core/benches/size_benches.rs
+++ b/mithril-core/benches/size_benches.rs
@@ -39,7 +39,7 @@ where
 
     let closed_reg = key_reg.close::<H>();
 
-    let signer = ps[0].clone().new_signer(closed_reg).unwrap();
+    let signer = ps[0].clone().new_signer(closed_reg);
     let sig = signer.sign(&msg).unwrap();
 
     println!(
diff --git a/mithril-core/benches/stm.rs b/mithril-core/benches/stm.rs
index ea8820f3a..0023c6585 100644
--- a/mithril-core/benches/stm.rs
+++ b/mithril-core/benches/stm.rs
@@ -55,7 +55,7 @@ where
 
     let signers = initializers
         .into_par_iter()
-        .map(|p| p.new_signer(closed_reg.clone()).unwrap())
+        .map(|p| p.new_signer(closed_reg.clone()))
         .collect::<Vec<StmSigner<H>>>();
 
     group.bench_function(BenchmarkId::new("Play all lotteries", &param_string), |b| {
diff --git a/mithril-core/examples/key_registration.rs b/mithril-core/examples/key_registration.rs
index 50a8b8282..5f448f920 100644
--- a/mithril-core/examples/key_registration.rs
+++ b/mithril-core/examples/key_registration.rs
@@ -59,10 +59,10 @@ fn main() {
 
     // Now, with information of all participating parties (we can create the Merkle Tree), the
     // signers can be initialised.
-    let party_0 = party_0_init.new_signer(key_reg_0).unwrap();
-    let party_1 = party_1_init.new_signer(key_reg_1).unwrap();
-    let party_2 = party_2_init.new_signer(key_reg_2).unwrap();
-    let party_3 = party_3_init.new_signer(key_reg_3).unwrap();
+    let party_0 = party_0_init.new_signer(key_reg_0);
+    let party_1 = party_1_init.new_signer(key_reg_1);
+    let party_2 = party_2_init.new_signer(key_reg_2);
+    let party_3 = party_3_init.new_signer(key_reg_3);
 
     /////////////////////
     // operation phase //
diff --git a/mithril-core/src/error.rs b/mithril-core/src/error.rs
index ec250b6c5..f1a79e6f9 100644
--- a/mithril-core/src/error.rs
+++ b/mithril-core/src/error.rs
@@ -100,7 +100,7 @@ pub enum MerkleTreeError<D: Digest + FixedOutput> {
 }
 
 /// Errors which can be outputted by key registration.
-#[derive(Debug, Clone, thiserror::Error, PartialEq, Eq)]
+#[derive(Debug, Clone, thiserror::Error)]
 pub enum RegisterError {
     /// This key has already been registered by a participant
     #[error("This key has already been registered.")]
@@ -113,10 +113,6 @@ pub enum RegisterError {
     /// Serialization error
     #[error("Serialization error")]
     SerializationError,
-
-    /// UnregisteredInitializer error
-    #[error("Initializer not registered. Cannot participate as a signer.")]
-    UnregisteredInitializer,
 }
 
 impl<D: Digest + FixedOutput> From<RegisterError> for StmSignatureError<D> {
@@ -125,7 +121,6 @@ impl<D: Digest + FixedOutput> From<RegisterError> for StmSignatureError<D> {
             RegisterError::SerializationError => Self::SerializationError,
             RegisterError::KeyInvalid(e) => Self::IvkInvalid(e.vk),
             RegisterError::KeyRegistered(_) => unreachable!(),
-            RegisterError::UnregisteredInitializer => unreachable!(),
         }
     }
 }
diff --git a/mithril-core/src/key_reg.rs b/mithril-core/src/key_reg.rs
index c8a267807..3e986e69b 100644
--- a/mithril-core/src/key_reg.rs
+++ b/mithril-core/src/key_reg.rs
@@ -134,7 +134,6 @@ mod tests {
                         assert!(a.check().is_err());
                     }
                     Err(RegisterError::SerializationError) => unreachable!(),
-                    Err(RegisterError::UnregisteredInitializer) => unreachable!(),
                 }
             }
 
diff --git a/mithril-core/src/lib.rs b/mithril-core/src/lib.rs
index e36df9a13..92103f24e 100644
--- a/mithril-core/src/lib.rs
+++ b/mithril-core/src/lib.rs
@@ -10,4 +10,4 @@ pub mod stm;
 
 mod multi_sig;
 
-pub use crate::error::{AggregationError, RegisterError};
+pub use crate::error::AggregationError;
diff --git a/mithril-core/src/stm.rs b/mithril-core/src/stm.rs
index 028c324d6..8791a684d 100644
--- a/mithril-core/src/stm.rs
+++ b/mithril-core/src/stm.rs
@@ -63,11 +63,11 @@
 //! // Close the key registration.
 //! let closed_reg = key_reg.close();
 //!
-//! // Finalize the StmInitializer and turn it into a StmSigner, which can execute the
+//! // Finialize the StmInitializer and turn it into a StmSigner, which can execute the
 //! // rest of the protocol.
 //! let ps = ps
 //!     .into_par_iter()
-//!     .map(|p| p.new_signer(closed_reg.clone()).unwrap())
+//!     .map(|p| p.new_signer(closed_reg.clone()))
 //!     .collect::<Vec<StmSigner<D>>>();
 //!
 //! /////////////////////
@@ -288,12 +288,12 @@ impl StmInitializer {
     /// * this `StmSigner`'s parameter valuation
     /// * the `avk` as built from the current registered parties (according to the registration service)
     /// * the current total stake (according to the registration service)
-    /// Returns an error
+    /// # Panics
     /// If the initializer is not registered.
     pub fn new_signer<D: Digest + FixedOutput + Clone>(
         self,
         closed_reg: ClosedKeyReg<D>,
-    ) -> Result<StmSigner<D>, RegisterError> {
+    ) -> StmSigner<D> {
         let mut my_index = None;
         for (i, rp) in closed_reg.reg_parties.iter().enumerate() {
             if rp.0 == self.pk.vk {
@@ -301,18 +301,15 @@ impl StmInitializer {
                 break;
             }
         }
-        if my_index.is_none() {
-            return Err(RegisterError::UnregisteredInitializer);
-        }
-
-        Ok(StmSigner {
-            mt_index: my_index.unwrap(),
+        StmSigner {
+            mt_index: my_index
+                .expect("Initializer not registered. Cannot participate as a signer."),
             stake: self.stake,
             params: self.params,
             sk: self.sk,
             vk: self.pk.vk,
             closed_reg,
-        })
+        }
     }
 
     /// Convert to bytes
@@ -457,8 +454,8 @@ impl<D: Clone + Digest + FixedOutput> StmSigner<D> {
     ///     // signers can be initialised (we can create the Merkle Tree).
     ///     // The (closed) key registration is consumed, to ensure that it
     ///     // is not used to initialise a signer at a different epoch.
-    ///     let party_0 = party_0_init_e1.new_signer(party_0_key_reg_e1).unwrap();
-    ///     let party_1 = party_1_init_e1.new_signer(party_1_key_reg_e1).unwrap();
+    ///     let party_0 = party_0_init_e1.new_signer(party_0_key_reg_e1);
+    ///     let party_1 = party_1_init_e1.new_signer(party_1_key_reg_e1);
     ///
     ///     ////////////////////////////////
     ///     //////// EPOCH 2 ///////////////
@@ -907,7 +904,7 @@ mod tests {
             .collect::<Vec<_>>();
         let closed_reg = kr.close();
         ps.into_iter()
-            .map(|p| p.new_signer(closed_reg.clone()).unwrap())
+            .map(|p| p.new_signer(closed_reg.clone()))
             .collect()
     }
 
diff --git a/mithril-infra/Dockerfile.cardano b/mithril-infra/Dockerfile.cardano
index f860a6002..b2cbc86f1 100644
--- a/mithril-infra/Dockerfile.cardano
+++ b/mithril-infra/Dockerfile.cardano
@@ -1,4 +1,4 @@
-FROM inputoutput/cardano-node:1.35.2
+FROM inputoutput/cardano-node:1.35.3
 
 # Fix env file rights
 # In order to be able to interact with the Cardano node trough its 'node.socket'
diff --git a/mithril-infra/aggregator.tf b/mithril-infra/aggregator.tf
index b49435a4d..29aa8fec2 100644
--- a/mithril-infra/aggregator.tf
+++ b/mithril-infra/aggregator.tf
@@ -54,7 +54,7 @@ resource "null_resource" "mithril-aggregator" {
 
   provisioner "remote-exec" {
     inline = [
-      "IMAGE_ID=${var.image_id} GOOGLE_APPLICATION_CREDENTIALS_JSON='${var.google_application_credentials_json}' CURRENT_UID=$(id -u) DOCKER_GID=$(getent group docker | cut -d: -f3)  docker-compose -f /home/curry/docker-compose.yaml --profile all up -d"
+      "NETWORK=preview IMAGE_ID=${var.image_id} GOOGLE_APPLICATION_CREDENTIALS_JSON='${var.google_application_credentials_json}' CURRENT_UID=$(id -u) DOCKER_GID=$(getent group docker | cut -d: -f3)  docker-compose -f /home/curry/docker-compose.yaml --profile all up -d"
     ]
   }
 }
diff --git a/mithril-infra/docker-compose.yaml b/mithril-infra/docker-compose.yaml
index 14ff23ab1..5216624cd 100644
--- a/mithril-infra/docker-compose.yaml
+++ b/mithril-infra/docker-compose.yaml
@@ -2,7 +2,7 @@ version: "3.9"
 
 services:
   cardano-node:
-    image: cardano-node/1.35.2-modified
+    image: cardano-node/1.35.3-modified
     build:
       context: .
       dockerfile: Dockerfile.cardano
@@ -16,8 +16,8 @@ services:
         max-size: "100m"
         max-file: "5"
     volumes:
-      - ./cardano-configurations/network/testnet:/config
-      - ./testnet/node.db:/db
+      - ./cardano-configurations/network/${NETWORK}:/config
+      - ./${NETWORK}/node.db:/db
       - ./ipc:/ipc
     environment:
       - CARDANO_SOCKET_PATH=/ipc/node.socket # used by cardano-node
@@ -56,22 +56,21 @@ services:
       - all
     environment:
       - RUST_BACKTRACE=1
-      - NETWORK_MAGIC=1097911063
       - GOOGLE_APPLICATION_CREDENTIALS_JSON=${GOOGLE_APPLICATION_CREDENTIALS_JSON}
-      - NETWORK=${NETWORK:-testnet}
+      - NETWORK=${NETWORK}
       - PROTOCOL_PARAMETERS__K=5
       - PROTOCOL_PARAMETERS__M=100
       - PROTOCOL_PARAMETERS__PHI_F=0.65
       - RUN_INTERVAL=60000
-      - URL_SNAPSHOT_MANIFEST=https://storage.googleapis.com/cardano-${NETWORK:-testnet}/snapshots.json
+      - URL_SNAPSHOT_MANIFEST=https://storage.googleapis.com/cardano-${NETWORK}/snapshots.json
       - SNAPSHOT_STORE_TYPE=local
       - SNAPSHOT_UPLOADER_TYPE=gcp
       - DATA_STORES_DIRECTORY=/mithril-aggregator
       - CARDANO_NODE_SOCKET_PATH=/ipc/node.socket
       - CARDANO_CLI_PATH=/app/bin/cardano-cli
     volumes:
-      - ./testnet/mithril-aggregator:/mithril-aggregator
-      - ./testnet/node.db:/db
+      - ./${NETWORK}/mithril-aggregator:/mithril-aggregator
+      - ./${NETWORK}/node.db:/db
       - ./ipc:/ipc
     ports:
       - "8080:8080"
@@ -106,17 +105,16 @@ services:
     environment:
       - RUST_BACKTRACE=1
       - AGGREGATOR_ENDPOINT=http://mithril-aggregator:8080/aggregator
-      - NETWORK=testnet
-      - NETWORK_MAGIC=1097911063
-      - PARTY_ID=pool1frevxe70aqw2ce58c0muyesnahl88nfjjsp25h85jwakzgd2g2l # IOG1 / https://testnet.cardanoscan.io/pool/48f2c367cfe81cac6687c3f7c26613edfe73cd329402aa5cf493bb61
+      - NETWORK=${NETWORK}
+      - PARTY_ID=pool10g0tvpyc3phkym8r6hamdulyzd6shzjldpahyvdkljl7ur2adfe # ADA Capital / https://preview.cexplorer.io/pool/pool10g0tvpyc3phkym8r6hamdulyzd6shzjldpahyvdkljl7ur2adfe
       - RUN_INTERVAL=60000
       - DB_DIRECTORY=/db
       - DATA_STORES_DIRECTORY=/mithril-signer-0
       - CARDANO_NODE_SOCKET_PATH=/ipc/node.socket
       - CARDANO_CLI_PATH=/app/bin/cardano-cli
     volumes:
-      - ./testnet/mithril-signer-0:/mithril-signer-0
-      - ./testnet/node.db:/db
+      - ./${NETWORK}/mithril-signer-0:/mithril-signer-0
+      - ./${NETWORK}/node.db:/db
       - ./ipc:/ipc
     logging:
       driver: "json-file"
@@ -134,73 +132,16 @@ services:
     environment:
       - RUST_BACKTRACE=1
       - AGGREGATOR_ENDPOINT=http://mithril-aggregator:8080/aggregator
-      - NETWORK=testnet
-      - NETWORK_MAGIC=1097911063
-      - PARTY_ID=pool1987uhrx7pt5rea26sqsdrlenfclm2wxafd4rpspvz3qyqshnuyy # IOG2 / https://testnet.cardanoscan.io/pool/29fdcb8cde0ae83cf55a8020d1ff334e3fb538dd4b6a30c02c144040
+      - NETWORK=${NETWORK}
+      - PARTY_ID=pool1t2zfh44yjhgxxrmt563k0wjw9v7vc7jn29vpyyz4vrq4yk8k4m6 # APEX / https://preview.cexplorer.io/pool/pool1t2zfh44yjhgxxrmt563k0wjw9v7vc7jn29vpyyz4vrq4yk8k4m6
       - RUN_INTERVAL=60000
       - DB_DIRECTORY=/db
       - DATA_STORES_DIRECTORY=/mithril-signer-1
       - CARDANO_NODE_SOCKET_PATH=/ipc/node.socket
       - CARDANO_CLI_PATH=/app/bin/cardano-cli
     volumes:
-      - ./testnet/mithril-signer-1:/mithril-signer-1
-      - ./testnet/node.db:/db
-      - ./ipc:/ipc
-    logging:
-      driver: "json-file"
-      options:
-        max-size: "100m"
-        max-file: "5"
-
-  mithril-signer-2:
-    image: ghcr.io/input-output-hk/mithril-signer:${IMAGE_ID:-latest}
-    restart: always
-    user: ${CURRENT_UID}
-    profiles:
-      - mithril
-      - all
-    environment:
-      - RUST_BACKTRACE=1
-      - AGGREGATOR_ENDPOINT=http://mithril-aggregator:8080/aggregator
-      - NETWORK=testnet
-      - NETWORK_MAGIC=1097911063
-      - PARTY_ID=pool12luf5ysaxajd0g8dvtpuyxdchln363dhed8637psyx2eqzatmcv # Random unnamed pool / https://testnet.cardanoscan.io/pool/57f89a121d3764d7a0ed62c3c219b8bfe71d45b7cb4fa8f830219590
-      - RUN_INTERVAL=60000
-      - DB_DIRECTORY=/db
-      - DATA_STORES_DIRECTORY=/mithril-signer-2
-      - CARDANO_NODE_SOCKET_PATH=/ipc/node.socket
-      - CARDANO_CLI_PATH=/app/bin/cardano-cli
-    volumes:
-      - ./testnet/mithril-signer-2:/mithril-signer-2
-      - ./testnet/node.db:/db
-      - ./ipc:/ipc
-    logging:
-      driver: "json-file"
-      options:
-        max-size: "100m"
-        max-file: "5"
-
-  mithril-signer-3:
-    image: ghcr.io/input-output-hk/mithril-signer:${IMAGE_ID:-latest}
-    restart: always
-    user: ${CURRENT_UID}
-    profiles:
-      - mithril
-      - all
-    environment:
-      - RUST_BACKTRACE=1
-      - AGGREGATOR_ENDPOINT=http://mithril-aggregator:8080/aggregator
-      - NETWORK=testnet
-      - NETWORK_MAGIC=1097911063
-      - PARTY_ID=pool17vmenrzf2ylr92t390wwhk3jn0ursx00sy92cfa807s3whyknrc # PIPE / https://testnet.cardanoscan.io/pool/f337998c49513e32a9712bdcebda329bf83819ef810aac27a77fa117
-      - RUN_INTERVAL=60000
-      - DB_DIRECTORY=/db
-      - DATA_STORES_DIRECTORY=/mithril-signer-3
-      - CARDANO_NODE_SOCKET_PATH=/ipc/node.socket
-      - CARDANO_CLI_PATH=/app/bin/cardano-cli
-    volumes:
-      - ./testnet/mithril-signer-3:/mithril-signer-3
-      - ./testnet/node.db:/db
+      - ./${NETWORK}/mithril-signer-1:/mithril-signer-1
+      - ./${NETWORK}/node.db:/db
       - ./ipc:/ipc
     logging:
       driver: "json-file"
diff --git a/mithril-signer/Cargo.toml b/mithril-signer/Cargo.toml
index f20f20ade..1b5149c2b 100644
--- a/mithril-signer/Cargo.toml
+++ b/mithril-signer/Cargo.toml
@@ -11,7 +11,6 @@ blake2      = "0.9.2"
 clap = { version = "3.1.6", features = ["derive"] }
 config = "0.13.1"
 hex = "0.4.3"
-mithril = { path = "../mithril-core" }
 mithril-common = { path = "../mithril-common" }
 rand_chacha = "0.3.1"
 rand_core   = "0.6.3"
diff --git a/mithril-signer/config/preview.json b/mithril-signer/config/preview.json
new file mode 100644
index 000000000..05d6d6a58
--- /dev/null
+++ b/mithril-signer/config/preview.json
@@ -0,0 +1,8 @@
+{
+    "network": "preview",
+    "aggregator_endpoint": "https://aggregator.api.mithril.network/aggregator",
+    "party_id": 0,
+    "run_interval": 20000,
+    "db_directory": "/db",
+    "data_stores_directory": "./mithril-signer/stores"
+}
\ No newline at end of file
diff --git a/mithril-signer/src/entities.rs b/mithril-signer/src/entities.rs
index 10c63f193..3d07bf570 100644
--- a/mithril-signer/src/entities.rs
+++ b/mithril-signer/src/entities.rs
@@ -40,30 +40,7 @@ pub struct Config {
 impl Config {
     /// Return the CardanoNetwork value from the configuration.
     pub fn get_network(&self) -> Result<CardanoNetwork, ConfigError> {
-        match self.network.to_lowercase().as_str() {
-            "mainnet" => Ok(CardanoNetwork::MainNet),
-            "testnet" => {
-                if let Some(magic) = self.network_magic {
-                    Ok(CardanoNetwork::TestNet(magic))
-                } else {
-                    Err(ConfigError::Message(
-                        "no NETWORK MAGIC number given for testnet network".to_string(),
-                    ))
-                }
-            }
-            "devnet" => {
-                if let Some(magic) = self.network_magic {
-                    Ok(CardanoNetwork::DevNet(magic))
-                } else {
-                    Err(ConfigError::Message(
-                        "no NETWORK MAGIC number given for devnet network".to_string(),
-                    ))
-                }
-            }
-            what => Err(ConfigError::Message(format!(
-                "could not parse network '{}', the only recognized networks are: mainnet, devnet, testnet",
-                what
-            ))),
-        }
+        CardanoNetwork::from_code(self.network.clone(), self.network_magic)
+            .map_err(|e| ConfigError::Message(e.to_string()))
     }
 }
diff --git a/mithril-signer/src/single_signer.rs b/mithril-signer/src/single_signer.rs
index 0452fd6d3..7dbb9e61a 100644
--- a/mithril-signer/src/single_signer.rs
+++ b/mithril-signer/src/single_signer.rs
@@ -2,7 +2,6 @@ use hex::ToHex;
 use slog_scope::{info, trace, warn};
 use thiserror::Error;
 
-use mithril::RegisterError;
 use mithril_common::crypto_helper::{
     key_decode_hex, key_encode_hex, ProtocolClerk, ProtocolInitializer, ProtocolKeyRegistration,
     ProtocolSigner,
@@ -81,10 +80,6 @@ pub enum SingleSignerError {
     #[error("the protocol initializer is missing")]
     ProtocolInitializerMissing(),
 
-    /// Could not fetch a signer from a protocol initializer.
-    #[error("the protocol initializer is not registered")]
-    ProtocolInitializerNotRegistered(#[from] RegisterError),
-
     /// Encoding / Decoding error.
     #[error("codec error: '{0}'")]
     Codec(String),
@@ -128,7 +123,7 @@ impl MithrilSingleSigner {
         }
         let closed_reg = key_reg.close();
 
-        Ok(protocol_initializer.to_owned().new_signer(closed_reg)?)
+        Ok(protocol_initializer.to_owned().new_signer(closed_reg))
     }
 }
 
diff --git a/shell.nix b/shell.nix
new file mode 100644
index 000000000..133a62099
--- /dev/null
+++ b/shell.nix
@@ -0,0 +1,105 @@
+# A shell setup providing build tools and utilities for a development
+# environment.
+{ compiler ? "ghc8107"
+  # nixpkgs 21.11
+  # you can always find the latest nixpkgs revision for some edition on the corresponding git branch,
+  # e.g. https://github.com/nixos/nixpkgs/tree/release-21.05 for 21.05
+, pkgs ? import (builtins.fetchTarball "https://github.com/NixOS/nixpkgs/archive/a7ecde854aee5c4c7cd6177f54a99d2c1ff28a31.tar.gz") { }
+, libsodium-vrf ? pkgs.libsodium.overrideAttrs (oldAttrs: {
+    name = "libsodium-1.0.18-vrf";
+    src = pkgs.fetchFromGitHub {
+      owner = "input-output-hk";
+      repo = "libsodium";
+      # branch tdammers/rebased-vrf
+      rev = "66f017f16633f2060db25e17c170c2afa0f2a8a1";
+      sha256 = "12g2wz3gyi69d87nipzqnq4xc6nky3xbmi2i2pb2hflddq8ck72f";
+    };
+    nativeBuildInputs = [ pkgs.autoreconfHook ];
+    configureFlags = "--enable-static";
+  })
+, # Add cardano-node & cardano-cli for our shell environment.
+  # This is stable as it doesn't mix dependencies with this code-base; the
+  # fetched binaries are the "standard" builds that people test. This should be
+  # fast as it mostly fetches Hydra (CI) caches without building much.
+  cardano-node ? import
+    (pkgs.fetchgit {
+      url = "https://github.com/input-output-hk/cardano-node";
+      rev = "1.34.1";
+      sha256 = "1hh53whcj5y9kw4qpkiza7rmkniz18r493vv4dzl1a8r5fy3b2bv";
+    })
+    { }
+}:
+let
+  libs = [
+    pkgs.clang
+    pkgs.gtest
+    pkgs.m4
+    pkgs.openssl.dev
+    pkgs.zlib
+    pkgs.zlib.dev
+    pkgs.lzma
+    libsodium-vrf
+  ];
+
+  tools = [
+    pkgs.rustc
+    pkgs.cargo
+    pkgs.rls
+    pkgs.rustfmt
+    pkgs.pkgconfig
+    pkgs.haskellPackages.ghcid
+    pkgs.haskellPackages.hspec-discover
+    pkgs.haskellPackages.graphmod
+    pkgs.haskellPackages.cabal-plan
+    pkgs.haskellPackages.cabal-fmt
+    # For plotting results of local-cluster benchmarks
+    pkgs.gnuplot
+    # For generating documentation
+    pkgs.yarn
+    # To interact with cardano-node and testing out things
+    cardano-node.cardano-cli
+    cardano-node.cardano-node
+  ];
+
+  # A "cabal-only" shell which does not use haskell.nix
+  cabalShell = pkgs.mkShell {
+    name = "mithril-stack-shell";
+
+    buildInputs = tools ++ libs ++ [
+      pkgs.haskell.compiler.${compiler}
+      pkgs.cabal-install
+      pkgs.stack
+      pkgs.haskell-language-server
+      pkgs.ormolu
+      pkgs.git
+      pkgs.pkgconfig
+    ];
+
+    # Ensure that libz.so and other libraries are available to TH splices.
+    LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath libs;
+
+    # Force a UTF-8 locale because many Haskell programs and tests
+    # assume this.
+    LANG = "en_US.UTF-8";
+    # This is required by postgres
+    LC_ALL = "C.UTF-8";
+
+    # Make the shell suitable for the stack nix integration
+    # <nixpkgs/pkgs/development/haskell-modules/generic-stack-builder.nix>
+    GIT_SSL_CAINFO = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
+    STACK_IN_NIX_SHELL = "true";
+    phases = [ "buildPhase" ];
+
+    buildPhase = ''
+    echo "------------------------------------------------------------" >>$out
+    echo " WARNING: the existence of this path is not guaranteed." >>$out
+    echo " It is an internal implementation detail for pkgs.mkShell."   >>$out
+    echo "------------------------------------------------------------" >>$out
+    echo >> $out
+    # Record all build inputs as runtime dependencies
+    export >> $out
+  '';
+  };
+
+in
+cabalShell


Test patch:
diff --git a/mithril-aggregator/config/testnet.json b/mithril-aggregator/config/testnet.json
index 0c6582c12..cf3f16242 100644
--- a/mithril-aggregator/config/testnet.json
+++ b/mithril-aggregator/config/testnet.json
@@ -2,7 +2,6 @@
     "cardano_cli_path": "cardano-cli",
     "cardano_cli_socket_path": "/tmp/cardano.sock",
     "network": "testnet",
-    "network_magic": 1097911063,
     "run_interval": 60000,
     "protocol_parameters": {
         "k": 5,
diff --git a/mithril-common/src/crypto_helper/tests_setup.rs b/mithril-common/src/crypto_helper/tests_setup.rs
index 1a6940ec3..2922d672f 100644
--- a/mithril-common/src/crypto_helper/tests_setup.rs
+++ b/mithril-common/src/crypto_helper/tests_setup.rs
@@ -104,8 +104,7 @@ pub fn setup_signers_from_parties(
                 protocol_initializer.verification_key(),
                 protocol_initializer
                     .clone()
-                    .new_signer(closed_key_registration.clone())
-                    .unwrap(),
+                    .new_signer(closed_key_registration.clone()),
                 protocol_initializer,
             )
         })
diff --git a/mithril-core/tests/integration.rs b/mithril-core/tests/integration.rs
index 28cf4da02..26fb94ab3 100644
--- a/mithril-core/tests/integration.rs
+++ b/mithril-core/tests/integration.rs
@@ -44,7 +44,7 @@ fn test_full_protocol() {
 
     let ps = ps
         .into_par_iter()
-        .map(|p| p.new_signer(closed_reg.clone()).unwrap())
+        .map(|p| p.new_signer(closed_reg.clone()))
         .collect::<Vec<StmSigner<H>>>();
 
     /////////////////////
diff --git a/mithril-infra/scripts/configure-testnet.sh b/mithril-infra/scripts/configure-testnet.sh
index 0f336b083..6b6ea9bf4 100644
--- a/mithril-infra/scripts/configure-testnet.sh
+++ b/mithril-infra/scripts/configure-testnet.sh
@@ -7,17 +7,6 @@ set -e
 # accept github.com key
 ssh-keyscan github.com >> ~/.ssh/known_hosts
 
-# download gpg key signing testnet dump
-curl https://api.github.com/users/abailly-iohk/gpg_keys | jq -r '.[] | .raw_key' | gpg --import
-
-# download & verify testnet archive
-# TODO: use mithril client!
-curl -o testnet.tar.gz https://storage.googleapis.com/cardano-testnet/testnet.tar.gz
-curl -o testnet.tar.gz.asc https://storage.googleapis.com/cardano-testnet/testnet.tar.gz.asc
-gpg --verify testnet.tar.gz.asc
-
-tar xzf testnet.tar.gz
-
 # get cardano network configuration
 git clone https://github.com/input-output-hk/cardano-configurations
 
diff --git a/mithril-test-lab/README.md b/mithril-test-lab/README.md
index c34f71c27..efb3581e1 100644
--- a/mithril-test-lab/README.md
+++ b/mithril-test-lab/README.md
@@ -4,6 +4,23 @@ Haskell components and tests for the Mithril system.
 
 # Installing
 
+## Using Nix
+
+We provide a `shell.nix` to set up a development environment. So a simple call to nix-shell should put everything in place for building, testing, and general development.
+Make sure the following caches are listed in your `nix.conf` for a speedy setup:
+
+```nix
+substituters = https://cache.nixos.org https://iohk.cachix.org https://hydra.iohk.io
+trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= iohk.cachix.org-1:DpRUyj7h7V830dp/i6Nti+NEO2/nhblbov/8MW7Rqoo= hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=
+```
+
+Also, some of us use `direnv` and `nix-direnv` to automatically import & cache the nix-shell environment into our favorite shell or editor.
+Within the nix-shell, `cabal build` and `cabal test` should work as expected.
+
+You can also use `nix-build` to build the project and all executables. You will find them in `result/bin/` after the build.
+
+## Using Cabal
+
 1. Install basic Haskell development environment, for example using ghcup. Hydra requires GHC 8.10.7 and a recent cabal (> 3.0).
 1. Install various system dependencies On Debian-like:
 
diff --git a/mithril-test-lab/mithril-aggregator-server/stack.yaml b/mithril-test-lab/mithril-aggregator-server/stack.yaml
index b2ea94806..df636e059 100644
--- a/mithril-test-lab/mithril-aggregator-server/stack.yaml
+++ b/mithril-test-lab/mithril-aggregator-server/stack.yaml
@@ -6,3 +6,7 @@ extra-deps:
 - servant-client-core-0.16
 packages:
 - '.'
+nix:
+  enable: false
+  packages:
+    - zlib
\ No newline at end of file