Skip to content

Commit

Permalink
feat(offchain): adds the authority-claimer
Browse files Browse the repository at this point in the history
  • Loading branch information
renan061 committed Aug 10, 2023
1 parent c75563d commit 2c552ae
Show file tree
Hide file tree
Showing 16 changed files with 706 additions and 1 deletion.
19 changes: 19 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
26 changes: 26 additions & 0 deletions offchain/authority-claimer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[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" }

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
49 changes: 49 additions & 0 deletions offchain/authority-claimer/src/checker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// (c) Cartesi and individual authors (see AUTHORS)
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)

use async_trait::async_trait;
use rollups_events::RollupsClaim;
use snafu::Snafu;
use std::fmt::Debug;

/// The `DuplicateChecker` checks if a given claim was already submitted
/// to the blockchain.
#[async_trait]
pub trait DuplicateChecker: Debug {
type Error: snafu::Error;

async fn is_duplicated_rollups_claim(
&self,
rollups_claim: &RollupsClaim,
) -> Result<bool, Self::Error>;
}

// ------------------------------------------------------------------------------------------------
// DefaultDuplicateChecker
// ------------------------------------------------------------------------------------------------

#[derive(Debug)]
pub struct DefaultDuplicateChecker;

#[derive(Debug, Snafu)]
pub enum DefaultDuplicateCheckerError {
Todo,
}

impl DefaultDuplicateChecker {
pub fn new() -> Result<Self, DefaultDuplicateCheckerError> {
todo!()
}
}

#[async_trait]
impl DuplicateChecker for DefaultDuplicateChecker {
type Error = DefaultDuplicateCheckerError;

async fn is_duplicated_rollups_claim(
&self,
_rollups_claim: &RollupsClaim,
) -> Result<bool, Self::Error> {
todo!()
}
}
89 changes: 89 additions & 0 deletions offchain/authority-claimer/src/claimer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// (c) Cartesi and individual authors (see AUTHORS)
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)

use async_trait::async_trait;
use snafu::ResultExt;
use tracing::{info, trace};

use crate::{
checker::DuplicateChecker, listener::BrokerListener,
sender::TransactionSender,
};

/// The `AuthorityClaimer` starts an event loop that waits for claim messages
/// from the broker, and then sends the claims to the blockchain. It checks to
/// see if the claim is duplicated before sending.
///
/// It uses three injected traits, `BrokerListener`, `DuplicateChecker`, and
/// `TransactionSender`, to, respectivelly, listen for messages, check for
/// duplicated claims, and send claims to the blockchain.
#[async_trait]
pub trait AuthorityClaimer {
async fn start<L, C, S>(
&self,
broker_listener: L,
duplicate_checker: C,
transaction_sender: S,
) -> Result<(), AuthorityClaimerError<L, C, S>>
where
L: BrokerListener + Send + Sync,
C: DuplicateChecker + Send + Sync,
S: TransactionSender + Send,
{
trace!("Starting the authority claimer loop");
let mut transaction_sender = transaction_sender;
loop {
let rollups_claim = broker_listener
.listen()
.await
.context(BrokerListenerSnafu)?;
trace!("Got a claim from the broker: {:?}", rollups_claim);

let is_duplicated_rollups_claim = duplicate_checker
.is_duplicated_rollups_claim(&rollups_claim)
.await
.context(DuplicateCheckerSnafu)?;
if is_duplicated_rollups_claim {
trace!("It was a duplicated claim");
continue;
}

info!("Sending a new rollups claim");
transaction_sender = transaction_sender
.send_rollups_claim(rollups_claim)
.await
.context(TransactionSenderSnafu)?
}
}
}

#[derive(Debug, snafu::Snafu)]
pub enum AuthorityClaimerError<
L: BrokerListener + 'static,
C: DuplicateChecker + 'static,
S: TransactionSender + 'static,
> {
#[snafu(display("broker listener error"))]
BrokerListenerError { source: L::Error },

#[snafu(display("duplicate checker error"))]
DuplicateCheckerError { source: C::Error },

#[snafu(display("transaction sender error"))]
TransactionSenderError { source: S::Error },
}

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

#[derive(Default)]
pub struct DefaultAuthorityClaimer;

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

impl AuthorityClaimer for DefaultAuthorityClaimer {}
136 changes: 136 additions & 0 deletions offchain/authority-claimer/src/config/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// (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::{
AuthorityClaimerConfigError, InvalidRegionSnafu, MnemonicFileSnafu,
TxManagerSnafu, TxSigningConfigError, TxSigningSnafu,
},
json::{read_json_file, DappDeployment},
AuthorityClaimerConfig, TxSigningConfig,
};

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

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

#[command(flatten)]
tx_signing_config: TxSigningCLIConfig,

#[command(flatten)]
broker_config: BrokerCLIConfig,

/// Path to a file with the deployment json of the 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 tx_manager_config =
TxManagerConfig::initialize(cli_config.tx_manager_config)
.context(TxManagerSnafu)?;

let tx_signing_config =
TxSigningConfig::try_from(cli_config.tx_signing_config)
.context(TxSigningSnafu)?;

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 {
tx_manager_config,
tx_signing_config,
tx_manager_priority: Priority::Normal,
broker_config,
dapp_address,
dapp_deploy_block_hash,
})
}
}

// ------------------------------------------------------------------------------------------------
// TxSigningConfig
// ------------------------------------------------------------------------------------------------

#[derive(Debug, Clone, Parser)]
#[command(name = "tx_signing_config")]
#[command(about = "Configuration for signing transactions")]
pub(crate) struct TxSigningCLIConfig {
/// Signer mnemonic, overrides `tx_signing_mnemonic_file` and `tx_signing_aws_kms_*`
#[arg(long, env)]
tx_signing_mnemonic: Option<String>,

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

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

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

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

impl TryFrom<TxSigningCLIConfig> for TxSigningConfig {
type Error = TxSigningConfigError;

fn try_from(cli: TxSigningCLIConfig) -> Result<Self, Self::Error> {
let account_index = cli.tx_signing_mnemonic_account_index;
if let Some(mnemonic) = cli.tx_signing_mnemonic {
Ok(TxSigningConfig::Mnemonic {
mnemonic,
account_index,
})
} else if let Some(path) = cli.tx_signing_mnemonic_file {
let mnemonic = fs::read_to_string(path.clone())
.context(MnemonicFileSnafu { path })?
.trim()
.to_string();
Ok(TxSigningConfig::Mnemonic {
mnemonic,
account_index,
})
} else {
match (cli.tx_signing_aws_kms_key_id, cli.tx_signing_aws_kms_region)
{
(None, _) => Err(TxSigningConfigError::MissingConfiguration),
(Some(_), None) => Err(TxSigningConfigError::MissingRegion),
(Some(key_id), Some(region)) => {
let region = Region::from_str(&region)
.context(InvalidRegionSnafu)?;
Ok(TxSigningConfig::Aws { key_id, region })
}
}
}
}
}
48 changes: 48 additions & 0 deletions offchain/authority-claimer/src/config/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// (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"))]
TxManagerError { source: TxManagerConfigError },

#[snafu(display("TxSigning configuration error"))]
TxSigningError { source: TxSigningConfigError },

#[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 TxSigningConfigError {
#[snafu(display("Missing auth configuration"))]
MissingConfiguration,

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

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

#[snafu(display("Invalid AWS region"))]
InvalidRegion { source: ParseRegionError },
}
Loading

0 comments on commit 2c552ae

Please sign in to comment.