-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make
writeStateDiffAt
call on missing block and add metrics (#20)
* Add chainID flag and make chainConfig optional * Add prometheus metrics to monitor validation progress * Make writeStateDiffAt calls on missing blocks * Update docs and config improvements * Use standard logger * Add copyright to files * Upgrade dependencies
- Loading branch information
1 parent
716978e
commit acf8e6f
Showing
21 changed files
with
787 additions
and
144 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,75 +1,124 @@ | ||
- [Validator-README](#validator-readme) | ||
- [Overview](#overview) | ||
- [Intention for the Validator](#intention-for-the-validator) | ||
- [Edge Cases](#edge-cases) | ||
- [Instructions for Testing](#instructions-for-testing) | ||
- [Code Overview](#code-overview) | ||
- [Known Bugs](#known-bugs) | ||
- [Tests on 03/03/22](#tests-on-03-03-22) | ||
- [Set Up](#set-up) | ||
- [Testing Failures](#testing-failures) | ||
# ipld-eth-db-validator | ||
|
||
> `ipld-eth-db-validator` performs validation checks on indexed Ethereum IPLD objects in a Postgres database: | ||
> * Attempt to apply transactions in each block and validate resultant block hash | ||
> * Check referential integrity between IPLD blocks and index tables | ||
## Setup | ||
|
||
Build the binary: | ||
|
||
<small><i><a href='http://ecotrust-canada.github.io/markdown-toc/'>Table of contents generated with markdown-toc</a></i></small> | ||
```bash | ||
make build | ||
``` | ||
|
||
# Overview | ||
## Configuration | ||
|
||
This repository contains the validator. The purpose of the validator is to ensure that the data in the Core Postgres database match the data on the blockchain. | ||
An example config file: | ||
|
||
# Intention for the Validator | ||
```toml | ||
[database] | ||
# db credentials | ||
name = "vulcanize_public" # DATABASE_NAME | ||
hostname = "localhost" # DATABASE_HOSTNAME | ||
port = 5432 # DATABASE_PORT | ||
user = "vdbm" # DATABASE_USER | ||
password = "..." # DATABASE_PASSWORD | ||
|
||
The perfect scenario for the validator is as follows: | ||
[validate] | ||
# block height to initiate database validation at | ||
blockHeight = 1 # VALIDATE_BLOCK_HEIGHT (default: 1) | ||
# number of blocks to trail behind the head | ||
trail = 16 # VALIDATE_TRAIL (default: 16) | ||
# sleep interval after validator has caught up to (head-trail) height (in sec) | ||
sleepInterval = 10 # VALIDATE_SLEEP_INTERVAL (default: 10) | ||
|
||
1. The validator will have the capacity to perform historical checks for the Core Postgres database. Users can contain these historical checks to specified configurations (block range). | ||
2. The validator will validate a certain number of trailing blocks, `t`, trailing the head, `n`. Therefore the validator will constantly perform real-time validation starting at `n` and ending at `n - t`. | ||
3. The validator validates the IPLD blocks in the Core Database; it will update the core database to indicate that the validator validated it. | ||
# whether to perform a statediffing call on a missing block | ||
stateDiffMissingBlock = true # (default: false) | ||
# statediffing call timeout period (in sec) | ||
stateDiffTimeout = 240 # (default: 240) | ||
|
||
## Edge Cases | ||
[ethereum] | ||
# node info | ||
# path to json chain config (optional) | ||
chainConfig = "" # ETH_CHAIN_CONFIG | ||
# eth chain id for config (overridden by chainConfig) | ||
chainID = "1" # ETH_CHAIN_ID (default: 1) | ||
# http RPC endpoint URL for a statediffing node | ||
httpPath = "localhost:8545" # ETH_HTTP_PATH | ||
|
||
We must consider the following edge cases for the validator. | ||
[prom] | ||
# prometheus metrics | ||
metrics = true # PROM_METRICS (default: false) | ||
http = true # PROM_HTTP (default: false) | ||
httpAddr = "0.0.0.0" # PROM_HTTP_ADDR (default: 127.0.0.1) | ||
httpPort = "9001" # PROM_HTTP_PORT (default: 9001) | ||
dbStats = true # PROM_DB_STATS (default: false) | ||
|
||
- There are three different data types that the validator must account for. | ||
[log] | ||
# log level (trace, debug, info, warn, error, fatal, panic) | ||
level = "info" # LOG_LEVEL (default: info) | ||
# file path for logging, leave unset to log to stdout | ||
file = "" # LOG_FILE_PATH | ||
``` | ||
|
||
# Instructions for Testing | ||
|
||
Follow steps in [test/README.md](./test/README.md) | ||
* The validation process trails behind the latest block number in the database by config parameter `validate.trail`. | ||
|
||
# Code Overview | ||
* If the validator has caught up to (head-trail) height, it waits for a configured time interval (`validate.sleepInterval`) before again querying the database. | ||
|
||
This section will provide some insight into specific files and their purpose. | ||
* If the validator encounters a missing block (gap) in the database, it makes a `writeStateDiffAt` call to the configured statediffing endpoint (`ethereum.httpPath`) if `validate.stateDiffMissingBlock` is set to `true`. Here it is assumed that the statediffing node pointed to is writing out to the database. | ||
|
||
- `validator_test/chain_maker.go` - This file contains the code for creating a “test” blockchain. | ||
- `validator_test/validator_test.go` - This file contains testing to validate the validator. It leverages `chain_maker.go` to create a blockchain to validate. | ||
- `pkg/validator/validator.go` - This file contains most of the core logic for the validator. | ||
### Local Setup | ||
|
||
# Known Bugs | ||
* Create a chain config file `chain.json` according to chain config in genesis json file used by local geth. | ||
|
||
1. The validator is improperly handling missing headers from the database. | ||
1. Scenario | ||
1. The IPLD blocks from the mock blockchain are inserted into the Postgres Data. | ||
2. The validator runs, and all tests pass. | ||
3. Users manually remove the last few rows from the database. | ||
4. The validator runs, and all tests pass - This behavior is neither expected nor wanted. | ||
Example: | ||
|
||
# Tests on 03/03/22 | ||
```json | ||
{ | ||
"chainId": 41337, | ||
"homesteadBlock": 0, | ||
"eip150Block": 0, | ||
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", | ||
"eip155Block": 0, | ||
"eip158Block": 0, | ||
"byzantiumBlock": 0, | ||
"constantinopleBlock": 0, | ||
"petersburgBlock": 0, | ||
"istanbulBlock": 0, | ||
"clique": { | ||
"period": 5, | ||
"epoch": 30000 | ||
} | ||
} | ||
``` | ||
|
||
The tests highlighted below were conducted to validate the initial behavior of the validator. | ||
Provide the path to the above file in the config. | ||
|
||
## Set Up | ||
## Usage | ||
|
||
Below are the steps utilized to set up the test environment. | ||
* Create / update the config file (refer to example config above). | ||
|
||
1. Run the `scripts/run_integration_test.sh` script. | ||
1. First comment outline 130 to 133 from `validator_test/validator_test.go` | ||
2. Once the code has completed running, comment out lines 55 to 126, 38 to 40, and 42 to 44. | ||
1. Make the following change `db, err = setupDB() --> db, _ = setupDB()` | ||
3. Run the following command: `ginkgo -r validator_test/ -v` | ||
1. All tests should pass | ||
* Run validator: | ||
|
||
## Testing Failures | ||
```bash | ||
./ipld-eth-db-validator stateValidator --config=<config path> | ||
``` | ||
|
||
Once we had populated the database, we tested for failures. | ||
Example: | ||
|
||
1. Removing a Transaction from `transaction_cids` - If we removed a transaction from the database and ran the test, the test would fail. **This is the expected behavior.** | ||
2. Removing Headers from `eth.header_cids` | ||
1. If we removed a header block sandwiched between two header blocks, the test would fail (For example, we removed the entry for block 4, and the block range is 1-10). **This is the expected behavior.** | ||
2. If we removed the tail block(s) from the table, the test would pass (For example, we remove the entry for blocks 8, 9, 10, and the block range is 1-10). **This is _not_ the expected behavior.** | ||
```bash | ||
./ipld-eth-db-validator stateValidator --config=environments/example.toml | ||
``` | ||
|
||
## Monitoring | ||
|
||
* Enable metrics using config parameters `prom.metrics` and `prom.http`. | ||
* `ipld-eth-db-validator` exposes following prometheus metrics at `/metrics` endpoint: | ||
* `last_validated_block`: Last validated block number. | ||
* DB stats if `prom.dbStats` set to `true`. | ||
|
||
## Tests | ||
|
||
* Follow [Test Instructions](./test/README.md) to run unit and integration tests locally. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// VulcanizeDB | ||
// Copyright © 2022 Vulcanize | ||
|
||
// This program is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU Affero General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
|
||
// This program is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU Affero General Public License for more details. | ||
|
||
// You should have received a copy of the GNU Affero General Public License | ||
// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
package cmd | ||
|
||
import ( | ||
"github.com/spf13/viper" | ||
) | ||
|
||
const ( | ||
LOG_LEVEL = "LOG_LEVEL" | ||
LOG_FILE_PATH = "LOG_FILE_PATH" | ||
|
||
PROM_METRICS = "PROM_METRICS" | ||
PROM_HTTP = "PROM_HTTP" | ||
PROM_HTTP_ADDR = "PROM_HTTP_ADDR" | ||
PROM_HTTP_PORT = "PROM_HTTP_PORT" | ||
PROM_DB_STATS = "PROM_DB_STATS" | ||
|
||
DATABASE_NAME = "DATABASE_NAME" | ||
DATABASE_HOSTNAME = "DATABASE_HOSTNAME" | ||
DATABASE_PORT = "DATABASE_PORT" | ||
DATABASE_USER = "DATABASE_USER" | ||
DATABASE_PASSWORD = "DATABASE_PASSWORD" | ||
|
||
DATABASE_MAX_IDLE_CONNECTIONS = "DATABASE_MAX_IDLE_CONNECTIONS" | ||
DATABASE_MAX_OPEN_CONNECTIONS = "DATABASE_MAX_OPEN_CONNECTIONS" | ||
DATABASE_MAX_CONN_LIFETIME = "DATABASE_MAX_CONN_LIFETIME" | ||
|
||
ETH_CHAIN_CONFIG = "ETH_CHAIN_CONFIG" | ||
ETH_CHAIN_ID = "ETH_CHAIN_ID" | ||
ETH_HTTP_PATH = "ETH_HTTP_PATH" | ||
|
||
VALIDATE_BLOCK_HEIGHT = "VALIDATE_BLOCK_HEIGHT" | ||
VALIDATE_TRAIL = "VALIDATE_TRAIL" | ||
VALIDATE_SLEEP_INTERVAL = "VALIDATE_SLEEP_INTERVAL" | ||
VALIDATE_STATEDIFF_MISSING_BLOCK = "VALIDATE_STATEDIFF_MISSING_BLOCK" | ||
VALIDATE_STATEDIFF_TIMEOUT = "VALIDATE_STATEDIFF_TIMEOUT" | ||
) | ||
|
||
// Bind env vars | ||
func init() { | ||
viper.BindEnv("log.level", LOG_LEVEL) | ||
viper.BindEnv("log.file", LOG_FILE_PATH) | ||
|
||
viper.BindEnv("prom.metrics", PROM_METRICS) | ||
viper.BindEnv("prom.http", PROM_HTTP) | ||
viper.BindEnv("prom.httpAddr", PROM_HTTP_ADDR) | ||
viper.BindEnv("prom.httpPort", PROM_HTTP_PORT) | ||
viper.BindEnv("prom.dbStats", PROM_DB_STATS) | ||
|
||
viper.BindEnv("database.name", DATABASE_NAME) | ||
viper.BindEnv("database.hostname", DATABASE_HOSTNAME) | ||
viper.BindEnv("database.port", DATABASE_PORT) | ||
viper.BindEnv("database.user", DATABASE_USER) | ||
viper.BindEnv("database.password", DATABASE_PASSWORD) | ||
|
||
viper.BindEnv("database.maxIdle", DATABASE_MAX_IDLE_CONNECTIONS) | ||
viper.BindEnv("database.maxOpen", DATABASE_MAX_OPEN_CONNECTIONS) | ||
viper.BindEnv("database.maxLifetime", DATABASE_MAX_CONN_LIFETIME) | ||
|
||
viper.BindEnv("ethereum.chainConfig", ETH_CHAIN_CONFIG) | ||
viper.BindEnv("ethereum.chainID", ETH_CHAIN_ID) | ||
viper.BindEnv("ethereum.httpPath", ETH_HTTP_PATH) | ||
|
||
viper.BindEnv("validate.blockHeight", VALIDATE_BLOCK_HEIGHT) | ||
viper.BindEnv("validate.trail", VALIDATE_TRAIL) | ||
viper.BindEnv("validate.sleepInterval", VALIDATE_SLEEP_INTERVAL) | ||
viper.BindEnv("validate.stateDiffMissingBlock", VALIDATE_STATEDIFF_MISSING_BLOCK) | ||
viper.BindEnv("validate.stateDiffTimeout", VALIDATE_STATEDIFF_TIMEOUT) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.