Skip to main content

Coinweb computation system

The Coinweb computation system can be broken down into creation of blocks in shards, processing of blocks, and processing of transactions.

Processing blocks

At a high level, computation in Coinweb consists of processing blocks. Blocks are produced by the shuffler. The shuffler in turn gets information from underlying L1 blockchains, such as Bitcoin or Ethereum, as well as information (in the form of Jump transactions) from other Coinweb shards.

The processing of blocks is broken down into Computations. Computations are similar to smart contracts, but:

  • They are extensions to the Coinweb protocol, meaning third parties are NOT free to include arbitrary computations.

  • They are not restricted by gas or resources.

  • They are provided a richer API to interact with the ClaimDB.

  • Instead of being called by transactions, they are executed after every block.

This way, computations are intended to extend the core of Coinweb, rather than specific dApps

The current Computations in Coinweb are as follows:

blockdiag

The computations in yellow are run in parallel and consist of:

  • Computing statistics about a block.
  • Computing block information.
  • Computing wCWEB staking information.

The computations in red are parallel internally.

info

The computation block for staking will be moved into the smart contract layer in a future update.

After the yellow computations are run, a fixed number of steps are performed, where each step executes the Coinweb VM computation and the Tokenization Block computation.

Transaction execution

In Coinweb, the execution of smart contracts is done in parallel, and the Coinweb architecture also supports multiple smart contract virtual machines running side-by-side with full interoperability between the smart contract VMs.

This is done by separating I/O operations (communicating, reading, and writing claims) as well as transactionality, from smart contract execution. I/O and smart contract execution are interleaved in defined steps.

Smart contracts

In Coinweb, a smart contract is a function that takes as input a transaction, and either fails or returns a list of transactions. The smart contract does not do I/O directly but does so indirectly through the output transaction it produces.

Smart contracts are invoked using the Call operation which identifies the smart contract virtual machine type (currently only WebAssembly is supported), dynamically loads the code to be executed, and executes the smart contract in its own VM.

Smart contract execution overview

Transactions are arrays of Coinweb VM operations.

In Transactions (v0), the available operations are:

  • Data - Data passed alongside a chain of transactions. Commonly used as input for smart contracts.

  • Read - Database lookups

  • Block - Block transaction on condition.

  • Take - Remove from the database a claim owned by the transaction issuer, and add the claim to the transaction. If the claim doesn't exist the transaction fails.

  • Store - Store into the database a claim owned by the transaction issuer.

  • Call - Invokes a smart contract.

Conceptually a smart contract is a function that takes as input a slice of a transaction and produces as output either a set of new transactions or a failure.

From the point of view of smart contract executions there are two types of operations:

  • Call: specify which smart contract to call, and the number of arguments (slice) to use

  • Any other op: operations such as Data, Read, Take, Agg, or Store will become arguments to the invoked smart contract. Each of them has some special behavior, that gets executed before the smart contract is invoked; for example, ReadOp will read a claim. For each op on the inputs slice, the smart contract will be able to see the op and the result of the op execution.

  • Given a parent transaction, for each call op, a smart contract will be called, and the combined output of all these contracts will become the transaction's children.

Example

The first example is a transaction with a call to a smart contract that takes 3 parameters. Two of the parameters are read as claims, and the 3rd parameter is inlined into the transaction. The transaction also includes a write (the write will be aborted if the smart contract fails).

Note that there is an operation that will not be present on any slice of a smart contract (the Store).

ditaa

The second example is a transaction with 4 smart contract calls, all of them succeeding, and producing 11 children.

Note that there will be parent transactions with no calls to smart contracts, and transactions with operations that will not be present on any slice to a smart contract. All operations, even those that are not input to smart contracts, get executed; notice that the execution of an operation has side effects that are useful by themselves (for example, Take removes a claim from the DB and increases the CWEB balance of the transaction, Read could be used to force the transaction to fail conditionally, Store creates a new transaction in the DB...etc).

Note that it is valid for a successful smart contract execution to produce 0 children.

Note that a transaction made up of 0 operations is considered valid (though not that useful).

ditaa

Transactionality rules

If any operation or any contract execution fails, the whole parent transaction and its children are aborted (that's why it is called a transaction!).

The transaction also gets aborted if:

  • The child transaction carry more CWEB than the parent transaction.

    • Otherwise, transactions would be allowed to create CWEB out of nowhere.
    • The CWEB carried by a transaction is the CWEB inside executed Take plus the CWEB inside Data minus the CWEB inside Store.
  • The CWEB difference between parent and child transactions is not enough to cover the network fees.

  • Child transactions contain (counting repetitions) some claim inside a Data that is neither issued by the called smart contract nor present on the parent transaction.

If there's no reason to abort it, the changes are committed and the child transactions will become "parent transaction" on the next execution iteration, producing their own new child transactions (or failing).

Once a child transaction becomes a parent transaction, it will be "detached" from its "siblings", meaning that if it gets aborted, it will not cascade to its children or its previous parent (which was already committed).

note

This means that individual transactions are transactional, but the overall execution and results of on-chain computation is NOT transactional! This property (or lack thereof) is unique to Coinweb and implies big tradeoffs:

  • Pros:

    • Scalability.
    • "Never-ending" contracts are possible.
    • Custom transactional semantics become a possibility.
  • Cons: Correctness of smart contracts is more tricky.

Example

In this example, we show two transactions being executed in a (parallel) step, and the second smart contract from the first transaction fails.

Things to note: The first (parent) transaction, including any external I/O effects, such as Take or Store are aborted. Also all children of any smart contract call from that transaction are aborted. This is shown in black.

The second transaction is unaffected by the failing smart contract in the first transaction.

ditaa

In this example, both parent transactions are aborted when there is a Store conflict in the child transactions. Everything in black is aborted because of the conflict in the red boxes.

For simplicity, the claims are called x, and y. When both 1 and 2 is simultaneously stored into y, this causes a conflict and both transactions are aborted.

On the other hand, two simultaneous writes to x would not cause transactions to be aborted as both Store operations update x with the same value and thus no conflict exists.

Creating an explicit conflict is a useful technique to enforce failure of otherwise unrelated transactions.

ditaa

Step loop

On each shard, the loop of "parent transaction creates children, then children become new parents" iterates forever, we call it the "execution loop".

On every block, we will execute a fixed number of "execution loop" iterations; After these iterations, the intermediate result is saved on disk/memory and resumed when the next block is found.

The number of iterations is determined by the constant shard_genesis_state.exec_iterations_per_block.