Skip to content

Commit

Permalink
updated docs
Browse files Browse the repository at this point in the history
  • Loading branch information
NoahSaso committed Sep 20, 2024
1 parent 8403d26 commit f662957
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 46 deletions.
12 changes: 5 additions & 7 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@ There are three pieces: the indexer, the API, Meilisearch, and Soketi.

![diagram](./images/indexer.png)

The indexer is composed of an instrumented blockchain binary, represented in the
diagram above as `wasmd`, and an exporter process that runs alongside it. The
blockchain binary is modified to emit state changes as they occur to the
specified file (`wasm.out`), and the exporter listens for changes to the file
and writes them to a PostgreSQL database. Each line written to the file is a
JSON object representing a state change and its metadata, such as the contract
and block height.
The indexer is composed of a blockchain node running the normal binary,
represented in the diagram above as `node`, and an exporter process that runs
alongside it. The exporter listens for state events and writes them to a
PostgreSQL database. Each state write is formatted as a JSON object representing
a state change and its metadata, such as the contract and block height.

Read the [exporter docs](./exporter.md) for more information on how the exporter
works and what its responsibilities are.
Expand Down
7 changes: 6 additions & 1 deletion docs/cache.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Cache

The indexer relies heavily on caching to speed up queries. This document
> _NOTE_: The cache is currently inefficient, and while it speeds up queries
> significantly, the invalidation and revalidation logic causes the indexer to
> fall behind. The indexer is quite fast without the cache, so we disabled it
> for now. Fixing this would be awesome.
The indexer utilizes caching to avoid unnecessary queries. This document
explains how the cache works. The cache is represented by the
[`Computation`](../src/db/models/Computation.ts) model in the database.

Expand Down
13 changes: 11 additions & 2 deletions docs/formulas.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ Formulas are defined in the `data/formulas` directory.

There are four types of formulas:

- `account`
- `contract`
- `generic`
- `validator`
- `wallet`

`generic` takes no address, whereas the others take an address. They are
differentiated because they are expected to work slightly differently, though
under the hood they are processed the same way. When using `generic` formulas,
any address can be passed in, but it is ignored.
any address can be passed in, and it is ignored.

## Formula Structure

Expand Down Expand Up @@ -80,6 +80,15 @@ type Env = {
getCodeIdKeyForContract: FormulaCodeIdKeyForContractGetter
getSlashEvents: FormulaSlashEventsGetter
getTxEvents: FormulaTxEventsGetter
getBalance: FormulaBalanceGetter
getBalances: FormulaBalancesGetter
getProposal: FormulaProposalGetter
getProposals: FormulaProposalsGetter
getProposalCount: FormulaProposalCountGetter
getProposalVote: FormulaProposalVoteGetter
getProposalVotes: FormulaProposalVotesGetter
getProposalVoteCount: FormulaProposalVoteCountGetter
getCommunityPoolBalances: FormulaCommunityPoolBalancesGetter
}
```
Expand Down
Binary file modified docs/images/indexer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
76 changes: 40 additions & 36 deletions docs/start.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,58 @@

## Why does this exist?

This indexer is designed to be used with blockchains running the Cosmos SDK that
support smart contracts via the x/wasm module. Its critical innovation is that,
rather than indexing the transactions and events that end up in a block and are
made queryable by an RPC node, it indexes the _state changes_ that occur as a
result of those transactions and events.
This indexer is designed to be used with blockchains running the Cosmos SDK, and
primarily those that support smart contracts via the x/wasm module. Its critical
distinction is that it indexes the _state changes_ that occur as a result of
transactions, instead of indexing the transactions and events that end up in a
block and made queryable by an RPC node.

### Why index state changes instead?

Transaction-based indexers can only read the inputs and outputs of on-chain
events, i.e. wallet messages executed on a contract and the events that contract
events, e.g. wallet messages executed on a contract and the events that contract
responds with. They _cannot_ access the state changes that occur as a result of
those messages, nor access data that the contract developer did not decide to
emit in the execution events log. This is a problem for two reasons:

1. If a contract doesn't emit the data you want to index, you can't index it.
2. If a contract executes another contract, you can't index the sub-execution
(since they don't show up in logs).

One example of this is indexing the cw20 (token smart contract) balances for a
wallet. The default cw20 implementation does not emit the resulting balance of a
transfer—only the amount transferred. To index the balance of a wallet, you
would have to index every single transfer message in order, from the beginning,
and keep a running total manually. If the indexer crashes, you'll have to make
sure to start from the same block height, and if you miss a block, the number
will be wrong. Moreover, if a _different_ contract executes a transfer on a cw20
contract (as part of a \_sub_message, which is one contract calling another),
you won't be able to index that transfer at all, unless that contract decided to
emit the transfer amount in its events too. Thus, to index the balance of a
cw20, you would have to know every single contract that could possibly execute a
transfer on that cw20 contract and index all of those, assuming all contracts
also log the transfer amount. If any contract doesn't, your cw20 balances will
always be slightly off. On a permissionless blockchain where any smart contract
can be deployed, this essentially makes keeping track of balances impossible.

This also applies to cw721 (NFT smart contract) balances. If an NFT is sent to a
contract which performs a submessage to transfer that NFT somewhere else, the
NFT will be lost to the indexer forever, since you can no longer follow its
path.
emit in the execution events log.

This is a problem because a contract may not emit the data you need to index,
and thus a transaction-based indexer cannot index it. It also means that you
have to index every contract message in every block just to check if a specific
contract did something you care about, since any contract can call any other
contract. And if you want to track both historical and recent information,
you'll need to maintain and mutate your own state based on with laborious
event-matching code. Indexing new information in a transaction-based indexer may
even require rescanning old blocks.

One example of this is indexing cw20 token contract balances for a wallet. The
default cw20 implementation does not emit the resulting balance of a transfer in
a send transaction—only the amount transferred. So to index the balance of a
wallet, you would have to index every single transfer message in order, from the
beginning, and keep a running total manually. If the indexer crashes, you'll
have to make sure to start from the same block height, and if you miss a block,
the number will be wrong. Moreover, if a _different_ contract executes a
transfer on a cw20 contract (as part of a *sub*message, which is one contract
calling another), you will have to check every event from every message to find
the cw20 contract execution events, and you will only be able to detect it if
you know exactly which contract address to look for. On a permissionless
blockchain where any smart contract can be deployed and call any other contract,
this essentially makes keeping track of historical cw20 balances infeasible.

## Why does DAO DAO need this?

DAO DAO is an ecosystem of modular smart contracts that work together, and as
such, there is a lot of communication between them in the form of submessages.
For example, when a proposal is executed on a proposal module, it tells the core
contract to execute the messages contained in the proposal. To effectively index
this, we need to be able to access state changes that occur in all of the smart
contracts that get executed by other contracts. Thus this state-indexer was
born.
contract to execute the messages contained in the proposal, and reports the
execution status back to the proposal module. DAOs can also interact with
arbitrary contracts and enact arbitrary authorization flows—if another contract
is responsible for updating voting config or making proposals in the DAO, the
frontend needs to know about it as quickly as possible.

The indexer does all the hard work, making it very easy to index new contracts
and write new queries. You write formulas to create API endpoints that can
access any data from any contract, and it's immediately available at the latest
block and as far back as the indexer was running.

## How does it work?

Expand Down

0 comments on commit f662957

Please sign in to comment.