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:
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.
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 ofData
operations, Verified and Unverified. A VerifiedData
operation holds proof of who issued the claim. -
Read
- Database lookups. ARead
executing in parallel with aTake
will return the data as it was before theTake
. ARead
executing in parallel with aStore
will likewise return the data as it was before theStore
. ARead
does not depend on who issues theRead
, unlikeTake
andStore
. 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. TwoTake
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
, orStore
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.
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
).
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).
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 insideData
minus the CWEB insideStore
.
-
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).
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.
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.
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.
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
.