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

MLS Validation Service for SCW #1098

Merged
merged 34 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
2a06d7f
wip
codabrink Sep 26, 2024
4e44d80
Merge branch 'offsite/scw-libxmtp' into coda/mls-validation-service
codabrink Sep 26, 2024
7a372bb
wip
codabrink Sep 30, 2024
2c53796
patchwork
codabrink Sep 30, 2024
40e5729
a few fixes
codabrink Sep 30, 2024
937ea92
update to use scw verifier for inbox_ids
codabrink Sep 30, 2024
d4c85fc
Fix build errors
codabrink Oct 1, 2024
bd6222e
Merge branch 'offsite/scw-libxmtp' into coda/mls-validation-service
codabrink Oct 1, 2024
969dd49
fix signature
codabrink Oct 1, 2024
81b72cf
lint
codabrink Oct 1, 2024
00ab606
remove debug
codabrink Oct 1, 2024
ed8ad3f
lint
codabrink Oct 1, 2024
d073516
revert gen protos script
codabrink Oct 1, 2024
da7a3c4
restore tests too
codabrink Oct 1, 2024
ad674fd
feedback
codabrink Oct 1, 2024
67899fb
cleanup, feedback
codabrink Oct 1, 2024
8e9c489
cleanup
codabrink Oct 1, 2024
0e47839
cleanup
codabrink Oct 1, 2024
57009c7
hash
codabrink Oct 1, 2024
eb348f9
Actually do the work, and take the hash from the proto
codabrink Oct 1, 2024
ff5be08
cleanup
codabrink Oct 1, 2024
d2d45e4
more cleanup
codabrink Oct 2, 2024
7ebe3c2
not using error msg
codabrink Oct 2, 2024
749d82b
use latest proto
codabrink Oct 2, 2024
9d35205
wip
codabrink Oct 2, 2024
6cdd0cd
cleanup
codabrink Oct 2, 2024
bac6571
nicer error
codabrink Oct 2, 2024
10a7ae4
Merge remote-tracking branch 'origin/offsite/scw-libxmtp' into coda/m…
codabrink Oct 2, 2024
a82d2c1
revert
codabrink Oct 2, 2024
27030d6
unnecessary change
codabrink Oct 2, 2024
3fe479a
remove env logger
codabrink Oct 2, 2024
d667eb7
cleanup
codabrink Oct 2, 2024
14570fa
false in place of none
codabrink Oct 3, 2024
c57aa97
lint
codabrink Oct 3, 2024
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.

5 changes: 3 additions & 2 deletions bindings_ffi/src/mls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,17 +203,18 @@ impl FfiSignatureRequest {
pub async fn add_scw_signature(
&self,
signature_bytes: Vec<u8>,
address: String,
account_address: String,
chain_id: u64,
block_number: u64,
) -> Result<(), GenericError> {
let mut inner = self.inner.lock().await;
let account_id = AccountId::new_evm(chain_id, address);
let account_id = AccountId::new_evm(chain_id, account_address);

let signature = UnverifiedSignature::new_smart_contract_wallet(
signature_bytes,
account_id,
block_number,
chain_id,
);
inner
.add_signature(signature, self.scw_verifier.clone().as_ref())
Expand Down
1 change: 0 additions & 1 deletion bindings_node/src/groups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ pub struct NapiGroupMember {
pub permission_level: NapiPermissionLevel,
}

#[derive(Debug)]
#[napi]
pub struct NapiGroup {
inner_client: Arc<RustXmtpClient>,
Expand Down
3 changes: 1 addition & 2 deletions bindings_wasm/src/mls_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,6 @@ impl WasmClient {
signature_bytes: Uint8Array,
chain_id: u64,
account_address: String,
// TODO:nm Remove this
_chain_rpc_url: String,
block_number: u64,
) -> Result<(), JsError> {
if self.is_registered() {
Expand All @@ -180,6 +178,7 @@ impl WasmClient {
signature_bytes.to_vec(),
account_id,
block_number,
chain_id,
);

self.signatures.insert(
Expand Down
7 changes: 3 additions & 4 deletions mls_validation_service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ path = "src/main.rs"
clap = { version = "4.4.6", features = ["derive"] }
ed25519-dalek = { workspace = true, features = ["digest"] }
env_logger = "0.11"
codabrink marked this conversation as resolved.
Show resolved Hide resolved
ethers = { workspace = true }
futures = { workspace = true }
hex = { workspace = true }
log = { workspace = true }
Expand All @@ -23,12 +24,10 @@ thiserror.workspace = true
tokio = { workspace = true, features = ["full"] }
tonic = { workspace = true }
warp = "0.3.6"
xmtp_cryptography = { path = "../xmtp_cryptography" }
xmtp_id.workspace = true
xmtp_mls.workspace = true
xmtp_proto = { path = "../xmtp_proto", features = [
"proto_full",
"convert",
] }
xmtp_proto = { path = "../xmtp_proto", features = ["proto_full", "convert"] }

[dev-dependencies]
anyhow.workspace = true
Expand Down
80 changes: 67 additions & 13 deletions mls_validation_service/src/handlers.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use ethers::types::{BlockNumber, U64};
use futures::future::{join_all, try_join_all};
use openmls::prelude::{tls_codec::Deserialize, MlsMessageIn, ProtocolMessage};
use openmls_rust_crypto::RustCrypto;
use tonic::{Request, Response, Status};

use xmtp_cryptography::hash::sha256_bytes;
use xmtp_id::{
associations::{
self, try_map_vec, unverified::UnverifiedIdentityUpdate, AssociationError,
self, try_map_vec, unverified::UnverifiedIdentityUpdate, AccountId, AssociationError,
DeserializationError, SignatureError,
},
scw_verifier::SmartContractSignatureVerifier,
Expand All @@ -15,13 +17,15 @@ use xmtp_mls::{
verified_key_package_v2::{KeyPackageVerificationError, VerifiedKeyPackageV2},
};
use xmtp_proto::xmtp::{
identity::associations::IdentityUpdate as IdentityUpdateProto,
identity::associations::{IdentityUpdate as IdentityUpdateProto, SmartContractWalletSignature},
mls_validation::v1::{
validate_group_messages_response::ValidationResponse as ValidateGroupMessageValidationResponse,
validate_inbox_id_key_packages_response::Response as ValidateInboxIdKeyPackageResponse,
validation_api_server::ValidationApi, GetAssociationStateRequest,
GetAssociationStateResponse, ValidateGroupMessagesRequest, ValidateGroupMessagesResponse,
ValidateInboxIdKeyPackagesRequest, ValidateInboxIdKeyPackagesResponse,
ValidateInboxIdKeyPackagesResponse, ValidateInboxIdsRequest, ValidateInboxIdsResponse,
ValidateKeyPackagesRequest, ValidateKeyPackagesResponse,
VerifySmartContractWalletSignaturesRequest, VerifySmartContractWalletSignaturesResponse,
},
};

Expand Down Expand Up @@ -55,6 +59,22 @@ impl ValidationService {

#[tonic::async_trait]
impl ValidationApi for ValidationService {
async fn validate_inbox_ids(
&self,
_request: tonic::Request<ValidateInboxIdsRequest>,
) -> Result<tonic::Response<ValidateInboxIdsResponse>, tonic::Status> {
// Stubbed for v2 nodes
unimplemented!()
}

async fn validate_key_packages(
&self,
_request: tonic::Request<ValidateKeyPackagesRequest>,
) -> std::result::Result<tonic::Response<ValidateKeyPackagesResponse>, tonic::Status> {
// Stubbed out for v2 nodes
unimplemented!()
}

async fn validate_group_messages(
&self,
request: Request<ValidateGroupMessagesRequest>,
Expand Down Expand Up @@ -99,19 +119,28 @@ impl ValidationApi for ValidationService {
.map_err(Into::into)
}

async fn verify_smart_contract_wallet_signatures(
&self,
request: Request<VerifySmartContractWalletSignaturesRequest>,
) -> Result<Response<VerifySmartContractWalletSignaturesResponse>, Status> {
let VerifySmartContractWalletSignaturesRequest { signatures } = request.into_inner();

verify_smart_contract_wallet_signatures(signatures, self.scw_verifier.as_ref()).await
}

async fn validate_inbox_id_key_packages(
&self,
request: Request<ValidateInboxIdKeyPackagesRequest>,
request: tonic::Request<ValidateKeyPackagesRequest>,
) -> Result<Response<ValidateInboxIdKeyPackagesResponse>, Status> {
let ValidateInboxIdKeyPackagesRequest { key_packages } = request.into_inner();
let ValidateKeyPackagesRequest { key_packages } = request.into_inner();

let responses: Vec<_> = key_packages
.into_iter()
.map(|k| k.key_package_bytes_tls_serialized)
.map(validate_inbox_id_key_package)
.collect();

let responses: Vec<ValidateInboxIdKeyPackageResponse> = join_all(responses)
let responses: Vec<_> = join_all(responses)
.await
.into_iter()
.map(|res| res.map_err(ValidateInboxIdKeyPackageResponse::from))
Expand Down Expand Up @@ -158,6 +187,30 @@ async fn validate_inbox_id_key_package(
})
}

async fn verify_smart_contract_wallet_signatures(
signatures: Vec<SmartContractWalletSignature>,
scw_verifier: &dyn SmartContractSignatureVerifier,
) -> Result<Response<VerifySmartContractWalletSignaturesResponse>, Status> {
let mut futures = vec![];

for sig in signatures {
let hash = sha256_bytes(&sig.signature)
codabrink marked this conversation as resolved.
Show resolved Hide resolved
.try_into()
.expect("Sha256 should be 32 bytes");

futures.push(scw_verifier.is_valid_signature(
AccountId::new_evm(sig.chain_id, sig.account_id),
hash,
sig.signature.into(),
Some(BlockNumber::Number(U64::from(sig.block_number))),
));
}

Ok(Response::new(VerifySmartContractWalletSignaturesResponse {
responses: vec![],
}))
}

async fn get_association_state(
old_updates: Vec<IdentityUpdateProto>,
new_updates: Vec<IdentityUpdateProto>,
Expand All @@ -169,13 +222,13 @@ async fn get_association_state(
let old_updates = try_join_all(
old_unverified_updates
.iter()
.map(|u| async { u.to_verified(scw_verifier).await }),
.map(|u| u.to_verified(scw_verifier)),
)
.await?;
let new_updates = try_join_all(
new_unverified_updates
.iter()
.map(|u| async { u.to_verified(scw_verifier).await }),
.map(|u| u.to_verified(scw_verifier)),
)
.await?;
if old_updates.is_empty() {
Expand Down Expand Up @@ -234,10 +287,11 @@ mod tests {
unverified::{UnverifiedAction, UnverifiedIdentityUpdate},
};
use xmtp_mls::configuration::CIPHERSUITE;
use xmtp_proto::xmtp::identity::{
associations::IdentityUpdate as IdentityUpdateProto, MlsCredential as InboxIdMlsCredential,
use xmtp_proto::xmtp::{
identity::associations::IdentityUpdate as IdentityUpdateProto,
identity::MlsCredential as InboxIdMlsCredential,
mls_validation::v1::validate_key_packages_request::KeyPackage as KeyPackageProtoWrapper,
};
use xmtp_proto::xmtp::mls_validation::v1::validate_inbox_id_key_packages_request::KeyPackage as KeyPackageProtoWrapper;

use super::*;

Expand Down Expand Up @@ -344,7 +398,7 @@ mod tests {
};

let key_package_bytes = build_key_package_bytes(&keypair, &credential_with_key, None);
let request = ValidateInboxIdKeyPackagesRequest {
let request = ValidateKeyPackagesRequest {
key_packages: vec![KeyPackageProtoWrapper {
key_package_bytes_tls_serialized: key_package_bytes,
is_inbox_id_credential: false,
Expand Down Expand Up @@ -385,7 +439,7 @@ mod tests {
};

let key_package_bytes = build_key_package_bytes(&keypair, &credential_with_key, None);
let request = ValidateInboxIdKeyPackagesRequest {
let request = ValidateKeyPackagesRequest {
key_packages: vec![KeyPackageProtoWrapper {
key_package_bytes_tls_serialized: key_package_bytes,
is_inbox_id_credential: false,
Expand Down
8 changes: 6 additions & 2 deletions xmtp_id/src/associations/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ pub enum DeserializationError {
Decode(#[from] DecodeError),
#[error("Invalid account id")]
InvalidAccountId,
#[error("Invalid hash (needs to be 32 bytes)")]
InvalidHash,
}

impl TryFrom<IdentityUpdateProto> for UnverifiedIdentityUpdate {
Expand Down Expand Up @@ -173,6 +175,7 @@ impl TryFrom<SignatureWrapperProto> for UnverifiedSignature {
sig.signature,
sig.account_id.try_into()?,
sig.block_number,
sig.chain_id,
),
),
};
Expand Down Expand Up @@ -257,8 +260,9 @@ impl From<UnverifiedSignature> for SignatureWrapperProto {
account_id: sig.account_id.into(),
block_number: sig.block_number,
signature: sig.signature_bytes,
// TOOD:nm Remove this field altogether
chain_rpc_url: "".to_string(),
// Ethereum
chain_id: sig.chain_id,
hash: sig.hash.to_vec(),
})
}
UnverifiedSignature::InstallationKey(sig) => {
Expand Down
2 changes: 1 addition & 1 deletion xmtp_id/src/associations/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ impl SmartContractSignatureVerifier for MockSmartContractSignatureVerifier {
&self,
_account_id: AccountId,
_hash: [u8; 32],
_signature: &Bytes,
_signature: Bytes,
_block_number: Option<BlockNumber>,
) -> Result<bool, VerifierError> {
Ok(self.is_valid_signature)
Expand Down
20 changes: 19 additions & 1 deletion xmtp_id/src/associations/unverified.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ use super::{
AccountId, Action, AddAssociation, CreateInbox, IdentityUpdate, RevokeAssociation,
SignatureError,
};
use ethers::{abi::AbiEncode, types::H256};
use futures::future::try_join_all;
use sha2::Digest;
use xmtp_cryptography::hash::sha256_bytes;
use xmtp_proto::xmtp::message_contents::SignedPublicKey as LegacySignedPublicKeyProto;

#[derive(Debug, Clone, PartialEq)]
Expand Down Expand Up @@ -294,11 +297,13 @@ impl UnverifiedSignature {
signature: Vec<u8>,
account_id: AccountId,
block_number: u64,
chain_id: u64,
) -> Self {
Self::SmartContractWallet(UnverifiedSmartContractWalletSignature::new(
signature,
account_id,
block_number,
chain_id,
codabrink marked this conversation as resolved.
Show resolved Hide resolved
))
}

Expand Down Expand Up @@ -344,14 +349,27 @@ pub struct UnverifiedSmartContractWalletSignature {
pub(crate) signature_bytes: Vec<u8>,
pub(crate) account_id: AccountId,
pub(crate) block_number: u64,
pub(crate) chain_id: u64,
pub(crate) hash: [u8; 32],
}

impl UnverifiedSmartContractWalletSignature {
pub fn new(signature_bytes: Vec<u8>, account_id: AccountId, block_number: u64) -> Self {
pub fn new(
signature_bytes: Vec<u8>,
account_id: AccountId,
block_number: u64,
chain_id: u64,
) -> Self {
let hash = sha256_bytes(&signature_bytes)
.try_into()
.expect("SHA256 hash should be 32 bytes");

Self {
signature_bytes,
account_id,
block_number,
chain_id,
hash,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion xmtp_id/src/associations/verified_signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ impl VerifiedSignature {
.is_valid_signature(
account_id.clone(),
hash_message(signature_text.as_ref()).into(),
&signature_bytes.to_vec().into(),
signature_bytes.to_vec().into(),
Some(BlockNumber::Number(U64::from(block_number))),
)
.await?;
Expand Down
Loading
Loading