# Block-STM ## Prior Works - [block-stm paper](https://arxiv.org/abs/2203.06871) - [sei-chain implementation](https://forum.sei.io/t/optimistic-concurrency-control/28) ## Our Contribution https://github.com/crypto-org-chain/go-block-stm - A new implementation as a standalone golang package - Can be hooked into cosmos-sdk with [minimal changes](https://github.com/crypto-org-chain/cosmos-sdk/pull/205) - Multiple optimisations on cosmos-sdk for better parallel execution performance ## Benchmark Results > Each op executes a block with `5000` erc20 transfer transactions, with `100` unique sender accounts distributed randomly. | Workers | ns/op | | | --------------------- | ----------- | ---- | | Sequential | 637,690,938 | 1x | | Block-STM, 8 workers | 137,451,953 | 4.6x | | Block-STM, 16 workers | 122,207,241 | 5.2x | [code](https://github.com/crypto-org-chain/cronos/blob/90ac130f7c99e0e62eefafbee9b799b1ba3f8606/app/bench_test.go#L237) ## Overview 1. Transactions are executed in parallel optimistically 2. The execution results are validated to detect conflicts 3. If validation fails, transactions are re-executed 4. The end results are identical to a sequential execution ## Overview (cont) - Transparent to users - Application logic should try to avoid shared state ## Accidentical State Sharings - Fee Collection - [Our refactoring](https://github.com/crypto-org-chain/cosmos-sdk/pull/237) to make it work with parallel execution - A few other cases in ethermint ## Cache - Contend for a `Mutex` in read operations - we removed the read cache from the cachekv stores, only buffer writes. ## Implementation ### Top-Level Scheduling - [Executor loop](https://github.com/crypto-org-chain/go-block-stm/blob/main/executor.go#L44) ### Multi-Version Data Structure [code](https://github.com/crypto-org-chain/go-block-stm/blob/main/mvdata.go) * Concept: `map[Key]map[TxnIndex]Value` ```golang // Read returns the value and the version of the value that's less than the given txn. // If the key is not found, returns `(nil, InvalidTxnVersion, false)`. // If the key is found but value is an estimate, returns `(nil, BlockingTxn, true)`. // If the key is found, returns `(value, version, false)`, `value` can be `nil` which means deleted. func (d *GMVData[V]) Read(key Key, txn TxnIndex) (V, TxnVersion, bool) { ``` ### Estimate Mark - Transaction that's aborted for failed validation will mark the keys that's touched with a "ESTIMATE" mark. - A transaction that reads a "ESTIMATE" mark will block and wait for the depending transaction to finish. ### Transaction State Machine ```mermaid stateDiagram-v2 [*] --> ReadyToExecute ReadyToExecute --> Executing: TrySetExecuting() Executing --> Executed: SetExecuted() Executing --> Suspended: Suspend(cond)\nset cond Executed --> Aborting: TryValidationAbort(incarnation) Aborting --> ReadyToExecute: SetReadyStatus()\nincarnation++ Suspended --> Executing: Resume() ```