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. There are two types of Data operations, Verified and Unverified. A Verified Data operation holds proof of who issued the claim.

  • Read - Database lookups. A Read executing in parallel with a Take will return the data as it was before the Take. A Read executing in parallel with a Store will likewise return the data as it was before the Store. A Read does not depend on who issues the Read, unlike Take and Store. Any smart contract can read any data written by other smart contracts.

  • Block - Block transaction on a set of conditions. Each condition must be satisfied before the rest of the transaction executes, but the conditions can be satisfied in any order, and do not need to hold when the transaction is unblocked (i.e. a condition can become true, but turn false before the transaction is executed).

  • Take - Remove from the database a claim issued by the transaction issuer, and add the claim to the transaction. If the claim doesn't exist the transaction fails. Two Take operations of the same claim (same key) will fail both transactions.

  • Store - Store into the database a claim as owned by the transaction issuer. Two store operations to the same claim (same issuer and same key) will fail both transactions if they are executed at the same execution step (if they are executed one after the other, then the latter will override the former).

  • Call - Invokes a smart contract.

Merging of identical transactions

Two transactions that are identical are merged into one transaction before execution. This means that for example duplicate Take or Store operations will not fail the transactions if the transactions are identical.

Two transactions are identical if all the operations are identical. This means, for example, that if the transaction contains any Take or Store, the transaction can only be merged with another transaction from the same smart contract (because these operations are related to the issuer and thus cannot be identical for transactions issued by different smart contracts).

This merging behavior can be exploited by parallel smart contract invocations to create a single next step transaction, for example to batch operations or to ensure that only a single side effect happens.

Smart contract calls

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.