From f645defb5210996162eeec3cc00f3d5755b64250 Mon Sep 17 00:00:00 2001 From: Danilo Tuler Date: Wed, 13 Mar 2024 15:34:02 -0300 Subject: [PATCH 1/2] feat: support specification of private key to claimer --- CHANGELOG.md | 1 + cmd/cartesi-rollups-node/services.go | 3 ++ docs/config.md | 8 +++ internal/config/auth.go | 5 ++ internal/config/config.go | 5 ++ internal/config/generate/Config.toml | 9 ++++ internal/config/get.go | 5 ++ offchain/authority-claimer/src/config/cli.rs | 10 +++- offchain/authority-claimer/src/config/mod.rs | 4 ++ .../authority-claimer/src/signer/signer.rs | 53 +++++++++++++++++-- 10 files changed, 97 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e41f824b4..de920d38f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added verification to ensure CARTESI_BLOCKCHAIN_ID matches the id returned from the Ethereum node +- Added support for CARTESI_AUTH_PRIVATE_KEY ## Changed diff --git a/cmd/cartesi-rollups-node/services.go b/cmd/cartesi-rollups-node/services.go index ad5395d2f..9464310cb 100644 --- a/cmd/cartesi-rollups-node/services.go +++ b/cmd/cartesi-rollups-node/services.go @@ -133,6 +133,9 @@ func newAuthorityClaimer() services.CommandService { s.Env = append(s.Env, fmt.Sprintf("AUTHORITY_CLAIMER_HTTP_SERVER_PORT=%v", getPort(portOffsetAuthorityClaimer))) switch auth := config.GetAuth().(type) { + case config.AuthPrivateKey: + s.Env = append(s.Env, + fmt.Sprintf("TX_SIGNING_PRIVATE_KEY=%v", auth.PrivateKey)) case config.AuthMnemonic: s.Env = append(s.Env, fmt.Sprintf("TX_SIGNING_MNEMONIC=%v", auth.Mnemonic)) diff --git a/docs/config.md b/docs/config.md index 8d2c6b3c7..f98efe833 100644 --- a/docs/config.md +++ b/docs/config.md @@ -53,6 +53,14 @@ Overrides `CARTESI_AUTH_AWS_KMS_*`. * **Type:** `string` +## `CARTESI_AUTH_PRIVATE_KEY` + +The node will use this private key to sign transactions. + +Overrides `CARTESI_AUTH_MNEMONIC`, `CARTESI_AUTH_MNEMONIC_FILE` and `CARTESI_AUTH_AWS_KMS_*`. + +* **Type:** `string` + ## `CARTESI_BLOCKCHAIN_BLOCK_TIMEOUT` Block subscription timeout in seconds. diff --git a/internal/config/auth.go b/internal/config/auth.go index bd71dc6bc..5e03875a1 100644 --- a/internal/config/auth.go +++ b/internal/config/auth.go @@ -6,6 +6,11 @@ package config // Auth objects are used to sign transactions. type Auth any +// Allows signing through private keys. +type AuthPrivateKey struct { + PrivateKey string +} + // Allows signing through mnemonics. type AuthMnemonic struct { Mnemonic string diff --git a/internal/config/config.go b/internal/config/config.go index 815b0019c..00aa30a86 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -59,6 +59,11 @@ var ( // ------------------------------------------------------------------------------------------------ func GetAuth() Auth { + // if private key is coming from an environment variable + if privateKey, ok := getCartesiAuthPrivateKey(); ok { + return AuthPrivateKey{PrivateKey: privateKey} + } + // getting the (optional) account index index, _ := getCartesiAuthMnemonicAccountIndex() diff --git a/internal/config/generate/Config.toml b/internal/config/generate/Config.toml index 908e872fa..c71813108 100644 --- a/internal/config/generate/Config.toml +++ b/internal/config/generate/Config.toml @@ -170,6 +170,15 @@ description = """ When using mnemonics to sign transactions, the node will use this account index to generate the private key.""" +[auth.CARTESI_AUTH_PRIVATE_KEY] +go-type = "string" +export = false +redact = true +description = """ +The node will use this private key to sign transactions. + +Overrides `CARTESI_AUTH_MNEMONIC`, `CARTESI_AUTH_MNEMONIC_FILE` and `CARTESI_AUTH_AWS_KMS_*`.""" + [auth.CARTESI_AUTH_AWS_KMS_KEY_ID] go-type = "string" export = false diff --git a/internal/config/get.go b/internal/config/get.go index 593e1ecb5..ad19242e4 100644 --- a/internal/config/get.go +++ b/internal/config/get.go @@ -39,6 +39,11 @@ func getCartesiAuthMnemonicFile() (string, bool) { return v, ok } +func getCartesiAuthPrivateKey() (string, bool) { + v, ok := getOptional("CARTESI_AUTH_PRIVATE_KEY", "", false, true, toString) + return v, ok +} + func GetCartesiBlockchainBlockTimeout() int { v := get("CARTESI_BLOCKCHAIN_BLOCK_TIMEOUT", "60", true, false, toInt) return v diff --git a/offchain/authority-claimer/src/config/cli.rs b/offchain/authority-claimer/src/config/cli.rs index f0d71e469..d1f07dc04 100644 --- a/offchain/authority-claimer/src/config/cli.rs +++ b/offchain/authority-claimer/src/config/cli.rs @@ -91,6 +91,10 @@ impl TryFrom for AuthorityClaimerConfig { #[derive(Debug, Parser)] #[command(name = "tx_signing_config")] pub(crate) struct TxSigningCLIConfig { + /// Signer private key, overrides `tx_signing_mnemonic` , `tx_signing_mnemonic_file` and `tx_signing_aws_kms_*` + #[arg(long, env)] + tx_signing_private_key: Option, + /// Signer mnemonic, overrides `tx_signing_mnemonic_file` and `tx_signing_aws_kms_*` #[arg(long, env)] tx_signing_mnemonic: Option, @@ -117,7 +121,11 @@ impl TryFrom for TxSigningConfig { fn try_from(cli: TxSigningCLIConfig) -> Result { let account_index = cli.tx_signing_mnemonic_account_index; - if let Some(mnemonic) = cli.tx_signing_mnemonic { + if let Some(private_key) = cli.tx_signing_private_key { + Ok(TxSigningConfig::PrivateKey { + private_key: Redacted::new(private_key), + }) + } else if let Some(mnemonic) = cli.tx_signing_mnemonic { Ok(TxSigningConfig::Mnemonic { mnemonic: Redacted::new(mnemonic), account_index, diff --git a/offchain/authority-claimer/src/config/mod.rs b/offchain/authority-claimer/src/config/mod.rs index 25c171c52..21d1932b2 100644 --- a/offchain/authority-claimer/src/config/mod.rs +++ b/offchain/authority-claimer/src/config/mod.rs @@ -35,6 +35,10 @@ pub struct AuthorityClaimerConfig { #[derive(Debug, Clone)] pub enum TxSigningConfig { + PrivateKey { + private_key: Redacted, + }, + Mnemonic { mnemonic: Redacted, account_index: Option, diff --git a/offchain/authority-claimer/src/signer/signer.rs b/offchain/authority-claimer/src/signer/signer.rs index 8a9deeca0..a38333c93 100644 --- a/offchain/authority-claimer/src/signer/signer.rs +++ b/offchain/authority-claimer/src/signer/signer.rs @@ -43,6 +43,15 @@ impl ConditionalSigner { tx_signing_config: &TxSigningConfig, ) -> Result { match tx_signing_config.clone() { + TxSigningConfig::PrivateKey { private_key } => { + let wallet = private_key + .inner() + .as_str() + .parse::() + .context(LocalWalletSnafu)? + .with_chain_id(chain_id); + Ok(ConditionalSigner::LocalWallet(wallet)) + } TxSigningConfig::Mnemonic { mnemonic, account_index, @@ -162,8 +171,19 @@ mod tests { // -------------------------------------------------------------------------------------------- #[tokio::test] - async fn new_local_wallet_conditional_signer() { - let conditional_signer = local_wallet_conditional_signer().await; + async fn new_local_wallet_mnemonic_conditional_signer() { + let conditional_signer = + local_wallet_mnemonic_conditional_signer().await; + assert!(matches!( + conditional_signer, + ConditionalSigner::LocalWallet(_) + )); + } + + #[tokio::test] + async fn new_local_wallet_private_key_conditional_signer() { + let conditional_signer = + local_wallet_private_key_conditional_signer().await; assert!(matches!( conditional_signer, ConditionalSigner::LocalWallet(_) @@ -175,8 +195,19 @@ mod tests { // -------------------------------------------------------------------------------------------- #[tokio::test] - async fn sign_transaction_with_local_wallet_conditional_signer() { - let conditional_signer = local_wallet_conditional_signer().await; + async fn sign_transaction_with_mnemonic_local_wallet_conditional_signer() { + let conditional_signer = + local_wallet_mnemonic_conditional_signer().await; + let message = eip1559_message(); + let result = conditional_signer.sign_transaction(&message).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn sign_transaction_with_private_key_local_wallet_conditional_signer() + { + let conditional_signer = + local_wallet_private_key_conditional_signer().await; let message = eip1559_message(); let result = conditional_signer.sign_transaction(&message).await; assert!(result.is_ok()); @@ -187,10 +218,12 @@ mod tests { // -------------------------------------------------------------------------------------------- const CHAIN_ID: u64 = 1; + const PRIVATE_KEY: &str = + "59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"; const MNEMONIC: &str = "indoor dish desk flag debris potato excuse depart ticket judge file exit"; - async fn local_wallet_conditional_signer() -> ConditionalSigner { + async fn local_wallet_mnemonic_conditional_signer() -> ConditionalSigner { let tx_signing_config = TxSigningConfig::Mnemonic { mnemonic: Redacted::new(MNEMONIC.to_string()), account_index: Some(1), @@ -200,6 +233,16 @@ mod tests { .unwrap() } + async fn local_wallet_private_key_conditional_signer() -> ConditionalSigner + { + let tx_signing_config = TxSigningConfig::PrivateKey { + private_key: Redacted::new(PRIVATE_KEY.to_string()), + }; + ConditionalSigner::new(CHAIN_ID, &tx_signing_config) + .await + .unwrap() + } + fn eip1559_message() -> TypedTransaction { TypedTransaction::Eip1559( Eip1559TransactionRequest::new() From ca3c79200dac8f7e2de352a61d1c850e04ee08e6 Mon Sep 17 00:00:00 2001 From: Danilo Tuler Date: Thu, 14 Mar 2024 10:15:07 -0300 Subject: [PATCH 2/2] feat: support specification of private key file to claimer --- CHANGELOG.md | 2 +- docs/config.md | 8 ++++++++ internal/config/generate/Config.toml | 9 +++++++++ internal/config/get.go | 5 +++++ offchain/authority-claimer/src/config/cli.rs | 14 +++++++++++++- 5 files changed, 36 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de920d38f..77d8bf6f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added verification to ensure CARTESI_BLOCKCHAIN_ID matches the id returned from the Ethereum node -- Added support for CARTESI_AUTH_PRIVATE_KEY +- Added support for CARTESI_AUTH_PRIVATE_KEY and CARTESI_AUTH_PRIVATE_KEY_FILE ## Changed diff --git a/docs/config.md b/docs/config.md index f98efe833..5f8979bba 100644 --- a/docs/config.md +++ b/docs/config.md @@ -57,6 +57,14 @@ Overrides `CARTESI_AUTH_AWS_KMS_*`. The node will use this private key to sign transactions. +Overrides `CARTESI_AUTH_PRIVATE_KEY_FILE`, `CARTESI_AUTH_MNEMONIC`, `CARTESI_AUTH_MNEMONIC_FILE` and `CARTESI_AUTH_AWS_KMS_*`. + +* **Type:** `string` + +## `CARTESI_AUTH_PRIVATE_KEY_FILE` + +The node will use the private key contained in this file to sign transactions. + Overrides `CARTESI_AUTH_MNEMONIC`, `CARTESI_AUTH_MNEMONIC_FILE` and `CARTESI_AUTH_AWS_KMS_*`. * **Type:** `string` diff --git a/internal/config/generate/Config.toml b/internal/config/generate/Config.toml index c71813108..212146fc6 100644 --- a/internal/config/generate/Config.toml +++ b/internal/config/generate/Config.toml @@ -177,6 +177,15 @@ redact = true description = """ The node will use this private key to sign transactions. +Overrides `CARTESI_AUTH_PRIVATE_KEY_FILE`, `CARTESI_AUTH_MNEMONIC`, `CARTESI_AUTH_MNEMONIC_FILE` and `CARTESI_AUTH_AWS_KMS_*`.""" + +[auth.CARTESI_AUTH_PRIVATE_KEY_FILE] +go-type = "string" +export = false +redact = true +description = """ +The node will use the private key contained in this file to sign transactions. + Overrides `CARTESI_AUTH_MNEMONIC`, `CARTESI_AUTH_MNEMONIC_FILE` and `CARTESI_AUTH_AWS_KMS_*`.""" [auth.CARTESI_AUTH_AWS_KMS_KEY_ID] diff --git a/internal/config/get.go b/internal/config/get.go index ad19242e4..d5d9419bc 100644 --- a/internal/config/get.go +++ b/internal/config/get.go @@ -44,6 +44,11 @@ func getCartesiAuthPrivateKey() (string, bool) { return v, ok } +func getCartesiAuthPrivateKeyFile() (string, bool) { + v, ok := getOptional("CARTESI_AUTH_PRIVATE_KEY_FILE", "", false, true, toString) + return v, ok +} + func GetCartesiBlockchainBlockTimeout() int { v := get("CARTESI_BLOCKCHAIN_BLOCK_TIMEOUT", "60", true, false, toInt) return v diff --git a/offchain/authority-claimer/src/config/cli.rs b/offchain/authority-claimer/src/config/cli.rs index d1f07dc04..5df9c6ffd 100644 --- a/offchain/authority-claimer/src/config/cli.rs +++ b/offchain/authority-claimer/src/config/cli.rs @@ -91,10 +91,14 @@ impl TryFrom for AuthorityClaimerConfig { #[derive(Debug, Parser)] #[command(name = "tx_signing_config")] pub(crate) struct TxSigningCLIConfig { - /// Signer private key, overrides `tx_signing_mnemonic` , `tx_signing_mnemonic_file` and `tx_signing_aws_kms_*` + /// Signer private key, overrides `tx_signing_private_key_file`, `tx_signing_mnemonic` , `tx_signing_mnemonic_file` and `tx_signing_aws_kms_*` #[arg(long, env)] tx_signing_private_key: Option, + /// Signer private key file, overrides `tx_signing_mnemonic` , `tx_signing_mnemonic_file` and `tx_signing_aws_kms_*` + #[arg(long, env)] + tx_signing_private_key_file: Option, + /// Signer mnemonic, overrides `tx_signing_mnemonic_file` and `tx_signing_aws_kms_*` #[arg(long, env)] tx_signing_mnemonic: Option, @@ -125,6 +129,14 @@ impl TryFrom for TxSigningConfig { Ok(TxSigningConfig::PrivateKey { private_key: Redacted::new(private_key), }) + } else if let Some(path) = cli.tx_signing_private_key_file { + let private_key = fs::read_to_string(path.clone()) + .context(MnemonicFileSnafu { path })? + .trim() + .to_string(); + Ok(TxSigningConfig::PrivateKey { + private_key: Redacted::new(private_key), + }) } else if let Some(mnemonic) = cli.tx_signing_mnemonic { Ok(TxSigningConfig::Mnemonic { mnemonic: Redacted::new(mnemonic),