Common Design Patterns
We now explore some common design patterns to take advantage of microchains.
Applications with only user chains
Some applications such as payments only require user chains, hence are fully horizontally scalable:
flowchart LR
user1(["user 1"]) -- initiates transfer --> chain1
subgraph validators_only_users["Validators"]
chain1["user chain 1"] -- "sends assets" --> chain2["user chain 2"]
end
chain2 -- notifies --> user2(["user 2"])
%% Styling
style validators_only_users fill:#1A4456,stroke:#70D4D3,stroke-width:2px,stroke-dasharray:3 3,rx:10,ry:10
style user1 fill:#8B7355,stroke:#EDE4D2,stroke-width:2px
style user2 fill:#8B7355,stroke:#EDE4D2,stroke-width:2px
style chain1 fill:#3A6B7A,stroke:#A0E3E2,stroke-width:2px
style chain2 fill:#3A6B7A,stroke:#A0E3E2,stroke-width:2px
Example: the fungible demo application of the Linera codebase.
Client/server applications
Pre-existing applications (e.g. written in Solidity) generally run on a single chain of blocks for all users. Those can be embedded in an app chain to act as a service.
flowchart LR
user1(["user 1"]) -- initiates request --> chain1
user1 ~~~ chain1
provider(["block producer"]) -- initiate response(s) --> chain3
chain1 -- notifies --> user1
chain3 -- notifies --> provider
subgraph validators_cs["Validators"]
chain1["user chain 1"] -- "sends request" --> chain3
chain1 ~~~ chain3
chain2["user chain 2"] --> chain3
chain3["app chain"] -- "sends response" --> chain1
end
%% Styling
style validators_cs fill:#1A4456,stroke:#70D4D3,stroke-width:2px,stroke-dasharray:3 3,rx:10,ry:10
style user1 fill:#8B7355,stroke:#EDE4D2,stroke-width:2px
style provider fill:#A0736B,stroke:#D2E8C8,stroke-width:2px
style chain1 fill:#3A6B7A,stroke:#A0E3E2,stroke-width:2px
style chain2 fill:#3A6B7A,stroke:#A0E3E2,stroke-width:2px
style chain3 fill:#4A7B75,stroke:#70D4D3,stroke-width:2px
Depending on the nature of the application, the blocks produced in the app chain may be restricted to only contain messages (no operations). This is to ensure that block producers have no influence on a chain, other than selecting incoming messages.
Example: the crowd-funding demo application of the Linera codebase.
Using personal chains to scale applications
User chains are useful to store the assets of their users and initiate requests to app chains. Yet, oftentimes, they can also help applications scale horizontally by taking work out of the app chains.
flowchart LR
user1(["user 1"]) -- submits ZK proof --> chain1
subgraph microchains_scale["Microchains"]
chain1["user chain 1"]
chain0["airdrop chain"]
chain1 -- "sends trusted message《ZK proof is valid》" --> chain0
chain1 ~~~ chain0
chain0 -- "sends tokens" --> chain1
chain2["user chain 2"] --> chain0
end
%% Styling
style microchains_scale fill:#1A4456,stroke:#70D4D3,stroke-width:2px,stroke-dasharray:3 3,rx:10,ry:10
style user1 fill:#8B7355,stroke:#EDE4D2,stroke-width:2px
style chain1 fill:#3A6B7A,stroke:#A0E3E2,stroke-width:2px
style chain2 fill:#3A6B7A,stroke:#A0E3E2,stroke-width:2px
style chain0 fill:#4A7B75,stroke:#70D4D3,stroke-width:2px
One of the benefits of personal chains is to enable transactions that would be too slow or not deterministic enough for traditional blockchains, including:
- Validating ZK proofs,
- Sending web queries to external oracle services (e.g. AI inference) and other API providers,
- Downloading data blobs from external data availability (”DA”) layers and computing app-specific invariants.
Example (unfinished): the airdrop demo application of the Linera project.
Using temporary chains to scale applications
Temporary chains can be created on demand and configured to accept blocks from specific users.
The following diagram allows a virtually unlimited number of games (e.g. chess game) to be spawned for a given tournament.
flowchart LR
subgraph microchains_scale["Microchains"]
chain1["user chain 1"] <--> chain3
chain2["user chain 2"] <--> chain3
chain3["tournament app chain"] -- creates --> chain0["temporary game chain"]
chain0 -- reports result --> chain3
end
user1(["user 1"]) -- request game --> chain1
user2(["user 2"]) -- request game --> chain2
user1 -- plays --> chain0
user2 -- plays --> chain0
%% Styling
style microchains_scale fill:#1A4456,stroke:#70D4D3,stroke-width:2px,stroke-dasharray:3 3,rx:10,ry:10
style user1 fill:#8B7355,stroke:#EDE4D2,stroke-width:2px
style user2 fill:#8B7355,stroke:#EDE4D2,stroke-width:2px
style chain1 fill:#3A6B7A,stroke:#A0E3E2,stroke-width:2px
style chain2 fill:#3A6B7A,stroke:#A0E3E2,stroke-width:2px
style chain3 fill:#4A7B75,stroke:#70D4D3,stroke-width:2px
Example: the hex-game demo application of the Linera codebase.
Just-in-time oracles
We have seen that Linera clients are connected and don’t rely on external RPC providers to read on-chain data from the chain. This ability to receive secure, censorship-resistant notifications and read data from the network is a game changer allowing on-chain applications to query certain clients in real time.
For instance, clients may be running an AI oracle off-chain in a trusted execution environment (TEE), allowing on-chain application to extract important information form the Internet.
flowchart LR
subgraph validators_only_users["Validators"]
chain2 -- oracle response --> chain1
chain1["app chain"] -- "oracle query" --> chain2["oracle chain"]
end
subgraph tee["Oracle TEE"]
user2(["oracle client"]) <--> ai["AI oracle"]
end
chain2 -- notifies --> user2
user2 -- submit response --> chain2
ai <--> web((Web))
%% Styling
style validators_only_users fill:#1A4456,stroke:#70D4D3,stroke-width:2px,stroke-dasharray:3 3,rx:10,ry:10
style tee fill:#1A4456,stroke:#A0E3E2,stroke-width:2px,stroke-dasharray:3 3,rx:10,ry:10
style user2 fill:#8B7355,stroke:#EDE4D2,stroke-width:2px
style ai fill:#A0736B,stroke:#D2E8C8,stroke-width:2px
style chain1 fill:#3A6B7A,stroke:#A0E3E2,stroke-width:2px
style chain2 fill:#3A6B7A,stroke:#A0E3E2,stroke-width:2px