---
# System prepended metadata

title: Block-STM

---

# 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()

```