Skip to content
This repository has been archived by the owner on Mar 12, 2024. It is now read-only.

Commit

Permalink
feat(offchain): adds the authority-claimer
Browse files Browse the repository at this point in the history
  • Loading branch information
renan061 committed Jul 31, 2023
1 parent c2cb48b commit 02fd9e9
Show file tree
Hide file tree
Showing 16 changed files with 630 additions and 1 deletion.
20 changes: 20 additions & 0 deletions offchain/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions offchain/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"advance-runner",
"authority-claimer",
"contracts",
"data",
"dispatcher",
Expand Down Expand Up @@ -38,6 +39,7 @@ diesel_migrations = "2.0"
env_logger = "0.10"
eth-tx-manager = "0.10"
ethabi = "18.0"
ethers = "1.0"
ethers-signers = "1.0"
futures = "0.3"
futures-util = "0.3"
Expand Down
28 changes: 28 additions & 0 deletions offchain/authority-claimer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "authority-claimer"
license = "Apache-2.0"
version = "1.0.0"
edition = "2021"

[[bin]]
name = "cartesi-rollups-authority-claimer"
path = "src/main.rs"
test = false

[dependencies]
http-server = { path = "../http-server" }
rollups-events = { path = "../rollups-events" }

thiserror = "1.0"

async-trait.workspace = true
clap = { workspace = true, features = ["derive"] }
eth-tx-manager.workspace = true
ethers.workspace = true
rusoto_core.workspace = true
serde.workspace = true
serde_json.workspace = true
snafu.workspace = true
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
tracing.workspace = true
73 changes: 73 additions & 0 deletions offchain/authority-claimer/src/claimer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// (c) Cartesi and individual authors (see AUTHORS)
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)

use async_trait::async_trait;
use tracing::{trace, warn};

use crate::{listener::BrokerListener, sender::ClaimSender};

/// The `AuthorityClaimer` starts an event loop that waits for claim messages
/// from the broker, and then sends the claims to the blockchain.
///
/// It uses a `BrokerListener` for listening for messages from the broker.
///
/// It also uses a `ClaimSender` that interacts with the blockchain and
/// effectively submits the claims.
#[async_trait]
pub trait AuthorityClaimer<'a, L: BrokerListener, S: ClaimSender>
where
L: 'a + Sync,
S: 'a,
{
async fn start(
&'a self,
broker_listener: L,
claim_sender: S,
) -> Result<(), AuthorityClaimerError<S, L>> {
trace!("Starting the authority claimer loop");
let mut claim_sender = claim_sender;
loop {
match broker_listener.listen().await {
Ok(rollups_claim) => {
trace!("Got a claim from the broker: {:?}", rollups_claim);
claim_sender = claim_sender
.send_claim(rollups_claim)
.await
.map_err(AuthorityClaimerError::ClaimSenderError)?;
}
Err(e) => {
warn!("Broker error `{}`", e);
}
}
}
}
}

#[derive(Debug, thiserror::Error)]
pub enum AuthorityClaimerError<S: ClaimSender, L: BrokerListener> {
#[error("claim sender error: {0}")]
ClaimSenderError(S::Error),

#[error("broker listener error: {0}")]
BrokerListenerError(L::Error),
}

// ------------------------------------------------------------------------------------------------
// DefaultAuthorityClaimer
// ------------------------------------------------------------------------------------------------

pub struct DefaultAuthorityClaimer;

impl DefaultAuthorityClaimer {
pub fn new() -> Self {
Self
}
}

impl<'a, L: BrokerListener, S: ClaimSender> AuthorityClaimer<'a, L, S>
for DefaultAuthorityClaimer
where
L: 'a + Sync,
S: 'a,
{
}
133 changes: 133 additions & 0 deletions offchain/authority-claimer/src/config/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// (c) Cartesi and individual authors (see AUTHORS)
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)

use clap::{command, Parser};
use eth_tx_manager::{
config::{TxEnvCLIConfig as TxManagerCLIConfig, TxManagerConfig},
Priority,
};
use rollups_events::{BrokerCLIConfig, BrokerConfig};
use rusoto_core::Region;
use snafu::ResultExt;
use std::{fs, path::PathBuf, str::FromStr};

use crate::config::{
error::{
AuthConfigError, AuthSnafu, AuthorityClaimerConfigError,
InvalidRegionSnafu, MnemonicFileSnafu, TxManagerSnafu,
},
json::{read_json_file, DappDeployment},
AuthConfig, AuthorityClaimerConfig,
};

// ------------------------------------------------------------------------------------------------
// AuthorityClaimerCLI
// ------------------------------------------------------------------------------------------------

#[derive(Clone, Parser)]
#[command(name = "rd_config")]
#[command(about = "Configuration for rollups authority claimer")]
pub(crate) struct AuthorityClaimerCLI {
#[command(flatten)]
txm_config: TxManagerCLIConfig,

#[command(flatten)]
auth_config: AuthCLIConfig,

#[command(flatten)]
broker_config: BrokerCLIConfig,

/// Path to file with deployment json of dapp
#[arg(long, env, default_value = "./dapp_deployment.json")]
dapp_deployment_file: PathBuf,
}

impl TryFrom<AuthorityClaimerCLI> for AuthorityClaimerConfig {
type Error = AuthorityClaimerConfigError;

fn try_from(cli_config: AuthorityClaimerCLI) -> Result<Self, Self::Error> {
let txm_config = TxManagerConfig::initialize(cli_config.txm_config)
.context(TxManagerSnafu)?;

let auth_config =
AuthConfig::try_from(cli_config.auth_config).context(AuthSnafu)?;

let broker_config = BrokerConfig::from(cli_config.broker_config);

let dapp_deployment =
read_json_file::<DappDeployment>(cli_config.dapp_deployment_file)?;
let dapp_address = dapp_deployment.dapp_address;
let dapp_deploy_block_hash = dapp_deployment.dapp_deploy_block_hash;

Ok(AuthorityClaimerConfig {
txm_config,
auth_config,
broker_config,
dapp_address,
dapp_deploy_block_hash,
txm_priority: Priority::Normal,
})
}
}

// ------------------------------------------------------------------------------------------------
// AuthConfig
// ------------------------------------------------------------------------------------------------

#[derive(Debug, Clone, Parser)]
#[command(name = "auth_config")]
#[command(about = "Configuration for signing authentication")]
pub(crate) struct AuthCLIConfig {
/// Signer mnemonic, overrides `auth_mnemonic_file` and `auth_aws_kms_*`
#[arg(long, env)]
auth_mnemonic: Option<String>,

/// Signer mnemonic file path, overrides `auth_aws_kms_*`
#[arg(long, env)]
auth_mnemonic_file: Option<String>,

/// Mnemonic account index
#[arg(long, env)]
auth_mnemonic_account_index: Option<u32>,

/// AWS KMS signer key-id
#[arg(long, env)]
auth_aws_kms_key_id: Option<String>,

/// AWS KMS signer region
#[arg(long, env)]
auth_aws_kms_region: Option<String>,
}

impl TryFrom<AuthCLIConfig> for AuthConfig {
type Error = AuthConfigError;

fn try_from(cli: AuthCLIConfig) -> Result<Self, Self::Error> {
let account_index = cli.auth_mnemonic_account_index;
if let Some(mnemonic) = cli.auth_mnemonic {
Ok(AuthConfig::Mnemonic {
mnemonic,
account_index,
})
} else if let Some(path) = cli.auth_mnemonic_file {
let mnemonic = fs::read_to_string(path.clone())
.context(MnemonicFileSnafu { path })?
.trim()
.to_string();
Ok(AuthConfig::Mnemonic {
mnemonic,
account_index,
})
} else {
match (cli.auth_aws_kms_key_id, cli.auth_aws_kms_region) {
(None, _) => Err(AuthConfigError::MissingConfiguration),
(Some(_), None) => Err(AuthConfigError::MissingRegion),
(Some(key_id), Some(region)) => {
let region = Region::from_str(&region)
.context(InvalidRegionSnafu)?;
Ok(AuthConfig::Aws { key_id, region })
}
}
}
}
}
52 changes: 52 additions & 0 deletions offchain/authority-claimer/src/config/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// (c) Cartesi and individual authors (see AUTHORS)
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)

use eth_tx_manager::config::Error as TxManagerConfigError;
use rusoto_core::region::ParseRegionError;
use snafu::Snafu;
use std::path::PathBuf;

#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))]
pub enum AuthorityClaimerConfigError {
#[snafu(display("TxManager configuration error: {}", source))]
TxManagerError { source: TxManagerConfigError },

#[snafu(display("Auth configuration error: {}", source))]
AuthError { source: AuthConfigError },

#[snafu(display("Read file error ({})", path.display()))]
ReadFileError {
path: PathBuf,
source: std::io::Error,
},

#[snafu(display("Json parse error ({})", path.display()))]
JsonParseError {
path: PathBuf,
source: serde_json::Error,
},
}

#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))]
pub enum AuthConfigError {
#[snafu(display("Missing auth configuration"))]
MissingConfiguration,

#[snafu(display(
"Could not read mnemonic file at path `{}`: {}",
path,
source
))]
MnemonicFileError {
path: String,
source: std::io::Error,
},

#[snafu(display("Missing AWS region"))]
MissingRegion,

#[snafu(display("Invalid AWS region"))]
InvalidRegion { source: ParseRegionError },
}
29 changes: 29 additions & 0 deletions offchain/authority-claimer/src/config/json.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// (c) Cartesi and individual authors (see AUTHORS)
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)

use ethers::types::{Address, H256};
use serde::{de::DeserializeOwned, Deserialize};
use snafu::ResultExt;
use std::{fs::File, io::BufReader, path::PathBuf};

use crate::config::error::{
AuthorityClaimerConfigError, JsonParseSnafu, ReadFileSnafu,
};

#[derive(Clone, Debug, Deserialize)]
pub(crate) struct DappDeployment {
#[serde(rename = "address")]
pub dapp_address: Address, // TODO: can I use rollups_events types?

#[serde(rename = "blockHash")]
pub dapp_deploy_block_hash: H256, // TODO: can I use rollups_events types?
}

pub(crate) fn read_json_file<T: DeserializeOwned>(
path: PathBuf,
) -> Result<T, AuthorityClaimerConfigError> {
let file =
File::open(&path).context(ReadFileSnafu { path: path.clone() })?;
let reader = BufReader::new(file);
serde_json::from_reader(reader).context(JsonParseSnafu { path })
}
Loading

0 comments on commit 02fd9e9

Please sign in to comment.