Skip to content

Commit

Permalink
Signer functionality (#54)
Browse files Browse the repository at this point in the history
This PR adds a signer crate to handle Signer instantiation.

---------

Co-authored-by: ricomateo <mrico@fi.uba.ar>
  • Loading branch information
TomasArrachea and ricomateo authored Jul 30, 2024
1 parent 66085bb commit f73b115
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 22 deletions.
56 changes: 36 additions & 20 deletions Cargo.lock

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

7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ members = [
"crates/types/",
"crates/metrics/",
"crates/types/",
"crates/signer/",
"crates/logging/",
"examples/info-operator-service/",
"testing/testing-utils/",
"examples/avsregistry-read",
"examples/avsregistry-write",
"examples/anvil-utils",
"examples/anvil-utils"
]

resolver = "2"
Expand Down Expand Up @@ -88,7 +89,9 @@ thiserror = "1.0"
tracing = "0.1.40"
tracing-subscriber = { version = "0.3", features = ["json"] }
hyper = "0.14.25"

eth-keystore = "0.5.0"
hex-literal = "0.4.1"
k256 = "0.13.3"

#misc
parking_lot = "0.12"
Expand Down
17 changes: 17 additions & 0 deletions crates/signer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "eigen-signer"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
repository.workspace = true

[dependencies]
alloy-consensus.workspace = true
alloy-network.workspace = true
alloy-primitives.workspace = true
alloy-signer.workspace = true
alloy-signer-local.workspace = true
eth-keystore.workspace = true
hex-literal.workspace = true
k256.workspace = true
thiserror.workspace = true
19 changes: 19 additions & 0 deletions crates/signer/mockdata/dummy.key.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"crypto": {
"cipher": "aes-128-ctr",
"cipherparams": {
"iv": "6087dab2f9fdbbfaddc31a909735c1e6"
},
"ciphertext": "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46",
"kdf": "pbkdf2",
"kdfparams": {
"c": 262144,
"dklen": 32,
"prf": "hmac-sha256",
"salt": "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd"
},
"mac": "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2"
},
"id": "3198bc9c-6672-5ab3-d995-4942343ae5b6",
"version": 3
}
6 changes: 6 additions & 0 deletions crates/signer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#![doc(
html_logo_url = "https://github.com/supernovahs/eigensdk-rs/assets/91280922/bd13caec-3c00-4afc-839a-b83d2890beb5",
issue_tracker_base_url = "https://github.com/supernovahs/eigen-rs/issues/"
)]

pub mod signer;
116 changes: 116 additions & 0 deletions crates/signer/src/signer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
use alloy_signer_local::PrivateKeySigner;
use eth_keystore::decrypt_key;
use std::path::Path;
use thiserror::Error;

/// Represents the input params to create a signer
#[derive(Debug)]
pub enum Config {
/// Hexadecimal private key
PrivateKey(String),
/// Keystore path and password
Keystore(String, String),
/// Web3 endpoint and address
Web3(String, String),
}

#[derive(Error, Debug)]
/// Possible errors raised in signer creation
pub enum SignerError {
#[error("invalid private key")]
InvalidPrivateKey,
#[error("invalid keystore password")]
InvalidPassword,
}

impl Config {
/// Creates a signer from given config.
pub fn signer_from_config(c: Config) -> Result<PrivateKeySigner, SignerError> {
// TODO: check chain id to select signer
match c {
Config::PrivateKey(key) => key
.parse::<PrivateKeySigner>()
.map_err(|_| SignerError::InvalidPrivateKey),
Config::Keystore(path, password) => {
let keypath = Path::new(&path);
let private_key =
decrypt_key(keypath, password).map_err(|_| SignerError::InvalidPassword)?;
PrivateKeySigner::from_slice(&private_key)
.map_err(|_| SignerError::InvalidPrivateKey)
}
Config::Web3(_endpoint, _address) => {
todo!() // We are implementing this in a following PR
}
}
}
}

#[cfg(test)]
mod test {
use super::Config;
use alloy_consensus::TxLegacy;
use alloy_network::TxSignerSync;
use alloy_primitives::{bytes, Address, U256};
use alloy_signer::Signature;
use alloy_signer_local::PrivateKeySigner;
use hex_literal::hex;
use std::str::FromStr;

const PRIVATE_KEY: &str = "dcf2cbdd171a21c480aa7f53d77f31bb102282b3ff099c78e3118b37348c72f7";
const ADDRESS: [u8; 20] = hex!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
const SIGNATURE_R: &str =
"99963972037857174861280476053118856715670512199525969754644366601434507134123";
const SIGNATURE_S: &str =
"54587766196536123534774489028213321677166972433316011091332824361042811624091";
const SIGNATURE_Y_PARITY: u64 = 37;
const KEYSTORE_PATH: &str = "mockdata/dummy.key.json";
const KEYSTORE_PASSWORD: &str = "testpassword";

#[test]
fn sign_transaction_with_private_key() {
let config = Config::PrivateKey(PRIVATE_KEY.into());
let mut tx = TxLegacy {
to: Address::from(ADDRESS).into(),
value: U256::from(1_000_000_000),
gas_limit: 2_000_000,
nonce: 0,
gas_price: 21_000_000_000,
input: bytes!(),
chain_id: Some(1),
};

let signer = Config::signer_from_config(config);

let signature = signer.unwrap().sign_transaction_sync(&mut tx).unwrap();
let expected_signature = Signature::from_rs_and_parity(
U256::from_str(SIGNATURE_R).unwrap(),
U256::from_str(SIGNATURE_S).unwrap(),
SIGNATURE_Y_PARITY,
)
.unwrap();
assert_eq!(signature, expected_signature);
}

#[test]
fn sign_transaction_with_keystore() {
let config = Config::Keystore(KEYSTORE_PATH.into(), KEYSTORE_PASSWORD.into());
let mut tx = TxLegacy {
to: Address::from(ADDRESS).into(),
value: U256::from(1_000_000_000),
gas_limit: 2_000_000,
nonce: 0,
gas_price: 21_000_000_000,
input: bytes!(),
chain_id: Some(1),
};

let private_key = hex!("7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d");
let expected_signer = PrivateKeySigner::from_slice(&private_key).unwrap();
let expected_signature = expected_signer.sign_transaction_sync(&mut tx).unwrap();

let signer = Config::signer_from_config(config);
let signature = signer.unwrap().sign_transaction_sync(&mut tx).unwrap();

assert_eq!(signature, expected_signature);
}
}

0 comments on commit f73b115

Please sign in to comment.