Status

ACCEPTED

Stakeholders
Outcome

Unable to render Jira issues macro, execution error.

Due date
Owner

Background

Iroha Special Instructions and Iroha Queries processing requires a permissions based security model.

Whitepaper and Iroha v1 documentation were researched.

Problem

The White Paper requires protection of data from unauthorized read and write access.

Iroha 1 documentation gives a Permission's definition:

A named rule that gives the privilege to perform a command. Permission cannot be granted to an account directly, instead, account has roles, which are collections of permissions. Although, there is an exception, see Grantable Permission.

and Grant-able Permission:

Only grantable permission is given to an account directly. An account that holds grantable permission is allowed to perform some particular action on behalf of another account. For example, if account a@domain1 gives the account b@domain2 a permission that it can transfer assets — then b@domain2 can transfer assets of a@domain1 to anyone.

As you can see permissions were a first-level entities in Iroha 1 and had a strict hardcoded verification logic. Iroha1 was mainly planned to be used as a private blockchain and this system would work there. Yet Iroha2 is planned to be used in both private and public blockchains and therefore needs some degree of customization on how permissions are checked to implement some more complex cases.

Solution

As the Iroha project progresses towards being able to be used in different types of blockchains. It would be good to give more powers to developers. Therefore this RFC introduces a minimal Permission framework, which let's the developers of each blockchain have the power to set up their permission checks accordingly. Of course there will be an out of box implementations provided which can be used for simple blockchains, for more complex logic the developers will be able to define their completely own Permission checks implementation that would suit their needs.

TL;DR

  • Introduce Permission Checker trait
  • Make Iroha generic over `PermissionChecker` and `Permission` types
  • Implement out of box `IrohaPermissionChecker` with minimal checks and the ability to tweak it through the `IrohaPermissionCheckerBuilder`

Code Example


trait PermissionChecker<Permission> {
    pub fn check_permissions(instruction: ISI, authority: Id) -> Result<(), String>
}

Register<Permission, Account>: ISI #[derive(Encode, Decode, Serialize, Deserialize)] struct Account<Permission> { permissions: Vec<Permission>,
account_data: // .. other fields } struct Iroha<Permission, PC: PermissionChecker<Permission>> { pub checker: PC, // .. other fields }

Iroha<Iroha1Permission, Iroha1PermissionChecker>

enum Iroha1Permission {

}

Pros

  1. Customizable permissions check logic
  2. Customizable permission type
  3. Permission check logic is written in pure rust - which is a turing complete and convenient language while ISIs are not yet there.
  4. Faster than WASM
  5. Does not introduce additional complexity as WASM does.

Cons

  1. Can be customized only at compile time, can not be stored in genesis

Alternatives

  1. Use Iroha 1 approach with roles and grantable permissions and do hardcoded permissions checks inside instructions
    1. Pros:
      1. Tested in already running blockchains like Bakong and Byacco
    2. Cons:
      1. Hardcoded permission model - not possible to suit to different types of blockchains
  2. Use Iroha Special Instructions as scripting for checking permissions + Assets mechanisms to store. With two options: implement permission checks as triggers, or simply another part of validation pipeline.
    1. Pros:
      1. Customizable permissions check logic
      2. Can be changed at runtime and stored in file
    2. Cons:
      1. ISIs and Queries are not mature enough to write complex logic that might be needed for permission checks
      2. Triggers design is not finalized and they are not implemented.
  3. Use WASM permission check functions
    1. Pros:
      1. Customizable permissions check logic
      2. Can be changed at runtime and stored in file
      3. Can be written in any language that can be compiled to WASM
    2. Cons:
      1. WASM execution is in general slower
      2. Types through all the codebase will need to be adapted to be compatible with WASM
      3. More research about the WASM libraries and execution is needed

Additional Information

  • This solution impacts the Genesis Block design
  • This makes Iroha closer to a framework

24 Comments

  1. From one point of view it is nice, but the use of Asset word here may create misunderstanding and misuse. Maybe we can make derivable entities from some general Idea of something - maybe it will be Ownerable or Sharable interface - to Asset, Token, Permission, Role, and other entities. It might be important because some of the concepts will have additional properties, which other concepts will not have.

    Even the Asset type might require subtypes in the future for nonfungible tokens with a specific range of properties.

    1. I agree that we can try to find the better name for an `Asset` and we already have different types of assets (second part of your comment).

  2. > |account| account.asset("role") == ADMIN


    I think we should not compare the roles. Instead we should check that account has certain permission to perform some action. For example it might look like: `account.permissions.contains("can_transfer") == true`

    1. Otherwise it is good to go

      1. is it okay to store permissions in assets? `account.assets("permissions")...`

    2. It's important to also remember that permissions can be set on each asset, too. So you can transfer one asset but not another one.

      To me it seems like a bitmap/permission map on each asset makes sense. Similar to rwx in unix.

      1. So having an example of `XOR` Asset Definition and two accounts: `Nikita` and `Makoto` with 20 xor's assets each, do we place permission to transfer an `XOR` in general (definition) or this 20 xor's (assets)?

        1. This is a good question. My thoughts are it is with an account. If you want to create an asset that is not transferrable, then you would disable transfer permission on all accounts.

          1. ok, will return with a prototype soon


  3. The question is - should we execute them here or send a new transaction to the ledger?

    Such trigger executions should be a part of parent transactions called by account to identify the signature and account of a callee. Also possible to create nested transactions inside this original transaction especially if retry might happen or state updates independently for original action and trigger action. Ethereum uses nested transactions, but the only original transaction has a signature. Also, the transaction should be reverted, if the trigger was executed not successfully, so the trigger should be a part of the original transaction otherwise original transaction might be committed before the trigger result.

    Also trigger execution should depend on result of Original transaction if it failed or not.

  4. > Second one - how should we spread results of it's execution between peers and synchronize I/O actions like notifications, etc.?

    One of the approaches is that we share only the original transaction between peers, but every peer adds triggers by itself.

  5. First question - how should the listener understand was it triggered or not? It has only the current state, not the delta of changes.

    One of the possible approaches is that three should be some parameters of transaction which values can call a trigger. And if the transaction has some values in which this trigger is targeted, then the trigger will be executed. For example, if transactions have command transfer asset A to account B then trigger should be executed. The disadvantage of such an approach is that it requires additional checks for transactions that may slow the system.

    Smart Contract approach don't have this disadvantage.

  6. > Third one - how should we check that listener execution will not break the next transactions? Generally, it can break the next transactions it is how it should be. If the order is always deterministic and we know that listener execution finishes before the next transaction, then it is the right behavior. A problem will be if transactions and listeners will be executed concurrently or in indeterministic order.

  7. Listeners should be executed after all block's transactions execution


    Difficult to agree on it. Changes which listener add to the state on a business level are a part of original transaction. So, it looks like it is more appropriate that trigger breaks next transactions than next transactions break trigger called by the previous transaction.

  8. Makoto Takemiya 武宮誠 (Sora.org - Soramitsu), [19.06.20 14:40]
    [In reply to Iurii Vin]
    Event listeners should be stored on each validator and after every block commit, they should execute

    Makoto Takemiya 武宮誠 (Sora.org - Soramitsu), [19.06.20 14:40]
    I wrote a diagram of this a few months back

    Makoto Takemiya 武宮誠 (Sora.org - Soramitsu), [19.06.20 14:42]
    [ Photo ]

    Makoto Takemiya 武宮誠 (Sora.org - Soramitsu), [19.06.20 14:42]
    when a leader creates a block, the leader should then go through all events and collect transactions that would be executed after this block is confirmed

    Makoto Takemiya 武宮誠 (Sora.org - Soramitsu), [19.06.20 14:43]
    For example, if you have a Event: if account A receives > 100 XOR, send 10 XOR to account B

    Makoto Takemiya 武宮誠 (Sora.org - Soramitsu), [19.06.20 14:43]
    Then the block tx is: 115 XOR to account A from account C

    Makoto Takemiya 武宮誠 (Sora.org - Soramitsu), [19.06.20 14:44]
    and the validators see the event that was previously registered and generate the tx, 10 XOR to account B from account A

    Makoto Takemiya 武宮誠 (Sora.org - Soramitsu), [19.06.20 14:44]
    and this is just executed along with the block

    Makoto Takemiya 武宮誠 (Sora.org - Soramitsu), [19.06.20 14:46]
    [In reply to Makoto Takemiya 武宮誠 (Sora.org - Soramitsu)]
    This I mean w.r.t. on-chain events

    Makoto Takemiya 武宮誠 (Sora.org - Soramitsu), [19.06.20 14:46]
    If you mean something related to client-side, then this is not related

  9. If we use Follow up execution and create transactions, then we have to be careful on which peer creates this transactions. I think it should be only leader who creates this transactions, so that we will not have possible duplication of the same transaction executed multiple times.

    1. Ah okay, I see Makoto Takemiya also mentioned this.

  10. For the ASAP Execution I see a potential problem with OnTimestamp trigger that we have. As newly joined peers when synchronizing already will be in a different time period and will not execute this trigger.

  11. Comments from Andrei Lebedevabout triggers:

    it looks like an opportunity for decoupling, as triggers can be a part of iroha2-core, and permissions as a part of iroha2-isi.

    https://sawtooth.hyperledger.org/docs/core/releases/latest/architecture/events_and_transactions_receipts.html#events

    We can look at this as an example of such decoupling.

  12. I'd like also to have the ability to set permission for accounts and triggers itself.
    Most probably me or Vladislav already mentioned these points earlier.

    1. Permissions for triggers - For example, if we have permission for trigger than not matter if the trigger should be triggered or not by triggering conditions if account does not have permission for this trigger than the trigger will not be triggered and the state will be reverted except fee. =) Maybe this is possible with the current design?

    2. Permissions for an account - for example, permission to add assets to account Might be used to make this account sharable storage for different users even if they don't have private keys. Is this scenario possible with the current design?

    3. Also, it will be useful if we can give permission to triggers. For example that some trigger can change a specific asset, but other triggers cannot. It will protect us from the possibility to create similar but hacked triggers. is it possible with current design? 

    1. Thanks a lot for these cases Iurii Vinogradov, I will try to answer some of your questions today and maybe we will discuss more details on the RFC meeting.

      1. I think it's possible, one option to do it is to add additional check inside trigger. It can check permission of the account or maybe another asset (balance, status, reports, any business related) and prevent trigger execution if check failed.
      2. Yes, current prototype provides an ability to configure permissions based on accounts, domains and assets definition, for Mint Asset it looks like:
      610     #[derive(Clone, Debug, Serialize, Deserialize, Io, Encode, Decode)]                             
      611     pub struct MintAsset {                                                                          
      612         domain_name: Option<<Domain as Identifiable>::Id>,                                          
      613         account_id: Option<<Account as Identifiable>::Id>,                                          
      614         asset_definition_id: Option<<AssetDefinition as Identifiable>::Id>,                         
      615     }     

      3. This question is open. Right now I listed possible "authorities" for Iroha Triggers. We will protect Iroha from "hacked" triggers, but solution will depend on the answer to "authority" question.

  13. Just to be clear the second Alternative `Use assets, but do hardcoded permissions checks inside instructions` is the one we have implemented in the permissions prototype right now, and the suggestion is to move to the new solution proposed in this RFC.