Skip to content

Commit 16e0409

Browse files
committed
Update from latest protos
1 parent ca14014 commit 16e0409

File tree

4 files changed

+80
-22
lines changed

4 files changed

+80
-22
lines changed

xmtp_id/src/associations/association_log.rs

+36-15
Original file line numberDiff line numberDiff line change
@@ -102,21 +102,21 @@ impl LogEntry for AddAssociation {
102102

103103
let new_member_address = self.new_member_signature.recover_signer()?;
104104
let existing_member_address = self.existing_member_signature.recover_signer()?;
105+
let recovery_address = &existing_state.recovery_address;
106+
107+
// You cannot add yourself
105108
if new_member_address == existing_member_address {
106109
return Err(AssociationError::Generic("tried to add self".to_string()));
107110
}
108111

109-
if self.new_member_role == EntityRole::LegacyKey {
112+
// Only allow LegacyDelegated signatures on XIDs with a nonce of 0
113+
// Otherwise the client should use the regular wallet signature to create
114+
if self.new_member_signature.signature_kind() == SignatureKind::LegacyDelegated {
110115
if existing_state.xid != generate_xid(&existing_member_address, &0) {
111116
return Err(AssociationError::LegacySignatureReuse);
112117
}
113118
}
114119

115-
// Find the existing entity that authorized this add
116-
let existing_entity = existing_state
117-
.get(&existing_member_address)
118-
.ok_or(AssociationError::MissingExistingMember)?;
119-
120120
// Make sure that the signature type lines up with the role
121121
if !allowed_signature_for_role(
122122
&self.new_member_role,
@@ -128,10 +128,30 @@ impl LogEntry for AddAssociation {
128128
));
129129
}
130130

131+
let existing_member = existing_state.get(&existing_member_address);
132+
133+
let existing_entity_id = match existing_member {
134+
// If there is an existing member of the XID, use that member's ID
135+
Some(member) => member.id.clone(),
136+
None => {
137+
// Check if it is a signature from the recovery address, which is allowed to add members
138+
if existing_member_address.ne(recovery_address) {
139+
return Err(AssociationError::MissingExistingMember);
140+
}
141+
// BUT, the recovery address has to be used with a real wallet signature, can't be delegated
142+
if self.existing_member_signature.signature_kind() == SignatureKind::LegacyDelegated
143+
{
144+
return Err(AssociationError::LegacySignatureReuse);
145+
}
146+
// If it is a real wallet signature, then it is allowed to add members
147+
recovery_address.clone()
148+
}
149+
};
150+
131151
let new_member = Entity::new(
132152
self.new_member_role.clone(),
133153
new_member_address,
134-
Some(existing_entity.id),
154+
Some(existing_entity_id),
135155
);
136156

137157
println!(
@@ -166,6 +186,13 @@ impl LogEntry for RevokeAssociation {
166186
maybe_existing_state: Option<AssociationState>,
167187
) -> Result<AssociationState, AssociationError> {
168188
let existing_state = maybe_existing_state.ok_or(AssociationError::NotCreated)?;
189+
190+
if self.recovery_address_signature.signature_kind() == SignatureKind::LegacyDelegated {
191+
return Err(AssociationError::SignatureNotAllowed(
192+
EntityRole::Address,
193+
SignatureKind::LegacyDelegated,
194+
));
195+
}
169196
// Don't need to check for replay here since revocation is idempotent
170197
let recovery_signer = self.recovery_address_signature.recover_signer()?;
171198
// Make sure there is a recovery address set on the state
@@ -239,19 +266,13 @@ pub fn allowed_signature_for_role(role: &EntityRole, signature_kind: &SignatureK
239266
SignatureKind::Erc191 => true,
240267
SignatureKind::Erc1271 => true,
241268
SignatureKind::InstallationKey => false,
242-
SignatureKind::LegacyKey => false,
243-
},
244-
EntityRole::LegacyKey => match signature_kind {
245-
SignatureKind::Erc191 => false,
246-
SignatureKind::Erc1271 => false,
247-
SignatureKind::InstallationKey => false,
248-
SignatureKind::LegacyKey => true,
269+
SignatureKind::LegacyDelegated => true,
249270
},
250271
EntityRole::Installation => match signature_kind {
251272
SignatureKind::Erc191 => false,
252273
SignatureKind::Erc1271 => false,
253274
SignatureKind::InstallationKey => true,
254-
SignatureKind::LegacyKey => false,
275+
SignatureKind::LegacyDelegated => false,
255276
},
256277
}
257278
}

xmtp_id/src/associations/entity.rs

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
pub enum EntityRole {
33
Installation,
44
Address,
5-
LegacyKey,
65
}
76

87
#[derive(Clone, Debug)]

xmtp_id/src/associations/mod.rs

+43-5
Original file line numberDiff line numberDiff line change
@@ -190,22 +190,22 @@ mod tests {
190190
existing_member_signature: MockSignature::new_boxed(
191191
true,
192192
account_address.clone(),
193-
SignatureKind::Erc191,
193+
SignatureKind::LegacyDelegated,
194194
),
195195
new_member_signature: MockSignature::new_boxed(
196196
true,
197197
rand_string(),
198-
SignatureKind::LegacyKey,
198+
SignatureKind::InstallationKey,
199199
),
200-
new_member_role: EntityRole::LegacyKey,
200+
new_member_role: EntityRole::Installation,
201201
},
202202
};
203203

204204
let initial_state = get_state(vec![AssociationEvent::CreateXid(create_request)]).unwrap();
205205
assert_eq!(initial_state.entities().len(), 2);
206206

207-
let legacy_keys = initial_state.entities_by_role(EntityRole::LegacyKey);
208-
assert_eq!(legacy_keys.len(), 1);
207+
let installation_keys = initial_state.entities_by_role(EntityRole::Installation);
208+
assert_eq!(installation_keys.len(), 1);
209209
}
210210

211211
#[test]
@@ -338,4 +338,42 @@ mod tests {
338338
assert_eq!(replay_result.is_err(), true);
339339
assert_eq!(replay_result.err().unwrap(), AssociationError::Replay)
340340
}
341+
342+
#[test]
343+
fn test_add_from_recovery_address() {
344+
let create_request = CreateXid::default();
345+
let initial_wallet_address = create_request.account_address.clone();
346+
let revocation = RevokeAssociation {
347+
recovery_address_signature: MockSignature::new_boxed(
348+
true,
349+
initial_wallet_address.clone(),
350+
SignatureKind::Erc191,
351+
),
352+
revoked_member: initial_wallet_address.clone(),
353+
..Default::default()
354+
};
355+
356+
let state = get_state(vec![
357+
AssociationEvent::CreateXid(create_request),
358+
AssociationEvent::RevokeAssociation(revocation),
359+
])
360+
.unwrap();
361+
362+
// Ensure that the initial wallet isn't in there. The recovery address has not changed, however.
363+
assert_eq!(state.get(&initial_wallet_address).is_none(), true);
364+
365+
let add_from_recovery = AddAssociation {
366+
client_timestamp_ns: rand_u32(),
367+
existing_member_signature: MockSignature::new_boxed(
368+
true,
369+
initial_wallet_address,
370+
SignatureKind::Erc191,
371+
),
372+
..Default::default()
373+
};
374+
375+
add_from_recovery
376+
.update_state(Some(state))
377+
.expect("should be allowed because we are using the recovery address");
378+
}
341379
}

xmtp_id/src/associations/signature.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub enum SignatureKind {
1212
Erc191,
1313
Erc1271,
1414
InstallationKey,
15-
LegacyKey,
15+
LegacyDelegated,
1616
}
1717

1818
pub trait Signature {

0 commit comments

Comments
 (0)