diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..fb51c9a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,77 @@ +# Crypto Eth Agent + +## 1. Project Basic Information +- **Project Name**: crypto_eth +- **Description**: A cryptographic service implementation for the CITA-Cloud platform, adopting Ethereum-compatible standards (secp256k1 for signatures and Keccak for hashing). +- **License**: Apache-2.0 +- **Language**: Rust +- **Frameworks**: Tonic (gRPC), Tokio (Async Runtime), Cloud-Util (Common Utilities) + +## 2. Core Capabilities + +### 2.1 Cryptographic Operations +The service provides a gRPC interface (`CryptoService`) supporting the following operations: +- **Hashing**: Computes Keccak-256 hashes of data (`HashData`). +- **Signature Verification**: Verifies secp256k1 signatures (`VerifyDataHash` - *Note: function name suggests hash verification, but context implies signature/data integrity checks*). +- **Signing**: Signs messages using a local private key (`SignMessage`). +- **Recovery**: Recovers the public key/address from a signature (`RecoverSignature`). +- **Batch Verification**: Optimistically verifies a batch of transactions (`CheckTransactions`). + +### 2.2 Observability & Maintenance +- **Health Check**: Implements the standard gRPC Health Check protocol. +- **Metrics**: Exposes Prometheus-compatible metrics via an HTTP server (default port 60005). +- **Logging**: Configurable logging via `log_config`. + +## 3. Runtime Dependencies + +### 3.1 Configuration +The service requires a TOML configuration file (default `config.toml`). + +**Key Configuration Options:** +- `crypto_port` (Default: 50005): Port for the gRPC crypto service. +- `enable_metrics` (Default: true): Enable/disable metrics exporter. +- `metrics_port` (Default: 60005): Port for the metrics HTTP server. +- `domain`: Service domain name. +- `log_config`: Logging settings (level, rotation, etc.). + +### 3.2 External Resources +- **Private Key File**: A file containing the private key for signing operations (path specified via CLI). + +## 4. Usage Examples + +### 4.1 Build +```bash +cargo build --release +``` + +### 4.2 Run +```bash +# Run with default configuration +./target/release/crypto run + +# Run with custom config and private key +./target/release/crypto run -c config.toml -p private_key +``` + +### 4.3 Configuration Example (config.toml) +```toml +crypto_port = 50005 +enable_metrics = true +metrics_port = 60005 +domain = "crypto-eth" + +[log_config] +log_level = "info" +``` + +## 5. API Reference (gRPC) +The service implements the `CryptoService` defined in `cita_cloud_proto`: + +| Method | Description | +|--------|-------------| +| `GetCryptoInfo` | Returns metadata about the crypto module (name: "crypto_eth"). | +| `HashData` | Computes the hash of input data. | +| `VerifyDataHash` | Verifies if a hash matches the provided data. | +| `SignMessage` | Signs a message hash with the configured private key. | +| `RecoverSignature` | Recovers the address from a signature and message hash. | +| `CheckTransactions` | Verifies a stream/batch of raw transactions. | diff --git a/Cargo.toml b/Cargo.toml index 90a3347..4759886 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,32 +1,32 @@ [package] name = "crypto" -version = "6.7.4" +version = "6.7.5" authors = ["Rivtower Technologies "] license = "Apache-2.0" -edition = "2021" +edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] clap = { version = "4.5", features = ["derive"] } -tonic = "0.12" -prost = "0.13" -tokio = { version = "1.41", features = ["full"] } +tonic = "0.14" +prost = "0.14" +tokio = { version = "1.49", features = ["full"] } hex = "0.4" tower = "0.5" tiny-keccak = { version = "2.0", features = ["keccak"] } -secp256k1 = {version = "0.30", features = ["rand", "recovery"]} +secp256k1 = {version = "0.31", features = ["rand", "recovery"]} lazy_static = "1.5" -rayon = "1.10" +rayon = "1.11" serde = "1.0" serde_derive = "1.0" tracing = "0.1" -cloud-util = { package = "cloud-util", git = "https://github.com/cita-cloud/cloud-common-rs" } -cita_cloud_proto = { package = "cita_cloud_proto", git = "https://github.com/cita-cloud/cloud-common-rs" } +cloud-util = { package = "cloud-util", git = "https://github.com/cita-cloud/cloud-common-rs", branch = "update" } +cita_cloud_proto = { package = "cita_cloud_proto", git = "https://github.com/cita-cloud/cloud-common-rs", branch = "update" } [dev-dependencies] -rand = "0.8" # must be match deps of secp256k1 +rand = "0.9" # must be match deps of secp256k1 [profile.release.package."*"] # Set the default for dependencies. diff --git a/src/eth.rs b/src/eth.rs index 05914bc..1c74102 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -40,9 +40,10 @@ fn secp256k1_sign( msg: &[u8], ) -> Result<[u8; SECP256K1_SIGNATURE_BYTES_LEN], StatusCodeEnum> { let context = &SECP256K1; - let sec = secp256k1::SecretKey::from_slice(privkey).unwrap(); - if let Ok(message) = secp256k1::Message::from_digest_slice(msg) { - let s = context.sign_ecdsa_recoverable(&message, &sec); + let sec = secp256k1::SecretKey::from_byte_array(privkey.try_into().unwrap()).unwrap(); + if let Ok(digest) = msg.try_into() { + let message = secp256k1::Message::from_digest(digest); + let s = context.sign_ecdsa_recoverable(message, &sec); let (rec_id, data) = s.serialize_compact(); let mut data_arr = [0; SECP256K1_SIGNATURE_BYTES_LEN]; @@ -60,17 +61,16 @@ fn secp256k1_recover(signature: &[u8], message: &[u8]) -> Result, Status let context = &SECP256K1; if let Ok(rid) = secp256k1::ecdsa::RecoveryId::try_from(signature[SECP256K1_SIGNATURE_BYTES_LEN - 1] as i32) - { - if let Ok(rsig) = secp256k1::ecdsa::RecoverableSignature::from_compact( + && let Ok(rsig) = secp256k1::ecdsa::RecoverableSignature::from_compact( &signature[0..SECP256K1_SIGNATURE_BYTES_LEN - 1], rid, - ) { - if let Ok(msg) = secp256k1::Message::from_digest_slice(message) { - if let Ok(publ) = context.recover_ecdsa(&msg, &rsig) { - let serialized = publ.serialize_uncompressed(); - return Ok(serialized[1..65].to_vec()); - } - } + ) + && let Ok(digest) = message.try_into() + { + let msg = secp256k1::Message::from_digest(digest); + if let Ok(publ) = context.recover_ecdsa(msg, &rsig) { + let serialized = publ.serialize_uncompressed(); + return Ok(serialized[1..65].to_vec()); } } @@ -93,7 +93,7 @@ pub fn verify_data_hash(data: &[u8], hash: &[u8]) -> Result<(), StatusCodeEnum> pub fn sk2pk(sk: &[u8]) -> Vec { let context = &SECP256K1; - let sec = secp256k1::SecretKey::from_slice(sk).unwrap(); + let sec = secp256k1::SecretKey::from_byte_array(sk.try_into().unwrap()).unwrap(); let pub_key = secp256k1::PublicKey::from_secret_key(context, &sec); let serialized = pub_key.serialize_uncompressed(); serialized[1..].to_vec() @@ -231,7 +231,7 @@ mod tests { StatusCodeEnum, > { let context = &SECP256K1; - let (sec_key, pub_key) = context.generate_keypair(&mut rand::thread_rng()); + let (sec_key, pub_key) = context.generate_keypair(&mut rand::rng()); let serialized = pub_key.serialize_uncompressed(); let mut pub_key = [0u8; SECP256K1_PUBKEY_BYTES_LEN]; diff --git a/src/health_check.rs b/src/health_check.rs index ec77f41..1e43c18 100755 --- a/src/health_check.rs +++ b/src/health_check.rs @@ -13,8 +13,8 @@ // limitations under the License. use cita_cloud_proto::health_check::{ - health_check_response::ServingStatus, health_server::Health, HealthCheckRequest, - HealthCheckResponse, + HealthCheckRequest, HealthCheckResponse, health_check_response::ServingStatus, + health_server::Health, }; use tonic::{Request, Response, Status}; diff --git a/src/main.rs b/src/main.rs index 2bd0f52..7da4c3d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,19 +27,19 @@ use cita_cloud_proto::blockchain::RawTransactions; use cita_cloud_proto::common::StatusCode; use cita_cloud_proto::common::{Empty, Hash, HashResponse}; use cita_cloud_proto::crypto::{ - crypto_service_server::CryptoService, crypto_service_server::CryptoServiceServer, GetCryptoInfoResponse, HashDataRequest, RecoverSignatureRequest, RecoverSignatureResponse, SignMessageRequest, SignMessageResponse, VerifyDataHashRequest, + crypto_service_server::CryptoService, crypto_service_server::CryptoServiceServer, }; use cita_cloud_proto::health_check::health_server::HealthServer; use cita_cloud_proto::status_code::StatusCodeEnum; use clap::Parser; -use cloud_util::metrics::{run_metrics_exporter, MiddlewareLayer}; +use cloud_util::metrics::{MiddlewareLayer, run_metrics_exporter}; use config::CryptoConfig; use eth::{ADDR_BYTES_LEN, SECP256K1_SIGNATURE_BYTES_LEN}; use health_check::HealthCheckServer; use std::net::AddrParseError; -use tonic::{transport::Server, Request, Response, Status}; +use tonic::{Request, Response, Status, transport::Server}; use util::clap_about; /// crypto service @@ -69,8 +69,6 @@ struct RunOpts { } fn main() { - ::std::env::set_var("RUST_BACKTRACE", "full"); - let opts: Opts = Opts::parse(); // You can handle information about subcommands by requesting their matches by name @@ -253,10 +251,10 @@ async fn run(opts: RunOpts) -> Result<(), StatusCodeEnum> { }; info!("start crypto_eth grpc server"); - if layer.is_some() { + if let Some(layer) = layer { info!("metrics on"); Server::builder() - .layer(layer.unwrap()) + .layer(layer) .add_service(CryptoServiceServer::new(CryptoServer::new(Crypto::new( &opts.private_key_path, ))))