Rchain State

This is an in-progress document started by Michael Birch (Unlicensed) which represents describes what the Rchain state is and how it is represented in the blockchain. This document ignores any details related to namespaces/sharding

The RChain state is a giant Rholang term: mostly top-level contracts that are parred together, i.e., contract foo {...} | contract bar {...} | contract baz {...} | ... | contract zzz {...}. The Rchain state will be replicated and updated through the Rchain blockchain. The genesis block contains this term explicitly, while future blocks only contain changes to this term and the hash of the complete term. Changes are inserted to this term through Deployments, which in turn will trigger communication (Comm) events (modeling the pi calculus construct where an input meets an output). The post-state of a block is the state is obtained from the parent post-state by "parring-in" the new deployments and applying the subsequent comm. events (both of which are given in the block). Term normalization, the VM transition rules, and a log of Deployments and Comm events will help the Casper consensus protocol to produce a post-state of a block that is consistent across nodes.

Structure of a Block

The block consists of three different tries: the Deployment root, the Comm event root, and the state root. These roughly correspond respectively to the Ethereum transaction root, receipts root, and state root. The state root trie is a giant key value store that maps names/channels to a tuple of continuations and lists of data (see the Storage Specification for details); i.e. it is the tuplespace. The block header includes a list of parent block hashes. The transactions in the parent blocks must not conflict with one another.

Structure of a Deployment

The Deployment has the unserialized format Deploy(<code>, <wallet details>, <rev/phlo conversion rate>) where <code> is a valid Rholang process. Let S be the current Rchain state, at the start of applying a Deploy operation, the state will be S' = S | <code>. Then Comm events are processed in S', until no further reductions are possible (quiescence). The final state after the Deploy operation, S'', is the quiescent state (i.e. all inputs/outputs are waiting for their corresponding outputs/inputs). Note that, due to the concurrent nature of Rholang, multiple Deployments could be processed at the same time.

The details of payment for computation will be in another document (link? Joseph Denman).

Retrieving the Rchain State

Any client that has been online since the genesis block will have the an independently verified full copy of the state. If a client joins in the future, they will need to ask out of protocol who the current validators are, ask for some blocks to determine the current root state hash, ask for the current state itself, and verify that the state they received matches the current root state hash.

Tokens on RChain

Introduction to Purses

Tokens on RChain (in particular Rev) will be managed by decentralized purses as opposed to ERC20-style maps with IDs as keys and balances as values. This is for scalability reasons, as a global single resource (like the map object needed for ERC20) requires locking to modify (if consistency is to be maintained that is), thus introducing a bottleneck in processing transactions. A purse will only be able to carry tokens of a single mint (e.g. Rev purses will be different from purses which hold a different, user created, token type). A purse instance will allow the following methods:

  • getBalance() – return the number of tokens in the purse
  • sprout() – create a new purse (of the same mint) containing 0 tokens
  • getDecr() – return the function used to decrease the balance of the purse (necessary for the transfer functionality used in the deposit function)
  • deposit(amount, src) – increase the number of tokens in this purse by transferring amount tokens from src, with the restriction that this and src be of the same mint and 0 <= amount <= src.getBalance.

Each token has a unique mint instance with a method makePurse(amount) the produces a new purse instance containing amount tokens for that mint – this is how new tokens are created. Tokens can be destroyed by discarding all references to a purse containing tokens. For example, if purseCh is a (Rholang) channel containing  the only reference to a particular purse instance then for(_ ← purseCh){ Nil } would effectively destroy all the tokens contained in that purse.

Rholang versions of mint and purse objects have been committed to GitHub: https://github.com/rchain/rchain/blob/dev/rholang/examples/linking/v0.1/packages/MakeMint.rho

Security of Tokens through object capabilities

Purse and mint instances are very powerful objects. Having the reference to a mint instance would literally allow you to print money (assuming the token associated with that mint had any value), and having a reference to someone else's purse object would allow you steal all their tokens via a simple call: myPurse.deposit(theirPurse.getBalance(), theirPurse). Therefore, in practice contracts will be used to wrap instances of these objects in order to limit what someone can do with them. Such contracts will function as attenuating forwarders which only pass on specific facets (capabilities) of the objects under certain conditions in order to maintain security of the tokens. Moreover, transferring tokens between purses will be done via an intermediary purse to keep each user's primary purse reference safe.

For example, suppose Bob needs to transfer 10 tokens to Alice. This can be done securely in two different ways:

  1. Bob creates a new purse and sends that instance to Alice: 

    //The following should be considered pseudocode, not necessarily valid in any particular programming language
    BobsPaymentToAlice = BobsMainPurse.sprout() //create new purse instance to use for paying Alice
    BobsPaymentToAlice.deposit(10, BobsMainPurse)
    Alice!(BobsPaymentToAlice) //send new purse instance to Alice for her to deposit

    Since Bob never gives away the reference to his main purse, his tokens are safe. And as long as no one else can intercept the reference to the purse sent on Alice's channel then Alice will be able to receive her payment and deposit the coins into her own main purse (which she should do right away because she does not know who else might have access to the purse instance she got from Bob, so it is more secure to have the tokens in her own purse).

  2. Alice creates an attenuated forwarder for her purse instance which prevents it from being used as the src argument of a deposit function, but it itself still has a deposit function of its own in order to receive payment.

    //The following should be considered pseudocode, not necessarily valid in any particular programming language
    contract AlicesDepositOnlyForwarder(methodName, arg) = {
      if (methodName == "deposit") {
        match arg with [amount, src] => { AlicesMainPurse.deposit(amount, src) }
      } else {
        "Error"
      }
    }
    Bob!(AlicesDepositOnlyForwarder)

    When Bob recieves Alice's forwarder then he can deposit his coins: AlicesDepositOnlyForwarder("deposit", [10, BobsMainPurse]), but cannot steal coins from Alice as BobsMainPurse.deposit(10, AlicesDepositOnlyForwarder) would fail. However, if Bob is unable to verify the functionality of Alice's forwarder then he may still wish to make the payment via a new purse instance (as in method 1) instead of using his main purse in the forwarder because he would not know if Alice's forwarder secretly remembers the instances of purses used with it – thus allowing Alice to steal Bob's coins later. And Alice does have an incentive to make her forwarder opaque (not able to be destructred in a pattern match) so that someone cannot extract her main purse instance from the forwarder.

Contracts which wrap purse or mint instance with conditions can be used to achieve the behaviour of multi sig wallets, or other more complicated behaviours. When RChain launches we will provide some standard generators (e.g. makeSigVerified) which are contracts that take purse instances and wrap them in a contract with some condition for security (e.g. signature verification).

The Rev Blessed Contract

The Rev contract will be the contract which wraps the mint instance for the Rev token. It will be secured via a condition which is specifed by the coop. Moreover, the Casper (proof-of-stake) contract will have access to the Rev contract in order to mint new tokens for validator rewards according to the business logic determined by the coop.

Paying for things with Tokens

Payment for any system-related operations (e.g. buying gas, bonding to be come a validator in Capser) must be done by providing an object with the right capabilities as an argument to the cal (i.e. if withdraw from that purse needs to happen then it must expose getDecr, but if only depositing in needing, as in PoS unbonding, then a forwarder with deposit exposed is sufficient). Therefore, it will be important to have a standardized API so that we can be agnostic about the specific objects which are actually be passed as arguments (purses or forwarders). However, the important note is that all "accounts" are actually purse instances or contracts wrapping purse instances. Ideally all payments would be parametric in the mint used, but perhaps initially only Rev purses/forwarders will be accepted.

PoS Blessed Contract

This privileged proof-of-stake contract must perform the following functions.

  1. Maintain a separate account registry which can be read by client software to learn the identities and staked amount for all validators
  2. Accept “bonds” from user accounts and use them to create new validator accounts
  3. Accept “unbond” messages which delete the validator account and restore its bond to the original user
  4. Handle slashing.
  5. Distributes rewards.

Details of these functions are given on the  Details of Proof-of-Stake in RChain page.

The bonding/unbonding processes must have rules which are coded into the contract. For example, rate limiting bonding to prevent the “army of ants” attack. Note that this is all which is required by the on-chain PoS contract, while software written in scala handles the remaining tasks:

  • listening for new blocks (or new state change requests for validators); i.e. networking stuff
  • only accepting valid changes/blocks (r.f. Above note about arbitrary parring; also ignoring malformed messages; and perhaps something about forced comm. events to mitigate censorship)
  • decided current head block based on received information and some fork-choice rule (which should somehow use the stake balances that are inside the PoS contract’s state)
  • deduce and store current state based on transactions which have been received; i.e. storage layer and VM layer stuff

The Registry Blessed Contract

Rholang allows sending and receiving data on any name, including  quoted processes such as @"Hello World!". This is an important feature, as it allows for exressive ways of defining class-like "objects" (see examples on GitHub), however could also lead to unwanted behaviour if contracts are specified at names which do not contain any unforgable part (e.g. phishing, itercepting calls intended for another contract). Therefore, the blockchain runtime for Rholang will have a special set of reserved names for which sending/receiving privliges must be obtained via a registry contract. Names of the form @`rho:...` (i.e. quoted URIs starting with "rho:") will not be able to be used for sending/receiving by default. There are three categories of these sorts of names that can be registered:

  • Code body hashes: e.g. rho:hash:sha256:... – requires submitted code body to match hash in URI
  • Public keys: e.g. rho:pubkey:secp256:... – requires proof of ownsership via a signature
  • Externally owned domain names: e.g. rho:iana:.. – requires proof of ownerhship via usual domain name methods