Suggestion: Proposal for a capability-based facet-contract implementation for the STEEM blockchain.

in #utopian-io5 years ago (edited)

Repository

Proposal Description

The STEEM blockchain, despite its limited access-control model has a rich API that had given rise to a broad collection of DApps that rank quite decently on several DApp ranking sites. While many of the users of these DApps seem unbothered by the lack of fine-grained control over delegation that flow from the currently available authorization-by-proxy facilities for DApps currently provided by SteemConnect, that currently dominates both the authentication and the authorization needs for a wide range of different STEEM based DApps, the available facilities, from a least-authority perspective are far from perfect. Appart from usage by DApps, there are initiatives running on the STEEM blockchain that could do with a better fit for fine-grained delegation. One important example is the possibility to delegate STEEM-Power or SP to another account. For some purposes delegtion of SP is a good fit. For others, such as supporting the false-flag neutralizer service @freezepreach, or for following voting trails automatically, delegation of SP would be an ineffective alternative for delegation of fine-grained decomposed user authority. In this post I want to propose a capability-based facet-contract implementation for the STEEM blockchain that could benefit all these scenario's plus open up a new eccosystem for mesh-up service based DApps that the current infrastructure could never allow for.

Capabilities


(image credits: erights.org)

The proposal, that we will get to in a bit, builds upon the theory of capability capability formalized in the object-capability model. This model allows us to reason about access control differently and in a more dynamic, flexible and fine-grained fashion than is possible when thinking in terms of users (or roles) accessing resources according to some kind of access control list. The ideas behind capabilities are pretty straight forward ans surprisingly familiar for people with a programming background. You can think of the object capability model as a pure OO model without the inheritance stuff, that has no globals. An object A can only invoke an object B if it has a reference to it, and there are only three ways in which you could have gained a reference to the object:

  1. It was granted as part of the construction of A
  2. Object A created object B
  3. A reference to object B was passed to object A in an invocation of one of its methods.

With these basics in place, different o-cap design patterns can be used. One very common pattern it the Caretaker pattern. This pattern that provides the possibility for revocation of authority is so common and so essential to many access control situations that we consider it almost a primitive itself.

There is a lot more to the object capability model, that however falls outside of the scope of this article right now. The most important takeaway should be that capabilities are excellent for expressing dynamic fine-grained access control scenarios and play really nice with the OO way of looking at the world.

Delegating SP

From an access control point of view, delegating SP temporary relinquishes control over the SP asset in such a way that the owner is temporarily unable to use the asset himself. On some scenarios this is exactly what is desired. In other scenarios however, sharing access to this resource might be the preferred way. In our example scenario below we shall be discussing exactly one such a scenario.

SteemConnect Granularity


When using SteemConnect you will be asked to log into SteemConnect before delegating authority to a DApp. SteemConnect is what we call a Trusted Third Party. Note that we need to trust this TTP with at least our active key.
The active key permits the holder to do all kinds of operations, including the transfer of funds outside of our control. Basically, when using SteemConnect to delegate authority to a DApp, or even when just authenticating to a DApp that doesn't even require any of your keys authorities, you need to delegate full controll over all your STEEM and SBD funds to this TTP. Sure, this is better than trusting 20 DApps with that authority, but in the age of blockchains and object capabilities we must truly ask ourselves if delegating such authority to a TTP is something we should really want.


Now let's forget about the trust we place in the TTP. Once we get to delegating authority to a DApp using our TTP, what do we do? We get asked to allow the Dapp access to a somewhat lower rung of authority. In the example above, our full posting role. Notice again that this is quite a big chunk of attenuated authority with just a limited amount of decomposition down from the level of our active key. No, the DApp won't get access to our funds, but for example the authority to vote includes the authority to flag. Maybe we want to trust the Dapp with the ability to upvote, but not the ability to flag and downvote. Maybe we want to grant the ability to vote but not the ability to post. We want finer levels of granularity foe delegating our authorty.

The proposed solution

The proposed solution is to define a small set of new broadcast operations around the concept of capability-based facet contracts. The below list specifies the proposed smart contract framework in base properties.

  1. A contract is an on-chain active object with one or more invocable methods.
  2. Each contract method is defined by a unique private/public key pair of what the public part is defined and published as part of the contract. In this document, we refer to the private key of this key-pair as a syntactic capability.
  3. We distinguish between syntactic capabilities that are communicated through cryptographic signing with the private key of a method reference, and semantic capabilities that exist and are validated purely by applying the semantic rules imposed by the object-capability model.
  4. A contract that invokes or creates another contract and in doing so transfers part of its authority to the other contract, does so using only semantic capabilities.
  5. An API-client that invokes or creates a contract and in doing so transfers part of its authority to the contract in question does so using:
    • Syntactic capabilities
    • Only in construction, a signed description of an attenuated decomposition of her role specific authority. We refer to these authorities as role-primitives.
  6. From a logic, object-capability perspective, semantic and syntactic capabilities are the same, thus a capability argument on invocation may receive either.
  7. Arguments of an invocation may either be typed as data or as capability. A capability within the context of this article is defined to be an opaque reference to a contract method that may be used from within a contract to delegate authority to invoke a second contract to a third contract by means of invocation of one of that contract's methods.
  8. Authority can be installed upon a contract either through construction or through invocation of a method with a capability (either semantic or syntactic).
  9. A contract provides some base capability patterns as part of the standard contract. These include:
    • Auto-revocation of a method capability after N invocations.
    • Auto-revocation of the authority to invoke role-primitives after M uses.
    • Auto-revocation of the whole contract after L invocations of any method cap.
    • Auto-revocation of the whole contract at a set date/time.
  10. On construction, a contract initializes a persistent data structure for the whole contract, and potentially one for individual methods.
  11. The methods are defined in a small subset of JavaScript (possibly SES)

Mockups / Examples

Let us say I want to support @freezepeach in its efforts to neutralize false flags. I could delegate SP to @freezepeach, but as the need for its services are rather bursty, this would mostly mean my SP would go to waste or be used for things I didn't delegate it for.
A second alternative would be to delegate my entire posting role to @freezepeach using SteemConnect, but that would require me to trust SteemConnect with my active key and @freezepeach with my full posting role authority. What I really want to do is create a facet contract for my posting role and delegate that to @freezepeach. Let us examine a mock-up contract that works according to the above specs:

[
"contract_facet",
{ 
    "creator":  "pibara",
    "permlink": "my-first-contract",
    "limits": {
        "expires" : "2020-01-01T00:00:00Z" 
    },
    "authority": {
        "primatives": [
            {
                "role": "posting",
                "operation": "vote",
                "Call_limit": 1000  
            } 
        ]
    },
    "methods": [
        {
            "pubkeys": [
               "CTI5CCA4YCW5pU7B2f4SKYRZHqPDRj32DWG8RA4vUro7stLSwDxun",
               "CTR71mg2mBZ38i82PVWeFTytxzzNf6ZvbSn72DBqr6Fh8nm75bYrT"
            ],
            "name": "vote",
            "arguments" : {
                "data": {"author" : "str","permlink": "str","weight": "int"}
            },
            "data" : {
                "remaining": 2500000
            },
            "code": "
                let weight = arguments.data.weight;
                If  (weight > 10000) {
                    weight = 10000;
                }
                If (weight > method.data.remaining) {
                    weight = method.data.remaining;
                }
                contract.authority.primatives.posting.vote(contract.owner, arguments.data.author,
                                                                                  arguments.data.permlink, weight);
                method.data.remaining -= weight;
                If (method.data.remaining <= 0) {
                    method.revoke()
                }     
             " 
        }
    ]
}
]

Our user @pibara creates a facet contract named 'my-first-contract' that is set to expire at the end of the year. This facet contract is delegated the authority to invoke a vote broadcast operation up to 1,000 times before the expiration date. The facet contract defines a single method, also named 'vote' that takes three arguments. The method has a single integer value that is defined as the persistent state for the method and is initialized at 2,500,000.
The code section for the vote method imposes some additional limits. As 10,000 denotes 100% of voting strength for votes, the 2,500,000 equates 250 100% VS votes, or for example 1,000 votes at 25% of voting strength.

When just created, the creator of the contract, in our case @pibara, will be the one holding the secret key needed to invoke the facet contract. So the next step would be delegating the private key to @freezepeach.

[
"contract_delegate",
{ 
    "account" : "freezepeach",
    "type": "syntactic",
    "Method": {
        "owner": "pibara",
        "permlink": "my-first-contract",
        "name": "vote"
    },
    "delegation": "DLG6sUBN8dtsp1M8m7tPfUt7PacNNAFrt3jBSNtkIkM4Bv3e4CI4d
}
]

Note, this is just a mockup, the real delegation value might be a bit longer depending. The idea is that we encrypt the private key belonging to the vote method with @freezepeach's the memo key, and use that to create a contract_delegate broadcast operation.

It would also be possible for @pibara and @frrzepeach to exchange the private key off-chain if they had pre-established another secure form of communicating the key.

Now after @freezepeach has decrypted the private key for use with the vote facet method, it should be able to invoke the method:

[
"contract_invoke",
{
    "pubkey": "CTI5CCA4YCW5pU7B2f4SKYRZHqPDRj32DWG8RA4vUro7stLSwDxun",
    "nonce": 1844713915,
    "timestamp":  "2019-04-01T17:18:24Z",
    "signature": "SIG2BDA4PCN4bV7C2d2BBBBBBqFDPj31DPG5NA5vVpb4stSSvByvm"
    "arguments": {
        "author": "pibara",
        "permlink": "/ragnarok-conspiracy-1-44-prologue-nederlands",
        "weighth": 7500
    }
}
]

Note we don't use the contract owner, permlink and method name to address the method we are invoking. Instead, we use the private key of the method. The contract_invoke broadcast operation is called with the proper arguments, and in order to prove we hold the authority to invoke @pibara's contract this way, we sign the attributes, together with a timestamp and a unique nonce using the matching private key.

Now what if we wanted to change things. The contracts themselves, apart from their persistent state, are supposed to be immutable. Well, in that case we will just revoke the methods.

[
"contract_revoke",
{
    "pubkey": "CTR71mg2mBZ38i82PVWeFTytxzzNf6ZvbSn72DBqr6Fh8nm75bYrT",
    "nonce": 58714865,
    "timestamp":  "2019-04-01T17:18:24Z",
    "signature": "SIG4CAD3QNC2bW7A2d2BBCABApCGPj13DPD2AN4vWpd1stTTvCxmp"
}
]

This operation works similar to the contract_invoke operation.

Sample scenario semantic capabilities:

While the above was probably already quite technical for most, in order to discuss semantic capabilities, we need to step up the technical part even further. Don't worry if the below goes over your head when you read it for the first time. It isn't really that hard, but a change in the way of thinking. In our previous example, @pibara took the initiative to delegate his voting facet to @freezepeach. @freezepeach would need to do quite a bit of programming for just a tiny bit of voting power. Now what if @freezepeach would start off with a contract void of any authority with however a public interface for delegating voting facet contracts to it.

Please note the code in the below sample is lacking a lot of checks to keep the sample reasonably readable and to not overcomplicate this article. The idea is just to communicate the idea of semantic capabilities. I'm also omitting the pubkeys for brevity purposes and as not to distract from the core differences with our previous example.

[
"contract_facet",
{ 
    "creator":  "freezepeach",
    "permlink": "vote-collection",
    "authority" : {
        "vote" : {}
    },
    "methods": [
        {
            "pubkeys": [
                "",
                ""
            ],
            "public_cap": "CAP6zAadUoeL5BGYDjZAtJvVjJr9tgSHagwpvHJuf2q8ZSE3gYcyU",
            "name": "accept_contract",
            "arguments" : {
                "data" : { "account" : "str" } 
                "contract" : { 
                    "vote_contract" : {"author" : "str","permlink": "str","weight": "int"}
                }
            },
            "code" : "
                contract.authority.contracts.vote[arguments.data.account] =
                                                      arguments.contracts.vote_contract;
            "
        },
        {
            "pubkeys": [
                "",
                ""
            ],
            "name": "vote",
            "arguments" : {
                "data": {"author" : "str","permlink": "str","weight": "int"}
            },
            "code": "
                for(let key in contract.authority.contracts.vote) {
                    contract.authority.contracts.vote[key](arguments.data.author,
                                                                               arguments.data.permlink,
                                                                               arguments.data.weight);
                }
            "
        }
    ]
}
]

Now instead of delegating to @freezepeach, @pibara delegates his facet contract to @freezepeach's contract through invocation. Note that accept_contract has been made public access by publishing the private key of the method in the contract itself.


[
"contract_invoke",
{
    "pubkey": "",
    "nonce": 1844713915,
    "timestamp":  "2019-04-01T17:18:24Z",
    "signature": "",
    "contract_arguments": ["vote_contract"],
    "arguments": {
        "account": "pibara",
        "vote_contract": {
            "pubkey": "",
             "nonce": 1844713915,
             "timestamp":  "2019-04-01T17:18:24Z",
             "signature": "",
        }
    }
}
]

So far things should be straight forward. We have proven our authority to invoke the @freezepeach contract and our authority to invoke the @pibara contract as well. But now we come to the semantic part of things. No key material has actually been exchanged granting the @freezepeach contract the authority to invoke the @pibara contract. The delegation of the authority to invoke the @pibara to the @freezepeach contract is done purely by means of the semantics provided by the object-capability model. As all semantic links are recorded in the blockchain, the combination of syntactic and semantic capabilities makes for a very powerful way to express multi-stakeholder networks of simple facet style contracts.

Benefits

The benefits this proposal provides for the STEEM blockchain, above those provided by alternate smart-contract solutions, would lay partially in the fine-grained nature of the delegation of authority it provides, and the powerful targeted and/or least-authority revocation of this authority. The expressive nature of this proposal allows users and DApp developers to do tailored delegation without the need of a trusted third party such as SteemConnect. The capability-based nature of the proposal further allows a more service based architecture for DApp implementations, where multiple service could get woven together in a mesh-up using standard patterns of interaction.

More info

I realize this article may not fully convey the great match the STEEM blockchain and capabilities have the potential to be to those who never dealt with capability based security or the object capability model. So, as this article is supposed to be a proposal, not a capabilities primer, let me close with a few references:

GitHub Account

Sort:  

Hello, @mattockfs. This proposal is very detailed and informative. Well done! Thanks for keeping the proposal simple, although I had to go through it several times to understand some terms. I also appreciate that you provided some helpful resources to help understand the idea and how they could get implemented. You have done well.

I am looking forward to your next contribution. Thank you for using Utopian.

Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Chat with us on Discord.

[utopian-moderator]

Thank you for your review, @knowledges! Keep up the good work!

Hi @mattockfs!

Your post was upvoted by @steem-ua, new Steem dApp, using UserAuthority for algorithmic post curation!
Your post is eligible for our upvote, thanks to our collaboration with @utopian-io!
Feel free to join our @steem-ua Discord server

Hey, @mattockfs!

Thanks for contributing on Utopian.
Congratulations! Your contribution was Staff Picked to receive a maximum vote for the ideas category on Utopian for being of significant value to the project and the open source community.

We’re already looking forward to your next contribution!

Get higher incentives and support Utopian.io!
Simply set @utopian.pay as a 5% (or higher) payout beneficiary on your contribution post (via SteemPlus or Steeditor).

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

Hi @mattockfs, good idea. However, I think this is not an alternative to SteemConnect, why? because when you want to create a new facet-contract you will need to sign the operation with your active key, then you have to use a trusted software (cli-wallet, steemconnect, keychain, ...) to perform this operation, and this is the purpose of SteemConnect.

Coin Marketplace

STEEM 0.35
TRX 0.12
JST 0.040
BTC 70753.86
ETH 3589.34
USDT 1.00
SBD 4.75