Skip to content

Commit

Permalink
[VC Issuance] Use persistent storage for VCs and Revocations (#181)
Browse files Browse the repository at this point in the history
* Update vc_issuance_contract

* Update Readme

* Update CI to coverage
  • Loading branch information
MarioRodriguezS authored Feb 9, 2024
1 parent f77d75e commit 31507bc
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 36 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
version: 0.22.0
# Due to a bug with cargo-tarpaulin crate with multi-line chain calls, the test coverage was reduced.
# https://github.com/xd009642/tarpaulin/issues/949
args: --all-features --fail-under 98.0 --out Lcov
args: --all-features --fail-under 97.8 --out Lcov
- name: Upload to Coveralls
uses: coverallsapp/github-action@master
with:
Expand Down
6 changes: 4 additions & 2 deletions vc_issuance_contract/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ A contract error will be triggered if:

- Invoker is not the contract admin.
- Verifiable credential is not registered.
- Verifiable credential is already revoked.

```rust
fn revoke(e: Env, admin: Address, vc_id: String, date: String);
Expand Down Expand Up @@ -179,8 +180,9 @@ soroban contract invoke \
| 1 | `AlreadyInitialized` | Contract has already been initialized |
| 2 | `NotAuthorized` | Invoker is not the contract admin |
| 3 | `AmountLimitExceeded` | Provided amount exceeds the maximum allowed |
| 4 | `VCNotFound` | Verifiable credential not found |
| 5 | `IssuanceLimitExceeded` | Contract issuance limit exceeded |
| 4 | `VCNotFound` | Verifiable credential not found |
| 5 | `VCAlreadyRevoked` | Verifiable credential already revoked |
| 6 | `IssuanceLimitExceeded` | Contract issuance limit exceeded |

## Development

Expand Down
18 changes: 4 additions & 14 deletions vc_issuance_contract/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
use crate::error::ContractError;
use crate::revocation::Revocation;
use crate::storage;
use crate::vc_issuance_trait::VCIssuanceTrait;
use crate::verifiable_credential;
use crate::{error::ContractError, revocation};
use soroban_sdk::{
contract, contractimpl, contractmeta, map, panic_with_error, vec, Address, Env, FromVal, Map,
String, Symbol, Val, Vec,
};

// MAXIMUM ENTRY TTL:
// 31 days, 12 ledger close per minute.
// (12 * 60 * 24 * 31) - 1
const LEDGERS_TO_EXTEND: u32 = 535_679;
const DEFAULT_AMOUNT: u32 = 20;
const MAX_AMOUNT: u32 = 100;

Expand Down Expand Up @@ -41,9 +36,8 @@ impl VCIssuanceTrait for VCIssuanceContract {
storage::write_vcs(&e, &Vec::new(&e));
storage::write_vcs_revocations(&e, &Map::new(&e));

e.storage()
.instance()
.extend_ttl(LEDGERS_TO_EXTEND, LEDGERS_TO_EXTEND);
storage::extend_ttl_to_instance(&e);
storage::extend_ttl_to_persistent(&e);
}
fn issue(e: Env, admin: Address, vc_data: String, vault_contract: Address) -> String {
validate_admin(&e, &admin);
Expand Down Expand Up @@ -91,11 +85,7 @@ impl VCIssuanceTrait for VCIssuanceContract {
validate_admin(&e, &admin);
validate_vc(&e, &vc_id);

let mut revocations = storage::read_vcs_revocations(&e);

revocations.set(vc_id.clone(), Revocation { vc_id, date });

storage::write_vcs_revocations(&e, &revocations);
revocation::revoke_vc(&e, vc_id, date);
}
}

Expand Down
3 changes: 2 additions & 1 deletion vc_issuance_contract/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ pub enum ContractError {
NotAuthorized = 2,
AmountLimitExceeded = 3,
VCNotFound = 4,
IssuanceLimitExceeded = 5,
VCAlreadyRevoked = 5,
IssuanceLimitExceeded = 6,
}
14 changes: 13 additions & 1 deletion vc_issuance_contract/src/revocation.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
use soroban_sdk::{contracttype, String};
use crate::error::ContractError;
use crate::storage;
use soroban_sdk::{contracttype, panic_with_error, Env, String};

#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Revocation {
pub vc_id: String,
pub date: String,
}

pub fn revoke_vc(e: &Env, vc_id: String, date: String) {
let mut revocations = storage::read_vcs_revocations(e);

if revocations.contains_key(vc_id.clone()) {
panic_with_error!(e, ContractError::VCAlreadyRevoked)
}
revocations.set(vc_id.clone(), Revocation { vc_id, date });
storage::write_vcs_revocations(e, &revocations);
}
49 changes: 36 additions & 13 deletions vc_issuance_contract/src/storage.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
use crate::revocation::Revocation;
use soroban_sdk::{contracttype, Address, Env, Map, String, Vec};

// MAXIMUM ENTRY TTL:
// 31 days, 12 ledger close per minute.
// (12 * 60 * 24 * 31) - 1
const LEDGERS_TO_EXTEND: u32 = 535_679;

#[derive(Clone)]
#[contracttype]
pub enum DataKey {
Admin, // Address
IssuerDID, // String
Amount, // U32
VerifiableCredentials, // Vec<String>
RevocationList, // Map<String, Revocation>
Admin, // Address
IssuerDID, // String
Amount, // U32
VCs, // Vec<String>
Revocations, // Map<String, Revocation>
}

pub fn has_admin(e: &Env) -> bool {
Expand Down Expand Up @@ -47,21 +52,39 @@ pub fn write_amount(e: &Env, amount: &u32) {
}

pub fn write_vcs(e: &Env, vc: &Vec<String>) {
let key = DataKey::VerifiableCredentials;
e.storage().instance().set(&key, vc)
let key = DataKey::VCs;
e.storage().persistent().set(&key, vc)
}

pub fn read_vcs(e: &Env) -> Vec<String> {
let key = DataKey::VerifiableCredentials;
e.storage().instance().get(&key).unwrap()
let key = DataKey::VCs;
e.storage().persistent().get(&key).unwrap()
}

pub fn write_vcs_revocations(e: &Env, revocations: &Map<String, Revocation>) {
let key = DataKey::RevocationList;
e.storage().instance().set(&key, revocations)
let key = DataKey::Revocations;
e.storage().persistent().set(&key, revocations)
}

pub fn read_vcs_revocations(e: &Env) -> Map<String, Revocation> {
let key = DataKey::RevocationList;
e.storage().instance().get(&key).unwrap()
let key = DataKey::Revocations;
e.storage().persistent().get(&key).unwrap()
}

pub fn extend_ttl_to_instance(e: &Env) {
e.storage()
.instance()
.extend_ttl(LEDGERS_TO_EXTEND, LEDGERS_TO_EXTEND);
}

pub fn extend_ttl_to_persistent(e: &Env) {
let vcs_key = DataKey::VCs;
let revocations_key = DataKey::Revocations;

e.storage()
.persistent()
.extend_ttl(&vcs_key, LEDGERS_TO_EXTEND, LEDGERS_TO_EXTEND);
e.storage()
.persistent()
.extend_ttl(&revocations_key, LEDGERS_TO_EXTEND, LEDGERS_TO_EXTEND);
}
29 changes: 25 additions & 4 deletions vc_issuance_contract/src/test/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ fn test_issue_with_invalid_admin() {
}

#[test]
#[should_panic(expected = "HostError: Error(Contract, #5)")]
#[should_panic(expected = "HostError: Error(Contract, #6)")]
fn test_issue_when_amount_is_exceeded() {
let VCIssuanceContractTest {
env,
Expand All @@ -109,6 +109,24 @@ fn test_issue_when_amount_is_exceeded() {
contract.issue(&admin, &vc_data, &vault_contract_id);
}

#[test]
fn test_revoke_vc() {
let VCIssuanceContractTest {
env,
admin,
amount: _,
vc_data,
issuer_did,
contract,
} = VCIssuanceContractTest::setup();
let vault_contract_id = create_vc(&env, &admin, &contract, &issuer_did, &None);
let vc_id = contract.issue(&admin, &vc_data, &vault_contract_id);

let date = String::from_str(&env, "2023-12-05T21:37:44.389Z");

contract.revoke(&admin, &vc_id, &date);
}

#[test]
#[should_panic(expected = "HostError: Error(Contract, #4)")]
fn test_revoke_vc_with_invalid_vc() {
Expand All @@ -129,7 +147,8 @@ fn test_revoke_vc_with_invalid_vc() {
}

#[test]
fn test_revoke_vc() {
#[should_panic(expected = "HostError: Error(Contract, #5)")]
fn test_revoke_vc_when_it_was_already_revoked() {
let VCIssuanceContractTest {
env,
admin,
Expand All @@ -141,9 +160,11 @@ fn test_revoke_vc() {
let vault_contract_id = create_vc(&env, &admin, &contract, &issuer_did, &None);
let vc_id = contract.issue(&admin, &vc_data, &vault_contract_id);

let date = String::from_str(&env, "2023-12-05T21:37:44.389Z");
let date_1 = String::from_str(&env, "2023-12-05T21:37:44.389Z");
let date_2 = String::from_str(&env, "2023-21-05T21:37:44.389Z");

contract.revoke(&admin, &vc_id, &date);
contract.revoke(&admin, &vc_id, &date_1);
contract.revoke(&admin, &vc_id, &date_2);
}

#[test]
Expand Down

0 comments on commit 31507bc

Please sign in to comment.