Skip to content

Commit d370386

Browse files
authored
Allow same nonce in same tx (#48)
1 parent 79b1c20 commit d370386

File tree

3 files changed

+172
-7
lines changed

3 files changed

+172
-7
lines changed

contracts/wallet/src/lib.rs

Lines changed: 171 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ impl AccountInfo {
372372
let secp256k1blob = CheckSecp256k1::new(calldata, nonce.to_string().as_bytes()).expect()?;
373373
let public_key = hex::encode(secp256k1blob.public_key);
374374

375-
self.verify_and_update_nonce(nonce)?;
375+
self.verify_and_update_nonce(nonce, calldata)?;
376376

377377
self.use_session_key(public_key, calldata)
378378
}
@@ -389,7 +389,7 @@ impl AccountInfo {
389389
if self.identity != account {
390390
return Err("Account does not match registered identity".to_string());
391391
}
392-
self.verify_and_update_nonce(nonce)
392+
self.verify_and_update_nonce(nonce, calldata)
393393
}
394394
WalletAction::AddSessionKey {
395395
account,
@@ -402,7 +402,7 @@ impl AccountInfo {
402402
// Verify identity before executing the action
403403
self.auth_method.verify(calldata, nonce)?;
404404

405-
self.verify_and_update_nonce(nonce)?;
405+
self.verify_and_update_nonce(nonce, calldata)?;
406406

407407
if self.identity != account {
408408
return Err("Account does not match registered identity".to_string());
@@ -413,13 +413,46 @@ impl AccountInfo {
413413
// Verify identity before executing the action
414414
self.auth_method.verify(calldata, nonce)?;
415415

416-
self.verify_and_update_nonce(nonce)?;
416+
self.verify_and_update_nonce(nonce, calldata)?;
417417

418418
self.remove_session_key(key)
419419
}
420420
_ => unreachable!(),
421421
}
422422
}
423+
424+
/// Helper function to check if a VerifyIdentity action exists in previous blobs for the same user
425+
fn check_verify_identity_in_previous_blobs(
426+
&self,
427+
calldata: &sdk::Calldata,
428+
expected_account: &str,
429+
) -> Result<(), String> {
430+
// Iterate through blobs before the current one
431+
for (blob_index, blob) in &calldata.blobs {
432+
// Skip the current blob and any after it
433+
if blob_index >= &calldata.index {
434+
break;
435+
}
436+
437+
// Check if this is a wallet blob
438+
if blob.contract_name.0 == "wallet" {
439+
// Try to decode the blob as a WalletAction
440+
if let Ok(
441+
WalletAction::VerifyIdentity { account, .. }
442+
| WalletAction::RegisterIdentity { account, .. },
443+
) = WalletAction::from_blob_data(&blob.data)
444+
{
445+
if account == expected_account {
446+
return Ok(());
447+
}
448+
}
449+
}
450+
}
451+
452+
Err(format!(
453+
"No action that proves identity found in previous blobs for account: {expected_account}. calldata.blobs: {:?}", calldata.blobs
454+
))
455+
}
423456
}
424457

425458
/// State management methods for the Wallet contract
@@ -442,10 +475,19 @@ impl AccountInfo {
442475
Ok(ret)
443476
}
444477

445-
fn verify_and_update_nonce(&mut self, nonce: u128) -> Result<String, String> {
446-
if nonce <= self.nonce {
478+
fn verify_and_update_nonce(
479+
&mut self,
480+
nonce: u128,
481+
calldata: &sdk::Calldata,
482+
) -> Result<String, String> {
483+
if nonce < self.nonce {
447484
return Err("Invalid nonce".to_string());
448485
}
486+
if nonce == self.nonce {
487+
// Check if there's a VerifyIdentity action in previous blobs for this user
488+
self.check_verify_identity_in_previous_blobs(calldata, &self.identity)?;
489+
return Ok("Identity verified".to_string());
490+
}
449491
self.nonce = nonce;
450492
Ok("Identity verified".to_string())
451493
}
@@ -1004,4 +1046,127 @@ mod tests {
10041046
.handle(register_call)
10051047
.expect("Failed to handle register call");
10061048
}
1049+
1050+
#[test]
1051+
fn test_check_verify_identity_in_previous_blobs() {
1052+
let nonce = 1769086402327;
1053+
// Test based on the image showing 3 blobs with bob identity
1054+
let account_info = AccountInfo {
1055+
identity: "bob".to_string(),
1056+
auth_method: AuthMethod::Ethereum {
1057+
address: "0x6853cc7d35451325053706ad5f188df79f0387c".to_string(),
1058+
},
1059+
session_keys: vec![],
1060+
nonce,
1061+
};
1062+
1063+
// Create blob #0 - secp256k1 blob (from image)
1064+
let secp256k1_blob = Blob {
1065+
contract_name: sdk::ContractName("secp256k1".to_string()),
1066+
data: sdk::BlobData(vec![/* secp256k1 data would go here */]),
1067+
};
1068+
1069+
// Create blob #1 - secp256k1 blob (from image)
1070+
let secp256k1_blob2 = Blob {
1071+
contract_name: sdk::ContractName("secp256k1".to_string()),
1072+
data: sdk::BlobData(vec![/* secp256k1 data would go here */]),
1073+
};
1074+
1075+
// Create blob #2 - RegisterIdentity action for bob (from image)
1076+
let register_identity_blob = WalletAction::RegisterIdentity {
1077+
account: "bob".to_string(),
1078+
nonce,
1079+
salt: "***".to_string(),
1080+
auth_method: AuthMethod::Ethereum {
1081+
address: "0x6853cc7d35451325053706ad5f188df79f0387c".to_string(),
1082+
},
1083+
invite_code: "***".to_string(),
1084+
}
1085+
.as_blob(sdk::ContractName("wallet".to_string()));
1086+
1087+
// Create blob #3 - AddSessionKey action for bob (from image)
1088+
let add_session_key_blob = WalletAction::AddSessionKey {
1089+
account: "bob".to_string(),
1090+
key: "0288fb774209924f7ea2221bf136919b3765d662f6d1d93c36c2ef0b8c3b71db6".to_string(),
1091+
expiration_date: 1769945603806,
1092+
whitelist: None,
1093+
lane_id: None,
1094+
nonce,
1095+
}
1096+
.as_blob(sdk::ContractName("wallet".to_string()));
1097+
1098+
// Test case 1: Current blob is #3, should find RegisterIdentity in blob #2
1099+
let calldata_with_register = Calldata {
1100+
blobs: IndexedBlobs::from(vec![
1101+
secp256k1_blob.clone(),
1102+
secp256k1_blob2.clone(),
1103+
register_identity_blob.clone(),
1104+
add_session_key_blob.clone(),
1105+
]),
1106+
index: BlobIndex(3), // Current blob is #3 (AddSessionKey)
1107+
identity: "bob".into(),
1108+
..Default::default()
1109+
};
1110+
1111+
// Should succeed because RegisterIdentity for bob exists in blob #2
1112+
let result =
1113+
account_info.check_verify_identity_in_previous_blobs(&calldata_with_register, "bob");
1114+
assert!(
1115+
result.is_ok(),
1116+
"Should find RegisterIdentity in previous blobs"
1117+
);
1118+
1119+
// Test case 2: Current blob is #2, should not find any previous identity proof
1120+
let calldata_current_register = Calldata {
1121+
blobs: IndexedBlobs::from(vec![
1122+
secp256k1_blob.clone(),
1123+
secp256k1_blob2.clone(),
1124+
register_identity_blob.clone(),
1125+
add_session_key_blob.clone(),
1126+
]),
1127+
index: BlobIndex(2), // Current blob is #2 (RegisterIdentity)
1128+
identity: "bob".into(),
1129+
..Default::default()
1130+
};
1131+
1132+
// Should fail because no previous identity proof exists before blob #2
1133+
let result =
1134+
account_info.check_verify_identity_in_previous_blobs(&calldata_current_register, "bob");
1135+
assert!(
1136+
result.is_err(),
1137+
"Should not find identity proof in previous blobs"
1138+
);
1139+
1140+
// Test case 3: Wrong account name
1141+
let result = account_info
1142+
.check_verify_identity_in_previous_blobs(&calldata_with_register, "wrongAccount");
1143+
assert!(result.is_err(), "Should fail with wrong account name");
1144+
1145+
// Test case 4: Test with VerifyIdentity action instead of RegisterIdentity
1146+
let verify_identity_blob = WalletAction::VerifyIdentity {
1147+
account: "bob".to_string(),
1148+
nonce,
1149+
}
1150+
.as_blob(sdk::ContractName("wallet".to_string()));
1151+
1152+
let calldata_with_verify = Calldata {
1153+
blobs: IndexedBlobs::from(vec![
1154+
secp256k1_blob.clone(),
1155+
secp256k1_blob2.clone(),
1156+
verify_identity_blob.clone(),
1157+
add_session_key_blob.clone(),
1158+
]),
1159+
index: BlobIndex(3), // Current blob is #3
1160+
identity: "bob".into(),
1161+
..Default::default()
1162+
};
1163+
1164+
// Should succeed because VerifyIdentity for bob exists in blob #2
1165+
let result =
1166+
account_info.check_verify_identity_in_previous_blobs(&calldata_with_verify, "bob");
1167+
assert!(
1168+
result.is_ok(),
1169+
"Should find VerifyIdentity in previous blobs"
1170+
);
1171+
}
10071172
}

contracts/wallet/wallet.img

103 KB
Binary file not shown.

contracts/wallet/wallet.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
638b232b1b1e7f7f782e362780fc2ca9c1f0521bd8573c6382fc3e7b9f70de3d
1+
0af0edc50917c3cbc88fa179a53add0fe433e1dc88e9db6fca9e9b7c71e8c708

0 commit comments

Comments
 (0)