Security pattern presentation

Hello everyone!

I guess this pattern is ready to be shown to the world. I call it Security (as we’re implementing financial securities at dfinance.

Module code is here (link).

The problem

Managing permissions isn’t easy, given borrow checker limitations and inability to read structure outside of its module, and unless you want to let people to copy anything stored inside Permission<For>, you have to use a resource in it. Even more - to check this resource you’d need to unpack and then re-pack the resource after the check is done. This statement requires a lot of explanation which I’ll skip in this text.

Here’s a simple example of permission model (link).

If there’s an alternative (and cheaper) way to do it, I’ll be glad to hear about it @sam.

Another problem (which is solved by this pattern) is that some systems (such as dfinance) may require more gas on CRUD operations with the resource and it’s cheaper to carry/store representation of a resource instead of actually moving resource to its owner. It also solves the problem of tracking resources inside the system.

Example (a bit complicated):

  1. You have a debt contract and both lender and borrower need to know where the deal resource is stored. Howewer you want to allow borrower to sell his debt to another user who then becomes another borrower. Tracking this deal resource may become impossible if someone puts a bunch of debts into another resource and sells it as a pack. All the assets stored in this deal are locked unless the pack is unpacked (huh).

Solution

Security pattern - a resource pair where one is Security<For: copyable> and the other one is the Proof for this security. Both resources hold unique identifier of a security - a pair (address, id) which cannot be compomised as for getting address a signer type is used and ID is incremental for each each user.

If you’re new to move, see what is signer (link).

Properties

  1. Security can be proved only by its Proof. Why? Because they both hold unique ID which cannot be compromised.

  2. Proving operation automatically destroys both Proof and Security and confirms that Security can be applied in specific case.

  3. Security<For: copyable> can only hold copyable type which means that no one can lock their resources inside Security while being unable to prove.

  4. Proof is stored on the side of the prover (say, inside a Deal resource) while Security is can be passed/sold freely over the network.

  5. Security is meant to be lightweight resource - <For> generic inside may point to Deal address and maybe to deal_id.

  6. One can issue as many securitites as he wants for any purpose. The only thing he should care about is putting the right For structure in it.

Example

In words.

  1. Alice creates a bank - she gives curr A for collateral B.

  2. Bob wants to lend some money from Alice in exchange for collateral.

  3. Bob puts his collateral (B) into a bank and gets: Security and some amount of A.

  4. Proof for this security is stored in the bank.

  5. Whenever Bob (or anyone else who bought this security) wants to return money, he passes his Security into a bank, where it’s being checked/proved. If that results in success, he pays back the loan and gets the collateral.

What is important - Security becomes a single pass for some financial action at specific place. If someone buys/gets resource of kind Security<0x1::Bank::Debt>, he knows that it can be returned to the bank. And if conditions are met, this paper is destroyed-exchanged for an action.

Another example.

  1. Alice created a new token, but she only wants to allow some users to buy it (create an Option to buy this token). She issues 100 Securities of kind Security<TokenSale> and gives/sells them to some people.

  2. These people then (including Bob) are free to move/sell/buy these securities. And when the time of crowdsale comes, they can use this Option to buy the tokens.

Also an example for Collateral Debt Position (CDP) module. Careful - not an easy one to understand.

Known issues

It is possible that Security will never find its Proof. There can be multiple reasons for that. And to not store (but also not to oblige) Security can be issued with expiration date after which both Security and Proof can be freely destroyed. This is a minor issue because it is impossible to lock an asset inside.

Finally

I’ll be glad to hear any feedback on this. Spent some time trying this concept out in different ways. Yet I may not see something useful/harmful in any way.

And if there’s no RFC source for Move, maybe we should start one?

3 Likes

Hi, I remember having make a performance bond with my bank using my property as collateral. The bond expires in one years time.
I was a contractor by that time and I gave the bond to my client for the construction of a filling station which I agreed to complete for the sum of rs 1 200 000 in 100 days. The bond on the property was rs 72 000. I completed the word in the prescribed delay and the bank cancelled the bond after the completion of the contract and my deeds was returned to me. I think the bond acted as the Token. Am I right?
Thanks
Regards

Harold Marie
:mauritius: Libra Mauritius

Definitely some interesting stuff.

What would be really helpful for me is a smaller code example, even if it is a bit mocked up. the CDP module is definitely a bit large for just groking the usecases for Security and Proof

1 Like

Here’s a dummy contract. 0x1::Dfinance is the same as 0x1::Libra (if I remember it correctly) - has same methods. Coins must be :copyable - but that’s also de facto a standard.

Some implementations are omitted, but interface is obvious. Resource viewer or few reader-functions could help. But again - this is a simple example.

address 0xDF1 {

module ClosedSale {

    use 0x1::Time;
    use 0x1::Vector;
    use 0x1::Signer;
    use 0x1::Account;
    use 0x1::Dfinance as Finance;
    use 0x1::Security::{Self, Security, Proof};

    /// Owner of the option would know everything about the deal
    struct BuyOption<Off: copyable, Paid: copyable> {
        seller: address,
        amount: u128,
        sale_start: u64
    }

    resource struct T<Off: copyable, Paid: copyable> {
        offered: Finance::T<Off>,
        proofs: vector<Proof>,
        options_sum: u128,
        sale_start: u64,
        paid: Finance::T<Paid>
        // better to add Rate<Off, Paid> here
        // to set price for final exchange.
        // in this example we'll do 1:1 rate
    }

    public fun create_sale<Off: copyable, Paid: copyable>(
        account: &signer,
        offered: Finance::T<Off>,
        sale_start: u64 // timestamp
    ) {
        let options_sum = Finance::value(&offered);

        move_to(account, T<Off, Paid> {
            offered,
            sale_start,
            options_sum,
            paid: Finance::zero<Paid>(),
            proofs: Vector::empty<Proof>(),
        });
    }

    /// Create an Option and pass it somewhere, obviously
    public fun create_option<Off: copyable, Paid: copyable>(
        account: &signer,
        amount: u128
    ): Security<BuyOption<Off, Paid>> acquires T {
        let seller = Signer::address_of(account);
        let sale = borrow_global_mut<T<Off, Paid>>(seller);
        let sale_start = sale.sale_start;

        // options_sum is basically - how much money we've allowed to buy
        assert(sale.options_sum >= amount, 0);

        let (security, proof) = Security::issue(account, BuyOption<Off, Paid> {
            seller, amount, sale_start
        }, 0);

        sale.options_sum = sale.options_sum - amount;

        Vector::push_back(&mut sale.proofs, proof);

        (security)
    }

    /// That is the main trick - you can only pass Security here!
    /// And another trick is that it MUST be used!
    public fun use_option<Off: copyable, Paid: copyable>(
        account: &signer,
        security: Security<BuyOption<Off, Paid>>
    ): Finance::T<Off> acquires T {

        // Get the details for this Security
        // Safe due to inability to duplicate
        let BuyOption {
            seller,
            amount,
            sale_start
        } = *Security::borrow(&security);

        let sell  = borrow_global_mut<T<Off, Paid>>(seller);
        let proof = take_proof(&mut sell.proofs, &security);

        // This method fails if unable to prove
        Security::prove(security, proof);

        assert(Time::now() >= sale_start, 1);

        // maybe assets should be passed into this method, did withdraw for simplicity's sake        
        let to_pay = Account::withdraw_from_sender<Paid>(account, amount);

        Finance::deposit<Paid>(&mut sell.paid, to_pay);
        let rec = Finance::withdraw<Off>(&mut sell.offered, amount);

        (rec)
    }

    /// Did it native to skip implementation
    /// Should fail if there's no proof or not. Depends on implementation
    native fun take_proof<Off: copyable, Paid: copyable>(
        proofs: &vector<Proof>,
        security: &Security<BuyOption<Off, Paid>>
    ): Proof;
}
}

UPD: what I do love about this pattern is that main safety check is done for you - just match two resources, if they do not match, or if there’s no Proof at all - execution fails. And it is impossible to not use the Security (either pass it back or prove), but that’s up to developer to decide.

Dear Damir,
Thank you for your mail. I am already on Dfinance. Well we’ll be together there with Libra…
Regards

Harold Marie
:mauritius: Libra Mauritius