Skip to content

Commit 55f3672

Browse files
authored
KMS (#22)
* Add engine-eip7702-core package and update dependencies - Introduced the `engine-eip7702-core` package with its dependencies in `Cargo.lock` and `Cargo.toml`. - Updated the `executors` module to include `engine-eip7702-core` as a dependency. - Refactored execution options in `eip7702.rs` to support new execution models for EIP-7702. - Enhanced transaction handling in the `eip7702_executor` to accommodate new sender details and improve compatibility with existing job structures. - Updated webhook and job data structures to reflect changes in sender details and execution options. - Improved error handling and retry logic in job processing to enhance robustness. * clippy * Update dependencies and implement AWS KMS signing support - Updated `Cargo.lock` to reflect new versions for several dependencies, including `alloy-consensus`, `alloy-eips`, and AWS-related packages. - Introduced `AwsKmsCredential` for AWS KMS signing, allowing integration of AWS KMS into the signing process. - Enhanced `SigningCredential` enum to support AWS KMS credentials. - Implemented error handling for AWS KMS signing errors in the `EngineError` enum. - Updated user operation signing logic to utilize AWS KMS for signing user operations. - Refactored HTTP extractors to support AWS KMS credentials extraction from headers. These changes improve the integration of AWS KMS for signing operations, enhancing the overall functionality and security of the application. * Enhance AWS KMS integration and error handling - Updated AWS KMS credential extraction to use a more consistent header naming convention. - Improved error handling for retryable RPC errors in the EoaExecutorWorkerError, allowing for better job management. - Refactored AWS KMS credential validation to ensure proper extraction of key IDs and regions from ARNs. These changes aim to streamline AWS KMS operations and enhance the robustness of error handling in the executor worker. * Update dependencies and enhance signing capabilities - Updated `Cargo.lock` and `Cargo.toml` to reflect the new version `1.0.23` for the `alloy` package and added `serde_repr` as a dependency. - Introduced `PrivateKeySigner` for local signing in the `SigningCredential` enum, allowing for random private key generation for testing. - Enhanced the `AccountSigner` trait to support signing operations with the new `PrivateKey` variant. - Added 7702 tests for session keys as well as owner execution These changes improve the signing capabilities and facilitate testing with local private keys. * remove bad dbg
1 parent f79dea4 commit 55f3672

File tree

23 files changed

+2228
-229
lines changed

23 files changed

+2228
-229
lines changed

Cargo.lock

Lines changed: 629 additions & 98 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ members = [
1212
resolver = "2"
1313

1414
[workspace.dependencies]
15-
alloy = { version = "1.0.8" }
15+
alloy = { version = "1.0.23" }
1616
vault-types = { version = "0.1.0", git = "ssh://git@github.com/thirdweb-dev/vault.git", branch = "pb/update-alloy" }
1717
vault-sdk = { version = "0.1.0", git = "ssh://git@github.com/thirdweb-dev/vault.git", branch = "pb/update-alloy" }

aa-types/src/userop.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use alloy::{
22
core::sol_types::SolValue,
3-
primitives::{Address, B256, Bytes, ChainId, U256, keccak256},
3+
primitives::{Address, B256, Bytes, ChainId, U256, address, keccak256},
44
rpc::types::{PackedUserOperation, UserOperation},
55
};
66
use serde::{Deserialize, Serialize};
@@ -13,6 +13,9 @@ pub enum VersionedUserOp {
1313
V0_7(PackedUserOperation),
1414
}
1515

16+
pub const ENTRYPOINT_ADDRESS_V0_6: Address = address!("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"); // v0.6
17+
pub const ENTRYPOINT_ADDRESS_V0_7: Address = address!("0x0000000071727De22E5E9d8BAf0edAc6f37da032"); // v0.7
18+
1619
/// Error type for UserOp operations
1720
#[derive(
1821
Debug,
@@ -182,3 +185,34 @@ pub fn compute_user_op_v07_hash(
182185
let final_hash = keccak256(&outer_encoded);
183186
Ok(final_hash)
184187
}
188+
189+
impl VersionedUserOp {
190+
pub fn hash(&self, chain_id: ChainId) -> Result<B256, UserOpError> {
191+
match self {
192+
VersionedUserOp::V0_6(op) => {
193+
compute_user_op_v06_hash(op, ENTRYPOINT_ADDRESS_V0_6, chain_id)
194+
}
195+
VersionedUserOp::V0_7(op) => {
196+
compute_user_op_v07_hash(op, ENTRYPOINT_ADDRESS_V0_7, chain_id)
197+
}
198+
}
199+
}
200+
201+
pub fn hash_with_custom_entrypoint(
202+
&self,
203+
chain_id: ChainId,
204+
entrypoint: Address,
205+
) -> Result<B256, UserOpError> {
206+
match self {
207+
VersionedUserOp::V0_6(op) => compute_user_op_v06_hash(op, entrypoint, chain_id),
208+
VersionedUserOp::V0_7(op) => compute_user_op_v07_hash(op, entrypoint, chain_id),
209+
}
210+
}
211+
212+
pub fn default_entrypoint(&self) -> Address {
213+
match self {
214+
VersionedUserOp::V0_6(_) => ENTRYPOINT_ADDRESS_V0_6,
215+
VersionedUserOp::V0_7(_) => ENTRYPOINT_ADDRESS_V0_7,
216+
}
217+
}
218+
}

core/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@ thirdweb-core = { version = "0.1.0", path = "../thirdweb-core" }
1919
uuid = { version = "1.17.0", features = ["v4"] }
2020
utoipa = { version = "5.4.0", features = ["preserve_order"] }
2121
serde_with = "3.13.0"
22+
alloy-signer-aws = { version = "1.0.23", features = ["eip712"] }
23+
aws-config = "1.8.2"
24+
aws-sdk-kms = "1.79.0"
25+
aws-credential-types = "1.2.4"

core/src/constants.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,3 @@ pub const DEFAULT_FACTORY_ADDRESS_V0_6: Address =
1111

1212
pub const DEFAULT_IMPLEMENTATION_ADDRESS_V0_6: Address =
1313
address!("0xf22175c80c6e074C171811C59C6c0087e2a6a346");
14-
15-
pub const ENTRYPOINT_ADDRESS_V0_6: Address = address!("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"); // v0.6
16-
17-
pub const ENTRYPOINT_ADDRESS_V0_7: Address = address!("0x0000000071727De22E5E9d8BAf0edAc6f37da032"); // v0.7

core/src/credentials.rs

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,71 @@
1+
use alloy::primitives::ChainId;
2+
use alloy::signers::local::PrivateKeySigner;
3+
use alloy_signer_aws::AwsSigner;
4+
use aws_config::{BehaviorVersion, Region};
5+
use aws_credential_types::provider::future::ProvideCredentials as ProvideCredentialsFuture;
6+
use aws_sdk_kms::config::{Credentials, ProvideCredentials};
17
use serde::{Deserialize, Serialize};
28
use thirdweb_core::auth::ThirdwebAuth;
39
use thirdweb_core::iaw::AuthToken;
4-
use vault_types::enclave::auth::Auth;
10+
use vault_types::enclave::auth::Auth as VaultAuth;
11+
12+
use crate::error::EngineError;
13+
14+
impl SigningCredential {
15+
/// Create a random private key credential for testing
16+
pub fn random_local() -> Self {
17+
SigningCredential::PrivateKey(PrivateKeySigner::random())
18+
}
19+
}
520

621
#[derive(Debug, Clone, Serialize, Deserialize)]
722
pub enum SigningCredential {
8-
Vault(Auth),
9-
Iaw {
10-
auth_token: AuthToken,
11-
thirdweb_auth: ThirdwebAuth
23+
Vault(VaultAuth),
24+
Iaw {
25+
auth_token: AuthToken,
26+
thirdweb_auth: ThirdwebAuth,
1227
},
28+
AwsKms(AwsKmsCredential),
29+
/// Private key signer for testing and development
30+
/// Note: This should only be used in test environments
31+
#[serde(skip)]
32+
PrivateKey(PrivateKeySigner),
33+
}
34+
35+
#[derive(Debug, Clone, Serialize, Deserialize)]
36+
pub struct AwsKmsCredential {
37+
pub access_key_id: String,
38+
pub secret_access_key: String,
39+
pub key_id: String,
40+
pub region: String,
41+
}
42+
43+
impl ProvideCredentials for AwsKmsCredential {
44+
fn provide_credentials<'a>(&'a self) -> ProvideCredentialsFuture<'a>
45+
where
46+
Self: 'a,
47+
{
48+
let credentials = Credentials::new(
49+
self.access_key_id.clone(),
50+
self.secret_access_key.clone(),
51+
None,
52+
None,
53+
"engine-core",
54+
);
55+
ProvideCredentialsFuture::ready(Ok(credentials))
56+
}
57+
}
58+
59+
impl AwsKmsCredential {
60+
pub async fn get_signer(&self, chain_id: Option<ChainId>) -> Result<AwsSigner, EngineError> {
61+
let config = aws_config::defaults(BehaviorVersion::latest())
62+
.credentials_provider(self.clone())
63+
.region(Region::new(self.region.clone()))
64+
.load()
65+
.await;
66+
let client = aws_sdk_kms::Client::new(&config);
67+
68+
let signer = AwsSigner::new(client, self.key_id.clone(), chain_id).await?;
69+
Ok(signer)
70+
}
1371
}

core/src/error.rs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
use std::fmt::Debug;
2+
13
use crate::defs::AddressDef;
24
use alloy::{
35
primitives::Address,
46
transports::{
57
RpcError as AlloyRpcError, TransportErrorKind, http::reqwest::header::InvalidHeaderValue,
68
},
79
};
10+
use alloy_signer_aws::AwsSignerError;
11+
use aws_sdk_kms::error::SdkError;
812
use schemars::JsonSchema;
913
use serde::{Deserialize, Serialize};
1014
use thirdweb_core::error::ThirdwebError;
@@ -242,11 +246,150 @@ pub enum EngineError {
242246
#[error("Thirdweb error: {message}")]
243247
ThirdwebError { message: String },
244248

249+
#[schema(title = "AWS KMS Error")]
250+
#[error(transparent)]
251+
#[serde(rename_all = "camelCase")]
252+
AwsKmsSignerError {
253+
#[serde(flatten)]
254+
error: SerialisableAwsSignerError,
255+
},
256+
245257
#[schema(title = "Engine Internal Error")]
246258
#[error("Internal error: {message}")]
247259
InternalError { message: String },
248260
}
249261

262+
#[derive(thiserror::Error, Debug, Serialize, Clone, Deserialize, utoipa::ToSchema)]
263+
#[serde(rename_all = "SCREAMING_SNAKE_CASE", tag = "type")]
264+
pub enum SerialisableAwsSdkError {
265+
/// The request failed during construction. It was not dispatched over the network.
266+
#[error("Construction failure: {message}")]
267+
ConstructionFailure { message: String },
268+
269+
/// The request failed due to a timeout. The request MAY have been sent and received.
270+
#[error("Timeout error: {message}")]
271+
TimeoutError { message: String },
272+
273+
/// The request failed during dispatch. An HTTP response was not received. The request MAY
274+
/// have been sent.
275+
#[error("Dispatch failure: {message}")]
276+
DispatchFailure { message: String },
277+
278+
/// A response was received but it was not parseable according the the protocol (for example
279+
/// the server hung up without sending a complete response)
280+
#[error("Response error: {message}")]
281+
ResponseError { message: String },
282+
283+
/// An error response was received from the service
284+
#[error("Service error: {message}")]
285+
ServiceError { message: String },
286+
287+
#[error("Other error: {message}")]
288+
Other { message: String },
289+
}
290+
291+
#[derive(Error, Debug, Serialize, Clone, Deserialize, utoipa::ToSchema)]
292+
#[serde(rename_all = "SCREAMING_SNAKE_CASE", tag = "type")]
293+
pub enum SerialisableAwsSignerError {
294+
/// Thrown when the AWS KMS API returns a signing error.
295+
#[error(transparent)]
296+
Sign {
297+
aws_sdk_error: SerialisableAwsSdkError,
298+
},
299+
300+
/// Thrown when the AWS KMS API returns an error.
301+
#[error(transparent)]
302+
GetPublicKey {
303+
aws_sdk_error: SerialisableAwsSdkError,
304+
},
305+
306+
/// [`ecdsa`] error.
307+
#[error("ECDSA error: {message}")]
308+
K256 { message: String },
309+
310+
/// [`spki`] error.
311+
#[error("SPKI error: {message}")]
312+
Spki { message: String },
313+
314+
/// [`hex`](mod@hex) error.
315+
#[error("Hex error: {message}")]
316+
Hex { message: String },
317+
318+
/// Thrown when the AWS KMS API returns a response without a signature.
319+
#[error("signature not found in response")]
320+
SignatureNotFound,
321+
322+
/// Thrown when the AWS KMS API returns a response without a public key.
323+
#[error("public key not found in response")]
324+
PublicKeyNotFound,
325+
326+
#[error("Unknown error: {message}")]
327+
Unknown { message: String },
328+
}
329+
330+
impl<T: Debug> From<SdkError<T>> for SerialisableAwsSdkError {
331+
fn from(err: SdkError<T>) -> Self {
332+
match err {
333+
SdkError::ConstructionFailure(err) => SerialisableAwsSdkError::ConstructionFailure {
334+
message: format!("{:?}", err),
335+
},
336+
SdkError::TimeoutError(err) => SerialisableAwsSdkError::TimeoutError {
337+
message: format!("{:?}", err),
338+
},
339+
SdkError::DispatchFailure(err) => SerialisableAwsSdkError::DispatchFailure {
340+
message: format!("{:?}", err),
341+
},
342+
SdkError::ResponseError(err) => SerialisableAwsSdkError::ResponseError {
343+
message: format!("{:?}", err),
344+
},
345+
SdkError::ServiceError(err) => SerialisableAwsSdkError::ServiceError {
346+
message: format!("{:?}", err),
347+
},
348+
_ => SerialisableAwsSdkError::Other {
349+
message: format!("{:?}", err),
350+
},
351+
}
352+
}
353+
}
354+
355+
impl From<AwsSignerError> for EngineError {
356+
fn from(err: AwsSignerError) -> Self {
357+
match err {
358+
AwsSignerError::Sign(err) => EngineError::AwsKmsSignerError {
359+
error: SerialisableAwsSignerError::Sign {
360+
aws_sdk_error: err.into(),
361+
},
362+
},
363+
AwsSignerError::GetPublicKey(err) => EngineError::AwsKmsSignerError {
364+
error: SerialisableAwsSignerError::GetPublicKey {
365+
aws_sdk_error: err.into(),
366+
},
367+
},
368+
AwsSignerError::K256(err) => EngineError::AwsKmsSignerError {
369+
error: SerialisableAwsSignerError::K256 {
370+
message: err.to_string(),
371+
},
372+
},
373+
AwsSignerError::Spki(err) => EngineError::AwsKmsSignerError {
374+
error: SerialisableAwsSignerError::Spki {
375+
message: err.to_string(),
376+
},
377+
},
378+
AwsSignerError::Hex(err) => EngineError::AwsKmsSignerError {
379+
error: SerialisableAwsSignerError::Hex {
380+
message: err.to_string(),
381+
},
382+
},
383+
AwsSignerError::SignatureNotFound => EngineError::AwsKmsSignerError {
384+
error: SerialisableAwsSignerError::SignatureNotFound,
385+
},
386+
AwsSignerError::PublicKeyNotFound => EngineError::AwsKmsSignerError {
387+
error: SerialisableAwsSignerError::PublicKeyNotFound,
388+
},
389+
}
390+
}
391+
}
392+
250393
impl From<vault_sdk::error::VaultError> for EngineError {
251394
fn from(err: vault_sdk::error::VaultError) -> Self {
252395
let message = match &err {

core/src/execution_options/aa.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
use crate::{
2-
constants::{DEFAULT_FACTORY_ADDRESS_V0_6, ENTRYPOINT_ADDRESS_V0_6},
3-
defs::AddressDef,
4-
error::EngineError,
1+
use crate::{constants::DEFAULT_FACTORY_ADDRESS_V0_6, defs::AddressDef, error::EngineError};
2+
use alloy::{
3+
hex::FromHex,
4+
primitives::{Address, Bytes},
55
};
6-
use alloy::{hex::FromHex, primitives::{Address, Bytes}};
6+
use engine_aa_types::{ENTRYPOINT_ADDRESS_V0_6, ENTRYPOINT_ADDRESS_V0_7};
77
use schemars::JsonSchema;
88
use serde::{Deserialize, Deserializer, Serialize};
99

10-
use crate::constants::{DEFAULT_FACTORY_ADDRESS_V0_7, ENTRYPOINT_ADDRESS_V0_7};
10+
use crate::constants::DEFAULT_FACTORY_ADDRESS_V0_7;
1111

1212
#[derive(Deserialize, Serialize, Debug, JsonSchema, Clone, Copy, utoipa::ToSchema)]
1313
pub enum EntrypointVersion {

0 commit comments

Comments
 (0)