-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(offchain): adds the authority-claimer
- Loading branch information
Showing
16 changed files
with
705 additions
and
1 deletion.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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,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 |
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,46 @@ | ||
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!() | ||
} | ||
} |
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,85 @@ | ||
// (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::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 should_send_rollups_claim = !duplicate_checker | ||
.is_duplicated_rollups_claim(&rollups_claim) | ||
.await | ||
.context(DuplicateCheckerSnafu)?; | ||
|
||
if should_send_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 | ||
// ------------------------------------------------------------------------------------------------ | ||
|
||
pub struct DefaultAuthorityClaimer; | ||
|
||
impl DefaultAuthorityClaimer { | ||
pub fn new() -> Self { | ||
Self | ||
} | ||
} | ||
|
||
impl AuthorityClaimer for DefaultAuthorityClaimer {} |
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,138 @@ | ||
// (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, | ||
broker_config, | ||
dapp_address, | ||
dapp_deploy_block_hash, | ||
tx_manager_priority: Priority::Normal, | ||
}) | ||
} | ||
} | ||
|
||
// ------------------------------------------------------------------------------------------------ | ||
// TxSigningConfig | ||
// ------------------------------------------------------------------------------------------------ | ||
|
||
#[derive(Debug, Clone, Parser)] | ||
#[command(name = "tx_signing_config")] | ||
#[command( | ||
about = "Configuration for signing authentication for the 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(®ion) | ||
.context(InvalidRegionSnafu)?; | ||
Ok(TxSigningConfig::Aws { key_id, region }) | ||
} | ||
} | ||
} | ||
} | ||
} |
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,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("TxSigning configuration error: {}", source))] | ||
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, | ||
source | ||
))] | ||
MnemonicFileError { | ||
path: String, | ||
source: std::io::Error, | ||
}, | ||
|
||
#[snafu(display("Missing AWS region"))] | ||
MissingRegion, | ||
|
||
#[snafu(display("Invalid AWS region"))] | ||
InvalidRegion { source: ParseRegionError }, | ||
} |
Oops, something went wrong.