๐ฎ scriptorium
Mix.install([
# HTTP client
{:req, "~> 0.5.8"},
# JSON parser
{:jason, "~> 1.4"},
# For visualization
{:kino, "~> 0.14.2"},
])
Application.put_env(:livebook, :deployment, [
title: "scriptorium",
public_access: true
])
port = System.get_env("LIVEBOOK_PORT", "8080")
hostname = System.get_env("LIVEBOOK_HOSTNAME", "localhost")
base_url = "http://#{hostname}:#{port}/apps"
๐ What is Chainbase Network ๐
Kino.Markdown.new("""

# The Basics
### Three Types of Data
Every blockchain is an immutable ledger and sequential data store. Chainbase Network gives you super powers for accessing and manipulating a blockchain's data.
Let's explore each of the **three types of data** in the various tabs below.
""") |> Kino.render()
raw = Kino.Markdown.new("""
#### Raw
Raw data is unformatted data coming straight from the blockchain. This could be blocks, transactions, blobs, logs or trace calls.
OK - calling the data 'unformatted' is a bit of a misnomer. The data is actually neatly formatted into queryable data tables. For example, if we look at an Ethereum Block - we have the following "raw" data from `ethereum.blocks` table:
| Field | Type | Description | Example |
| ------------ | ------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| block_number | BIGINT | Block number uniquely identifying the block | 19224000 |
| hash | VARCHAR | Hash value representing the unique identity of the block(with bloom filter) | 0xb2b0ac76e3b526ece64896a9f764258c5d5cdd562e46def4bbad65ff3b423a6f |
| parent_hash | VARCHAR | Hash value of the parent block(with bloom filter) | 0xdd68e6c9e141dce7d6da9a0b2544f1344ed9d3db91362914dfaf7fed77441e20 |
| nonce | VARCHAR | Nonce value associated with the block | 0x0000000000000000 |
The data has been truncated to fit above, but its overall schema is observable from this table.
""")
decoded = Kino.Markdown.new("""
#### Decoded
Decoded blockchain contract data refers to the information extracted from smart contracts on a blockchain that has been translated into a human-readable format.
Some data is not human-readable in its raw form. We have the ability to request decoded data as well.
For example, as is shown in the `ethereum.trace_calls_decoded` table, we have various data types:
| Field Name | Type | Description | Example |
| ----------------- | -------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| block_number | BIGINT | Block number uniquely identifying the block | 5247667 |
| block_timestamp | DATETIME | Timestamp of when the block was mined | 2018-03-13 11:25:51 |
| transaction_hash | VARCHAR | Hash value representing the unique identity of the block (with bloom filter) | 0xeb903ee535e48212e883369edd4649708e3c738d89456e90d5a7eec1060657a4 |
| transaction_index | INT | Index of the transaction within the block | 107 |
| trace_index | INT | Index of the trace within the transaction | 6 |
| from_address | VARCHAR | Token transaction sender (with bloom filter) | 0xf87a7ec94884f44d9de33d36b73f42c7c0dd38b1 |
| to_address | VARCHAR | Token transaction target user | 0x9a0242b7a33dacbe40edb927834f96eb39f8fbcb |
| method_id | VARCHAR | Corresponding Smart Contract Methods | 0xdd62ed3e |
| signature | VARCHAR | The signature of the transaction | allowance(address,address) |
| decoded | VARCHAR | The decoded data of the transaction | [["0xf87a7ec94884f44d9de33d36b73f42c7c0dd38b1","0xc893f7e271d1436ae3758cd59d5242884c02fb7f"],["0"]] |
""")
abstracted = Kino.Markdown.new("""
#### Abstracted
Abstracted blockchain data refers to information which has been compiled from decoded data into a higher level of abstraction. In other words, it is a subset of decoded data further processed for viewing and analysis.
Data may be in human-readable form, but require further processing to be useful. Chainbase network gives the ability retrieve data in even greater abstraction than simple decoding.
For example, data can be captured of [ERC20 approval events](https://eips.ethereum.org/EIPS/eip-20#events) from the `ethereum.erc20_approval`
| Field | Type | Description | Example
|------------------|---------|------------------------------|-----------------
| block_number | BIGINT | Block number containing the transaction | 11990
| block_timestamp | DATETIME| Timestamp of the block containing the transaction | 2016-03-22 22:43:12
| transaction_index| INT | Index of the transaction within the block | 1
| transaction_hash | VARCHAR | Transaction hash | 0xf006b47cb08b6229e0c1f19fd9b4637577e22b30c1c45debeae258bc0cb2af27
| log_index | INT | Log index | 1
| contract_address | VARCHAR | Event contract address | 0xab872f3ae424b209cb9c060ee80df04d99d47633
| _owner | VARCHAR | | 0x5c1967da329bd532ec75414e50230940029d0756
| _spender | VARCHAR | | 0xfbb1b73c4f0bda4f67dca266ce6ef42f520fbb98
| _value | VARCHAR | | 115792089237316195423570985008687907853269984665640564039457584007913129639935
""")
Kino.Layout.tabs(
"๐ข Raw": raw,
"๐ Decoded": decoded,
"๐งฎ Abstracted": abstracted
)
data_flow_section = Kino.Markdown.new("""
### Data Flow
1. **Raw Blockchain Data** comes from various sources. It is put in a common format so that it can be digested by the network actors and manipulated by developers.
2. Data, once in proper format, is added to the **Data Access Layer**. At the moment, this is done by the Chainbase team, but in the future, data providers from outside the team will be able to provide properly formatted data to the network permissionlessly.
3. Developers define data sets from the available data and place these definitions on the **Co-Processor Layer**. These definitions are called *'manuscripts'* and can be executed by the Chainbase Virtual Machine.
4. When manuscripts are queried, the **Execution Layer** handles compute and verifies data.
- (4a.) Simultaneously, the Validators make consensus on all transactions, ensuring validity of state.
5. Finally, the **Chainbase Virtual Machine (CVM)** provides the processed and hygenic, AI-ready data to users.
""")
data_flow_diagram = Kino.Mermaid.new("""
graph TB
User["6 - User/Application"]
Raw["1 - Raw Blockchain Data"]
subgraph Chain["Chainbase Network"]
subgraph Process["Data Processing"]
Access["2 - Data Access Layer"]
Proc["3 - Co-processor Layer"]
Exec["4 - Execution Layer"]
Access --> Proc
Proc --> |"Manuscript"| Exec
Exec --> |"CVM"| Out
end
subgraph Security["Security & Consensus"]
Val["4a - Validators"]
Val -.-> Exec
end
Out{{"5 - AI-Ready Data"}}
end
Raw --> Access
Out --> User
style User fill:#4527a0,color:#fff
style Raw fill:#5e35b1,color:#fff
style Chain fill:#f5f5f5,stroke:#673ab7
style Process fill:#f8f5ff,stroke:#7e57c2
style Security fill:#f8f5ff,stroke:#7e57c2
style Access fill:#673ab7,color:#fff
style Proc fill:#7e57c2,color:#fff
style Exec fill:#9575cd,color:#fff
style Val fill:#b39ddb,color:#fff
style Out fill:#FFD700,color:#000,stroke:#B8860B
""")
Kino.Layout.grid(
[data_flow_section, data_flow_diagram], columns: 2
)
Kino.Markdown.new("""
### Understanding Manuscripts' Network Function
Developers build and deploy manuscripts. Right now, manuscripts must be deployed locally to the machine on which they are developed. In the future, manuscripts will be published to the co-processor layer where they are made available to others. Chainbase network users can then select manuscripts and get the processed data. To better understand this data flow, investigate the diagram below:
""") |> Kino.render()
Kino.Mermaid.new("""
graph LR
Dev(("Developer"))
Create["Creates Data Processing
Recipes with Manuscripts"]
Share["Publishes to
Co-processor Layer"]
Reward["Earns Rewards When
Others Use Manuscript"]
subgraph Usage["How Others Use It"]
User["Data Consumer"]
Select["Selects Manuscript"]
Process["Gets Processed Data"]
User --> Select
Select --> Process
end
Dev --> Create
Create --> Share
Share --> Reward
Share -.-> Usage
style Dev fill:#4527a0,color:#fff,stroke-width:4px
style Create fill:#5e35b1,color:#fff
style Share fill:#673ab7,color:#fff
style Reward fill:#7e57c2,color:#fff
style Usage fill:#f5f5f5,stroke-dash:5
""")
# Create frames for our different outputs
table_frame = Kino.Frame.new()
summary_frame = Kino.Frame.new()
# Helper functions defined as variables
fetch_data = fn ->
url = "https://api.chainbase.com/api/v1/metadata/network_chains"
response = Req.get!(url)
# Extract response metadata
api_info = %{
method: "GET",
response_code: response.body["code"],
message: response.body["message"]
}
# Process chains data
chains = response.body["graphData"]
|> Enum.map(fn item ->
%{
name: item["chain"]["name"],
status: item["chain"]["status"],
block_height: item["chain"]["blockHeight"],
data_volume: item["chain"]["dataVolume"],
last_update: item["chain"]["lastUpdate"]
}
end)
# Calculate summary statistics
summary = %{
total_chains: length(chains),
online_chains: Enum.count(chains, & &1.status == "Online"),
total_data_volume: Enum.sum(Enum.map(chains, & &1.data_volume))
}
{chains, summary, api_info}
end
create_api_info_text = fn api_info ->
Kino.Markdown.new("""
### API Request Details
๐ก **Response**
- `#{api_info.method}`
- Code: #{api_info.response_code}
- Message: #{api_info.message}
""")
end
create_summary_text = fn summary ->
online_percentage = (summary.online_chains / summary.total_chains * 100) |> round()
meter_filled = String.duplicate("โ", div(round(online_percentage), 5))
meter_empty = String.duplicate("โ", 20 - div(round(online_percentage), 5))
Kino.Markdown.new("""
#### Network Summary
๐ต **Total Chains**: #{summary.total_chains}
๐ข **Online Chains**: #{summary.online_chains} / #{summary.total_chains}
#{meter_filled}#{meter_empty} #{online_percentage}% online
๐พ **Total Data Volume**: #{summary.total_data_volume} GB
*Last updated: #{DateTime.utc_now() |> Calendar.strftime("%Y-%m-%d %H:%M:%S UTC")}*
""")
end
# Create and render the section header
Kino.Markdown.new("""
### Available Data
We can query the network directly using a `GET` request on the `https://api.chainbase.com/api/v1/metadata/network_chains` endpoint to learn about the status of the network. For your convenience, the results of this request are displayed below.
""") |> Kino.render()
# Create refresh button and loading indicator
refresh_button = Kino.Control.button("Refresh Network Status")
loading_frame = Kino.Frame.new()
# Render table frame
table_frame |> Kino.render()
# Create frames for API info and summary
api_info_frame = Kino.Frame.new()
# Create bottom section with API info and summary side by side
Kino.Layout.grid([api_info_frame, summary_frame], columns: 2) |> Kino.render()
# Create a boxed grid layout for the button and loading indicator
Kino.Layout.grid([
refresh_button,
loading_frame
], columns: 2, boxed: true) |> Kino.render()
# Update function
refresh_data = fn ->
{chains, summary, api_info} = fetch_data.()
Kino.Frame.render(table_frame, Kino.DataTable.new(chains))
Kino.Frame.render(api_info_frame, create_api_info_text.(api_info))
Kino.Frame.render(summary_frame, create_summary_text.(summary))
end
# Add button listener with simple loading indicator
Kino.listen(refresh_button, fn _ ->
Kino.Frame.render(loading_frame, Kino.Markdown.new("Loading โณโณโณ"))
refresh_data.()
Kino.Frame.render(loading_frame, Kino.Markdown.new("โ
"))
end)
# Initial data load with loading indicator
Kino.Frame.render(loading_frame, Kino.Markdown.new("Loading โณโณโณ"))
refresh_data.()
Kino.Frame.render(loading_frame, Kino.Markdown.new("โ
"))
Kino.Markdown.new("""
Take a second to persue the data that has been shared above. You can filter and search as needed to explore the chains available. This is a great interactive tool to learn a little bit more about what is meant by **omnichain** network.
""")
next_link = "#{base_url}/scriptorium-manuscripts"
prev_link = "#{base_url}/scriptorium-intro"
Kino.Markdown.new("""
### ๐งญ Navigation
โ๏ธ To view the source code for this project visit https://github.com/KagemniKarimu/scriptorium
**[โ Previous: Introduction to Scriptorium](#{prev_link}) | [Next: Manuscripts & Their Mechanisms โ](#{next_link})**
""")