Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remote signature verifier #1119

Merged
merged 10 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ version = "0.0.1"
anyhow = "1.0"
async-stream = "0.3"
async-trait = "0.1.77"
trait-variant = "0.1.2"
chrono = "0.4.38"
ctor = "0.2"
ed25519 = "2.2.3"
Expand Down Expand Up @@ -56,6 +55,7 @@ tokio = { version = "1.35.1", default-features = false }
tonic = "^0.12"
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = "0.3"
trait-variant = "0.1.2"
url = "2.5.0"

# Internal Crate Dependencies
Expand Down
1 change: 1 addition & 0 deletions bindings_ffi/Cargo.lock

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

26 changes: 13 additions & 13 deletions bindings_ffi/src/mls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ use std::convert::TryInto;
use std::sync::Arc;
use tokio::{sync::Mutex, task::AbortHandle};
use xmtp_api_grpc::grpc_api_helper::Client as TonicApiClient;
use xmtp_id::associations::unverified::NewUnverifiedSmartContractWalletSignature;
use xmtp_id::associations::unverified::UnverifiedSignature;
use xmtp_id::associations::AccountId;
use xmtp_id::associations::AssociationState;
use xmtp_id::associations::MemberIdentifier;
use xmtp_id::scw_verifier::RemoteSignatureVerifier;
use xmtp_id::scw_verifier::SmartContractSignatureVerifier;
use xmtp_id::{
associations::{builder::SignatureRequest, generate_inbox_id as xmtp_id_generate_inbox_id},
Expand Down Expand Up @@ -124,19 +126,23 @@ pub async fn create_client(
legacy_signed_private_key_proto,
);

let scw_verifier = RemoteSignatureVerifier::new(api_client.identity_client().clone());

let xmtp_client: RustXmtpClient = match history_sync_url {
Some(url) => {
ClientBuilder::new(identity_strategy)
.api_client(api_client)
.store(store)
.history_sync_url(&url)
.scw_signature_verifier(scw_verifier)
.build()
.await?
}
None => {
ClientBuilder::new(identity_strategy)
.api_client(api_client)
.store(store)
.scw_signature_verifier(scw_verifier)
.build()
.await?
}
Expand Down Expand Up @@ -211,25 +217,19 @@ impl FfiSignatureRequest {
let mut inner = self.inner.lock().await;
let account_id = AccountId::new_evm(chain_id, address);

let block_number = match block_number {
Some(bn) => bn,
None => {
self.scw_verifier
.current_block_number(&chain_id.to_string())
.await
.map_err(GenericError::Verifier)?
.0[0]
}
};

let signature = UnverifiedSignature::new_smart_contract_wallet(
let new_signature = NewUnverifiedSmartContractWalletSignature::new(
signature_bytes,
account_id,
block_number,
);

inner
.add_signature(signature, self.scw_verifier.clone().as_ref())
.add_new_unverified_smart_contract_signature(
new_signature,
self.scw_verifier.clone().as_ref(),
)
.await?;

Ok(())
}

Expand Down
1 change: 1 addition & 0 deletions bindings_node/Cargo.lock

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

5 changes: 5 additions & 0 deletions bindings_node/src/mls_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use xmtp_cryptography::signature::ed25519_public_key_to_address;
use xmtp_id::associations::builder::SignatureRequest;
use xmtp_id::associations::generate_inbox_id as xmtp_id_generate_inbox_id;
use xmtp_id::associations::unverified::UnverifiedSignature;
use xmtp_id::scw_verifier::RemoteSignatureVerifier;
use xmtp_mls::api::ApiClientWrapper;
use xmtp_mls::builder::ClientBuilder;
use xmtp_mls::identity::IdentityStrategy;
Expand Down Expand Up @@ -53,6 +54,8 @@ pub async fn create_client(
.await
.map_err(|_| Error::from_reason("Error creating Tonic API client"))?;

let scw_verifier = RemoteSignatureVerifier::new(api_client.identity_client().clone());

let storage_option = StorageOption::Persistent(db_path);

let store = match encryption_key {
Expand Down Expand Up @@ -81,12 +84,14 @@ pub async fn create_client(
.api_client(api_client)
.store(store)
.history_sync_url(&url)
.scw_signature_verifier(scw_verifier)
.build()
.await
.map_err(ErrorWrapper::from)?,
None => ClientBuilder::new(identity_strategy)
.api_client(api_client)
.store(store)
.scw_signature_verifier(scw_verifier)
.build()
.await
.map_err(ErrorWrapper::from)?,
Expand Down
33 changes: 17 additions & 16 deletions mls_validation_service/src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use xmtp_id::{
self, try_map_vec, unverified::UnverifiedIdentityUpdate, AssociationError,
DeserializationError, SignatureError,
},
scw_verifier::SmartContractSignatureVerifier,
scw_verifier::{SmartContractSignatureVerifier, ValidationResponse},
};
use xmtp_mls::{
utils::id::serialize_group_id,
Expand All @@ -18,8 +18,9 @@ use xmtp_mls::{
use xmtp_proto::xmtp::{
identity::{
api::v1::{
verify_smart_contract_wallet_signatures_response::ValidationResponse as VerifySmartContractWalletSignaturesResponseValidationResponse,
UnverifiedSmartContractWalletSignature, VerifySmartContractWalletSignaturesRequest,
verify_smart_contract_wallet_signatures_response::ValidationResponse as VerifySmartContractWalletSignaturesValidationResponse,
VerifySmartContractWalletSignatureRequestSignature,
VerifySmartContractWalletSignaturesRequest,
VerifySmartContractWalletSignaturesResponse,
},
associations::IdentityUpdate as IdentityUpdateProto,
Expand Down Expand Up @@ -199,33 +200,31 @@ async fn validate_inbox_id_key_package(
}

async fn verify_smart_contract_wallet_signatures(
signatures: Vec<UnverifiedSmartContractWalletSignature>,
signatures: Vec<VerifySmartContractWalletSignatureRequestSignature>,
scw_verifier: &dyn SmartContractSignatureVerifier,
) -> Result<Response<VerifySmartContractWalletSignaturesResponse>, Status> {
let mut responses = vec![];
for request in signatures {
for signature in signatures {
let handle = async move {
let Some(signature) = request.scw_signature else {
return Ok::<bool, GrpcServerError>(false);
};

let account_id = signature.account_id.try_into().map_err(|_e| {
GrpcServerError::Deserialization(DeserializationError::InvalidAccountId)
})?;

let valid = scw_verifier
let response = scw_verifier
.is_valid_signature(
account_id,
request.hash.try_into().map_err(|_| {
signature.hash.try_into().map_err(|_| {
GrpcServerError::Deserialization(DeserializationError::InvalidHash)
})?,
signature.signature.into(),
Some(BlockNumber::Number(U64::from(signature.block_number))),
signature
.block_number
.map(|bn| BlockNumber::Number(U64::from(bn))),
)
.await
.map_err(|e| GrpcServerError::Signature(SignatureError::VerifierError(e)))?;

Ok(valid)
Ok::<ValidationResponse, GrpcServerError>(response)
};

responses.push(handle);
Expand All @@ -235,12 +234,14 @@ async fn verify_smart_contract_wallet_signatures(
.await
.into_iter()
.map(|result| match result {
Err(err) => VerifySmartContractWalletSignaturesResponseValidationResponse {
Err(err) => VerifySmartContractWalletSignaturesValidationResponse {
is_valid: false,
block_number: None,
error: Some(format!("{err:?}")),
},
Ok(is_valid) => VerifySmartContractWalletSignaturesResponseValidationResponse {
is_valid,
Ok(response) => VerifySmartContractWalletSignaturesValidationResponse {
is_valid: response.is_valid,
block_number: response.block_number,
error: None,
},
})
Expand Down
5 changes: 2 additions & 3 deletions mls_validation_service/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use tokio::signal::unix::{signal, SignalKind};
use tonic::transport::Server;

use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt as _, EnvFilter};
use xmtp_id::scw_verifier::RpcSmartContractWalletVerifier;
use xmtp_id::scw_verifier::MultiSmartContractSignatureVerifier;
use xmtp_proto::xmtp::mls_validation::v1::validation_api_server::ValidationApiServer;

#[macro_use]
Expand All @@ -30,8 +30,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

let health_server = health_check_server(args.health_check_port as u16);

// TODO:nm replace with real verifier
let scw_verifier = RpcSmartContractWalletVerifier::new("http://fixme.com".to_string());
let scw_verifier = MultiSmartContractSignatureVerifier::new_from_file("chain_urls.json");

let grpc_server = Server::builder()
.add_service(ValidationApiServer::new(ValidationService::new(
Expand Down
4 changes: 4 additions & 0 deletions xmtp_api_grpc/src/grpc_api_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ impl Client {

req
}

pub fn identity_client(&self) -> &ProtoIdentityApiClient<Channel> {
&self.identity_client
}
}

impl ClientWithMetadata for Client {
Expand Down
5 changes: 3 additions & 2 deletions xmtp_id/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ version.workspace = true
[dependencies]
async-trait.workspace = true
chrono.workspace = true
dyn-clone = "1"
ed25519-dalek = { workspace = true, features = ["digest"] }
ethers.workspace = true
futures.workspace = true
hex.workspace = true
tracing.workspace = true
openmls_traits.workspace = true
prost.workspace = true
rand.workspace = true
Expand All @@ -21,10 +21,11 @@ serde_json.workspace = true
sha2.workspace = true
thiserror.workspace = true
tokio = { workspace = true, features = ["macros"] }
tonic.workspace = true
tracing.workspace = true
url = { workspace = true, features = ["serde"] }
xmtp_cryptography.workspace = true
xmtp_proto = { workspace = true, features = ["proto_full"] }
dyn-clone = "1"

[dev-dependencies]
ctor = "0.2.5"
Expand Down
55 changes: 49 additions & 6 deletions xmtp_id/src/associations/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ use super::{
UnsignedRevokeAssociation,
},
unverified::{
UnverifiedAction, UnverifiedAddAssociation, UnverifiedChangeRecoveryAddress,
UnverifiedCreateInbox, UnverifiedIdentityUpdate, UnverifiedRevokeAssociation,
UnverifiedSignature,
NewUnverifiedSmartContractWalletSignature, UnverifiedAction, UnverifiedAddAssociation,
UnverifiedChangeRecoveryAddress, UnverifiedCreateInbox, UnverifiedIdentityUpdate,
UnverifiedRevokeAssociation, UnverifiedSignature, UnverifiedSmartContractWalletSignature,
},
verified_signature::VerifiedSignature,
MemberIdentifier, MemberKind, SignatureError,
};

Expand Down Expand Up @@ -163,6 +164,8 @@ pub enum SignatureRequestError {
MissingSigner,
#[error("Signature error {0}")]
Signature(#[from] SignatureError),
#[error("Unable to get block number")]
BlockNumber,
}

/// A signature request is meant to be sent over the FFI barrier (wrapped in a mutex) to platform SDKs.
Expand Down Expand Up @@ -220,15 +223,55 @@ impl SignatureRequest {
.collect()
}

/// Often the front-end doesn't know the current block number when adding a smart contract.
/// This is for when you want to add a smart-contract wallet,
/// and need the verifier to populate the latest block number for you.
pub async fn add_new_unverified_smart_contract_signature(
&mut self,
mut signature: NewUnverifiedSmartContractWalletSignature,
scw_verifier: &dyn SmartContractSignatureVerifier,
) -> Result<(), SignatureRequestError> {
let verified_signature = VerifiedSignature::from_smart_contract_wallet(
&self.signature_text,
scw_verifier,
&signature.signature_bytes,
signature.account_id.clone(),
&mut signature.block_number,
)
.await?;

let Some(block_number) = signature.block_number else {
return Err(SignatureRequestError::BlockNumber);
};

self.add_verified_signature(
UnverifiedSignature::SmartContractWallet(UnverifiedSmartContractWalletSignature {
account_id: signature.account_id,
block_number,
signature_bytes: signature.signature_bytes,
}),
verified_signature,
)
}

pub async fn add_signature(
&mut self,
signature: UnverifiedSignature,
scw_verifier: &dyn SmartContractSignatureVerifier,
) -> Result<(), SignatureRequestError> {
let verified_sig = signature
let verified_signature = signature
.to_verified(self.signature_text.clone(), scw_verifier)
.await?;
let signer_identity = &verified_sig.signer;

self.add_verified_signature(signature, verified_signature)
}

fn add_verified_signature(
&mut self,
signature: UnverifiedSignature,
verified_signature: VerifiedSignature,
) -> Result<(), SignatureRequestError> {
let signer_identity = &verified_signature.signer;

let missing_signatures = self.missing_signatures();
tracing::info!("Provided Signer: {}", signer_identity);
Expand All @@ -239,7 +282,7 @@ impl SignatureRequest {
return Err(SignatureRequestError::UnknownSigner);
}

self.signatures.insert(verified_sig.signer, signature);
self.signatures.insert(verified_signature.signer, signature);

Ok(())
}
Expand Down
Loading