From 8a8ebe1a130c9167e9a1c4dd03a8694fcad36883 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Wed, 5 Jun 2024 18:06:43 +0900 Subject: [PATCH 01/22] add sign for ethereum --- kms/Cargo.toml | 9 ++++-- kms/src/client.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/kms/Cargo.toml b/kms/Cargo.toml index 1496f59e..8f2bc1d3 100644 --- a/kms/Cargo.toml +++ b/kms/Cargo.toml @@ -18,8 +18,9 @@ google-cloud-gax = { version = "0.17.0", path = "../foundation/gax"} tracing = "0.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -thiserror = "1.0" +thiserror = { version = "1.0", features = [] } prost-types = "0.12" +asn1 = { version = "0.16", optional = true } [dev-dependencies] tokio = { version="1.32", features=["rt-multi-thread"] } @@ -28,11 +29,13 @@ tracing-subscriber = { version="0.3.17", features=["env-filter"]} ctor = "0.1" tokio-util = {version ="0.7", features = ["codec"] } google-cloud-auth = { path = "../foundation/auth", default-features=false } +hex-literal = { version = "0.4" } [features] -default = ["default-tls", "auth"] +default = ["default-tls", "auth", "eth"] default-tls = ["google-cloud-auth?/default-tls"] rustls-tls = ["google-cloud-auth?/rustls-tls"] trace = [] auth = ["google-cloud-auth"] -external-account = ["google-cloud-auth?/external-account"] \ No newline at end of file +external-account = ["google-cloud-auth?/external-account"] +eth = ["asn1"] \ No newline at end of file diff --git a/kms/src/client.rs b/kms/src/client.rs index 9ba835f1..e03e72cd 100644 --- a/kms/src/client.rs +++ b/kms/src/client.rs @@ -1,9 +1,13 @@ use std::ops::Deref; use std::sync::Arc; +use hex_literal::hex; #[cfg(feature = "auth")] pub use google_cloud_auth; use google_cloud_gax::conn::{ConnectionOptions, Environment, Error}; +use google_cloud_gax::grpc::Status; +use google_cloud_gax::retry::RetrySetting; +use google_cloud_googleapis::cloud::kms::v1::{AsymmetricSignRequest, Digest, digest}; use google_cloud_token::{NopeTokenSourceProvider, TokenSourceProvider}; @@ -81,6 +85,8 @@ impl Client { kms_client: KmsGrpcClient::new(Arc::new(cm)), }) } + + } impl Deref for Client { @@ -91,9 +97,59 @@ impl Deref for Client { } } +#[cfg(feature = "eth")] +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +struct Signature<'a> { + r: asn1::BigUint<'a>, + s: asn1::BigUint<'a>, +} + +#[cfg(feature = "eth")] +#[derive(thiserror::Error, Debug)] +enum SignByECError { + #[error] + GRPC(#[transparent] Status), + #[error] + ParseError(#[transparent] asn1::ParseError) +} + +const SECP256K1N: [u8; 32] = hex!("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"); + +#[cfg(feature = "eth")] +impl Client { + + /// sign_by_ec calculates an ECDSA signature. + /// Use for signing ethereum transaction. + /// name: key name + /// digest: sha256 hash ( transaction hash ) + pub async fn sign_eth_tx(&self, name: String, digest: Vec , option: Option) -> Result, SignByECError> { + + let secp256k1n = asn1::BigUint::new(SECP256K1N.as_slice()).unwrap(); + + let result = self.asymmetric_sign(AsymmetricSignRequest { + name, + digest: Some(Digest { + digest: Some(digest::Digest::Sha256(digest)), + }), + digest_crc32c: None, + data: vec![], + data_crc32c: None, + }, option).await?; + + let sig = asn1::parse_single::(result.signature.as_slice())?; + let s = if sig.s > secp256k1n { + secp256k1n - sig.s + }else { + sig.s + }; + //TODO + println!("{:?}, {:?}", sig.r, sig.s); + Ok(vec![]) + } +} + #[cfg(test)] mod tests { - use serial_test::serial; use crate::grpc::kms::v1::{ @@ -275,4 +331,18 @@ mod tests { let raw = client.mac_verify(request, None).await.unwrap(); assert!(raw.success); } + + #[cfg(feature = "eth")] + #[tokio::test] + #[serial] + async fn test_sign_ecdsa() { + use hex_literal::hex; + + let (client, project) = new_client().await; + let key = format!( + "projects/{project}/locations/asia-northeast1/keyRings/gcr_test/cryptoKeys/eth-sign/cryptoKeyVersions/1" + ); + + let result = client.sign_eth_tx(key, hex!("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08").to_vec(), None).await; + } } From 99d78705de08e13e5b6406323fc745a87e611e0f Mon Sep 17 00:00:00 2001 From: yoshidan Date: Wed, 5 Jun 2024 20:01:55 +0900 Subject: [PATCH 02/22] add eth.rs --- kms/Cargo.toml | 3 +- kms/src/client.rs | 75 ++++------------------------------- kms/src/ethereum.rs | 96 +++++++++++++++++++++++++++++++++++++++++++++ kms/src/lib.rs | 1 + 4 files changed, 106 insertions(+), 69 deletions(-) create mode 100644 kms/src/ethereum.rs diff --git a/kms/Cargo.toml b/kms/Cargo.toml index 8f2bc1d3..41a970ff 100644 --- a/kms/Cargo.toml +++ b/kms/Cargo.toml @@ -21,6 +21,7 @@ serde_json = "1.0" thiserror = { version = "1.0", features = [] } prost-types = "0.12" asn1 = { version = "0.16", optional = true } +once_cell = { version="1.19" , optional = true} [dev-dependencies] tokio = { version="1.32", features=["rt-multi-thread"] } @@ -38,4 +39,4 @@ rustls-tls = ["google-cloud-auth?/rustls-tls"] trace = [] auth = ["google-cloud-auth"] external-account = ["google-cloud-auth?/external-account"] -eth = ["asn1"] \ No newline at end of file +eth = ["asn1", "once_cell"] \ No newline at end of file diff --git a/kms/src/client.rs b/kms/src/client.rs index e03e72cd..9a0907d5 100644 --- a/kms/src/client.rs +++ b/kms/src/client.rs @@ -1,14 +1,15 @@ +use hex_literal::hex; use std::ops::Deref; use std::sync::Arc; -use hex_literal::hex; #[cfg(feature = "auth")] pub use google_cloud_auth; use google_cloud_gax::conn::{ConnectionOptions, Environment, Error}; use google_cloud_gax::grpc::Status; use google_cloud_gax::retry::RetrySetting; -use google_cloud_googleapis::cloud::kms::v1::{AsymmetricSignRequest, Digest, digest}; +use google_cloud_googleapis::cloud::kms::v1::{digest, AsymmetricSignRequest, Digest}; +use crate::ethereum::EthereumSigner; use google_cloud_token::{NopeTokenSourceProvider, TokenSourceProvider}; use crate::grpc::apiv1::conn_pool::{ConnectionManager, KMS, SCOPES}; @@ -86,7 +87,10 @@ impl Client { }) } - + #[cfg(feature = "eth")] + pub fn ethereum(&self) -> EthereumSigner { + EthereumSigner::new(&self.kms_client) + } } impl Deref for Client { @@ -97,57 +101,6 @@ impl Deref for Client { } } -#[cfg(feature = "eth")] -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -struct Signature<'a> { - r: asn1::BigUint<'a>, - s: asn1::BigUint<'a>, -} - -#[cfg(feature = "eth")] -#[derive(thiserror::Error, Debug)] -enum SignByECError { - #[error] - GRPC(#[transparent] Status), - #[error] - ParseError(#[transparent] asn1::ParseError) -} - -const SECP256K1N: [u8; 32] = hex!("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"); - -#[cfg(feature = "eth")] -impl Client { - - /// sign_by_ec calculates an ECDSA signature. - /// Use for signing ethereum transaction. - /// name: key name - /// digest: sha256 hash ( transaction hash ) - pub async fn sign_eth_tx(&self, name: String, digest: Vec , option: Option) -> Result, SignByECError> { - - let secp256k1n = asn1::BigUint::new(SECP256K1N.as_slice()).unwrap(); - - let result = self.asymmetric_sign(AsymmetricSignRequest { - name, - digest: Some(Digest { - digest: Some(digest::Digest::Sha256(digest)), - }), - digest_crc32c: None, - data: vec![], - data_crc32c: None, - }, option).await?; - - let sig = asn1::parse_single::(result.signature.as_slice())?; - let s = if sig.s > secp256k1n { - secp256k1n - sig.s - }else { - sig.s - }; - //TODO - println!("{:?}, {:?}", sig.r, sig.s); - Ok(vec![]) - } -} - #[cfg(test)] mod tests { use serial_test::serial; @@ -331,18 +284,4 @@ mod tests { let raw = client.mac_verify(request, None).await.unwrap(); assert!(raw.success); } - - #[cfg(feature = "eth")] - #[tokio::test] - #[serial] - async fn test_sign_ecdsa() { - use hex_literal::hex; - - let (client, project) = new_client().await; - let key = format!( - "projects/{project}/locations/asia-northeast1/keyRings/gcr_test/cryptoKeys/eth-sign/cryptoKeyVersions/1" - ); - - let result = client.sign_eth_tx(key, hex!("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08").to_vec(), None).await; - } } diff --git a/kms/src/ethereum.rs b/kms/src/ethereum.rs new file mode 100644 index 00000000..04221218 --- /dev/null +++ b/kms/src/ethereum.rs @@ -0,0 +1,96 @@ +use crate::grpc::apiv1::kms_client::Client as KmsGrpcClient; +use asn1::BigInt; +use google_cloud_gax::grpc::Status; +use google_cloud_gax::retry::RetrySetting; +use google_cloud_googleapis::cloud::kms::v1::{digest, AsymmetricSignRequest, Digest}; +use hex_literal::hex; +use once_cell::sync::Lazy; + +const _SECP256K1N: [u8; 32] = hex!("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"); + +static SECP256K1N: Lazy = Lazy::new(|| BigInt::new(_SECP256K1N.as_slice()).unwrap()); + +#[derive(asn1::Asn1Read, asn1::Asn1Write)] +struct Signature<'a> { + r: BigInt<'a>, + s: BigInt<'a>, +} + +#[derive(thiserror::Error, Debug)] +pub enum SignByECError { + #[error(transparent)] + GRPC(#[from] Status), + #[error(transparent)] + ParseError(#[from] asn1::ParseError), +} + +pub struct EthereumSigner<'a> { + client: &'a KmsGrpcClient, +} + +impl<'a> EthereumSigner<'a> { + pub fn new(client: &'a KmsGrpcClient) -> Self { + Self { client } + } + + pub async fn sign( + &self, + name: String, + digest: Vec, + option: Option, + ) -> Result, SignByECError> { + let result = self + .client + .asymmetric_sign( + AsymmetricSignRequest { + name, + digest: Some(Digest { + digest: Some(digest::Digest::Sha256(digest)), + }), + digest_crc32c: None, + data: vec![], + data_crc32c: None, + }, + option, + ) + .await?; + + let sig = asn1::parse_single::(result.signature.as_slice())?; + + //TODO + println!("{:?}, {:?}", sig.r, sig.s); + Ok(vec![]) + } +} + +mod tests { + use crate::client::{Client, ClientConfig}; + use serial_test::serial; + + async fn new_client() -> (Client, String) { + let cred = google_cloud_auth::credentials::CredentialsFile::new().await.unwrap(); + let project = cred.project_id.clone().unwrap(); + let config = ClientConfig::default().with_credentials(cred).await.unwrap(); + (Client::new(config).await.unwrap(), project) + } + + #[tokio::test] + #[serial] + async fn test_sign_ecdsa() { + use hex_literal::hex; + + let (client, project) = new_client().await; + let key = format!( + "projects/{project}/locations/asia-northeast1/keyRings/gcr_test/cryptoKeys/eth-sign/cryptoKeyVersions/1" + ); + + let result = client + .ethereum() + .sign( + key, + hex!("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08").to_vec(), + None, + ) + .await; + } +} diff --git a/kms/src/lib.rs b/kms/src/lib.rs index 557547cb..5666d726 100644 --- a/kms/src/lib.rs +++ b/kms/src/lib.rs @@ -112,4 +112,5 @@ //! } //!} pub mod client; +mod ethereum; pub mod grpc; From c3358d49a1dfe7ec191b0c82540d93e4dc75daa9 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Thu, 6 Jun 2024 15:22:18 +0900 Subject: [PATCH 03/22] update --- kms/Cargo.toml | 3 ++- kms/src/ethereum.rs | 59 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/kms/Cargo.toml b/kms/Cargo.toml index 41a970ff..2d3c13bd 100644 --- a/kms/Cargo.toml +++ b/kms/Cargo.toml @@ -22,6 +22,7 @@ thiserror = { version = "1.0", features = [] } prost-types = "0.12" asn1 = { version = "0.16", optional = true } once_cell = { version="1.19" , optional = true} +primitive-types = { version="0.12", optional = true } [dev-dependencies] tokio = { version="1.32", features=["rt-multi-thread"] } @@ -39,4 +40,4 @@ rustls-tls = ["google-cloud-auth?/rustls-tls"] trace = [] auth = ["google-cloud-auth"] external-account = ["google-cloud-auth?/external-account"] -eth = ["asn1", "once_cell"] \ No newline at end of file +eth = ["asn1", "once_cell", "primitive-types"] \ No newline at end of file diff --git a/kms/src/ethereum.rs b/kms/src/ethereum.rs index 04221218..ad0b64d9 100644 --- a/kms/src/ethereum.rs +++ b/kms/src/ethereum.rs @@ -1,19 +1,35 @@ use crate::grpc::apiv1::kms_client::Client as KmsGrpcClient; -use asn1::BigInt; +use asn1::{BigInt, ParseError, ParseErrorKind, ParseResult, SimpleAsn1Readable, Tag}; use google_cloud_gax::grpc::Status; use google_cloud_gax::retry::RetrySetting; use google_cloud_googleapis::cloud::kms::v1::{digest, AsymmetricSignRequest, Digest}; use hex_literal::hex; use once_cell::sync::Lazy; +use primitive_types::U256; -const _SECP256K1N: [u8; 32] = hex!("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"); +const _SECP256K1_N: [u8; 32] = hex!("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"); -static SECP256K1N: Lazy = Lazy::new(|| BigInt::new(_SECP256K1N.as_slice()).unwrap()); +static SECP256K1_N: Lazy = Lazy::new(|| U256::from(_SECP256K1_N.as_slice())); +static SECP256K1_HALF_N: Lazy = Lazy::new(|| *SECP256K1_N / 2); -#[derive(asn1::Asn1Read, asn1::Asn1Write)] -struct Signature<'a> { - r: BigInt<'a>, - s: BigInt<'a>, +struct U256Bridge<'a> { + value: U256, +} + +struct Signature { + r: U256, + s: U256, +} + +impl<'a> SimpleAsn1Readable<'a> for U256Bridge<'a> { + const TAG: Tag = Tag::primitive(0x02); + fn parse_data(data: &'a [u8]) -> ParseResult { + let value= U256::try_from(data).map_err(|_| ParseError::new(ParseErrorKind::InvalidValue))?; + Ok(Self { + value, + bytes: data + }) + } } #[derive(thiserror::Error, Debug)] @@ -55,10 +71,33 @@ impl<'a> EthereumSigner<'a> { ) .await?; - let sig = asn1::parse_single::(result.signature.as_slice())?; + let mut signature = asn1::parse(result.signature.as_slice(), |d| { + return d.read_element::()?.parse(|d| { + let r = d.read_element::()?; + let s = d.read_element::()?; + Ok((r, s)) + }) + })?; + let (r,s) = signature; + let s = if s.value < *SECP256K1_HALF_N { + *SECP256K1_N - s.value + }else { + s.value + }; + let mut s_bytes: Vec = vec![]; + s.to_big_endian(&mut s_bytes); + + let mut r_bytes: Vec = vec![]; + r.to_big_endian(&mut r_bytes); + while s_bytes.len() < 32 { + s_bytes.insert(0, 0); + } + while r_bytes.len() < 32 { + r_bytes.insert(0, 0); + } + + //TODO generate recovery_id - //TODO - println!("{:?}, {:?}", sig.r, sig.s); Ok(vec![]) } } From 3139d3dfeff6ef48c4d2887ef11862c5701d010e Mon Sep 17 00:00:00 2001 From: yoshidan Date: Thu, 6 Jun 2024 21:35:22 +0900 Subject: [PATCH 04/22] tweak --- kms/src/ethereum.rs | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/kms/src/ethereum.rs b/kms/src/ethereum.rs index ad0b64d9..182bbfa5 100644 --- a/kms/src/ethereum.rs +++ b/kms/src/ethereum.rs @@ -16,6 +16,18 @@ struct U256Bridge<'a> { value: U256, } +impl<'a> U256Bridge<'a> { + pub fn as_bytes32(&self) -> [u8; 32] { + let mut b: Vec = vec![]; + self.value.to_big_endian(&mut b); + + while self.len() < 32 { + b.insert(0, 0); + } + b.into() + } +} + struct Signature { r: U256, s: U256, @@ -27,7 +39,6 @@ impl<'a> SimpleAsn1Readable<'a> for U256Bridge<'a> { let value= U256::try_from(data).map_err(|_| ParseError::new(ParseErrorKind::InvalidValue))?; Ok(Self { value, - bytes: data }) } } @@ -78,22 +89,9 @@ impl<'a> EthereumSigner<'a> { Ok((r, s)) }) })?; - let (r,s) = signature; - let s = if s.value < *SECP256K1_HALF_N { - *SECP256K1_N - s.value - }else { - s.value - }; - let mut s_bytes: Vec = vec![]; - s.to_big_endian(&mut s_bytes); - - let mut r_bytes: Vec = vec![]; - r.to_big_endian(&mut r_bytes); - while s_bytes.len() < 32 { - s_bytes.insert(0, 0); - } - while r_bytes.len() < 32 { - r_bytes.insert(0, 0); + let (mut r,mut s) = signature; + if s.value < *SECP256K1_HALF_N { + s.value = *SECP256K1_N - s.value } //TODO generate recovery_id From 0d364ddd9aecaa23fae659fc1b88689e1c55ab05 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Wed, 12 Jun 2024 18:13:43 +0900 Subject: [PATCH 05/22] tweak --- kms/src/ethereum.rs | 55 +++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/kms/src/ethereum.rs b/kms/src/ethereum.rs index 182bbfa5..7b72865f 100644 --- a/kms/src/ethereum.rs +++ b/kms/src/ethereum.rs @@ -1,3 +1,4 @@ +use crate::ethereum::SignByECError::InvalidSignature; use crate::grpc::apiv1::kms_client::Client as KmsGrpcClient; use asn1::{BigInt, ParseError, ParseErrorKind, ParseResult, SimpleAsn1Readable, Tag}; use google_cloud_gax::grpc::Status; @@ -28,18 +29,11 @@ impl<'a> U256Bridge<'a> { } } -struct Signature { - r: U256, - s: U256, -} - impl<'a> SimpleAsn1Readable<'a> for U256Bridge<'a> { const TAG: Tag = Tag::primitive(0x02); fn parse_data(data: &'a [u8]) -> ParseResult { - let value= U256::try_from(data).map_err(|_| ParseError::new(ParseErrorKind::InvalidValue))?; - Ok(Self { - value, - }) + let value = U256::try_from(data).map_err(|_| ParseError::new(ParseErrorKind::InvalidValue))?; + Ok(Self { value }) } } @@ -49,6 +43,28 @@ pub enum SignByECError { GRPC(#[from] Status), #[error(transparent)] ParseError(#[from] asn1::ParseError), + #[error("invalid signature")] + InvalidSignature(Vec), +} + +pub struct Signature { + value: [u8; 65], +} + +impl AsRef<[u8; 65]> for Signature { + fn as_ref(&self) -> &[u8; 65] { + &self.value + } +} + +impl Signature { + pub fn set_recovery_id(&mut self, recovery_id: u8) { + self.value[64] = recovery_id; + } + + pub fn get_recovery_id(&self) -> u8 { + self.value[64] + } } pub struct EthereumSigner<'a> { @@ -65,7 +81,7 @@ impl<'a> EthereumSigner<'a> { name: String, digest: Vec, option: Option, - ) -> Result, SignByECError> { + ) -> Result { let result = self .client .asymmetric_sign( @@ -87,16 +103,27 @@ impl<'a> EthereumSigner<'a> { let r = d.read_element::()?; let s = d.read_element::()?; Ok((r, s)) - }) + }); })?; - let (mut r,mut s) = signature; + + let (mut r, mut s) = signature; if s.value < *SECP256K1_HALF_N { s.value = *SECP256K1_N - s.value } - //TODO generate recovery_id + for rid in 0..1 { + let signature = [s.as_bytes32(), r.as_bytes32(), [rid]].concat(); + let address = self.ecrecover(signature.as_slice())?; + //TODO check equality + return Ok(Signature { + value: signature.into(), + }); + } + return Err(InvalidSignature(result.signature)); + } - Ok(vec![]) + fn ecrecover(&self, signature: &[u8]) -> Result<[u8; 20], SignByECError> { + Ok([0u8; 20]) } } From 14cd4f7df81fe58073478a78461be508ddbca679 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Wed, 12 Jun 2024 18:28:31 +0900 Subject: [PATCH 06/22] add ecrecover --- kms/Cargo.toml | 5 ++++- kms/src/ethereum.rs | 29 +++++++++++++++++++++++------ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/kms/Cargo.toml b/kms/Cargo.toml index 2d3c13bd..dc39b9e7 100644 --- a/kms/Cargo.toml +++ b/kms/Cargo.toml @@ -23,6 +23,9 @@ prost-types = "0.12" asn1 = { version = "0.16", optional = true } once_cell = { version="1.19" , optional = true} primitive-types = { version="0.12", optional = true } +k256 = {version = "0.13", optional = true} +elliptic-curve = {version = "0.13", optional = true } +tiny-keccak = {version = "2.0", features = ["keccak"], optional = true} [dev-dependencies] tokio = { version="1.32", features=["rt-multi-thread"] } @@ -40,4 +43,4 @@ rustls-tls = ["google-cloud-auth?/rustls-tls"] trace = [] auth = ["google-cloud-auth"] external-account = ["google-cloud-auth?/external-account"] -eth = ["asn1", "once_cell", "primitive-types"] \ No newline at end of file +eth = ["asn1", "once_cell", "primitive-types", "k256", "elliptic-curve", "tiny-keccak"] \ No newline at end of file diff --git a/kms/src/ethereum.rs b/kms/src/ethereum.rs index 7b72865f..2e4588a8 100644 --- a/kms/src/ethereum.rs +++ b/kms/src/ethereum.rs @@ -1,12 +1,15 @@ use crate::ethereum::SignByECError::InvalidSignature; use crate::grpc::apiv1::kms_client::Client as KmsGrpcClient; use asn1::{BigInt, ParseError, ParseErrorKind, ParseResult, SimpleAsn1Readable, Tag}; +use elliptic_curve::sec1::ToEncodedPoint; use google_cloud_gax::grpc::Status; use google_cloud_gax::retry::RetrySetting; use google_cloud_googleapis::cloud::kms::v1::{digest, AsymmetricSignRequest, Digest}; use hex_literal::hex; +use k256::ecdsa::{RecoveryId, Signature as ECSignature, VerifyingKey}; use once_cell::sync::Lazy; use primitive_types::U256; +use tiny_keccak::{Hasher, Keccak}; const _SECP256K1_N: [u8; 32] = hex!("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"); @@ -88,7 +91,7 @@ impl<'a> EthereumSigner<'a> { AsymmetricSignRequest { name, digest: Some(Digest { - digest: Some(digest::Digest::Sha256(digest)), + digest: Some(digest::Digest::Sha256(digest.clone())), }), digest_crc32c: None, data: vec![], @@ -112,18 +115,32 @@ impl<'a> EthereumSigner<'a> { } for rid in 0..1 { - let signature = [s.as_bytes32(), r.as_bytes32(), [rid]].concat(); - let address = self.ecrecover(signature.as_slice())?; + let sr = [s.as_bytes32(), r.as_bytes32()].concat(); + let address = self.ecrecover(digest.as_slice(), sr.as_slice(), rid)?; //TODO check equality return Ok(Signature { - value: signature.into(), + value: [sr, [rid]].concat().into(), }); } return Err(InvalidSignature(result.signature)); } - fn ecrecover(&self, signature: &[u8]) -> Result<[u8; 20], SignByECError> { - Ok([0u8; 20]) + fn ecrecover(&self, digest: &[u8], sr: &[u8], rid: u8) -> Result<[u8; 20], SignByECError> { + let rid = RecoveryId::from_byte(rid)?; + let ec_signature = ECSignature::try_from(sr)?; + let verifying_key = VerifyingKey::recover_from_prehash(digest, &ec_signature, rid)?; + let point = verifying_key.as_affine().to_encoded_point(false); + let pubkey = point.to_bytes().try_into()?; + Ok(Self::kecckac256(&pubkey[1..])[12..].try_into()?) + } + + fn keccak256(v: &[u8]) -> [u8; 32] { + let mut k = Keccak::v256(); + k.update(v); + + let mut o = [0u8; 32]; + k.finalize(&mut o); + o } } From a59e03fcb6d00aec30dc9aa500ae64e673244cc5 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Wed, 12 Jun 2024 20:14:37 +0900 Subject: [PATCH 07/22] add get address --- kms/src/ethereum.rs | 110 ++++++++++++++++++++++++++++---------------- 1 file changed, 70 insertions(+), 40 deletions(-) diff --git a/kms/src/ethereum.rs b/kms/src/ethereum.rs index 2e4588a8..b22a1c47 100644 --- a/kms/src/ethereum.rs +++ b/kms/src/ethereum.rs @@ -1,12 +1,14 @@ -use crate::ethereum::SignByECError::InvalidSignature; +use crate::ethereum::Error::InvalidSignature; use crate::grpc::apiv1::kms_client::Client as KmsGrpcClient; use asn1::{BigInt, ParseError, ParseErrorKind, ParseResult, SimpleAsn1Readable, Tag}; use elliptic_curve::sec1::ToEncodedPoint; +use elliptic_curve::weierstrass::add; use google_cloud_gax::grpc::Status; use google_cloud_gax::retry::RetrySetting; -use google_cloud_googleapis::cloud::kms::v1::{digest, AsymmetricSignRequest, Digest}; +use google_cloud_googleapis::cloud::kms::v1::{digest, AsymmetricSignRequest, Digest, GetPublicKeyRequest}; use hex_literal::hex; use k256::ecdsa::{RecoveryId, Signature as ECSignature, VerifyingKey}; +use k256::pkcs8::DecodePublicKey; use once_cell::sync::Lazy; use primitive_types::U256; use tiny_keccak::{Hasher, Keccak}; @@ -41,7 +43,7 @@ impl<'a> SimpleAsn1Readable<'a> for U256Bridge<'a> { } #[derive(thiserror::Error, Debug)] -pub enum SignByECError { +pub enum Error { #[error(transparent)] GRPC(#[from] Status), #[error(transparent)] @@ -50,6 +52,22 @@ pub enum SignByECError { InvalidSignature(Vec), } +pub struct PublicKey { + key: VerifyingKey, + address: [u8; 20], +} + +impl TryFrom for PublicKey { + type Error = (); + + fn try_from(value: VerifyingKey) -> Result { + let point = value.as_affine().to_encoded_point(false); + let pubkey = point.to_bytes().try_into()?; + let address = keccak256(&pubkey[1..])[12..].try_into()?; + Ok(Self { key: value, address }) + } +} + pub struct Signature { value: [u8; 65], } @@ -79,27 +97,18 @@ impl<'a> EthereumSigner<'a> { Self { client } } - pub async fn sign( - &self, - name: String, - digest: Vec, - option: Option, - ) -> Result { - let result = self + pub async fn get_address(&self, name: &str, option: Option) -> Result<[u8; 20], Error> { + let pubkey = self .client - .asymmetric_sign( - AsymmetricSignRequest { - name, - digest: Some(Digest { - digest: Some(digest::Digest::Sha256(digest.clone())), - }), - digest_crc32c: None, - data: vec![], - data_crc32c: None, - }, - option, - ) + .get_public_key(GetPublicKeyRequest { name: name.to_string() }, option) .await?; + let pubkey = VerifyingKey::from_public_key_pem(&pubkey.pem)?; + key_to_address(pubkey) + } + + pub async fn sign(&self, name: &str, digest: &[u8], option: Option) -> Result { + let request = asymmetric_sign_request(name, digest.to_vec()); + let result = self.client.asymmetric_sign(request, option.clone()).await?; let mut signature = asn1::parse(result.signature.as_slice(), |d| { return d.read_element::()?.parse(|d| { @@ -114,34 +123,55 @@ impl<'a> EthereumSigner<'a> { s.value = *SECP256K1_N - s.value } + let expected_address = self.get_address(name, option).await?; + for rid in 0..1 { let sr = [s.as_bytes32(), r.as_bytes32()].concat(); - let address = self.ecrecover(digest.as_slice(), sr.as_slice(), rid)?; - //TODO check equality + let address = ecrecover(digest, sr.as_slice(), rid)?; + if expected_address != address { + continue; + } return Ok(Signature { value: [sr, [rid]].concat().into(), }); } return Err(InvalidSignature(result.signature)); } +} - fn ecrecover(&self, digest: &[u8], sr: &[u8], rid: u8) -> Result<[u8; 20], SignByECError> { - let rid = RecoveryId::from_byte(rid)?; - let ec_signature = ECSignature::try_from(sr)?; - let verifying_key = VerifyingKey::recover_from_prehash(digest, &ec_signature, rid)?; - let point = verifying_key.as_affine().to_encoded_point(false); - let pubkey = point.to_bytes().try_into()?; - Ok(Self::kecckac256(&pubkey[1..])[12..].try_into()?) +fn asymmetric_sign_request(name: &str, digest: Vec) -> AsymmetricSignRequest { + AsymmetricSignRequest { + name: name.to_string(), + digest: Some(Digest { + digest: Some(digest::Digest::Sha256(digest)), + }), + digest_crc32c: None, + data: vec![], + data_crc32c: None, } +} - fn keccak256(v: &[u8]) -> [u8; 32] { - let mut k = Keccak::v256(); - k.update(v); +fn ecrecover(digest: &[u8], sr: &[u8], rid: u8) -> Result<[u8; 20], Error> { + let rid = RecoveryId::from_byte(rid)?; + let ec_signature = ECSignature::try_from(sr)?; + let pubkey = VerifyingKey::recover_from_prehash(digest, &ec_signature, rid)?; + key_to_address(pubkey) +} - let mut o = [0u8; 32]; - k.finalize(&mut o); - o - } +fn key_to_address(value: VerifyingKey) -> Result<[u8; 20], Error> { + let point = value.as_affine().to_encoded_point(false); + let pubkey = point.to_bytes().try_into()?; + let address = keccak256(&pubkey[1..])[12..].try_into()?; + Ok(address) +} + +fn keccak256(v: &[u8]) -> [u8; 32] { + let mut k = Keccak::v256(); + k.update(v); + + let mut o = [0u8; 32]; + k.finalize(&mut o); + o } mod tests { @@ -168,8 +198,8 @@ mod tests { let result = client .ethereum() .sign( - key, - hex!("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08").to_vec(), + &key, + &hex!("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"), None, ) .await; From 0a770c76da94d499c718c3db83b16f0f32236fa7 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Thu, 13 Jun 2024 09:51:23 +0900 Subject: [PATCH 08/22] remove deps --- kms/Cargo.toml | 5 +---- kms/src/ethereum.rs | 21 +++++++-------------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/kms/Cargo.toml b/kms/Cargo.toml index dc39b9e7..cdbd1e6c 100644 --- a/kms/Cargo.toml +++ b/kms/Cargo.toml @@ -21,10 +21,7 @@ serde_json = "1.0" thiserror = { version = "1.0", features = [] } prost-types = "0.12" asn1 = { version = "0.16", optional = true } -once_cell = { version="1.19" , optional = true} -primitive-types = { version="0.12", optional = true } k256 = {version = "0.13", optional = true} -elliptic-curve = {version = "0.13", optional = true } tiny-keccak = {version = "2.0", features = ["keccak"], optional = true} [dev-dependencies] @@ -43,4 +40,4 @@ rustls-tls = ["google-cloud-auth?/rustls-tls"] trace = [] auth = ["google-cloud-auth"] external-account = ["google-cloud-auth?/external-account"] -eth = ["asn1", "once_cell", "primitive-types", "k256", "elliptic-curve", "tiny-keccak"] \ No newline at end of file +eth = ["asn1", "k256", "tiny-keccak"] \ No newline at end of file diff --git a/kms/src/ethereum.rs b/kms/src/ethereum.rs index b22a1c47..9b9a6d02 100644 --- a/kms/src/ethereum.rs +++ b/kms/src/ethereum.rs @@ -1,23 +1,17 @@ use crate::ethereum::Error::InvalidSignature; use crate::grpc::apiv1::kms_client::Client as KmsGrpcClient; -use asn1::{BigInt, ParseError, ParseErrorKind, ParseResult, SimpleAsn1Readable, Tag}; -use elliptic_curve::sec1::ToEncodedPoint; -use elliptic_curve::weierstrass::add; +use asn1::{ParseError, ParseErrorKind, ParseResult, SimpleAsn1Readable, Tag}; use google_cloud_gax::grpc::Status; use google_cloud_gax::retry::RetrySetting; use google_cloud_googleapis::cloud::kms::v1::{digest, AsymmetricSignRequest, Digest, GetPublicKeyRequest}; -use hex_literal::hex; use k256::ecdsa::{RecoveryId, Signature as ECSignature, VerifyingKey}; +use k256::elliptic_curve::bigint::Encoding; use k256::pkcs8::DecodePublicKey; -use once_cell::sync::Lazy; -use primitive_types::U256; +use k256::U256; +use k256::elliptic_curve::Curve; +use k256::elliptic_curve::sec1::ToEncodedPoint; use tiny_keccak::{Hasher, Keccak}; -const _SECP256K1_N: [u8; 32] = hex!("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"); - -static SECP256K1_N: Lazy = Lazy::new(|| U256::from(_SECP256K1_N.as_slice())); -static SECP256K1_HALF_N: Lazy = Lazy::new(|| *SECP256K1_N / 2); - struct U256Bridge<'a> { value: U256, } @@ -119,10 +113,9 @@ impl<'a> EthereumSigner<'a> { })?; let (mut r, mut s) = signature; - if s.value < *SECP256K1_HALF_N { - s.value = *SECP256K1_N - s.value + if s.value < k256::Secp256k1::ORDER / 2 { + s.value = k256::Secp256k1::ORDER - s.value } - let expected_address = self.get_address(name, option).await?; for rid in 0..1 { From 0c2bd6ebf56fc4070cafe532f6f20a2367369c01 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Thu, 13 Jun 2024 11:39:00 +0900 Subject: [PATCH 09/22] pass test --- kms/Cargo.toml | 5 +- kms/src/ethereum.rs | 129 +++++++++++++------------------------------- 2 files changed, 40 insertions(+), 94 deletions(-) diff --git a/kms/Cargo.toml b/kms/Cargo.toml index cdbd1e6c..39b4df6f 100644 --- a/kms/Cargo.toml +++ b/kms/Cargo.toml @@ -20,8 +20,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" thiserror = { version = "1.0", features = [] } prost-types = "0.12" -asn1 = { version = "0.16", optional = true } -k256 = {version = "0.13", optional = true} +k256 = {version = "0.13", features = ["pem"], optional = true} tiny-keccak = {version = "2.0", features = ["keccak"], optional = true} [dev-dependencies] @@ -40,4 +39,4 @@ rustls-tls = ["google-cloud-auth?/rustls-tls"] trace = [] auth = ["google-cloud-auth"] external-account = ["google-cloud-auth?/external-account"] -eth = ["asn1", "k256", "tiny-keccak"] \ No newline at end of file +eth = ["k256", "tiny-keccak"] \ No newline at end of file diff --git a/kms/src/ethereum.rs b/kms/src/ethereum.rs index 9b9a6d02..9d94b29e 100644 --- a/kms/src/ethereum.rs +++ b/kms/src/ethereum.rs @@ -1,84 +1,44 @@ use crate::ethereum::Error::InvalidSignature; use crate::grpc::apiv1::kms_client::Client as KmsGrpcClient; -use asn1::{ParseError, ParseErrorKind, ParseResult, SimpleAsn1Readable, Tag}; use google_cloud_gax::grpc::Status; use google_cloud_gax::retry::RetrySetting; use google_cloud_googleapis::cloud::kms::v1::{digest, AsymmetricSignRequest, Digest, GetPublicKeyRequest}; use k256::ecdsa::{RecoveryId, Signature as ECSignature, VerifyingKey}; -use k256::elliptic_curve::bigint::Encoding; -use k256::pkcs8::DecodePublicKey; -use k256::U256; -use k256::elliptic_curve::Curve; +use k256::elliptic_curve::bigint::{CheckedSub, Encoding}; use k256::elliptic_curve::sec1::ToEncodedPoint; +use k256::elliptic_curve::Curve; +use k256::pkcs8::DecodePublicKey; +use std::ops::Div; use tiny_keccak::{Hasher, Keccak}; -struct U256Bridge<'a> { - value: U256, -} - -impl<'a> U256Bridge<'a> { - pub fn as_bytes32(&self) -> [u8; 32] { - let mut b: Vec = vec![]; - self.value.to_big_endian(&mut b); - - while self.len() < 32 { - b.insert(0, 0); - } - b.into() - } -} - -impl<'a> SimpleAsn1Readable<'a> for U256Bridge<'a> { - const TAG: Tag = Tag::primitive(0x02); - fn parse_data(data: &'a [u8]) -> ParseResult { - let value = U256::try_from(data).map_err(|_| ParseError::new(ParseErrorKind::InvalidValue))?; - Ok(Self { value }) - } -} - #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] GRPC(#[from] Status), #[error(transparent)] - ParseError(#[from] asn1::ParseError), + K256ECError(#[from] k256::ecdsa::Error), + #[error(transparent)] + K256PKCSError(#[from] k256::pkcs8::spki::Error), #[error("invalid signature")] InvalidSignature(Vec), } -pub struct PublicKey { - key: VerifyingKey, - address: [u8; 20], -} - -impl TryFrom for PublicKey { - type Error = (); - - fn try_from(value: VerifyingKey) -> Result { - let point = value.as_affine().to_encoded_point(false); - let pubkey = point.to_bytes().try_into()?; - let address = keccak256(&pubkey[1..])[12..].try_into()?; - Ok(Self { key: value, address }) - } -} - +#[derive(Clone, Debug)] pub struct Signature { - value: [u8; 65], -} - -impl AsRef<[u8; 65]> for Signature { - fn as_ref(&self) -> &[u8; 65] { - &self.value - } + pub r: [u8; 32], + pub s: [u8; 32], + pub v: u8, } impl Signature { - pub fn set_recovery_id(&mut self, recovery_id: u8) { - self.value[64] = recovery_id; - } - - pub fn get_recovery_id(&self) -> u8 { - self.value[64] + pub fn to_bytes(&self) -> [u8; 65] { + let mut z = [0; 65]; + let (r, rest) = z.split_at_mut(32); + let (s, v) = rest.split_at_mut(32); + r.copy_from_slice(&self.r); + s.copy_from_slice(&self.s); + v[0] = self.v; + z } } @@ -91,42 +51,33 @@ impl<'a> EthereumSigner<'a> { Self { client } } - pub async fn get_address(&self, name: &str, option: Option) -> Result<[u8; 20], Error> { + pub async fn get_pubkey(&self, name: &str, option: Option) -> Result { let pubkey = self .client .get_public_key(GetPublicKeyRequest { name: name.to_string() }, option) .await?; - let pubkey = VerifyingKey::from_public_key_pem(&pubkey.pem)?; - key_to_address(pubkey) + Ok(VerifyingKey::from_public_key_pem(&pubkey.pem)?) } pub async fn sign(&self, name: &str, digest: &[u8], option: Option) -> Result { let request = asymmetric_sign_request(name, digest.to_vec()); let result = self.client.asymmetric_sign(request, option.clone()).await?; - - let mut signature = asn1::parse(result.signature.as_slice(), |d| { - return d.read_element::()?.parse(|d| { - let r = d.read_element::()?; - let s = d.read_element::()?; - Ok((r, s)) - }); - })?; - - let (mut r, mut s) = signature; - if s.value < k256::Secp256k1::ORDER / 2 { - s.value = k256::Secp256k1::ORDER - s.value + let mut signature = ECSignature::from_der(&result.signature)?; + if let Some(new_sig) = signature.normalize_s() { + signature = new_sig } - let expected_address = self.get_address(name, option).await?; + let expected_key = self.get_pubkey(name, option).await?; for rid in 0..1 { - let sr = [s.as_bytes32(), r.as_bytes32()].concat(); - let address = ecrecover(digest, sr.as_slice(), rid)?; - if expected_address != address { - continue; + let recovery_id = RecoveryId::from_byte(rid).unwrap(); + let recovered_pubkey = VerifyingKey::recover_from_prehash(digest, &signature, recovery_id)?; + if recovered_pubkey == expected_key { + return Ok(Signature { + r: signature.r().to_bytes().into(), + s: signature.s().to_bytes().into(), + v: rid, + }); } - return Ok(Signature { - value: [sr, [rid]].concat().into(), - }); } return Err(InvalidSignature(result.signature)); } @@ -143,20 +94,14 @@ fn asymmetric_sign_request(name: &str, digest: Vec) -> AsymmetricSignRequest data_crc32c: None, } } - -fn ecrecover(digest: &[u8], sr: &[u8], rid: u8) -> Result<[u8; 20], Error> { - let rid = RecoveryId::from_byte(rid)?; - let ec_signature = ECSignature::try_from(sr)?; - let pubkey = VerifyingKey::recover_from_prehash(digest, &ec_signature, rid)?; - key_to_address(pubkey) -} - +/* fn key_to_address(value: VerifyingKey) -> Result<[u8; 20], Error> { let point = value.as_affine().to_encoded_point(false); let pubkey = point.to_bytes().try_into()?; let address = keccak256(&pubkey[1..])[12..].try_into()?; Ok(address) } + */ fn keccak256(v: &[u8]) -> [u8; 32] { let mut k = Keccak::v256(); @@ -195,6 +140,8 @@ mod tests { &hex!("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"), None, ) - .await; + .await + .unwrap(); + print!("{:?}", result); } } From a0a7cf9e4a126a7316fbfe8db5012685d4dd9126 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Thu, 13 Jun 2024 11:44:51 +0900 Subject: [PATCH 10/22] tweak --- kms/src/ethereum.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/kms/src/ethereum.rs b/kms/src/ethereum.rs index 9d94b29e..6eae1a88 100644 --- a/kms/src/ethereum.rs +++ b/kms/src/ethereum.rs @@ -1,14 +1,12 @@ -use crate::ethereum::Error::InvalidSignature; use crate::grpc::apiv1::kms_client::Client as KmsGrpcClient; use google_cloud_gax::grpc::Status; use google_cloud_gax::retry::RetrySetting; use google_cloud_googleapis::cloud::kms::v1::{digest, AsymmetricSignRequest, Digest, GetPublicKeyRequest}; -use k256::ecdsa::{RecoveryId, Signature as ECSignature, VerifyingKey}; +use k256::ecdsa::{RecoveryId, VerifyingKey}; use k256::elliptic_curve::bigint::{CheckedSub, Encoding}; use k256::elliptic_curve::sec1::ToEncodedPoint; use k256::elliptic_curve::Curve; use k256::pkcs8::DecodePublicKey; -use std::ops::Div; use tiny_keccak::{Hasher, Keccak}; #[derive(thiserror::Error, Debug)] @@ -62,7 +60,7 @@ impl<'a> EthereumSigner<'a> { pub async fn sign(&self, name: &str, digest: &[u8], option: Option) -> Result { let request = asymmetric_sign_request(name, digest.to_vec()); let result = self.client.asymmetric_sign(request, option.clone()).await?; - let mut signature = ECSignature::from_der(&result.signature)?; + let mut signature = k256::ecdsa::Signature::from_der(&result.signature)?; if let Some(new_sig) = signature.normalize_s() { signature = new_sig } @@ -79,7 +77,7 @@ impl<'a> EthereumSigner<'a> { }); } } - return Err(InvalidSignature(result.signature)); + return Err(Error::InvalidSignature(result.signature)); } } From a8844871eefaef8bd654ef428f7db289afb716d8 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Thu, 13 Jun 2024 13:52:03 +0900 Subject: [PATCH 11/22] tweak --- kms/Cargo.toml | 3 +-- kms/src/ethereum.rs | 49 ++++++++++++++++----------------------------- 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/kms/Cargo.toml b/kms/Cargo.toml index 39b4df6f..f5800eec 100644 --- a/kms/Cargo.toml +++ b/kms/Cargo.toml @@ -21,7 +21,6 @@ serde_json = "1.0" thiserror = { version = "1.0", features = [] } prost-types = "0.12" k256 = {version = "0.13", features = ["pem"], optional = true} -tiny-keccak = {version = "2.0", features = ["keccak"], optional = true} [dev-dependencies] tokio = { version="1.32", features=["rt-multi-thread"] } @@ -39,4 +38,4 @@ rustls-tls = ["google-cloud-auth?/rustls-tls"] trace = [] auth = ["google-cloud-auth"] external-account = ["google-cloud-auth?/external-account"] -eth = ["k256", "tiny-keccak"] \ No newline at end of file +eth = ["k256"] \ No newline at end of file diff --git a/kms/src/ethereum.rs b/kms/src/ethereum.rs index 6eae1a88..0f466ef2 100644 --- a/kms/src/ethereum.rs +++ b/kms/src/ethereum.rs @@ -7,24 +7,26 @@ use k256::elliptic_curve::bigint::{CheckedSub, Encoding}; use k256::elliptic_curve::sec1::ToEncodedPoint; use k256::elliptic_curve::Curve; use k256::pkcs8::DecodePublicKey; -use tiny_keccak::{Hasher, Keccak}; #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] GRPC(#[from] Status), #[error(transparent)] - K256ECError(#[from] k256::ecdsa::Error), + K256Error(#[from] k256::ecdsa::signature::Error), #[error(transparent)] - K256PKCSError(#[from] k256::pkcs8::spki::Error), + SPKIError(#[from] k256::pkcs8::spki::Error), #[error("invalid signature")] InvalidSignature(Vec), } #[derive(Clone, Debug)] pub struct Signature { + /// The output of an ECDSA signature pub r: [u8; 32], + /// The output of an ECDSA signature pub s: [u8; 32], + /// The recovery id to get pubkey. The value is 0 or 1. pub v: u8, } @@ -49,14 +51,6 @@ impl<'a> EthereumSigner<'a> { Self { client } } - pub async fn get_pubkey(&self, name: &str, option: Option) -> Result { - let pubkey = self - .client - .get_public_key(GetPublicKeyRequest { name: name.to_string() }, option) - .await?; - Ok(VerifyingKey::from_public_key_pem(&pubkey.pem)?) - } - pub async fn sign(&self, name: &str, digest: &[u8], option: Option) -> Result { let request = asymmetric_sign_request(name, digest.to_vec()); let result = self.client.asymmetric_sign(request, option.clone()).await?; @@ -79,6 +73,14 @@ impl<'a> EthereumSigner<'a> { } return Err(Error::InvalidSignature(result.signature)); } + + async fn get_pubkey(&self, name: &str, option: Option) -> Result { + let pubkey = self + .client + .get_public_key(GetPublicKeyRequest { name: name.to_string() }, option) + .await?; + Ok(VerifyingKey::from_public_key_pem(&pubkey.pem)?) + } } fn asymmetric_sign_request(name: &str, digest: Vec) -> AsymmetricSignRequest { @@ -92,27 +94,11 @@ fn asymmetric_sign_request(name: &str, digest: Vec) -> AsymmetricSignRequest data_crc32c: None, } } -/* -fn key_to_address(value: VerifyingKey) -> Result<[u8; 20], Error> { - let point = value.as_affine().to_encoded_point(false); - let pubkey = point.to_bytes().try_into()?; - let address = keccak256(&pubkey[1..])[12..].try_into()?; - Ok(address) -} - */ - -fn keccak256(v: &[u8]) -> [u8; 32] { - let mut k = Keccak::v256(); - k.update(v); - - let mut o = [0u8; 32]; - k.finalize(&mut o); - o -} mod tests { use crate::client::{Client, ClientConfig}; use serial_test::serial; + use crate::ethereum::Error; async fn new_client() -> (Client, String) { let cred = google_cloud_auth::credentials::CredentialsFile::new().await.unwrap(); @@ -131,15 +117,14 @@ mod tests { "projects/{project}/locations/asia-northeast1/keyRings/gcr_test/cryptoKeys/eth-sign/cryptoKeyVersions/1" ); - let result = client + let value = client .ethereum() .sign( &key, &hex!("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"), None, ) - .await - .unwrap(); - print!("{:?}", result); + .await.unwrap(); + println!("{:?}", value.to_bytes()); } } From bace8733b7426143ce50191b6db9d44acd5305b9 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Thu, 13 Jun 2024 14:28:52 +0900 Subject: [PATCH 12/22] add send tx test --- kms/Cargo.toml | 1 + kms/src/ethereum.rs | 55 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/kms/Cargo.toml b/kms/Cargo.toml index f5800eec..74e576c5 100644 --- a/kms/Cargo.toml +++ b/kms/Cargo.toml @@ -30,6 +30,7 @@ ctor = "0.1" tokio-util = {version ="0.7", features = ["codec"] } google-cloud-auth = { path = "../foundation/auth", default-features=false } hex-literal = { version = "0.4" } +ethers = "2.0" [features] default = ["default-tls", "auth", "eth"] diff --git a/kms/src/ethereum.rs b/kms/src/ethereum.rs index 0f466ef2..7c7ddee4 100644 --- a/kms/src/ethereum.rs +++ b/kms/src/ethereum.rs @@ -97,8 +97,14 @@ fn asymmetric_sign_request(name: &str, digest: Vec) -> AsymmetricSignRequest mod tests { use crate::client::{Client, ClientConfig}; + use crate::ethereum::Signature; + use ethers::prelude::{LocalWallet, SignerMiddleware}; + use ethers::providers::{Http, Provider}; + use ethers::types::{Address, TransactionRequest}; + use ethers::utils::parse_ether; + use k256::ecdsa::signature::hazmat::PrehashSigner; + use k256::ecdsa::Error; use serial_test::serial; - use crate::ethereum::Error; async fn new_client() -> (Client, String) { let cred = google_cloud_auth::credentials::CredentialsFile::new().await.unwrap(); @@ -124,7 +130,50 @@ mod tests { &hex!("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"), None, ) - .await.unwrap(); - println!("{:?}", value.to_bytes()); + .await + .unwrap(); + println!("{:?}", value.to_bytes()); + } + + struct RemoteSigner { + client: Client, + key_name: String, + } + + impl PrehashSigner for RemoteSigner { + fn sign_prehash(&self, prehash: &[u8]) -> Result { + self.client.ethereum().sign(self.key_name.as_str(), prehash, None) + } + } + #[tokio::test] + #[serial] + async fn test_send_ethereum_transaction() { + let provider = Provider::::try_from("https://ethereum-sepolia-rpc.publicnode.com")?; + + let to_addr: Address = "0x295a70b2de5e3953354a6a8344e616ed314d7251".parse()?; + let amount: u128 = 1000000000000000000; + + let (client, project) = new_client().await; + let key_name = format!( + "projects/{project}/locations/asia-northeast1/keyRings/gcr_test/cryptoKeys/eth-sign/cryptoKeyVersions/1" + ); + let signer = RemoteSigner { client, key_name }; + + let mut eth_client = SignerMiddleware::new_with_provider_chain(provider, signer) + .await + .unwrap(); + + let tx = TransactionRequest::new() + .to(to_addr.parse::
().unwrap()) + .value(amount) + .gas(8500000) + .gas_price(40000000000) + .chain_id(11155111); // sepolia + + let res = eth_client.send_transaction(tx, None).await.unwrap(); + + let receipt = res.confirmations(10).await.unwrap(); + + println!("sendEth: {:?}", receipt); } } From 223665225b3fbd3dc81d3ec435336b1b6d9232e0 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Thu, 13 Jun 2024 19:06:51 +0900 Subject: [PATCH 13/22] send eth tx --- kms/Cargo.toml | 11 +- kms/src/client.rs | 11 +- kms/src/ethereum.rs | 179 ---------------------- kms/src/grpc/apiv1/kms_client.rs | 2 +- kms/src/lib.rs | 2 +- kms/src/signer/ethereum.rs | 250 +++++++++++++++++++++++++++++++ kms/src/signer/mod.rs | 2 + 7 files changed, 263 insertions(+), 194 deletions(-) delete mode 100644 kms/src/ethereum.rs create mode 100644 kms/src/signer/ethereum.rs create mode 100644 kms/src/signer/mod.rs diff --git a/kms/Cargo.toml b/kms/Cargo.toml index 74e576c5..664b7ae9 100644 --- a/kms/Cargo.toml +++ b/kms/Cargo.toml @@ -20,7 +20,12 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" thiserror = { version = "1.0", features = [] } prost-types = "0.12" -k256 = {version = "0.13", features = ["pem"], optional = true} + +# ethereum +ethers-core = { version = "2.0", optional = true} +ethers-signers = { version = "2.0", optional = true} +async-trait = { version = "0.1.80", optional = true, features = [] } +k256 = { version = "0.13", features = ["pem"], optional = true} [dev-dependencies] tokio = { version="1.32", features=["rt-multi-thread"] } @@ -33,10 +38,10 @@ hex-literal = { version = "0.4" } ethers = "2.0" [features] -default = ["default-tls", "auth", "eth"] +default = ["default-tls", "auth", "ethereum"] default-tls = ["google-cloud-auth?/default-tls"] rustls-tls = ["google-cloud-auth?/rustls-tls"] trace = [] auth = ["google-cloud-auth"] external-account = ["google-cloud-auth?/external-account"] -eth = ["k256"] \ No newline at end of file +ethereum = ["ethers-core", "ethers-signers", "async-trait", "k256"] \ No newline at end of file diff --git a/kms/src/client.rs b/kms/src/client.rs index 9a0907d5..4b1a5318 100644 --- a/kms/src/client.rs +++ b/kms/src/client.rs @@ -5,11 +5,7 @@ use std::sync::Arc; #[cfg(feature = "auth")] pub use google_cloud_auth; use google_cloud_gax::conn::{ConnectionOptions, Environment, Error}; -use google_cloud_gax::grpc::Status; -use google_cloud_gax::retry::RetrySetting; -use google_cloud_googleapis::cloud::kms::v1::{digest, AsymmetricSignRequest, Digest}; -use crate::ethereum::EthereumSigner; use google_cloud_token::{NopeTokenSourceProvider, TokenSourceProvider}; use crate::grpc::apiv1::conn_pool::{ConnectionManager, KMS, SCOPES}; @@ -67,7 +63,7 @@ impl Default for ClientConfig { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Client { kms_client: KmsGrpcClient, } @@ -86,11 +82,6 @@ impl Client { kms_client: KmsGrpcClient::new(Arc::new(cm)), }) } - - #[cfg(feature = "eth")] - pub fn ethereum(&self) -> EthereumSigner { - EthereumSigner::new(&self.kms_client) - } } impl Deref for Client { diff --git a/kms/src/ethereum.rs b/kms/src/ethereum.rs deleted file mode 100644 index 7c7ddee4..00000000 --- a/kms/src/ethereum.rs +++ /dev/null @@ -1,179 +0,0 @@ -use crate::grpc::apiv1::kms_client::Client as KmsGrpcClient; -use google_cloud_gax::grpc::Status; -use google_cloud_gax::retry::RetrySetting; -use google_cloud_googleapis::cloud::kms::v1::{digest, AsymmetricSignRequest, Digest, GetPublicKeyRequest}; -use k256::ecdsa::{RecoveryId, VerifyingKey}; -use k256::elliptic_curve::bigint::{CheckedSub, Encoding}; -use k256::elliptic_curve::sec1::ToEncodedPoint; -use k256::elliptic_curve::Curve; -use k256::pkcs8::DecodePublicKey; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - GRPC(#[from] Status), - #[error(transparent)] - K256Error(#[from] k256::ecdsa::signature::Error), - #[error(transparent)] - SPKIError(#[from] k256::pkcs8::spki::Error), - #[error("invalid signature")] - InvalidSignature(Vec), -} - -#[derive(Clone, Debug)] -pub struct Signature { - /// The output of an ECDSA signature - pub r: [u8; 32], - /// The output of an ECDSA signature - pub s: [u8; 32], - /// The recovery id to get pubkey. The value is 0 or 1. - pub v: u8, -} - -impl Signature { - pub fn to_bytes(&self) -> [u8; 65] { - let mut z = [0; 65]; - let (r, rest) = z.split_at_mut(32); - let (s, v) = rest.split_at_mut(32); - r.copy_from_slice(&self.r); - s.copy_from_slice(&self.s); - v[0] = self.v; - z - } -} - -pub struct EthereumSigner<'a> { - client: &'a KmsGrpcClient, -} - -impl<'a> EthereumSigner<'a> { - pub fn new(client: &'a KmsGrpcClient) -> Self { - Self { client } - } - - pub async fn sign(&self, name: &str, digest: &[u8], option: Option) -> Result { - let request = asymmetric_sign_request(name, digest.to_vec()); - let result = self.client.asymmetric_sign(request, option.clone()).await?; - let mut signature = k256::ecdsa::Signature::from_der(&result.signature)?; - if let Some(new_sig) = signature.normalize_s() { - signature = new_sig - } - let expected_key = self.get_pubkey(name, option).await?; - - for rid in 0..1 { - let recovery_id = RecoveryId::from_byte(rid).unwrap(); - let recovered_pubkey = VerifyingKey::recover_from_prehash(digest, &signature, recovery_id)?; - if recovered_pubkey == expected_key { - return Ok(Signature { - r: signature.r().to_bytes().into(), - s: signature.s().to_bytes().into(), - v: rid, - }); - } - } - return Err(Error::InvalidSignature(result.signature)); - } - - async fn get_pubkey(&self, name: &str, option: Option) -> Result { - let pubkey = self - .client - .get_public_key(GetPublicKeyRequest { name: name.to_string() }, option) - .await?; - Ok(VerifyingKey::from_public_key_pem(&pubkey.pem)?) - } -} - -fn asymmetric_sign_request(name: &str, digest: Vec) -> AsymmetricSignRequest { - AsymmetricSignRequest { - name: name.to_string(), - digest: Some(Digest { - digest: Some(digest::Digest::Sha256(digest)), - }), - digest_crc32c: None, - data: vec![], - data_crc32c: None, - } -} - -mod tests { - use crate::client::{Client, ClientConfig}; - use crate::ethereum::Signature; - use ethers::prelude::{LocalWallet, SignerMiddleware}; - use ethers::providers::{Http, Provider}; - use ethers::types::{Address, TransactionRequest}; - use ethers::utils::parse_ether; - use k256::ecdsa::signature::hazmat::PrehashSigner; - use k256::ecdsa::Error; - use serial_test::serial; - - async fn new_client() -> (Client, String) { - let cred = google_cloud_auth::credentials::CredentialsFile::new().await.unwrap(); - let project = cred.project_id.clone().unwrap(); - let config = ClientConfig::default().with_credentials(cred).await.unwrap(); - (Client::new(config).await.unwrap(), project) - } - - #[tokio::test] - #[serial] - async fn test_sign_ecdsa() { - use hex_literal::hex; - - let (client, project) = new_client().await; - let key = format!( - "projects/{project}/locations/asia-northeast1/keyRings/gcr_test/cryptoKeys/eth-sign/cryptoKeyVersions/1" - ); - - let value = client - .ethereum() - .sign( - &key, - &hex!("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"), - None, - ) - .await - .unwrap(); - println!("{:?}", value.to_bytes()); - } - - struct RemoteSigner { - client: Client, - key_name: String, - } - - impl PrehashSigner for RemoteSigner { - fn sign_prehash(&self, prehash: &[u8]) -> Result { - self.client.ethereum().sign(self.key_name.as_str(), prehash, None) - } - } - #[tokio::test] - #[serial] - async fn test_send_ethereum_transaction() { - let provider = Provider::::try_from("https://ethereum-sepolia-rpc.publicnode.com")?; - - let to_addr: Address = "0x295a70b2de5e3953354a6a8344e616ed314d7251".parse()?; - let amount: u128 = 1000000000000000000; - - let (client, project) = new_client().await; - let key_name = format!( - "projects/{project}/locations/asia-northeast1/keyRings/gcr_test/cryptoKeys/eth-sign/cryptoKeyVersions/1" - ); - let signer = RemoteSigner { client, key_name }; - - let mut eth_client = SignerMiddleware::new_with_provider_chain(provider, signer) - .await - .unwrap(); - - let tx = TransactionRequest::new() - .to(to_addr.parse::
().unwrap()) - .value(amount) - .gas(8500000) - .gas_price(40000000000) - .chain_id(11155111); // sepolia - - let res = eth_client.send_transaction(tx, None).await.unwrap(); - - let receipt = res.confirmations(10).await.unwrap(); - - println!("sendEth: {:?}", receipt); - } -} diff --git a/kms/src/grpc/apiv1/kms_client.rs b/kms/src/grpc/apiv1/kms_client.rs index 65ef436f..c40ba94f 100644 --- a/kms/src/grpc/apiv1/kms_client.rs +++ b/kms/src/grpc/apiv1/kms_client.rs @@ -40,7 +40,7 @@ fn default_setting() -> RetrySetting { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Client { cm: Arc, } diff --git a/kms/src/lib.rs b/kms/src/lib.rs index 5666d726..bf9754a6 100644 --- a/kms/src/lib.rs +++ b/kms/src/lib.rs @@ -112,5 +112,5 @@ //! } //!} pub mod client; -mod ethereum; pub mod grpc; +pub mod signer; diff --git a/kms/src/signer/ethereum.rs b/kms/src/signer/ethereum.rs new file mode 100644 index 00000000..9e716013 --- /dev/null +++ b/kms/src/signer/ethereum.rs @@ -0,0 +1,250 @@ +use crate::client::Client; +use ethers_core::k256::ecdsa::RecoveryId; +use ethers_core::k256::elliptic_curve::bigint::{CheckedSub, Encoding}; +use ethers_core::k256::elliptic_curve::sec1::ToEncodedPoint; +use ethers_core::k256::elliptic_curve::Curve; +use ethers_core::k256::pkcs8::DecodePublicKey; +use ethers_core::k256::FieldBytes; +use ethers_core::types::{Signature, U256}; +use ethers_core::utils::public_key_to_address; +use ethers_core::{ + k256::ecdsa::{Error as K256Error, Signature as KSig, VerifyingKey}, + types::{ + transaction::{eip2718::TypedTransaction, eip712::Eip712}, + Address, + }, + utils::hash_message, +}; +use ethers_signers::Signer as EthSigner; +use google_cloud_gax::grpc::Status; +use google_cloud_gax::retry::RetrySetting; +use google_cloud_googleapis::cloud::kms::v1::{digest, AsymmetricSignRequest, Digest, GetPublicKeyRequest}; +use std::fmt::Debug; +use tokio::time::error; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("{0}")] + GRPC(#[from] Status), + #[error("{0}")] + K256(#[from] K256Error), + #[error("{0}")] + SPKIError(#[from] k256::pkcs8::spki::Error), + #[error("error encoding eip712 struct: {0:?}")] + Eip712Error(String), + #[error("invalid signature: {0:?}")] + InvalidSignature(Vec), +} + +#[derive(Clone, Debug)] +pub struct Signer { + client: Client, + key_name: String, + pubkey: VerifyingKey, + address: Address, + chain_id: u64, + retry_setting: Option, +} + +impl Signer { + pub fn new( + client: Client, + key_name: String, + pubkey: VerifyingKey, + address: Address, + chain_id: u64, + retry_setting: Option, + ) -> Self { + Self { + client, + key_name, + pubkey, + address, + chain_id, + retry_setting, + } + } + pub async fn create( + client: Client, + key_name: &str, + chain_id: u64, + retry: Option, + ) -> Result { + let pubkey = client + .get_public_key( + GetPublicKeyRequest { + name: key_name.to_string(), + }, + retry.clone(), + ) + .await?; + let pubkey = VerifyingKey::from_public_key_pem(&pubkey.pem)?; + let address = public_key_to_address(&pubkey); + Ok(Self { + client, + key_name: key_name.to_string(), + pubkey, + address, + chain_id, + retry_setting: None, + }) + } + + pub async fn sign_digest(&self, digest: &[u8]) -> Result { + let request = Self::asymmetric_sign_request(&self.key_name, digest.to_vec()); + let result = self.client.asymmetric_sign(request, self.retry_setting.clone()).await?; + let mut signature = KSig::from_der(&result.signature)?; + if let Some(new_sig) = signature.normalize_s() { + signature = new_sig + } + + for rid in 0..=1 { + let recovery_id = RecoveryId::from_byte(rid).unwrap(); + let recovered_pubkey = VerifyingKey::recover_from_prehash(digest, &signature, recovery_id)?; + let address = public_key_to_address(&recovered_pubkey); + if recovered_pubkey == self.pubkey { + let r_bytes: FieldBytes = signature.r().into(); + let s_bytes: FieldBytes = signature.s().into(); + return Ok(Signature { + r: U256::from_big_endian(r_bytes.as_slice()), + s: U256::from_big_endian(s_bytes.as_slice()), + v: rid as u64, + }); + } + } + return Err(Error::InvalidSignature(result.signature)); + } + + fn asymmetric_sign_request(name: &str, digest: Vec) -> AsymmetricSignRequest { + AsymmetricSignRequest { + name: name.to_string(), + digest: Some(Digest { + digest: Some(digest::Digest::Sha256(digest)), + }), + digest_crc32c: None, + data: vec![], + data_crc32c: None, + } + } + + fn with_eip155(&self, mut signature: Signature) -> Signature { + signature.v = (self.chain_id * 2 + 35) + signature.v; + return signature; + } +} + +#[async_trait::async_trait] +impl EthSigner for Signer { + type Error = Error; + + async fn sign_message>(&self, message: S) -> Result { + let message = message.as_ref(); + let message_hash = hash_message(message); + let signature = self.sign_digest(message_hash.as_bytes()).await?; + Ok(self.with_eip155(signature)) + } + + async fn sign_transaction(&self, tx: &TypedTransaction) -> Result { + let mut tx_with_chain = tx.clone(); + let chain_id = tx_with_chain.chain_id().map(|id| id.as_u64()).unwrap_or(self.chain_id); + tx_with_chain.set_chain_id(chain_id); + + let sighash = tx_with_chain.sighash(); + + let signature = self.sign_digest(sighash.as_bytes()).await?; + Ok(self.with_eip155(signature)) + } + + async fn sign_typed_data(&self, payload: &T) -> Result { + let digest = payload + .encode_eip712() + .map_err(|e| Self::Error::Eip712Error(e.to_string()))?; + + let signature = self.sign_digest(&digest).await?; + Ok(signature) + } + + fn address(&self) -> Address { + self.address + } + + /// Returns the signer's chain id + fn chain_id(&self) -> u64 { + self.chain_id + } + + /// Sets the signer's chain id + fn with_chain_id>(mut self, chain_id: T) -> Self { + self.chain_id = chain_id.into(); + self + } +} + +mod tests { + use crate::client::{Client, ClientConfig}; + use crate::signer::ethereum::Signer; + use ethers::middleware::SignerMiddleware; + use ethers::providers::{Http, Middleware, Provider}; + use ethers_core::types::{Address, TransactionRequest}; + use ethers_signers::Signer as EthSigner; + use serial_test::serial; + + async fn new_client() -> (Client, String) { + let cred = google_cloud_auth::credentials::CredentialsFile::new().await.unwrap(); + let project = cred.project_id.clone().unwrap(); + let config = ClientConfig::default().with_credentials(cred).await.unwrap(); + (Client::new(config).await.unwrap(), project) + } + + #[tokio::test] + #[serial] + async fn test_sign_ecdsa() { + use hex_literal::hex; + + let (client, project) = new_client().await; + let key = format!( + "projects/{project}/locations/asia-northeast1/keyRings/gcr_test/cryptoKeys/eth-sign/cryptoKeyVersions/1" + ); + + let signer = Signer::create(client, &key, 1, None).await.unwrap(); + let signature = signer + .sign_digest(&hex!("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08")) + .await + .unwrap(); + println!("{:?}", signature); + } + + #[tokio::test] + #[serial] + async fn test_send_ethereum_transaction() { + let provider = Provider::::try_from("https://ethereum-sepolia-rpc.publicnode.com").unwrap(); + + let amount: u128 = 100_000_000_000_000; // 0.0001 eth + + let (client, project) = new_client().await; + let key = format!( + "projects/{project}/locations/asia-northeast1/keyRings/gcr_test/cryptoKeys/eth-sign/cryptoKeyVersions/1" + ); + + let signer = Signer::create(client, &key, 1, None).await.unwrap(); + let signer_address = signer.address(); + tracing::info!("signerAddress = {:?}", signer_address); + + let eth_client = SignerMiddleware::new_with_provider_chain(provider, signer) + .await + .unwrap(); + + let tx = TransactionRequest::new() + .to(signer_address) + .value(amount) + .gas(8_600_000 as u64) + .gas_price(40_000_000_000 as u64) + .chain_id(11155111); // sepolia + + let res = eth_client.send_transaction(tx, None).await.unwrap(); + tracing::info!("tx res: {:?}", res); + + let receipt = res.confirmations(10).await.unwrap(); + tracing::info!("receipt: {:?}", receipt); + } +} diff --git a/kms/src/signer/mod.rs b/kms/src/signer/mod.rs new file mode 100644 index 00000000..a157179d --- /dev/null +++ b/kms/src/signer/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "ethereum")] +pub mod ethereum; From 9da9f04324b2860ebec9405f0f0cfad0c5cd7fc8 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Thu, 13 Jun 2024 20:08:20 +0900 Subject: [PATCH 14/22] tweak --- kms/Cargo.toml | 4 ++-- kms/src/client.rs | 1 - kms/src/signer/ethereum.rs | 27 ++++++++++++--------------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/kms/Cargo.toml b/kms/Cargo.toml index 664b7ae9..7527f76a 100644 --- a/kms/Cargo.toml +++ b/kms/Cargo.toml @@ -38,10 +38,10 @@ hex-literal = { version = "0.4" } ethers = "2.0" [features] -default = ["default-tls", "auth", "ethereum"] +default = ["default-tls", "auth", "eth"] default-tls = ["google-cloud-auth?/default-tls"] rustls-tls = ["google-cloud-auth?/rustls-tls"] trace = [] auth = ["google-cloud-auth"] external-account = ["google-cloud-auth?/external-account"] -ethereum = ["ethers-core", "ethers-signers", "async-trait", "k256"] \ No newline at end of file +eth = ["ethers-core", "ethers-signers", "async-trait", "k256"] \ No newline at end of file diff --git a/kms/src/client.rs b/kms/src/client.rs index 4b1a5318..aa6c0444 100644 --- a/kms/src/client.rs +++ b/kms/src/client.rs @@ -1,4 +1,3 @@ -use hex_literal::hex; use std::ops::Deref; use std::sync::Arc; diff --git a/kms/src/signer/ethereum.rs b/kms/src/signer/ethereum.rs index 9e716013..b268eb97 100644 --- a/kms/src/signer/ethereum.rs +++ b/kms/src/signer/ethereum.rs @@ -1,8 +1,5 @@ use crate::client::Client; use ethers_core::k256::ecdsa::RecoveryId; -use ethers_core::k256::elliptic_curve::bigint::{CheckedSub, Encoding}; -use ethers_core::k256::elliptic_curve::sec1::ToEncodedPoint; -use ethers_core::k256::elliptic_curve::Curve; use ethers_core::k256::pkcs8::DecodePublicKey; use ethers_core::k256::FieldBytes; use ethers_core::types::{Signature, U256}; @@ -20,7 +17,6 @@ use google_cloud_gax::grpc::Status; use google_cloud_gax::retry::RetrySetting; use google_cloud_googleapis::cloud::kms::v1::{digest, AsymmetricSignRequest, Digest, GetPublicKeyRequest}; use std::fmt::Debug; -use tokio::time::error; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -101,7 +97,6 @@ impl Signer { for rid in 0..=1 { let recovery_id = RecoveryId::from_byte(rid).unwrap(); let recovered_pubkey = VerifyingKey::recover_from_prehash(digest, &signature, recovery_id)?; - let address = public_key_to_address(&recovered_pubkey); if recovered_pubkey == self.pubkey { let r_bytes: FieldBytes = signature.r().into(); let s_bytes: FieldBytes = signature.s().into(); @@ -112,7 +107,7 @@ impl Signer { }); } } - return Err(Error::InvalidSignature(result.signature)); + Err(Error::InvalidSignature(result.signature)) } fn asymmetric_sign_request(name: &str, digest: Vec) -> AsymmetricSignRequest { @@ -128,8 +123,8 @@ impl Signer { } fn with_eip155(&self, mut signature: Signature) -> Signature { - signature.v = (self.chain_id * 2 + 35) + signature.v; - return signature; + signature.v += self.chain_id * 2 + 35; + signature } } @@ -180,12 +175,13 @@ impl EthSigner for Signer { } } +#[cfg(test)] mod tests { use crate::client::{Client, ClientConfig}; use crate::signer::ethereum::Signer; use ethers::middleware::SignerMiddleware; use ethers::providers::{Http, Middleware, Provider}; - use ethers_core::types::{Address, TransactionRequest}; + use ethers_core::types::{TransactionReceipt, TransactionRequest}; use ethers_signers::Signer as EthSigner; use serial_test::serial; @@ -217,9 +213,9 @@ mod tests { #[tokio::test] #[serial] async fn test_send_ethereum_transaction() { - let provider = Provider::::try_from("https://ethereum-sepolia-rpc.publicnode.com").unwrap(); + let provider = Provider::::try_from("https://bsc-testnet-rpc.publicnode.com").unwrap(); - let amount: u128 = 100_000_000_000_000; // 0.0001 eth + let amount: u128 = 100_000_000_000_000; // 0.0003 BNB let (client, project) = new_client().await; let key = format!( @@ -237,14 +233,15 @@ mod tests { let tx = TransactionRequest::new() .to(signer_address) .value(amount) - .gas(8_600_000 as u64) - .gas_price(40_000_000_000 as u64) - .chain_id(11155111); // sepolia + .gas(1_500_000_u64) + .gas_price(4_000_000_000_u64) + .chain_id(97); // BSC testnet let res = eth_client.send_transaction(tx, None).await.unwrap(); tracing::info!("tx res: {:?}", res); - let receipt = res.confirmations(10).await.unwrap(); + let receipt: TransactionReceipt = res.confirmations(3).await.unwrap().unwrap(); tracing::info!("receipt: {:?}", receipt); + assert_eq!(receipt.from, signer_address); } } From a61c51b93178dfd59626fcb180b8b14f2e29151d Mon Sep 17 00:00:00 2001 From: yoshidan Date: Thu, 13 Jun 2024 20:10:17 +0900 Subject: [PATCH 15/22] remove eth from default features' --- kms/Cargo.toml | 2 +- kms/src/signer/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kms/Cargo.toml b/kms/Cargo.toml index 7527f76a..38338199 100644 --- a/kms/Cargo.toml +++ b/kms/Cargo.toml @@ -38,7 +38,7 @@ hex-literal = { version = "0.4" } ethers = "2.0" [features] -default = ["default-tls", "auth", "eth"] +default = ["default-tls", "auth"] default-tls = ["google-cloud-auth?/default-tls"] rustls-tls = ["google-cloud-auth?/rustls-tls"] trace = [] diff --git a/kms/src/signer/mod.rs b/kms/src/signer/mod.rs index a157179d..bc19f17b 100644 --- a/kms/src/signer/mod.rs +++ b/kms/src/signer/mod.rs @@ -1,2 +1,2 @@ -#[cfg(feature = "ethereum")] +#[cfg(feature = "eth")] pub mod ethereum; From f56654b8b4956c9b022fe48f14c14e6e6ee2b542 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Fri, 14 Jun 2024 17:18:42 +0900 Subject: [PATCH 16/22] add doc --- kms/README.md | 43 ++++++++++++++++++++- kms/src/lib.rs | 41 +++++++++++++++++++- kms/src/signer/ethereum.rs | 77 ++++++++++++++++++++++++++------------ 3 files changed, 135 insertions(+), 26 deletions(-) diff --git a/kms/README.md b/kms/README.md index 5408b9d2..4adb61a2 100644 --- a/kms/README.md +++ b/kms/README.md @@ -84,7 +84,7 @@ google-cloud-kms = "version" // list match client - .list_key_rings( + .list_key_rings( ListKeyRingsRequest { parent: "projects/qovery-gcp-tests/locations/europe-west9".to_string(), page_size: 5, @@ -121,3 +121,44 @@ google-cloud-kms = "version" } Err(err) => panic!("err: {:?}", err), } + } +``` + +### Ethereum Integration + +Enable 'eth' feature. + +```toml +[dependencies] +google-cloud-kms = { version="version", features=["eth"] } +``` + + ```rust + use ethers::prelude::SignerMiddleware; + use ethers::providers::{Http, Middleware, Provider}; + use ethers_core::types::{TransactionReceipt, TransactionRequest}; + use ethers_signers::Signer as EthSigner; + use google_cloud_kms::client::Client; + use google_cloud_kms::signer::ethereum::{Error, Signer}; + + pub async fn send_bnb(client: Client, key_name: &str) { + + // BSC testnet + let chain_id = 97; + + let signer = Signer::new(client, key_name, chain_id, None).await.unwrap(); + let provider = Provider::::try_from("https://bsc-testnet-rpc.publicnode.com").unwrap(); + + let eth_client = SignerMiddleware::new_with_provider_chain(provider, signer).await.unwrap(); + + let tx = TransactionRequest::new() + .to(signer.address()) + .value(100_000_000_000_000_u128) + .gas(1_500_000_u64) + .gas_price(4_000_000_000_u64) + .chain_id(chain_id); // BSC testnet + + let res = eth_client.send_transaction(tx, None).await.unwrap(); + let receipt: TransactionReceipt = res.confirmations(3).await.unwrap().unwrap(); + } +``` \ No newline at end of file diff --git a/kms/src/lib.rs b/kms/src/lib.rs index bf9754a6..e28c8c9c 100644 --- a/kms/src/lib.rs +++ b/kms/src/lib.rs @@ -40,10 +40,10 @@ //! ``` //! //! ### Usage -//! +//! //! #### Key ring operations //! -//! ```rust +//! ``` //! use std::collections::HashMap; //! use prost_types::FieldMask; //! use google_cloud_googleapis::cloud::kms::v1::{CreateKeyRingRequest, GetKeyRingRequest, ListKeyRingsRequest}; @@ -111,6 +111,43 @@ //! Err(err) => panic!("err: {:?}", err), //! } //!} +//!``` +//! +//! ### Ethereum Integration +//! +//! Enable 'eth' feature. +//! google-cloud-kms = { version="version", features=["eth"] } +//! +//! ``` +//! use ethers::prelude::SignerMiddleware; +//! use ethers::providers::{Http, Middleware, Provider}; +//! use ethers_core::types::{TransactionReceipt, TransactionRequest}; +//! use ethers_signers::Signer as EthSigner; +//! use google_cloud_kms::client::Client; +//! use google_cloud_kms::signer::ethereum::{Error, Signer}; +//! +//! pub async fn send_bnb(client: Client, key_name: &str) { +//! +//! // BSC testnet +//! let chain_id = 97; +//! +//! let signer = Signer::new(client, key_name, chain_id, None).await.unwrap(); +//! let provider = Provider::::try_from("https://bsc-testnet-rpc.publicnode.com").unwrap(); +//! +//! +//! let eth_client = SignerMiddleware::new_with_provider_chain(provider, signer).await.unwrap(); +//! +//! let tx = TransactionRequest::new() +//! .to(signer.address()) +//! .value(100_000_000_000_000_u128) +//! .gas(1_500_000_u64) +//! .gas_price(4_000_000_000_u64) +//! .chain_id(chain_id); // BSC testnet +//! +//! let res = eth_client.send_transaction(tx, None).await.unwrap(); +//! let receipt: TransactionReceipt = res.confirmations(3).await.unwrap().unwrap(); +//! } +//! ``` pub mod client; pub mod grpc; pub mod signer; diff --git a/kms/src/signer/ethereum.rs b/kms/src/signer/ethereum.rs index b268eb97..0674acbb 100644 --- a/kms/src/signer/ethereum.rs +++ b/kms/src/signer/ethereum.rs @@ -35,17 +35,21 @@ pub enum Error { #[derive(Clone, Debug)] pub struct Signer { client: Client, + /// key_name managed by GoogleClod. + /// Format: "projects/{project}/locations/{region}/keyRings/{keyRing}/cryptoKeys/{key}/cryptoKeyVersions/{version}" + /// It must be ECDSA secp256k1. key_name: String, + /// ECDSA/secp256k1 pubkey pubkey: VerifyingKey, + /// Ethereum address address: Address, chain_id: u64, - retry_setting: Option, -} + retry_setting: Option, } impl Signer { - pub fn new( + pub fn new_with_pubkey( client: Client, - key_name: String, + key_name: &str, pubkey: VerifyingKey, address: Address, chain_id: u64, @@ -53,14 +57,50 @@ impl Signer { ) -> Self { Self { client, - key_name, + key_name: key_name.to_string(), pubkey, address, chain_id, retry_setting, } } - pub async fn create( + + /// Instantiate a new signer from an existing `Client` and Key ID. + /// + /// This function retrieves the public key from Google Cloud and calculates the Etheruem address. + /// It is therefore `async`. + /// + /// ``` + /// use ethers::prelude::SignerMiddleware; + /// use ethers::providers::{Http, Middleware, Provider}; + /// use ethers_core::types::{TransactionReceipt, TransactionRequest}; + /// use ethers_signers::Signer as EthSigner; + /// use google_cloud_kms::client::Client; + /// use google_cloud_kms::signer::ethereum::{Error, Signer}; + /// + /// pub async fn run(client: Client, key_name: &str) { + /// + /// // BSC testnet + /// let chain_id = 97; + /// + /// let signer = Signer::new(client, key_name, chain_id, None).await.unwrap(); + /// let provider = Provider::::try_from("https://bsc-testnet-rpc.publicnode.com").unwrap(); + /// + /// + /// let eth_client = SignerMiddleware::new_with_provider_chain(provider, signer).await.unwrap(); + /// + /// let tx = TransactionRequest::new() + /// .to(signer.address()) + /// .value(100_000_000_000_000_u128) + /// .gas(1_500_000_u64) + /// .gas_price(4_000_000_000_u64) + /// .chain_id(chain_id); // BSC testnet + /// + /// let res = eth_client.send_transaction(tx, None).await.unwrap(); + /// let receipt: TransactionReceipt = res.confirmations(3).await.unwrap().unwrap(); + /// } + /// ``` + pub async fn new( client: Client, key_name: &str, chain_id: u64, @@ -76,19 +116,13 @@ impl Signer { .await?; let pubkey = VerifyingKey::from_public_key_pem(&pubkey.pem)?; let address = public_key_to_address(&pubkey); - Ok(Self { - client, - key_name: key_name.to_string(), - pubkey, - address, - chain_id, - retry_setting: None, - }) + Ok(Self::new_with_pubkey(client, key_name, pubkey, address, chain_id, retry)) } pub async fn sign_digest(&self, digest: &[u8]) -> Result { let request = Self::asymmetric_sign_request(&self.key_name, digest.to_vec()); let result = self.client.asymmetric_sign(request, self.retry_setting.clone()).await?; + let mut signature = KSig::from_der(&result.signature)?; if let Some(new_sig) = signature.normalize_s() { signature = new_sig @@ -163,12 +197,10 @@ impl EthSigner for Signer { self.address } - /// Returns the signer's chain id fn chain_id(&self) -> u64 { self.chain_id } - /// Sets the signer's chain id fn with_chain_id>(mut self, chain_id: T) -> Self { self.chain_id = chain_id.into(); self @@ -202,7 +234,7 @@ mod tests { "projects/{project}/locations/asia-northeast1/keyRings/gcr_test/cryptoKeys/eth-sign/cryptoKeyVersions/1" ); - let signer = Signer::create(client, &key, 1, None).await.unwrap(); + let signer = Signer::new(client, &key, 1, None).await.unwrap(); let signature = signer .sign_digest(&hex!("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08")) .await @@ -215,14 +247,12 @@ mod tests { async fn test_send_ethereum_transaction() { let provider = Provider::::try_from("https://bsc-testnet-rpc.publicnode.com").unwrap(); - let amount: u128 = 100_000_000_000_000; // 0.0003 BNB - let (client, project) = new_client().await; let key = format!( "projects/{project}/locations/asia-northeast1/keyRings/gcr_test/cryptoKeys/eth-sign/cryptoKeyVersions/1" ); - - let signer = Signer::create(client, &key, 1, None).await.unwrap(); + let chain_id = 97; + let signer = Signer::new(client, &key, chain_id, None).await.unwrap(); let signer_address = signer.address(); tracing::info!("signerAddress = {:?}", signer_address); @@ -232,10 +262,10 @@ mod tests { let tx = TransactionRequest::new() .to(signer_address) - .value(amount) + .value(100_000_000_000_000_u128) .gas(1_500_000_u64) .gas_price(4_000_000_000_u64) - .chain_id(97); // BSC testnet + .chain_id(chain_id); // BSC testnet let res = eth_client.send_transaction(tx, None).await.unwrap(); tracing::info!("tx res: {:?}", res); @@ -243,5 +273,6 @@ mod tests { let receipt: TransactionReceipt = res.confirmations(3).await.unwrap().unwrap(); tracing::info!("receipt: {:?}", receipt); assert_eq!(receipt.from, signer_address); + assert_eq!(receipt.status.unwrap().as_u64(), 1); } } From f021275cf36c384ef017837e4adbe564d6bda7b1 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Fri, 14 Jun 2024 17:19:53 +0900 Subject: [PATCH 17/22] tweak --- kms/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kms/Cargo.toml b/kms/Cargo.toml index 38338199..b9fae8b9 100644 --- a/kms/Cargo.toml +++ b/kms/Cargo.toml @@ -24,7 +24,7 @@ prost-types = "0.12" # ethereum ethers-core = { version = "2.0", optional = true} ethers-signers = { version = "2.0", optional = true} -async-trait = { version = "0.1.80", optional = true, features = [] } +async-trait = { version = "0.1", optional = true, features = [] } k256 = { version = "0.13", features = ["pem"], optional = true} [dev-dependencies] From a597544e653f08129b0c46204b89528ff7620c88 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Fri, 14 Jun 2024 17:23:56 +0900 Subject: [PATCH 18/22] tweak --- kms/Cargo.toml | 4 ++-- kms/src/lib.rs | 2 +- kms/src/signer/ethereum.rs | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/kms/Cargo.toml b/kms/Cargo.toml index b9fae8b9..83d50fd4 100644 --- a/kms/Cargo.toml +++ b/kms/Cargo.toml @@ -23,8 +23,8 @@ prost-types = "0.12" # ethereum ethers-core = { version = "2.0", optional = true} -ethers-signers = { version = "2.0", optional = true} -async-trait = { version = "0.1", optional = true, features = [] } +ethers-signers = { version = "2.0", optional = true} +async-trait = { version = "0.1", optional = true } k256 = { version = "0.13", features = ["pem"], optional = true} [dev-dependencies] diff --git a/kms/src/lib.rs b/kms/src/lib.rs index e28c8c9c..46c0796f 100644 --- a/kms/src/lib.rs +++ b/kms/src/lib.rs @@ -40,7 +40,7 @@ //! ``` //! //! ### Usage -//! +//! //! #### Key ring operations //! //! ``` diff --git a/kms/src/signer/ethereum.rs b/kms/src/signer/ethereum.rs index 0674acbb..b8c5fb0e 100644 --- a/kms/src/signer/ethereum.rs +++ b/kms/src/signer/ethereum.rs @@ -44,7 +44,8 @@ pub struct Signer { /// Ethereum address address: Address, chain_id: u64, - retry_setting: Option, } + retry_setting: Option, +} impl Signer { pub fn new_with_pubkey( From c9c859d14c7e5554cca5eec7f2507290a8cca724 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Fri, 14 Jun 2024 17:26:32 +0900 Subject: [PATCH 19/22] tweak --- kms/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kms/Cargo.toml b/kms/Cargo.toml index 83d50fd4..e4f5560e 100644 --- a/kms/Cargo.toml +++ b/kms/Cargo.toml @@ -18,7 +18,7 @@ google-cloud-gax = { version = "0.17.0", path = "../foundation/gax"} tracing = "0.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -thiserror = { version = "1.0", features = [] } +thiserror = "1.0" prost-types = "0.12" # ethereum @@ -34,7 +34,7 @@ tracing-subscriber = { version="0.3.17", features=["env-filter"]} ctor = "0.1" tokio-util = {version ="0.7", features = ["codec"] } google-cloud-auth = { path = "../foundation/auth", default-features=false } -hex-literal = { version = "0.4" } +hex-literal = "0.4" ethers = "2.0" [features] @@ -44,4 +44,4 @@ rustls-tls = ["google-cloud-auth?/rustls-tls"] trace = [] auth = ["google-cloud-auth"] external-account = ["google-cloud-auth?/external-account"] -eth = ["ethers-core", "ethers-signers", "async-trait", "k256"] \ No newline at end of file +eth = ["ethers-core", "ethers-signers", "async-trait", "k256"] From db00be3a6c85ec9cae0672bf1a68d6b170c6e343 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Fri, 14 Jun 2024 17:29:02 +0900 Subject: [PATCH 20/22] twweak --- kms/README.md | 2 +- kms/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kms/README.md b/kms/README.md index 4adb61a2..14269072 100644 --- a/kms/README.md +++ b/kms/README.md @@ -156,7 +156,7 @@ google-cloud-kms = { version="version", features=["eth"] } .value(100_000_000_000_000_u128) .gas(1_500_000_u64) .gas_price(4_000_000_000_u64) - .chain_id(chain_id); // BSC testnet + .chain_id(chain_id); let res = eth_client.send_transaction(tx, None).await.unwrap(); let receipt: TransactionReceipt = res.confirmations(3).await.unwrap().unwrap(); diff --git a/kms/src/lib.rs b/kms/src/lib.rs index 46c0796f..3c39d2fc 100644 --- a/kms/src/lib.rs +++ b/kms/src/lib.rs @@ -142,7 +142,7 @@ //! .value(100_000_000_000_000_u128) //! .gas(1_500_000_u64) //! .gas_price(4_000_000_000_u64) -//! .chain_id(chain_id); // BSC testnet +//! .chain_id(chain_id); //! //! let res = eth_client.send_transaction(tx, None).await.unwrap(); //! let receipt: TransactionReceipt = res.confirmations(3).await.unwrap().unwrap(); From cdfec8c772030f411790b372c3b257477b0cb41d Mon Sep 17 00:00:00 2001 From: yoshidan Date: Sun, 16 Jun 2024 11:38:41 +0900 Subject: [PATCH 21/22] fix lint --- kms/README.md | 9 +++++---- kms/src/lib.rs | 8 ++++---- kms/src/signer/ethereum.rs | 4 ++-- pubsub/src/subscription.rs | 2 +- spanner/src/client.rs | 5 +---- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/kms/README.md b/kms/README.md index 14269072..14ee32c0 100644 --- a/kms/README.md +++ b/kms/README.md @@ -141,18 +141,19 @@ google-cloud-kms = { version="version", features=["eth"] } use google_cloud_kms::client::Client; use google_cloud_kms::signer::ethereum::{Error, Signer}; - pub async fn send_bnb(client: Client, key_name: &str) { + pub async fn send_bnb(client: Client, key_name: &str, rpc_node: &str) { // BSC testnet let chain_id = 97; let signer = Signer::new(client, key_name, chain_id, None).await.unwrap(); - let provider = Provider::::try_from("https://bsc-testnet-rpc.publicnode.com").unwrap(); - + let provider = Provider::::try_from(rpc_node).unwrap(); + + let signer_address = signer.address(); let eth_client = SignerMiddleware::new_with_provider_chain(provider, signer).await.unwrap(); let tx = TransactionRequest::new() - .to(signer.address()) + .to(signer_address) .value(100_000_000_000_000_u128) .gas(1_500_000_u64) .gas_price(4_000_000_000_u64) diff --git a/kms/src/lib.rs b/kms/src/lib.rs index 3c39d2fc..e73ff04a 100644 --- a/kms/src/lib.rs +++ b/kms/src/lib.rs @@ -126,19 +126,19 @@ //! use google_cloud_kms::client::Client; //! use google_cloud_kms::signer::ethereum::{Error, Signer}; //! -//! pub async fn send_bnb(client: Client, key_name: &str) { +//! pub async fn send_bnb(client: Client, key_name: &str, rpc_node: &str) { //! //! // BSC testnet //! let chain_id = 97; //! //! let signer = Signer::new(client, key_name, chain_id, None).await.unwrap(); -//! let provider = Provider::::try_from("https://bsc-testnet-rpc.publicnode.com").unwrap(); -//! +//! let provider = Provider::::try_from(rpc_node).unwrap(); +//! let signer_address = signer.address(); //! //! let eth_client = SignerMiddleware::new_with_provider_chain(provider, signer).await.unwrap(); //! //! let tx = TransactionRequest::new() -//! .to(signer.address()) +//! .to(signer_address) //! .value(100_000_000_000_000_u128) //! .gas(1_500_000_u64) //! .gas_price(4_000_000_000_u64) diff --git a/kms/src/signer/ethereum.rs b/kms/src/signer/ethereum.rs index b8c5fb0e..688daa18 100644 --- a/kms/src/signer/ethereum.rs +++ b/kms/src/signer/ethereum.rs @@ -86,12 +86,12 @@ impl Signer { /// /// let signer = Signer::new(client, key_name, chain_id, None).await.unwrap(); /// let provider = Provider::::try_from("https://bsc-testnet-rpc.publicnode.com").unwrap(); - /// + /// let signer_address = signer.address(); /// /// let eth_client = SignerMiddleware::new_with_provider_chain(provider, signer).await.unwrap(); /// /// let tx = TransactionRequest::new() - /// .to(signer.address()) + /// .to(signer_address) /// .value(100_000_000_000_000_u128) /// .gas(1_500_000_u64) /// .gas_price(4_000_000_000_u64) diff --git a/pubsub/src/subscription.rs b/pubsub/src/subscription.rs index 9d9cbf04..4c4d2b8c 100644 --- a/pubsub/src/subscription.rs +++ b/pubsub/src/subscription.rs @@ -681,7 +681,7 @@ impl Subscription { } let cfg = self.config(None).await?; let mut default_cfg = SubscriberConfig { - stream_ack_deadline_seconds: max(min(cfg.1.ack_deadline_seconds, 600), 10), + stream_ack_deadline_seconds: cfg.1.ack_deadline_seconds.clamp(10, 600), ..Default::default() }; if cfg.1.enable_exactly_once_delivery { diff --git a/spanner/src/client.rs b/spanner/src/client.rs index 09456398..49227a0b 100644 --- a/spanner/src/client.rs +++ b/spanner/src/client.rs @@ -339,10 +339,7 @@ impl Client { Ok(tx) => tx, Err(e) => return Err((Error::GRPC(e.status), Some(e.session))), }; - let qo = match options.query_options.clone() { - Some(o) => o, - None => QueryOptions::default(), - }; + let qo = options.query_options.clone().unwrap_or_default(); tx.update_with_option(stmt.clone(), qo) .await .map_err(|e| (Error::GRPC(e), tx.take_session())) From 7685b40cd111d55a5eb42d2101ba4d54163c0502 Mon Sep 17 00:00:00 2001 From: yoshidan Date: Sun, 16 Jun 2024 11:54:34 +0900 Subject: [PATCH 22/22] fix lint --- pubsub/src/subscription.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pubsub/src/subscription.rs b/pubsub/src/subscription.rs index 4c4d2b8c..e256a0d2 100644 --- a/pubsub/src/subscription.rs +++ b/pubsub/src/subscription.rs @@ -1,4 +1,3 @@ -use std::cmp::{max, min}; use std::collections::HashMap; use std::future::Future; use std::pin::Pin;