pub fn revoke_notification(env: Env, notification_id: BytesN<32>, caller: Address)Parameters:
notification_id: The ID of the notification to revokecaller: The address performing the revocation (must be authenticated)
Authentication: Required - caller must be authenticated
Authorization: Only notification creator or contract admin
Returns: Result<(), Error>
Possible Errors:
ContractPaused- Contract is currently pausedNotFound- Notification with this ID doesn't existNotificationRevoked- Notification is already revokedNotificationExpired- Notification has expired (cannot revoke)NotAuthorizedToRevoke- Caller is not creator or admin
Example:
let notification_id = BytesN::from_array(&env, &[1u8; 32]);
let creator = Address::generate(&env);
// Creator revoking their own notification
client.revoke_notification(¬ification_id, &creator);
// Admin revoking any notification
let admin = client.get_admin();
client.revoke_notification(¬ification_id, &admin);pub fn is_notification_revoked(env: Env, notification_id: BytesN<32>) -> boolParameters:
notification_id: The ID of the notification to check
Returns: bool - true if revoked, false if not
Possible Errors:
NotFound- Notification with this ID doesn't exist
Example:
if client.is_notification_revoked(¬ification_id) {
println!("Notification is revoked");
} else {
println!("Notification is still active");
}pub fn get_notification(env: Env, notification_id: BytesN<32>) -> ScheduledNotificationReturns: ScheduledNotification struct with these fields:
id: BytesN<32>- Notification IDcreator: Address- Who created the notificationcreated_at: u64- Ledger timestamp when createdexpires_at: u64- Ledger timestamp when it expiresrevoked_by: Option<Address>- Who revoked it (None if not revoked)revoked_at: Option<u64>- When it was revoked in ledger seconds
Example:
let notification = client.get_notification(¬ification_id);
println!("Created by: {}", notification.creator);
println!("Expires at: {}", notification.expires_at);
if let Some(revoked_by) = notification.revoked_by {
println!("Revoked by: {} at timestamp {}",
revoked_by,
notification.revoked_at.unwrap());
}1. Schedule Notification
└─> client.schedule_notification(id, creator, ttl_seconds)
2. Later: Decide to Revoke
└─> client.revoke_notification(id, caller)
- caller must be creator or admin
- notification must exist and not be expired
3. Check Status
└─> is_revoked = client.is_notification_revoked(id)
4. Try to Cancel (will fail)
└─> client.cancel_notification(id, caller)
└─> ❌ Error::NotificationRevoked
┌─────────────────────────────────────────┐
│ Notification Lifecycle │
└─────────────────────────────────────────┘
schedule_notification()
│
▼
┌────────────────┐
│ Scheduled │
└────────────────┘
│ │
│ revoke_notification()
│ │
│ ▼
│ ┌──────────────────┐
│ │ Revoked │
│ │ (Permanent) │
│ └──────────────────┘
│ │
│ Can't Cancel ❌
│ Can't Expire ❌
│ Can Query ✓
│
Wait │
TTL │
▼
┌────────────────┐
│ Expired │
└────────────────┘
│
expire_notification()
│
▼
┌────────────────┐
│ Removed │
│ (Reaped) │
└────────────────┘
match client.revoke_notification(&id, &unauthorized_user) {
Err(Error::NotAuthorizedToRevoke) => {
println!("Only creator or admin can revoke");
},
_ => {}
}// First revocation succeeds
client.revoke_notification(&id, &creator)?;
// Second revocation fails
match client.revoke_notification(&id, &creator) {
Err(Error::AlreadyRevoked) => {
println!("Notification is already revoked");
},
_ => {}
}// Schedule with short TTL
client.schedule_notification(&id, &creator, &10)?; // 10 seconds
// Wait for expiration...
// env.ledger().set_timestamp(start_time + 20);
// Try to revoke - will fail
match client.revoke_notification(&id, &creator) {
Err(Error::NotificationExpired) => {
println!("Cannot revoke - notification already expired");
},
_ => {}
}Emitted when a notification is successfully revoked.
Topics (indexed for filtering):
- Event name:
notification_revoked notification_id(BytesN<32>) - Which notification was revokedrevoked_by(Address) - Who performed the revocationcategory(NotificationCategory) - AlwaysNotificationpriority(NotificationPriority) - AlwaysHigh(security-relevant)
Data:
revoked_at(u64) - Ledger timestamp of revocation
Example Event Listener:
// Off-chain listener example (pseudo-code)
contract.on('notification_revoked', (event) => {
console.log(`Notification ${event.notification_id} revoked`);
console.log(`Revoked by: ${event.revoked_by}`);
console.log(`At timestamp: ${event.revoked_at}`);
// Route to alerting system (high priority)
alertSystem.sendAlert({
type: 'revocation',
notification_id: event.notification_id,
revoked_by: event.revoked_by,
timestamp: event.revoked_at
});
});┌─────────────────────────────────────┐
│ Can Revoke Any Notification │
│ ✓ Contract Admin │
│ │
│ Can Revoke Their Own Notifications │
│ ✓ Notification Creator │
│ │
│ Cannot Revoke │
│ ✗ Other addresses │
│ ✗ Group members (not creators) │
│ ✗ Payment payers │
└─────────────────────────────────────┘
let admin = client.get_admin();
let notification_creator = Address::generate(&env);
let notification_id = BytesN::from_array(&env, &[1u8; 32]);
// Creator schedules notification
client.schedule_notification(¬ification_id, ¬ification_creator, &3600);
// Admin can revoke it even though they didn't create it
client.revoke_notification(¬ification_id, &admin)?; // ✓ Works
// But non-admin can't
let hacker = Address::generate(&env);
client.revoke_notification(¬ification_id, &hacker); // ✗ Error::NotAuthorizedToRevokeRevocation is blocked while contract is paused:
// Revocation works normally
client.revoke_notification(&id, &creator)?; // ✓ OK
// Pause contract
client.pause(&admin);
// Try to revoke while paused
client.revoke_notification(&id2, &creator); // ✗ Error::ContractPaused
// Unpause
client.unpause(&admin);
// Now revocation works again
client.revoke_notification(&id2, &creator)?; // ✓ OK// Before using a notification
pub fn use_notification(env: Env, notification_id: BytesN<32>) {
// Check if notification is revoked
if client.is_notification_revoked(¬ification_id) {
return Error::NotificationRevoked;
}
// Check if expired
if client.is_notification_expired(¬ification_id) {
return Error::NotificationExpired;
}
// Now safe to use
process_notification(¬ification_id);
}// Get full notification details for auditing
let notification = client.get_notification(&id);
let audit_record = AuditLog {
id: notification.id,
creator: notification.creator,
created_at: notification.created_at,
expires_at: notification.expires_at,
status: if notification.revoked_by.is_some() {
"Revoked"
} else {
"Active"
},
revoked_by: notification.revoked_by,
revoked_at: notification.revoked_at,
};
audit_log.record(audit_record);fn safely_get_notification(
client: &AutoShareContractClient,
notification_id: &BytesN<32>
) -> Result<ScheduledNotification, Error> {
let notification = client.get_notification(notification_id)?;
// Check revocation
if notification.revoked_by.is_some() {
return Err(Error::NotificationRevoked);
}
Ok(notification)
}fn revoke_with_audit(
client: &AutoShareContractClient,
notification_id: BytesN<32>,
revoker: Address,
reason: String
) -> Result<(), Error> {
// Log before revocation
audit_log.record_action("revoke_initiated", &revoker, &reason);
// Perform revocation
client.revoke_notification(¬ification_id, &revoker)?;
// Log success
audit_log.record_action("revoke_success", &revoker, &reason);
Ok(())
}fn get_active_notifications(
client: &AutoShareContractClient,
ids: &Vec<BytesN<32>>
) -> Vec<ScheduledNotification> {
ids.iter()
.filter_map(|id| {
if let Ok(notif) = client.get_notification(id) {
if notif.revoked_by.is_none() {
return Some(notif);
}
}
None
})
.collect()
}#[test]
fn test_my_revocation_logic() {
let test_env = setup_test_env();
let client = AutoShareContractClient::new(&test_env.env, &test_env.contract);
let creator = test_env.users[0].clone();
// Create notification
let id = make_id(&test_env.env, 1);
client.schedule_notification(&id, &creator, &3600);
assert!(!client.is_notification_revoked(&id));
// Revoke it
client.revoke_notification(&id, &creator);
assert!(client.is_notification_revoked(&id));
// Verify can't cancel
let result = std::panic::catch_unwind(|| {
client.cancel_notification(&id, &creator);
});
assert!(result.is_err());
}- Revocation Time: O(1) - Direct storage update
- Query Time: O(1) - Direct storage read
- Storage Cost: +2 fields (Option, Option) per notification
- Event Cost: Standard event emission
| Problem | Cause | Solution |
|---|---|---|
NotAuthorizedToRevoke |
Not creator or admin | Ensure caller is creator or get admin address |
NotFound |
ID doesn't exist | Verify notification was created first |
AlreadyRevoked |
Trying to revoke twice | Check is_notification_revoked() first |
NotificationExpired |
Past expiration time | Can only revoke active notifications |
ContractPaused |
Contract is paused | Wait for unpause or have admin unpause |