Skip to content

metacontract/indexer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

metacontract/indexer

  • A mc-companion embedded indexer.
  • Solidity-esque low-config portable binary program written in Rust.
  • Your Schema.sol will automatically be GraphQL API (backend: Seaography-SQLite)
  • You can run it in your frontend server or Cloudflare D1 instance (edge computing for low-latency querying).
  • Scale until "Your RAM Size > Your Dapp Indexer Size" with in-memory SQLite setting.

Dependencies

  • brew install pkg-config openssl

Diagrams

classDiagram
    class Compiler {
        +solc_path: String
        +base_slot_ast_cache: Option<String>
        +storage_layout_ast_cache: Option<String>
        +new(solc_path: String) -> Self
        +prepare_base_slots() -> Result<Value, Box<dyn std::error::Error>>
        +prepare_slorage_layout() -> Result<Value, Box<dyn std::error::Error>>
    }

    class Extractor {
        +compiler: Compiler
        +file: File
        +evm: EVM
        +eth_call: EthCall
        +registry: Registry
        +initial_members: Executable[]
        +new() -> Self
        +init_members_from_compiler()
        +listen()
        +scan_contract()
    }


    class TypeKind {
        <<enumeration>>
        Primitive
        NaiveStruct
        Array
        Mapping
    }
    class Executable {
        +name:String
        +step: usize
        +type_kind: TypeKind
        +value_type: String
        +belongs_to: Option<Executable>
        +offset: usize
        +relative_slot: String
        +absolute_slot: Option<String>
        +iter: Option<IteratorMeta>
        +executor: Executor
        +is_iterish: bool
        +mapping_key: String
        +new(name: String, type_kind: TypeKind, value_type: String, relative_slot: String, belongs_to: Executable, mapping_key: String, iter: Option<IteratorMeta>) -> Self
        +calculat_abs_slot(): String
        +get_edfs():String
        +get_type_and_name() -> String
        +enqueue_execution()
        +increment_step()
        +get_children(ast_node:String): Option<Vec<Executable>>
    }

    class IteratorMeta {
        +key_type: String
        +from: Option<usize>
        +to: Option<usize>
        +set_from(parsed_from:usize)
        +set_to(parsed_to:usize)
    }
    class Executor {
        +queue_per_step: Vec<Vec<Executable>>
        +executed_per_step: Vec<Vec<Executable>>
        +bulk_exec_and_enqueue_and_set_primitive_to_output(step:usize)
        +flush_queue(step:usize)
        +flush_executed(step:usize)
    }

    class Registry {
        +perf_config_items: HashMap<String, PerfConfigItem>
        +output_flatten: HashMap<String, Executable>
        +ast_node: ASTNode
        +set_output(edfs:String, e: Executable)
        +get_output(edfs: &str) -> Option<&Executable>
        +get_output_flatten() -> &Vec<Executable>
        +new(perf_config_items: HashMap<String, PerfConfigItem>, storage_layout: Value) -> Self
        +get_perf_config_item(edfs:String): PerfConfigItem
        +get_node(&self, edfs: &str) -> Option<&Node>
    }
    class ASTNode {
        +nodes: HashMap<String, Node>
        +from_storage_layout(storage_layout: Value) -> Self
        +parse_members(types: &Value, node_type: &str) -> Vec<Node>
        +get_node(&self, edfs: &str) -> Option<&Node>
    }
    class Node {
        label: String,
        members: Vec<Node>,
        offset: usize,
        slot: String,
        node_type: String,
    }
    class PerfConfigItem {
        +edfs: String,
        +from: Option<String>,
        +to: Option<String>,
        +from_executed: Option<usize>,
        +to_executed: Option<usize>
    }
    class PerfExpressionEvaluator {
        static eval(expression:String) -> usize
    }

    Main --> Extractor
    Extractor --> Compiler
    Extractor --> Registry
    Registry <-- PerfConfigItem
    Executable<--IteratorMeta
    Executor-->PerfExpressionEvaluator
    Executable-->TypeKind
    Executor --> Registry
    Executor --> Executable
    Registry --> ASTNode
    ASTNode --> Node



Loading
sequenceDiagram
    participant Main
    participant Extractor
    participant Compiler
    participant Registry
    participant Executable
    participant Executor

    Main->>Extractor: new()
    activate Extractor

    Extractor->>Compiler: new(solc_path)
    activate Compiler
    Compiler-->>Extractor: compiler
    deactivate Compiler

    Extractor->>Extractor: init_members_from_compiler()
    activate Extractor
    Extractor->>Compiler: prepare_base_slots()
    activate Compiler
    Compiler-->>Extractor: base_slots
    deactivate Compiler
    Extractor->>Compiler: prepare_slorage_layout()
    activate Compiler
    Compiler-->>Extractor: storage_layout
    deactivate Compiler
    Extractor->>Extractor: create Member objects from base_slots and storage_layout
    Extractor->>Registry: new(perf_config_items, storage_layout)
    activate Registry
    Registry-->>Extractor: registry
    deactivate Registry
    Extractor-->>Extractor: initial_members
    deactivate Extractor

    Main->>Extractor: listen()
    activate Extractor
    Extractor->>Extractor: scan_contract()
    activate Extractor

    %% step = 0
    loop for each step
        Extractor->>Executor: bulk_exec_and_enqueue_and_set_primitive_to_output(step)
        activate Executor

        loop for each queued executables
            Executor->>Executable: calculate_abs_slot()
            activate Executable
            Executable-->>Executor: abs_slot
            deactivate Executable
        end

        Executor-->>EthCall: get values by slots
        EthCall-->>Executor: values
        Executor-->>Executor: flush_queue(step)

        loop for each executed
            Executor->>Registry: get_perf_config_item(edfs: String)
            activate Registry
            Registry-->>Executor: perf_config_item 
            deactivate Registry

            %% For Mapping, skip. Because it doesn't have value in the slot. iter.to will be filled a logic below.
            %% For NaiveStruct, skip. Becuase 1st child is overlapping the slot.
            %% In the logic of set_value()
            %%   For Primitive, self.value = value
            %%   For Array, self.iter.to = value
            Executor-->>Executable: set_value
            activate Executable
            Executable-->>Executor: 
            deactivate Executable

            critical executable.typeKind == TypeKind.Primitive
                option true
                    Executor->>Registry: push_output(executable)
                    activate Registry
                    Registry-->>Executor: 
                    deactivate Registry
                    %% Executed primitive must be recorded.
                option false
                    %% Executed NaiveStruct, Array, Mapping
                    Executor->>Executable: get_children(ast_node)
                    activate Executable
                    Executable-->>Executor: Executables
                    deactivate Executable

                    %% enqueueability check for one-step below executables
                    Executor->>Registry: get_perf_config_item(edfs: String)
                    activate Registry
                    Registry-->>Executor: perf_config_item 
                    deactivate Registry

                    critical !executable.iter
                        option false
                            critical !!executable.iter.to
                                option true
                                    loop i in executable.iter.to
                                        Executor->>Executor: Executable.new()
                                        Executor->>Executable: enqueue_execution()
                                        activate Executable
                                        Executable-->>Executor: 
                                        deactivate Executable
                                    end
                                option false
                                    %% trying to fill iter.{from,to}
                                    %% Array: Just have been set executed value
                                    %% Mapping: with eval-ing conf
                                    Executor->>PerfExpressionEvaluator: eval(from_expression:String)
                                    activate PerfExpressionEvaluator
                                    PerfExpressionEvaluator-->>Executor: parsed_from
                                    deactivate PerfExpressionEvaluator
                                    Executor->>PerfExpressionEvaluator: eval(to_expression:String)
                                    activate PerfExpressionEvaluator
                                    PerfExpressionEvaluator-->>Executor: parsed_to
                                    Executor-->>IteratorMeta: set_from(parsed_from)
                                    Executor-->>IteratorMeta: set_to(parsed_to)
                                    deactivate PerfExpressionEvaluator

                                    %% Skipping algo for a mapping's unloaded bin_index
                                    critical executable.iter.to > 0
                                        option false
                                            Executor->>Executable: increment_step()
                                            Executor->>Executable: enqueue_execution()
                                            activate Executable
                                            Executable-->>Executor: 
                                            deactivate Executable
                                            %% if perf_config_item is not found, enqueue the executable for the next step
                                    end
                            end
                        option true
                            critical !!executable.abs_slot && !executable.value
                                option true
                                    Executor->>Executable: enqueue_execution()
                                    activate Executable
                                    Executable-->>Executor: 
                                    deactivate Executable
                                %% "option false" case means this executable should simply be value-filled on next bulk exec.                        
                            end
                    end

            end

            Executor-->>Extractor: 
            deactivate Executor
        end

        Executor-->>Executor: flush_executed(step)
        %% step += 1;

        critical step > 15
            option true
                Executor-->>Executor: break;
                %% 2^4 nest is the limit for safety and efficiency
                %% malformed perf_config causes infinite loop due to enqueue skipping algo 
        end
    end

    Extractor->>Registry: get_output_flatten()
    activate Registry
    Registry-->>Extractor: output_flatten
    deactivate Registry
    Extractor-->>Main: output_flatten
    deactivate Extractor

    deactivate Extractor
Loading

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages