Skip to content

Commit b2d51f1

Browse files
committed
Merge branch '04-07-identityupdate_serialization' of github.com:xmtp/libxmtp into 04-07-identityupdate_serialization
1 parent 78b417e commit b2d51f1

15 files changed

+1271
-800
lines changed

dev/gen_protos.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ if ! cargo install --list | grep "protoc-gen-prost-crate" > /dev/null; then
66
fi
77
fi
88

9-
if ! buf generate https://github.com/xmtp/proto.git#branch=nm/prototype-identity-apis,subdir=proto; then
9+
if ! buf generate https://github.com/xmtp/proto.git#branch=main,subdir=proto; then
1010
echo "Failed to generate protobuf definitions"
1111
exit 1
1212
fi

xmtp_id/Cargo.toml

+12-13
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,26 @@
11
[package]
2+
edition = "2021"
23
name = "xmtp_id"
34
version = "0.1.0"
4-
edition = "2021"
55

66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

88
[dependencies]
9+
async-trait.workspace = true
10+
chrono.workspace = true
11+
futures.workspace = true
12+
hex.workspace = true
913
log.workspace = true
10-
tracing.workspace = true
11-
thiserror.workspace = true
12-
xmtp_cryptography.workspace = true
13-
xmtp_mls.workspace = true
14-
xmtp_proto.workspace = true
15-
openmls_traits.workspace = true
1614
openmls.workspace = true
1715
openmls_basic_credential.workspace = true
1816
openmls_rust_crypto.workspace = true
17+
openmls_traits.workspace = true
1918
prost.workspace = true
20-
chrono.workspace = true
19+
rand.workspace = true
2120
serde.workspace = true
22-
async-trait.workspace = true
23-
futures.workspace = true
2421
sha2 = "0.10.8"
25-
rand.workspace = true
26-
hex.workspace = true
27-
22+
thiserror.workspace = true
23+
tracing.workspace = true
24+
xmtp_cryptography.workspace = true
25+
xmtp_mls.workspace = true
26+
xmtp_proto.workspace = true

xmtp_id/src/associations/association_log.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
use super::hashes::generate_inbox_id;
22
use super::member::{Member, MemberIdentifier, MemberKind};
3+
use super::serialization::{
4+
from_identity_update_proto, to_identity_update_proto, DeserializationError, SerializationError,
5+
};
36
use super::signature::{Signature, SignatureError, SignatureKind};
47
use super::state::AssociationState;
58

69
use thiserror::Error;
10+
use xmtp_proto::xmtp::identity::associations::IdentityUpdate as IdentityUpdateProto;
711

812
#[derive(Debug, Error, PartialEq)]
913
pub enum AssociationError {
@@ -23,6 +27,8 @@ pub enum AssociationError {
2327
LegacySignatureReuse,
2428
#[error("The new member identifier does not match the signer")]
2529
NewMemberIdSignatureMismatch,
30+
#[error("Wrong inbox_id specified on association")]
31+
WrongInboxId,
2632
#[error("Signature not allowed for role {0:?} {1:?}")]
2733
SignatureNotAllowed(String, String),
2834
#[error("Replay detected")]
@@ -303,17 +309,27 @@ impl IdentityAction for Action {
303309

304310
/// An `IdentityUpdate` contains one or more Actions that can be applied to the AssociationState
305311
pub struct IdentityUpdate {
312+
pub inbox_id: String,
306313
pub client_timestamp_ns: u64,
307314
pub actions: Vec<Action>,
308315
}
309316

310317
impl IdentityUpdate {
311-
pub fn new(actions: Vec<Action>, client_timestamp_ns: u64) -> Self {
318+
pub fn new(actions: Vec<Action>, inbox_id: String, client_timestamp_ns: u64) -> Self {
312319
Self {
320+
inbox_id,
313321
actions,
314322
client_timestamp_ns,
315323
}
316324
}
325+
326+
pub fn to_proto(&self) -> Result<IdentityUpdateProto, SerializationError> {
327+
to_identity_update_proto(self)
328+
}
329+
330+
pub fn from_proto(proto: IdentityUpdateProto) -> Result<Self, DeserializationError> {
331+
from_identity_update_proto(proto)
332+
}
317333
}
318334

319335
impl IdentityAction for IdentityUpdate {
@@ -327,6 +343,9 @@ impl IdentityAction for IdentityUpdate {
327343
}
328344

329345
let new_state = state.ok_or(AssociationError::NotCreated)?;
346+
if new_state.inbox_id().ne(&self.inbox_id) {
347+
return Err(AssociationError::WrongInboxId);
348+
}
330349

331350
// After all the updates in the LogEntry have been processed, add the list of signatures to the state
332351
// so that the signatures can not be re-used in subsequent updates

xmtp_id/src/associations/builder.rs

+44-26
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,8 @@ use super::{
1313
Action, IdentityUpdate, MemberIdentifier, Signature, SignatureError,
1414
};
1515

16-
#[derive(Error, Debug)]
17-
pub enum IdentityBuilderError {
18-
#[error("Missing signer")]
19-
MissingSigner,
20-
}
21-
16+
/// The SignatureField is used to map the signatures from a [SignatureRequest] back to the correct
17+
/// field in an [IdentityUpdate]. It is used in the `pending_signatures` map in a [PendingIdentityAction]
2218
#[derive(Clone, PartialEq, Hash, Eq)]
2319
enum SignatureField {
2420
InitialAddress,
@@ -33,13 +29,17 @@ pub struct PendingIdentityAction {
3329
pending_signatures: HashMap<SignatureField, MemberIdentifier>,
3430
}
3531

36-
pub struct IdentityUpdateBuilder {
32+
/// The SignatureRequestBuilder is used to collect all of the actions in
33+
/// an IdentityUpdate, but without the signatures.
34+
/// It outputs a SignatureRequest, which can then collect the relevant signatures and be turned into
35+
/// an IdentityUpdate.
36+
pub struct SignatureRequestBuilder {
3737
inbox_id: String,
3838
client_timestamp_ns: u64,
3939
actions: Vec<PendingIdentityAction>,
4040
}
4141

42-
impl IdentityUpdateBuilder {
42+
impl SignatureRequestBuilder {
4343
/// Create a new IdentityUpdateBuilder for the given `inbox_id`
4444
pub fn new(inbox_id: String) -> Self {
4545
Self {
@@ -76,7 +76,6 @@ impl IdentityUpdateBuilder {
7676
self.actions.push(PendingIdentityAction {
7777
unsigned_action: UnsignedAction::AddAssociation(UnsignedAddAssociation {
7878
new_member_identifier: new_member_identifier.clone(),
79-
inbox_id: self.inbox_id.clone(),
8079
}),
8180
pending_signatures: HashMap::from([
8281
(
@@ -101,7 +100,6 @@ impl IdentityUpdateBuilder {
101100
recovery_address_identifier.clone(),
102101
)]),
103102
unsigned_action: UnsignedAction::RevokeAssociation(UnsignedRevokeAssociation {
104-
inbox_id: self.inbox_id.clone(),
105103
revoked_member,
106104
}),
107105
});
@@ -120,24 +118,32 @@ impl IdentityUpdateBuilder {
120118
recovery_address_identifier.clone(),
121119
)]),
122120
unsigned_action: UnsignedAction::ChangeRecoveryAddress(UnsignedChangeRecoveryAddress {
123-
inbox_id: self.inbox_id.clone(),
124121
new_recovery_address,
125122
}),
126123
});
127124

128125
self
129126
}
130127

131-
pub fn to_signature_request(self) -> SignatureRequest {
128+
pub fn build(self) -> SignatureRequest {
132129
let unsigned_actions: Vec<UnsignedAction> = self
133130
.actions
134131
.iter()
135132
.map(|pending_action| pending_action.unsigned_action.clone())
136133
.collect();
137134

138-
let signature_text = get_signature_text(unsigned_actions, self.client_timestamp_ns);
135+
let signature_text = get_signature_text(
136+
unsigned_actions,
137+
self.inbox_id.clone(),
138+
self.client_timestamp_ns,
139+
);
139140

140-
SignatureRequest::new(self.actions, signature_text, self.client_timestamp_ns)
141+
SignatureRequest::new(
142+
self.actions,
143+
signature_text,
144+
self.inbox_id,
145+
self.client_timestamp_ns,
146+
)
141147
}
142148
}
143149

@@ -161,15 +167,18 @@ pub struct SignatureRequest {
161167
signature_text: String,
162168
signatures: HashMap<MemberIdentifier, Box<dyn Signature>>,
163169
client_timestamp_ns: u64,
170+
inbox_id: String,
164171
}
165172

166173
impl SignatureRequest {
167174
pub fn new(
168175
pending_actions: Vec<PendingIdentityAction>,
169176
signature_text: String,
177+
inbox_id: String,
170178
client_timestamp_ns: u64,
171179
) -> Self {
172180
Self {
181+
inbox_id,
173182
pending_actions,
174183
signature_text,
175184
signatures: HashMap::new(),
@@ -220,7 +229,7 @@ impl SignatureRequest {
220229
self.signature_text.clone()
221230
}
222231

223-
pub fn build_identity_update(&self) -> Result<IdentityUpdate, SignatureRequestError> {
232+
pub fn build_identity_update(self) -> Result<IdentityUpdate, SignatureRequestError> {
224233
if !self.is_ready() {
225234
return Err(SignatureRequestError::MissingSigner);
226235
}
@@ -232,7 +241,11 @@ impl SignatureRequest {
232241
.map(|pending_action| build_action(pending_action, &self.signatures))
233242
.collect::<Result<Vec<Action>, SignatureRequestError>>()?;
234243

235-
Ok(IdentityUpdate::new(actions, self.client_timestamp_ns))
244+
Ok(IdentityUpdate::new(
245+
actions,
246+
self.inbox_id,
247+
self.client_timestamp_ns,
248+
))
236249
}
237250
}
238251

@@ -317,10 +330,15 @@ fn build_action(
317330
}
318331
}
319332

320-
fn get_signature_text(actions: Vec<UnsignedAction>, client_timestamp_ns: u64) -> String {
333+
fn get_signature_text(
334+
actions: Vec<UnsignedAction>,
335+
inbox_id: String,
336+
client_timestamp_ns: u64,
337+
) -> String {
321338
let identity_update = UnsignedIdentityUpdate {
322339
client_timestamp_ns,
323340
actions,
341+
inbox_id,
324342
};
325343

326344
identity_update.signature_text()
@@ -337,7 +355,7 @@ mod tests {
337355

338356
use super::*;
339357

340-
// Helper function to add all the missing signatures
358+
// Helper function to add all the missing signatures, since we don't have real signers available
341359
fn add_missing_signatures_to_request(signature_request: &mut SignatureRequest) {
342360
let missing_signatures = signature_request.missing_signatures();
343361
for member_identifier in missing_signatures {
@@ -362,9 +380,9 @@ mod tests {
362380
let account_address = "account_address".to_string();
363381
let nonce = 0;
364382
let inbox_id = generate_inbox_id(&account_address, &nonce);
365-
let mut signature_request = IdentityUpdateBuilder::new(inbox_id)
383+
let mut signature_request = SignatureRequestBuilder::new(inbox_id)
366384
.create_inbox(account_address.into(), nonce)
367-
.to_signature_request();
385+
.build();
368386

369387
add_missing_signatures_to_request(&mut signature_request);
370388

@@ -383,10 +401,10 @@ mod tests {
383401
let existing_member_identifier: MemberIdentifier = account_address.into();
384402
let new_member_identifier: MemberIdentifier = rand_vec().into();
385403

386-
let mut signature_request = IdentityUpdateBuilder::new(inbox_id)
404+
let mut signature_request = SignatureRequestBuilder::new(inbox_id)
387405
.create_inbox(existing_member_identifier.clone(), nonce)
388406
.add_association(new_member_identifier, existing_member_identifier)
389-
.to_signature_request();
407+
.build();
390408

391409
add_missing_signatures_to_request(&mut signature_request);
392410

@@ -405,10 +423,10 @@ mod tests {
405423
let inbox_id = generate_inbox_id(&account_address, &nonce);
406424
let existing_member_identifier: MemberIdentifier = account_address.clone().into();
407425

408-
let mut signature_request = IdentityUpdateBuilder::new(inbox_id)
426+
let mut signature_request = SignatureRequestBuilder::new(inbox_id)
409427
.create_inbox(existing_member_identifier.clone(), nonce)
410428
.revoke_association(existing_member_identifier.clone(), account_address.into())
411-
.to_signature_request();
429+
.build();
412430

413431
add_missing_signatures_to_request(&mut signature_request);
414432

@@ -426,9 +444,9 @@ mod tests {
426444
let account_address = "account_address".to_string();
427445
let nonce = 0;
428446
let inbox_id = generate_inbox_id(&account_address, &nonce);
429-
let mut signature_request = IdentityUpdateBuilder::new(inbox_id)
447+
let mut signature_request = SignatureRequestBuilder::new(inbox_id)
430448
.create_inbox(account_address.into(), nonce)
431-
.to_signature_request();
449+
.build();
432450

433451
let attempt_to_add_random_member = signature_request.add_signature(
434452
MockSignature::new_boxed(true, rand_string().into(), SignatureKind::Erc191, None),

0 commit comments

Comments
 (0)