#16 add compliance revocation lifecycle fix - #182
Open
onakijames-droid wants to merge 2 commits into
Open
Conversation
Contributor
| \nThis PR is currently blocked by merge conflicts.\n\nPlease update the branch with the latest main branch and resolve the conflicts before it can be merged. |
Contributor
| \nGitHub has not finished calculating whether this PR can be merged cleanly.\n\nThe auto-merge automation will skip this PR for now. Re-run the automation later. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CLOSED #16 add compliance revocation lifecycle
Description
Implemented full compliance revocation lifecycle for previously whitelisted investors in the Aegis RWA protocol.
State:
DataKey::Revoked(Address)persistent storage variant insrc/lib.rsis_whitelistednow checksWhitelist==true && !is_revoked, withis_revokedas separate helper. This distinguishes never-whitelisted vs revoked for clearer errors and audit.Admin update path:
whitelist_usernow clears revocation flag (Revoked=false) to enable single-step re-onboarding after compliance review.revoke_user(admin, user): admin-only (require_auth+ admin check), setsWhitelist=false,Revoked=true, emitswl_revevent. Fully freezes user.unrevoke_user(admin,user): clears revoked flag without whitelisting, enabling two-step governance (clear sanction then re-KYC).is_whitelisted_check,is_revoked_check,compliance_status()->(bool,bool)for off-chain queries.Transfer/Minting guards:
mint_asset: asserts!is_revoked(to)thenis_whitelisted(to)→ error messages "Receiver is revoked" vs "not whitelisted".transfer: asserts!is_revoked(from)("Sender is revoked"),!is_revoked(to)("Receiver is revoked"), then whitelist checks. Implements fully blocked / frozen policy: revoked cannot receive new restricted tokens nor send. Existing balance retained (not burned) for audit and forced redemption.Policy rationale: fully frozen is stronger compliance for sanctioned addresses than exit-only. Documented as one-line change if governance requires exit-only (remove sender check).
Events:
WhitelistRevokedwith topics("aegis","wl_rev",user)dataadmininsrc/events.rs, helperuser_revoked.Tests & Docs: Updated as below.
Key files modified:
src/lib.rs– added Revoked DataKeysrc/compliance.rs– revocation logic, admin paths, view helperssrc/events.rs– WhitelistRevoked eventsrc/asset.rs– revocation guards in mint/transfersrc/test.rs– 8 new revocation scenariosdocs/contract-spec.md– API, lifecycle, policy, event tabledocs/architecture.md– DataKey layout, design rationaledocs/revocation.md– NEW dedicated lifecycle docRelated Issues
Fixes compliance revocation lifecycle issue described in task:
Completion Table
DataKey::Revoked(Address)insrc/lib.rs:11;is_revoked()andis_whitelisted()composite check insrc/compliance.rs:105-123; persistent storage for both flagstest_revoked_retains_balance_but_frozeninsrc/test.rs:378-398checkscompliance_statusreturns(false,true)and helpers;test_rewhitelist_clears_revocationverifies flag cleareddocs/architecture.mdDataKey layout and rationale;docs/contract-spec.mdState Model section;docs/revocation.mdState Model tablemint_assetguardassert!(!is_revoked(&to), "Receiver is revoked")insrc/asset.rs:9-12;transferguard!is_revoked(to)insrc/asset.rs:28-31test_revoked_cannot_receive_mintinsrc/test.rs:324-340panics "Receiver is revoked";test_revoked_cannot_receive_transferinsrc/test.rs:343-363panics samedocs/contract-spec.mdcompliance lifecycle bullet "Cannot receive new restricted tokens";docs/revocation.mdTransfer and Minting Guards sectiontransferguardassert!(!is_revoked(&from), "Sender is revoked")insrc/asset.rs:24-27; Policy = fully blocked/frozen, balance retained not burned, documented in code commentsrc/compliance.rs:26-31test_revoked_cannot_send_transfer_fully_blockedinsrc/test.rs:366-382panics "Sender is revoked";test_revoked_retains_balance_but_frozenshows balance retaineddocs/contract-spec.mdFully Blocked section + alternative exit-only note;docs/architecture.mdRevocation Lifecycle - Design Rationale;docs/revocation.mdPolicy fully frozen + rationaleWhitelistRevokedstructtopics = ["aegis","wl_rev"]insrc/events.rs:29-42; helperuser_revoked()insrc/events.rs:106-111; called inrevoke_usersrc/compliance.rs:56test_revoke_emits_eventinsrc/test.rs:304-322verifies topics(aegis,wl_rev,user)and data admin;test_revocation_lifecycle_observableinsrc/test.rs:433-471verifies observable sequence includes wl_revdocs/contract-spec.mdevent table row for WhitelistRevoked;docs/architecture.mdEvent Layer new event;docs/revocation.mdEvents table + off-chain handlingsrc/test.rs:304-496: revoke emits, cannot receive mint, cannot receive transfer, cannot send (fully blocked), retains balance but frozen, re-whitelist clears, lifecycle observable, unrevoke without whitelist still blocked. All 17 tests passdocs/revocation.mdTests section lists coveragedocs/contract-spec.mdupdated with Compliance Revocation Lifecycle section;docs/architecture.mdupdated with rationale + monitoring; NEWdocs/revocation.mddedicated doc covering motivation, state, admin path, guards, events, off-chain handling, future extensionsDetailed Traceability Mapping
DataKey::Revokedinlib.rs;is_revoked(),is_whitelisted()composite incompliance.rs;compliance_status()viewWhitelist(Address)->bool,Revoked(Address)->bool;whitelist_usersetsWhitelist=true, Revoked=false;revoke_usersetsWhitelist=false, Revoked=true;unrevoke_usersetsRevoked=false; Effective whitelist =Whitelist && !Revokedtest_revoked_retains_balance_but_frozenL378-398 checks(false,true);test_rewhitelist_clears_revocationL401-431 checks(false)->(true,true)after re-whitelistasset.rs: mint checks!is_revoked(to)before whitelist; transfer checks!is_revoked(to)Revokedpersistent before write; no storage write on failure (panic)MintandTransferonly when not revokedtest_revoked_cannot_receive_mintL324-340;test_revoked_cannot_receive_transferL343-363 both#[should_panic(expected="Receiver is revoked")]!is_revoked(from)inasset.rs:24-27; Policy = fully frozen documented incompliance.rs:26-31andcontract-spec.mdRevokedfor sender; balance remains in storage (not burned) for frozen user; no auto burnTransfertest_revoked_cannot_send_transfer_fully_blockedL366-382;test_revoked_retains_balance_but_frozenL378-398 verifies balance retained frozen;test_rewhitelist_clears_revocationverifies send restored after re-whitelistWhitelistRevokedevent struct topics["aegis","wl_rev"]inevents.rs:29-42;user_revoked()helper; invoked inrevoke_user("aegis","wl_rev",user)->admin:Address; namespaced for single RPC filter; topic1="wl_rev" for specific subscription; indexed user addresstest_revoke_emits_eventL304-322 checks topics/data;test_revocation_lifecycle_observableL433-471 checks sequence init+wl_add+mint+wl_rev; dump_event_xdr includes wl_revwl_revverified in two testsmock_all_auths()to isolate logic; should_panic checks exact messages; status tuple checks both flags; lifecycle test verifies event orderingType of Change
Note: Non-breaking because existing whitelist/mint/transfer still work; added new admin functions and storage key
Revokedwhich is additive. Existing whitelisted users unaffected.PR Evidence Checklist
Before opening this PR, complete every applicable item in the evidence
checklist below.
1. Issue Reference
Fixes #123).2. Implementation Summary
New functions:
revoke_user(env, admin, user)– admin revokesunrevoke_user(env, admin, user)– clears revoked flagis_whitelisted_check(env,user)->bool,is_revoked_check(env,user)->bool,compliance_status(env,user)->(bool,bool)view helpersNew event:
WhitelistRevokedtopics("aegis","wl_rev",user)dataadmin3. Tests Added or Justification
Tests added in
src/test.rs:test_revoke_emits_eventL304-322 – happy path eventtest_revoked_cannot_receive_mintL324-340 – failure minttest_revoked_cannot_receive_transferL343-363 – failure transfer-intest_revoked_cannot_send_transfer_fully_blockedL366-382 – failure transfer-out (fully blocked)test_revoked_retains_balance_but_frozenL385-398 – holding behaviortest_rewhitelist_clears_revocationL401-431 – re-onboarding happy pathtest_revocation_lifecycle_observableL433-471 – event observability including wl_revtest_non_whitelisted_still_blocked_after_unrevoke_without_whitelistL474-496 – failure after unrevoke alone4. Commands Run
make verifypasses locally. (Project usesmake testandcargo build)cargo fmthas no changes (formatted).5. CI Status
6. Acceptance Criteria Coverage
N/Awith a reason when no documentation changes are needed.Policy & Standards
CONTRIBUTING.mdguidelines.cargo fmt).cargo clippy– none from changes, only pre-existing warnings if any).cargo test).Additional Context
Build evidence:
Test XDR dump includes new event:
Policy decision justification:
docs/revocation.mdandcontract-spec.mdas conscious choice with one-line change note for alternative.Future extensions noted in docs/revocation.md: