diff --git a/doc/architecture.md b/doc/architecture.md new file mode 100644 index 00000000..457b0482 --- /dev/null +++ b/doc/architecture.md @@ -0,0 +1,114 @@ +Beerus Architecture Overview +============================ + +## Components + +* Beerus client library (`src/client.rs`) +* Beerus RPC server + - Based on `axum` HTTP server + - RPC impl (`src/rpc.rs`) +* JSON-RPC spec (generated code: `src/gen.rs`) + - Starknet spec 0.7.1 (`etc/spec/starknet/0.7.1`) + - Generated by `iamgroot` (Rust DTO codegen tool) +* Ethereum client (`src/eth.rs`) + - Based on `helios` Ethereum light client +* Merkle proof check (`src/proof.rs`) +* Stateless execution (`src/exe/mod.rs`) + - `blockifier` + - `cairo-vm` + - `cairo-lang-*` +* WebAssembly library (`web/beerus-web`) + +## Execution + +### Get current state + +```mermaid +sequenceDiagram +Beerus->>Helios: Start +loop Sync +Helios->>(Beacon Chain): Query Beacon Chain +(Beacon Chain)->>Helios: Beacon Chain data +end +Note right of Helios: Helios is ready +Helios->>Beerus: Synced +Note right of Beerus: Beerus is ready +Beerus->>Helios: Query State +Beerus->>Helios: Get Latest Block +Helios->>(Ethereum RPC): Get Latest Block +(Ethereum RPC)->>Helios: Latest Block Number +Helios->>Beerus: Latest Block Number + +Beerus->>Helios: Call stateBlockNumber() +Helios->>(Ethereum RPC): Call Starknet Core Contract +(Ethereum RPC)->>Helios: Get Result +Helios->>Beerus: Get Result + +Beerus->>Helios: Call stateBlockHash() +Helios->>(Ethereum RPC): Call Starknet Core Contract +(Ethereum RPC)->>Helios: Get Result +Helios->>Beerus: Get Result + +Beerus->>Helios: Call stateRoot() +Helios->>(Ethereum RPC): Call Starknet Core Contract +(Ethereum RPC)->>Helios: Get Result +Helios->>Beerus: Get Result + +Beerus->>Beerus: Store Current State +``` + +### Stateless call (RPC) + +```mermaid +sequenceDiagram +(RPC Server)->>Beerus: starknet_call +Beerus->>Beerus: Check current state +Beerus->>Blockifier: Prepare execution context +Blockifier->>Blockifier: Create Starknet client +Blockifier->>Blockifier: Create State reader & write +loop Stateless Execution +Blockifier->>State Reader: State Request +State Reader->>(Starknet RPC): Query State +(Starknet RPC)->>State Reader: State Result +State Reader->>(Starknet RPC): Query State Proof +(Starknet RPC)->>State Reader: State Proof +State Reader->>State Reader: Verify State Proof +State Reader->>Blockifier: State Result +end +Note right of (RPC Server): Other methods are proxied +(RPC Server)->>Beerus: starknet_* +Beerus->>(Starknet RPC): (proxy the request) +(Starknet RPC)->>Beerus: (proxy the response) +Beerus->>(RPC Server): response +``` + +### Stateless call (WASM) + +```mermaid +sequenceDiagram +(Browser)->>(Browser): Check Proxy +(Browser)->>Beerus: Init +Note right of Beerus: Beerus is set up to run in a WebWorker +Beerus->>(Browser): Ready +(Browser)->>Beerus: Call +Beerus->>Client: Inject post() function +Client->>Client: create blocking StateReader +Note right of Client: Blocking StateReader is required by Blockifier +Client->>Client: create async StateReader +Client->>Beerus: Ready +Beerus->>Blockifier: Execute call +loop Stateless Execution +Blockifier->>Client: State Request +Client->>(Starknet RPC): State Request +(Starknet RPC)->>Client: State Result +Client->>(Starknet RPC): Get State Proof +(Starknet RPC)->>Client: State Proof +Client->>Client: Verify State Proof +Client->>Blockifier: State Result +end +Blockifier->>Beerus: Call Result +``` + +Beerus allows Blockifier to execute calls in a stateless manner by providing implementation of a `StateReader`. The `StateReader` implementation fetches necessary state (the value for the provided key to be exact) directly from Starknet RPC (and then pulls merkle proof for the value and verifies that it is valid). Thus during call execution Beerus has no control over which specific RPC methods are being called and how often - it depends on Blockifier and specific execution context of the call (contract & method that are being executed). + +Beerus workload is purely IO bound, as the only computation being performed is the verification of a merkle proof for a received key-value pairs. Thus performance of the stateless call execution depends on latency and frequency of RPC calls performed by Blockifier.