Compare commits
68
Commits
v0.2.9
...
v4.0.1-alpha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1dc90d0417 | ||
|
|
784ffb8726 | ||
|
|
1ddffe65be | ||
|
|
fdeece4b5d | ||
|
|
a58b16b0a1 | ||
|
|
a99dcb4ca9 | ||
|
|
d3fd7ef532 | ||
|
|
3e8f5d34fb | ||
|
|
1df0e8b840 | ||
|
|
5bdec6bc41 | ||
|
|
9fbc07c836 | ||
|
|
cdcc3df9f2 | ||
|
|
5f6aec35b2 | ||
|
|
867f5c9971 | ||
|
|
257b16e184 | ||
|
|
b4a899cc8c | ||
|
|
4363bf5c07 | ||
|
|
7c19e86698 | ||
|
|
6ed1bc739d | ||
|
|
32724ff46c | ||
|
|
9a7ecf9ef3 | ||
|
|
ea4fb6222b | ||
|
|
1d10009678 | ||
|
|
feca7b67db | ||
|
|
edef920ae4 | ||
|
|
ec0fdb5215 | ||
|
|
040bc850f0 | ||
|
|
0c630b6e99 | ||
|
|
b68bd29304 | ||
|
|
c3b007de26 | ||
|
|
e9428ac208 | ||
|
|
9521bda9d0 | ||
|
|
9f76540c3a | ||
|
|
747c5f5400 | ||
|
|
3725006984 | ||
|
|
e08d31e242 | ||
|
|
0ff60a69da | ||
|
|
a99b1975e4 | ||
|
|
316926a6b5 | ||
|
|
15cb0b8e8c | ||
|
|
caa1938ce6 | ||
|
|
0d60752b8a | ||
|
|
1126466d51 | ||
|
|
76155a1516 | ||
|
|
01ce8b26b5 | ||
|
|
c5f2e209b4 | ||
|
|
6e604c3db6 | ||
|
|
0c2a9bc171 | ||
|
|
f35c5cf71e | ||
|
|
fe94c5a33c | ||
|
|
55a20ac15f | ||
|
|
eee4330bf4 | ||
|
|
5543a52472 | ||
|
|
db786ae3b0 | ||
|
|
d8a3177a20 | ||
|
|
fb54640823 | ||
|
|
731c986d75 | ||
|
|
30b481db24 | ||
|
|
dcdfb9a05a | ||
|
|
d78a503ba4 | ||
|
|
ddbb690ee6 | ||
|
|
bb5d5322d2 | ||
|
|
108a6f89f1 | ||
|
|
d9eb68b2d2 | ||
|
|
c5c230a2ea | ||
|
|
dcf8afb96c | ||
|
|
212bf4c4b2 | ||
|
|
a4a3ec1d7a |
@@ -1,25 +0,0 @@
|
||||
name: Docker Compose Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Run docker build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Get the version
|
||||
id: vars
|
||||
run: echo ::set-output name=sha::$(echo ${GITHUB_SHA:0:7})
|
||||
- name: Run docker build
|
||||
run: make docker-build
|
||||
- name: Tag docker image
|
||||
run: docker tag vulcanize/eth-statediff-service docker.pkg.github.com/vulcanize/eth-statediff-service/eth-statediff-service:${{steps.vars.outputs.sha}}
|
||||
- name: Docker Login
|
||||
run: echo ${{ secrets.GITHUB_TOKEN }} | docker login https://docker.pkg.github.com -u vulcanize --password-stdin
|
||||
- name: Docker Push
|
||||
run: docker push docker.pkg.github.com/vulcanize/eth-statediff-service/eth-statediff-service:${{steps.vars.outputs.sha}}
|
||||
|
||||
@@ -3,10 +3,5 @@ name: Docker Build
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Run docker build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Run docker build
|
||||
run: make docker-build
|
||||
run-tests:
|
||||
uses: ./.github/workflows/tests.yml
|
||||
|
||||
@@ -3,9 +3,29 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
jobs:
|
||||
run-tests:
|
||||
uses: ./.github/workflows/tests.yml
|
||||
build:
|
||||
name: Run docker build
|
||||
runs-on: ubuntu-latest
|
||||
needs: run-tests
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Get the version
|
||||
id: vars
|
||||
run: echo ::set-output name=sha::$(echo ${GITHUB_SHA:0:7})
|
||||
- name: Run docker build
|
||||
run: make docker-build
|
||||
- name: Tag docker image
|
||||
run: docker tag vulcanize/eth-statediff-service docker.pkg.github.com/vulcanize/eth-statediff-service/eth-statediff-service:${{steps.vars.outputs.sha}}
|
||||
- name: Docker Login
|
||||
run: echo ${{ secrets.GITHUB_TOKEN }} | docker login https://docker.pkg.github.com -u vulcanize --password-stdin
|
||||
- name: Docker Push
|
||||
run: docker push docker.pkg.github.com/vulcanize/eth-statediff-service/eth-statediff-service:${{steps.vars.outputs.sha}}
|
||||
push_to_registries:
|
||||
name: Push Docker image to Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Get the version
|
||||
id: vars
|
||||
@@ -22,4 +42,3 @@ jobs:
|
||||
run: docker tag docker.pkg.github.com/vulcanize/eth-statediff-service/eth-statediff-service:${{steps.vars.outputs.sha}} vulcanize/eth-statediff-service:${{steps.vars.outputs.tag}}
|
||||
- name: Docker Push to Docker Hub
|
||||
run: docker push vulcanize/eth-statediff-service:${{steps.vars.outputs.tag}}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Tests for Geth that are used in multiple jobs.
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
GOPATH: /tmp/go
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Run docker build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run docker build
|
||||
run: make docker-build
|
||||
|
||||
statediff-unit-test:
|
||||
name: Run statediff unit tests
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GO111MODULE: on
|
||||
steps:
|
||||
- name: Create GOPATH
|
||||
run: mkdir -p /tmp/go
|
||||
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ">=1.18.0"
|
||||
check-latest: true
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
make test
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.13-alpine as builder
|
||||
FROM golang:1.18-alpine as builder
|
||||
|
||||
RUN apk --update --no-cache add make git g++ linux-headers
|
||||
# DEBUG
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
## Build docker image
|
||||
.PHONY: docker-build
|
||||
docker-build:
|
||||
docker build -t vulcanize/eth-statediff-service .
|
||||
docker build -t vulcanize/eth-statediff-service .
|
||||
|
||||
.PHONY: test
|
||||
test: | $(GOOSE)
|
||||
go test -p 1 ./pkg/... -v
|
||||
|
||||
build:
|
||||
go fmt ./...
|
||||
go build
|
||||
|
||||
@@ -2,14 +2,85 @@
|
||||
|
||||
[](https://goreportcard.com/report/github.com/vulcanize/eth-statediff-service)
|
||||
|
||||
>> standalone statediffing service ontop of leveldb
|
||||
>> standalone statediffing service ontop of LevelDB
|
||||
|
||||
Purpose:
|
||||
|
||||
Stand up a statediffing service directly on top of a go-ethereum leveldb instance.
|
||||
Stand up a statediffing service directly on top of a go-ethereum LevelDB instance.
|
||||
This service can serve historical state data over the same rpc interface as
|
||||
[statediffing geth](https://github.com/vulcanize/go-ethereum/releases/tag/v1.9.11-statediff-0.0.5) without needing to run a full node
|
||||
|
||||
## Setup
|
||||
|
||||
Build the binary:
|
||||
|
||||
```bash
|
||||
make build
|
||||
```
|
||||
|
||||
### Local Setup
|
||||
|
||||
* Create a chain config file `chain.json` according to chain config in genesis json file used by local geth.
|
||||
|
||||
Example:
|
||||
```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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* Change the following in [config file](./environments/config.toml)
|
||||
|
||||
```toml
|
||||
[leveldb]
|
||||
mode = "local"
|
||||
# Path to geth LevelDB data
|
||||
path = "/path-to-local-geth-data/chaindata"
|
||||
ancient = "/path-to-local-geth-data/chaindata/ancient"
|
||||
|
||||
[ethereum]
|
||||
chainConfig = "./chain.json" # Path to custom chain config file
|
||||
chainID = 41337 # Same chain ID as in chain.json
|
||||
|
||||
[database]
|
||||
# Update database config
|
||||
name = "vulcanize_testing"
|
||||
hostname = "localhost"
|
||||
port = 5432
|
||||
user = "postgres"
|
||||
password = "postgres"
|
||||
type = "postgres"
|
||||
```
|
||||
|
||||
* To write statediff for a range of block make changes in [config file](./environments/config.toml)
|
||||
```toml
|
||||
[prerun]
|
||||
only = false
|
||||
ranges = [
|
||||
[8, 15] # Block number range for which to write statediff.
|
||||
]
|
||||
```
|
||||
|
||||
* To use remote LevelDB RPC endpoint change the following in [config file](./environments/config.toml)
|
||||
```toml
|
||||
[leveldb]
|
||||
mode = "remote"
|
||||
url = "http://127.0.0.1:8082/" # Remote LevelDB RPC url
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### `serve`
|
||||
@@ -18,19 +89,24 @@ To serve state diffs over RPC:
|
||||
|
||||
`eth-statediff-service serve --config=<config path>`
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
./eth-statediff-service serve --config environments/config.toml
|
||||
```
|
||||
|
||||
Available RPC methods are:
|
||||
* `statediff_stateTrieAt()`
|
||||
* `statediff_streamCodeAndCodeHash()`
|
||||
* `statediff_stateDiffAt()`
|
||||
* `statediff_writeStateDiffAt()`
|
||||
* `statediff_writeStateDiffsInRange()`
|
||||
|
||||
### `write`
|
||||
e.g. `curl -X POST -H 'Content-Type: application/json' --data '{"jsonrpc":"2.0","method":"statediff_writeStateDiffsInRange","params":['"$BEGIN"', '"$END"', {"intermediateStateNodes":true,"intermediateStorageNodes":true,"includeBlock":true,"includeReceipts":true,"includeTD":true,"includeCode":true}],"id":1}' "$HOST":"$PORT"`
|
||||
|
||||
To write state diffs directly to a database:
|
||||
|
||||
`eth-statediff-service write --config=<config path>`
|
||||
|
||||
This depends on the `database` settings being properly configured.
|
||||
The process can be configured locally with sets of ranges to process as a "prerun" to processing directed by the server endpoints.
|
||||
This is done by turning "prerun" on in the config (`statediff.prerun = true`) and defining ranged and params in the
|
||||
`prerun` section of the config as shown below.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -38,31 +114,70 @@ An example config file:
|
||||
|
||||
```toml
|
||||
[leveldb]
|
||||
mode = "local"
|
||||
# path and ancient LevelDB paths required in local mode
|
||||
path = "/Users/user/Library/Ethereum/geth/chaindata"
|
||||
ancient = "/Users/user/Library/Ethereum/geth/chaindata/ancient"
|
||||
# url for leveldb-ethdb-rpc endpoint required in remote mode
|
||||
url = "http://127.0.0.1:8082/"
|
||||
|
||||
[server]
|
||||
ipcPath = "~/.vulcanize/vulcanize.ipc"
|
||||
ipcPath = ".ipc"
|
||||
httpPath = "127.0.0.1:8545"
|
||||
|
||||
[write]
|
||||
ranges = [[1, 2], [3, 4]]
|
||||
[write.params]
|
||||
IntermediateStateNodes = true
|
||||
IntermediateStorageNodes = false
|
||||
IncludeBlock = true
|
||||
IncludeReceipts = true
|
||||
IncludeTD = true
|
||||
IncludeCode = false
|
||||
|
||||
[statediff]
|
||||
workers = 4
|
||||
prerun = true
|
||||
serviceWorkers = 1
|
||||
workerQueueSize = 1024
|
||||
trieWorkers = 4
|
||||
|
||||
[prerun]
|
||||
only = false
|
||||
ranges = [
|
||||
[0, 1000]
|
||||
]
|
||||
[prerun.params]
|
||||
intermediateStateNodes = true
|
||||
intermediateStorageNodes = true
|
||||
includeBlock = true
|
||||
includeReceipts = true
|
||||
includeTD = true
|
||||
includeCode = true
|
||||
watchedAddresses = []
|
||||
|
||||
[log]
|
||||
file = "~/.vulcanize/statediff.log"
|
||||
file = ""
|
||||
level = "info"
|
||||
|
||||
[eth]
|
||||
chainID = 1
|
||||
|
||||
[database]
|
||||
name = "vulcanize_test"
|
||||
hostname = "localhost"
|
||||
port = 5432
|
||||
user = "vulcanize"
|
||||
password = "..."
|
||||
type = "postgres"
|
||||
driver = "sqlx"
|
||||
dumpDestination = ""
|
||||
filePath = ""
|
||||
|
||||
[cache]
|
||||
database = 1024
|
||||
trie = 1024
|
||||
|
||||
[prom]
|
||||
dbStats = false
|
||||
metrics = true
|
||||
http = true
|
||||
httpAddr = "localhost"
|
||||
httpPort = "8889"
|
||||
|
||||
[ethereum]
|
||||
nodeID = ""
|
||||
clientName = "eth-statediff-service"
|
||||
genesisBlock = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
|
||||
networkID = 1
|
||||
chainID = 1
|
||||
```
|
||||
|
||||
+56
-13
@@ -18,8 +18,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
|
||||
pg "github.com/ethereum/go-ethereum/statediff/indexer/postgres"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -28,11 +26,14 @@ const (
|
||||
ETH_GENESIS_BLOCK = "ETH_GENESIS_BLOCK"
|
||||
ETH_NETWORK_ID = "ETH_NETWORK_ID"
|
||||
ETH_CHAIN_ID = "ETH_CHAIN_ID"
|
||||
ETH_CHAIN_CONFIG = "ETH_CHAIN_CONFIG"
|
||||
|
||||
DB_CACHE_SIZE_MB = "DB_CACHE_SIZE_MB"
|
||||
TRIE_CACHE_SIZE_MB = "TRIE_CACHE_SIZE_MB"
|
||||
LVLDB_MODE = "LVLDB_MODE"
|
||||
LVLDB_PATH = "LVLDB_PATH"
|
||||
LVLDB_ANCIENT = "LVLDB_ANCIENT"
|
||||
LVLDB_URL = "LVLDB_URL"
|
||||
|
||||
STATEDIFF_PRERUN = "STATEDIFF_PRERUN"
|
||||
STATEDIFF_TRIE_WORKERS = "STATEDIFF_TRIE_WORKERS"
|
||||
@@ -48,12 +49,36 @@ const (
|
||||
PROM_HTTP_PORT = "PROM_HTTP_PORT"
|
||||
PROM_DB_STATS = "PROM_DB_STATS"
|
||||
|
||||
PRERUN_ONLY = "PRERUN_ONLY"
|
||||
PRERUN_RANGE_START = "PRERUN_RANGE_START"
|
||||
PRERUN_RANGE_STOP = "PRERUN_RANGE_STOP"
|
||||
PRERUN_ONLY = "PRERUN_ONLY"
|
||||
PRERUN_RANGE_START = "PRERUN_RANGE_START"
|
||||
PRERUN_RANGE_STOP = "PRERUN_RANGE_STOP"
|
||||
PRERUN_INTERMEDIATE_STATE_NODES = "PRERUN_INTERMEDIATE_STATE_NODES"
|
||||
PRERUN_INTERMEDIATE_STORAGE_NODES = "PRERUN_INTERMEDIATE_STORAGE_NODES"
|
||||
PRERUN_INCLUDE_BLOCK = "PRERUN_INCLUDE_BLOCK"
|
||||
PRERUN_INCLUDE_RECEIPTS = "PRERUN_INCLUDE_RECEIPTS"
|
||||
PRERUN_INCLUDE_TD = "PRERUN_INCLUDE_TD"
|
||||
PRERUN_INCLUDE_CODE = "PRERUN_INCLUDE_CODE"
|
||||
|
||||
LOG_LEVEL = "LOG_LEVEL"
|
||||
LOG_FILE_PATH = "LOG_FILE_PATH"
|
||||
|
||||
DATABASE_NAME = "DATABASE_NAME"
|
||||
DATABASE_HOSTNAME = "DATABASE_HOSTNAME"
|
||||
DATABASE_PORT = "DATABASE_PORT"
|
||||
DATABASE_USER = "DATABASE_USER"
|
||||
DATABASE_PASSWORD = "DATABASE_PASSWORD"
|
||||
|
||||
DATABASE_TYPE = "DATABASE_TYPE"
|
||||
DATABASE_DRIVER_TYPE = "DATABASE_DRIVER_TYPE"
|
||||
DATABASE_DUMP_DST = "DATABASE_DUMP_DST"
|
||||
DATABASE_FILE_PATH = "DATABASE_FILE_PATH"
|
||||
|
||||
DATABASE_MAX_IDLE_CONNECTIONS = "DATABASE_MAX_IDLE_CONNECTIONS"
|
||||
DATABASE_MAX_OPEN_CONNECTIONS = "DATABASE_MAX_OPEN_CONNECTIONS"
|
||||
DATABASE_MIN_OPEN_CONNS = "DATABASE_MIN_OPEN_CONNS"
|
||||
DATABASE_MAX_CONN_LIFETIME = "DATABASE_MAX_CONN_LIFETIME"
|
||||
DATABASE_CONN_TIMEOUT = "DATABSE_CONN_TIMEOUT"
|
||||
DATABASE_MAX_CONN_IDLE_TIME = "DATABASE_MAX_CONN_IDLE_TIME"
|
||||
)
|
||||
|
||||
// Bind env vars for eth node and DB configuration
|
||||
@@ -66,21 +91,33 @@ func init() {
|
||||
viper.BindEnv("ethereum.genesisBlock", ETH_GENESIS_BLOCK)
|
||||
viper.BindEnv("ethereum.networkID", ETH_NETWORK_ID)
|
||||
viper.BindEnv("ethereum.chainID", ETH_CHAIN_ID)
|
||||
viper.BindEnv("ethereum.chainConfig", ETH_CHAIN_CONFIG)
|
||||
|
||||
viper.BindEnv("database.name", pg.DATABASE_NAME)
|
||||
viper.BindEnv("database.hostname", pg.DATABASE_HOSTNAME)
|
||||
viper.BindEnv("database.port", pg.DATABASE_PORT)
|
||||
viper.BindEnv("database.user", pg.DATABASE_USER)
|
||||
viper.BindEnv("database.password", pg.DATABASE_PASSWORD)
|
||||
viper.BindEnv("database.maxIdle", pg.DATABASE_MAX_IDLE_CONNECTIONS)
|
||||
viper.BindEnv("database.maxOpen", pg.DATABASE_MAX_OPEN_CONNECTIONS)
|
||||
viper.BindEnv("database.maxLifetime", pg.DATABASE_MAX_CONN_LIFETIME)
|
||||
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.minOpen", DATABASE_MIN_OPEN_CONNS)
|
||||
viper.BindEnv("database.maxConnLifetime", DATABASE_MAX_CONN_LIFETIME)
|
||||
viper.BindEnv("database.connTimeout", DATABASE_CONN_TIMEOUT)
|
||||
viper.BindEnv("database.maxIdleTime", DATABASE_MAX_CONN_IDLE_TIME)
|
||||
|
||||
viper.BindEnv("database.type", DATABASE_TYPE)
|
||||
viper.BindEnv("database.driver", DATABASE_DRIVER_TYPE)
|
||||
viper.BindEnv("database.dumpDestination", DATABASE_DUMP_DST)
|
||||
viper.BindEnv("database.filePath", DATABASE_FILE_PATH)
|
||||
|
||||
viper.BindEnv("cache.database", DB_CACHE_SIZE_MB)
|
||||
viper.BindEnv("cache.trie", TRIE_CACHE_SIZE_MB)
|
||||
|
||||
viper.BindEnv("leveldb.mode", LVLDB_MODE)
|
||||
viper.BindEnv("leveldb.path", LVLDB_PATH)
|
||||
viper.BindEnv("leveldb.ancient", LVLDB_ANCIENT)
|
||||
viper.BindEnv("leveldb.url", LVLDB_URL)
|
||||
|
||||
viper.BindEnv("prom.metrics", PROM_METRICS)
|
||||
viper.BindEnv("prom.http", PROM_HTTP)
|
||||
@@ -96,6 +133,12 @@ func init() {
|
||||
viper.BindEnv("prerun.only", PRERUN_ONLY)
|
||||
viper.BindEnv("prerun.start", PRERUN_RANGE_START)
|
||||
viper.BindEnv("prerun.stop", PRERUN_RANGE_STOP)
|
||||
viper.BindEnv("prerun.params.intermediateStateNodes", PRERUN_INTERMEDIATE_STATE_NODES)
|
||||
viper.BindEnv("prerun.params.intermediateStorageNodes", PRERUN_INTERMEDIATE_STORAGE_NODES)
|
||||
viper.BindEnv("prerun.params.includeBlock", PRERUN_INCLUDE_BLOCK)
|
||||
viper.BindEnv("prerun.params.includeReceipts", PRERUN_INCLUDE_RECEIPTS)
|
||||
viper.BindEnv("prerun.params.includeTD", PRERUN_INCLUDE_TD)
|
||||
viper.BindEnv("prerun.params.includeCode", PRERUN_INCLUDE_CODE)
|
||||
|
||||
viper.BindEnv("log.level", LOG_LEVEL)
|
||||
viper.BindEnv("log.file", LOG_FILE_PATH)
|
||||
|
||||
+127
-17
@@ -23,8 +23,13 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/database/dump"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/database/file"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/interfaces"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/node"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/postgres"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/shared"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
@@ -106,12 +111,15 @@ func init() {
|
||||
rootCmd.PersistentFlags().String("http-path", "", "vdb server http path")
|
||||
rootCmd.PersistentFlags().String("ipc-path", "", "vdb server ipc path")
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file location")
|
||||
|
||||
rootCmd.PersistentFlags().String("log-file", "", "file path for logging")
|
||||
rootCmd.PersistentFlags().String("log-level", log.InfoLevel.String(),
|
||||
"log level (trace, debug, info, warn, error, fatal, panic")
|
||||
|
||||
rootCmd.PersistentFlags().String("leveldb-mode", "local", "LevelDB access mode (local, remote)")
|
||||
rootCmd.PersistentFlags().String("leveldb-path", "", "path to primary datastore")
|
||||
rootCmd.PersistentFlags().String("ancient-path", "", "path to ancient datastore")
|
||||
rootCmd.PersistentFlags().String("leveldb-url", "", "url to primary leveldb-ethdb-rpc server")
|
||||
|
||||
rootCmd.PersistentFlags().Bool("prerun", false, "turn on prerun of toml configured ranges")
|
||||
rootCmd.PersistentFlags().Int("service-workers", 0, "number of range requests to process concurrently")
|
||||
@@ -123,9 +131,19 @@ func init() {
|
||||
rootCmd.PersistentFlags().String("database-hostname", "localhost", "database hostname")
|
||||
rootCmd.PersistentFlags().String("database-user", "", "database user")
|
||||
rootCmd.PersistentFlags().String("database-password", "", "database password")
|
||||
rootCmd.PersistentFlags().Int("database-max-idle", 0, "database max number of idle connections")
|
||||
rootCmd.PersistentFlags().Int("database-max-open", 0, "database max number of open connections")
|
||||
rootCmd.PersistentFlags().Int("database-min-open", 0, "database min number of open connections")
|
||||
rootCmd.PersistentFlags().Duration("database-max-conn-lifetime", 0, "database max connection lifetime")
|
||||
rootCmd.PersistentFlags().Duration("database-conn-timeout", 0, "database connection timeout")
|
||||
rootCmd.PersistentFlags().Duration("database-max-idle-time", 0, "database max connection idle time")
|
||||
rootCmd.PersistentFlags().String("database-type", "postgres", "database type (currently supported: postgres, dump)")
|
||||
rootCmd.PersistentFlags().String("database-driver", "sqlx", "database driver type (currently supported: sqlx, pgx)")
|
||||
rootCmd.PersistentFlags().String("database-dump-dst", "stdout", "dump destination (for database-type=dump; options: stdout, stderr, discard)")
|
||||
rootCmd.PersistentFlags().String("database-file-path", "", "full file path (for database-type=file)")
|
||||
|
||||
rootCmd.PersistentFlags().String("eth-node-id", "", "eth node id")
|
||||
rootCmd.PersistentFlags().String("eth-client-name", "Geth", "eth client name")
|
||||
rootCmd.PersistentFlags().String("eth-client-name", "eth-statediff-service", "eth client name")
|
||||
rootCmd.PersistentFlags().String("eth-genesis-block",
|
||||
"0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3", "eth genesis block hash")
|
||||
rootCmd.PersistentFlags().String("eth-network-id", "1", "eth network id")
|
||||
@@ -143,37 +161,70 @@ func init() {
|
||||
rootCmd.PersistentFlags().Bool("prerun-only", false, "only process pre-configured ranges; exit afterwards")
|
||||
rootCmd.PersistentFlags().Int("prerun-start", 0, "start height for a prerun range")
|
||||
rootCmd.PersistentFlags().Int("prerun-stop", 0, "stop height for a prerun range")
|
||||
rootCmd.PersistentFlags().Bool("prerun-intermediate-state-nodes", true, "include intermediate state nodes in state diff")
|
||||
rootCmd.PersistentFlags().Bool("prerun-intermediate-storage-nodes", true, "include intermediate storage nodes in state diff")
|
||||
rootCmd.PersistentFlags().Bool("prerun-include-block", true, "include block data in the statediff payload")
|
||||
rootCmd.PersistentFlags().Bool("prerun-include-receipts", true, "include receipts in the statediff payload")
|
||||
rootCmd.PersistentFlags().Bool("prerun-include-td", true, "include td in the statediff payload")
|
||||
rootCmd.PersistentFlags().Bool("prerun-include-code", true, "include code and codehash mappings in statediff payload")
|
||||
|
||||
viper.BindPFlag("server.httpPath", rootCmd.PersistentFlags().Lookup("http-path"))
|
||||
viper.BindPFlag("server.ipcPath", rootCmd.PersistentFlags().Lookup("ipc-path"))
|
||||
|
||||
viper.BindPFlag("log.file", rootCmd.PersistentFlags().Lookup("log-file"))
|
||||
viper.BindPFlag("log.level", rootCmd.PersistentFlags().Lookup("log-level"))
|
||||
|
||||
viper.BindPFlag("statediff.prerun", rootCmd.PersistentFlags().Lookup("prerun"))
|
||||
viper.BindPFlag("statediff.serviceWorkers", rootCmd.PersistentFlags().Lookup("service-workers"))
|
||||
viper.BindPFlag("statediff.trieWorkers", rootCmd.PersistentFlags().Lookup("trie-workers"))
|
||||
viper.BindPFlag("statediff.workerQueueSize", rootCmd.PersistentFlags().Lookup("worker-queue-size"))
|
||||
|
||||
viper.BindPFlag("leveldb.mode", rootCmd.PersistentFlags().Lookup("leveldb-mode"))
|
||||
viper.BindPFlag("leveldb.path", rootCmd.PersistentFlags().Lookup("leveldb-path"))
|
||||
viper.BindPFlag("leveldb.ancient", rootCmd.PersistentFlags().Lookup("ancient-path"))
|
||||
viper.BindPFlag("leveldb.url", rootCmd.PersistentFlags().Lookup("leveldb-url"))
|
||||
|
||||
viper.BindPFlag("database.name", rootCmd.PersistentFlags().Lookup("database-name"))
|
||||
viper.BindPFlag("database.port", rootCmd.PersistentFlags().Lookup("database-port"))
|
||||
viper.BindPFlag("database.hostname", rootCmd.PersistentFlags().Lookup("database-hostname"))
|
||||
viper.BindPFlag("database.user", rootCmd.PersistentFlags().Lookup("database-user"))
|
||||
viper.BindPFlag("database.password", rootCmd.PersistentFlags().Lookup("database-password"))
|
||||
viper.BindPFlag("database.maxIdle", rootCmd.PersistentFlags().Lookup("database-max-idle"))
|
||||
viper.BindPFlag("database.maxOpen", rootCmd.PersistentFlags().Lookup("database-max-open"))
|
||||
viper.BindPFlag("database.minOpen", rootCmd.PersistentFlags().Lookup("database-min-open"))
|
||||
viper.BindPFlag("database.maxConnLifetime", rootCmd.PersistentFlags().Lookup("database-max-conn-lifetime"))
|
||||
viper.BindPFlag("database.connTimeout", rootCmd.PersistentFlags().Lookup("database-conn-timeout"))
|
||||
viper.BindPFlag("database.maxIdleTime", rootCmd.PersistentFlags().Lookup("database-max-idle-time"))
|
||||
viper.BindPFlag("database.type", rootCmd.PersistentFlags().Lookup("database-type"))
|
||||
viper.BindPFlag("database.driver", rootCmd.PersistentFlags().Lookup("database-driver"))
|
||||
viper.BindPFlag("database.dumpDestination", rootCmd.PersistentFlags().Lookup("database-dump-dst"))
|
||||
viper.BindPFlag("database.filePath", rootCmd.PersistentFlags().Lookup("database-file-path"))
|
||||
|
||||
viper.BindPFlag("ethereum.nodeID", rootCmd.PersistentFlags().Lookup("eth-node-id"))
|
||||
viper.BindPFlag("ethereum.clientName", rootCmd.PersistentFlags().Lookup("eth-client-name"))
|
||||
viper.BindPFlag("ethereum.genesisBlock", rootCmd.PersistentFlags().Lookup("eth-genesis-block"))
|
||||
viper.BindPFlag("ethereum.networkID", rootCmd.PersistentFlags().Lookup("eth-network-id"))
|
||||
viper.BindPFlag("ethereum.chainID", rootCmd.PersistentFlags().Lookup("eth-chain-id"))
|
||||
viper.BindPFlag("ethereum.chainConfig", rootCmd.PersistentFlags().Lookup("eth-chain-config"))
|
||||
|
||||
viper.BindPFlag("cache.database", rootCmd.PersistentFlags().Lookup("cache-db"))
|
||||
viper.BindPFlag("cache.trie", rootCmd.PersistentFlags().Lookup("cache-trie"))
|
||||
|
||||
viper.BindPFlag("prom.http", rootCmd.PersistentFlags().Lookup("prom-http"))
|
||||
viper.BindPFlag("prom.httpAddr", rootCmd.PersistentFlags().Lookup("prom-http-addr"))
|
||||
viper.BindPFlag("prom.httpPort", rootCmd.PersistentFlags().Lookup("prom-http-port"))
|
||||
viper.BindPFlag("prom.dbStats", rootCmd.PersistentFlags().Lookup("prom-db-stats"))
|
||||
viper.BindPFlag("prom.metrics", rootCmd.PersistentFlags().Lookup("prom-metrics"))
|
||||
|
||||
viper.BindPFlag("prerun.only", rootCmd.PersistentFlags().Lookup("prerun-only"))
|
||||
viper.BindPFlag("prerun.start", rootCmd.PersistentFlags().Lookup("prerun-start"))
|
||||
viper.BindPFlag("prerun.stop", rootCmd.PersistentFlags().Lookup("prerun-stop"))
|
||||
viper.BindPFlag("prerun.params.intermediateStateNodes", rootCmd.PersistentFlags().Lookup("prerun-intermediate-state-nodes"))
|
||||
viper.BindPFlag("prerun.params.intermediateStorageNodes", rootCmd.PersistentFlags().Lookup("prerun-intermediate-storage-nodes"))
|
||||
viper.BindPFlag("prerun.params.includeBlock", rootCmd.PersistentFlags().Lookup("prerun-include-block"))
|
||||
viper.BindPFlag("prerun.params.includeReceipts", rootCmd.PersistentFlags().Lookup("prerun-include-receipts"))
|
||||
viper.BindPFlag("prerun.params.includeTD", rootCmd.PersistentFlags().Lookup("prerun-include-td"))
|
||||
viper.BindPFlag("prerun.params.includeCode", rootCmd.PersistentFlags().Lookup("prerun-include-code"))
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
@@ -191,7 +242,7 @@ func initConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
func GetEthNodeInfo() node.Info {
|
||||
func getEthNodeInfo() node.Info {
|
||||
var nodeID, genesisBlock, networkID, clientName string
|
||||
var chainID uint64
|
||||
if !viper.IsSet("ethereum.nodeID") {
|
||||
@@ -199,6 +250,7 @@ func GetEthNodeInfo() node.Info {
|
||||
} else {
|
||||
nodeID = viper.GetString("ethereum.nodeID")
|
||||
}
|
||||
genesisBlock = viper.GetString("ethereum.genesisBlock")
|
||||
if !viper.IsSet("ethereum.genesisBlock") {
|
||||
genesisBlock = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
|
||||
} else {
|
||||
@@ -238,20 +290,78 @@ func randSeq(n int) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func GetDBParams() postgres.ConnectionParams {
|
||||
return postgres.ConnectionParams{
|
||||
Name: viper.GetString("database.name"),
|
||||
Hostname: viper.GetString("database.hostname"),
|
||||
Port: viper.GetInt("database.port"),
|
||||
User: viper.GetString("database.user"),
|
||||
Password: viper.GetString("database.password"),
|
||||
// getConfig constructs and returns the appropriate config from viper params
|
||||
func getConfig(nodeInfo node.Info) (interfaces.Config, error) {
|
||||
dbTypeStr := viper.GetString("database.type")
|
||||
dbType, err := shared.ResolveDBType(dbTypeStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func GetDBConfig() postgres.ConnectionConfig {
|
||||
return postgres.ConnectionConfig{
|
||||
MaxIdle: viper.GetInt("database.maxIdle"),
|
||||
MaxOpen: viper.GetInt("database.maxOpen"),
|
||||
MaxLifetime: viper.GetInt("database.maxLifetime"),
|
||||
logWithCommand.Infof("configuring service for database type: %s", dbType)
|
||||
var indexerConfig interfaces.Config
|
||||
switch dbType {
|
||||
case shared.FILE:
|
||||
logWithCommand.Info("starting in sql file writing mode")
|
||||
filePathStr := viper.GetString("database.filePath")
|
||||
if filePathStr == "" {
|
||||
logWithCommand.Fatal("when operating in sql file writing mode a file path must be provided")
|
||||
}
|
||||
indexerConfig = file.Config{FilePath: filePathStr}
|
||||
case shared.DUMP:
|
||||
logWithCommand.Info("starting in data dump mode")
|
||||
dumpDstStr := viper.GetString("database.dumpDestination")
|
||||
dumpDst, err := dump.ResolveDumpType(dumpDstStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch dumpDst {
|
||||
case dump.STDERR:
|
||||
indexerConfig = dump.Config{Dump: os.Stdout}
|
||||
case dump.STDOUT:
|
||||
indexerConfig = dump.Config{Dump: os.Stderr}
|
||||
case dump.DISCARD:
|
||||
indexerConfig = dump.Config{Dump: dump.NewDiscardWriterCloser()}
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized dump destination: %s", dumpDst)
|
||||
}
|
||||
case shared.POSTGRES:
|
||||
logWithCommand.Info("starting in postgres mode")
|
||||
driverTypeStr := viper.GetString("database.driver")
|
||||
driverType, err := postgres.ResolveDriverType(driverTypeStr)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
pgConfig := postgres.Config{
|
||||
Hostname: viper.GetString("database.hostname"),
|
||||
Port: viper.GetInt("database.port"),
|
||||
DatabaseName: viper.GetString("database.name"),
|
||||
Username: viper.GetString("database.user"),
|
||||
Password: viper.GetString("database.password"),
|
||||
ID: nodeInfo.ID,
|
||||
ClientName: nodeInfo.ClientName,
|
||||
Driver: driverType,
|
||||
}
|
||||
if viper.IsSet("database.maxIdle") {
|
||||
pgConfig.MaxIdle = viper.GetInt("database.maxIdle")
|
||||
}
|
||||
if viper.IsSet("database.maxOpen") {
|
||||
pgConfig.MaxConns = viper.GetInt("database.maxOpen")
|
||||
}
|
||||
if viper.IsSet("database.minOpen") {
|
||||
pgConfig.MinConns = viper.GetInt("database.minOpen")
|
||||
}
|
||||
if viper.IsSet("database.maxConnLifetime") {
|
||||
pgConfig.MaxConnLifetime = viper.GetDuration("database.maxConnLifetime")
|
||||
}
|
||||
if viper.IsSet("database.connTimeout") {
|
||||
pgConfig.ConnTimeout = viper.GetDuration("database.connTimeout")
|
||||
}
|
||||
if viper.IsSet("database.maxIdleTime") {
|
||||
pgConfig.MaxConnIdleTime = viper.GetDuration("database.maxIdleTime")
|
||||
}
|
||||
indexerConfig = pgConfig
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized db type: %s", dbType)
|
||||
}
|
||||
return indexerConfig, nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ import (
|
||||
// serveCmd represents the serve command
|
||||
var serveCmd = &cobra.Command{
|
||||
Use: "serve",
|
||||
Short: "Stand up a standalone statediffing RPC service on top of leveldb",
|
||||
Short: "Stand up a standalone statediffing RPC service on top of LevelDB",
|
||||
Long: `Usage
|
||||
|
||||
./eth-statediff-service serve --config={path to toml config file}`,
|
||||
|
||||
+34
-49
@@ -1,19 +1,17 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/statediff"
|
||||
gethsd "github.com/ethereum/go-ethereum/statediff"
|
||||
ind "github.com/ethereum/go-ethereum/statediff/indexer"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/node"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/postgres"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
sd "github.com/vulcanize/eth-statediff-service/pkg"
|
||||
"github.com/vulcanize/eth-statediff-service/pkg/prom"
|
||||
)
|
||||
|
||||
type blockRange [2]uint64
|
||||
@@ -21,20 +19,41 @@ type blockRange [2]uint64
|
||||
func createStateDiffService() (sd.StateDiffService, error) {
|
||||
// load some necessary params
|
||||
logWithCommand.Info("Loading statediff service parameters")
|
||||
mode := viper.GetString("leveldb.mode")
|
||||
path := viper.GetString("leveldb.path")
|
||||
ancientPath := viper.GetString("leveldb.ancient")
|
||||
if path == "" || ancientPath == "" {
|
||||
logWithCommand.Fatal("require a valid eth leveldb primary datastore path and ancient datastore path")
|
||||
url := viper.GetString("leveldb.url")
|
||||
|
||||
if mode == "local" {
|
||||
if path == "" || ancientPath == "" {
|
||||
logWithCommand.Fatal("Require a valid eth LevelDB primary datastore path and ancient datastore path")
|
||||
}
|
||||
} else if mode == "remote" {
|
||||
if url == "" {
|
||||
logWithCommand.Fatal("Require a valid RPC url for accessing LevelDB")
|
||||
}
|
||||
} else {
|
||||
logWithCommand.Fatal("Invalid mode provided for LevelDB access")
|
||||
}
|
||||
|
||||
nodeInfo := getEthNodeInfo()
|
||||
|
||||
var chainConf *params.ChainConfig
|
||||
var err error
|
||||
chainConfigPath := viper.GetString("ethereum.chainConfig")
|
||||
|
||||
if chainConfigPath != "" {
|
||||
chainConf, err = statediff.LoadConfig(chainConfigPath)
|
||||
} else {
|
||||
chainConf, err = statediff.ChainConfig(nodeInfo.ChainID)
|
||||
}
|
||||
|
||||
nodeInfo := GetEthNodeInfo()
|
||||
chainConf, err := chainConfig(nodeInfo.ChainID)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
// create leveldb reader
|
||||
logWithCommand.Info("Creating leveldb reader")
|
||||
// create LevelDB reader
|
||||
logWithCommand.Info("Creating LevelDB reader")
|
||||
readerConf := sd.LvLDBReaderConfig{
|
||||
TrieConfig: &trie.Config{
|
||||
Cache: viper.GetInt("cache.trie"),
|
||||
@@ -42,8 +61,10 @@ func createStateDiffService() (sd.StateDiffService, error) {
|
||||
Preimages: false,
|
||||
},
|
||||
ChainConfig: chainConf,
|
||||
Mode: mode,
|
||||
Path: path,
|
||||
AncientPath: ancientPath,
|
||||
Url: url,
|
||||
DBCacheSize: viper.GetInt("cache.database"),
|
||||
}
|
||||
lvlDBReader, err := sd.NewLvlDBReader(readerConf)
|
||||
@@ -52,13 +73,13 @@ func createStateDiffService() (sd.StateDiffService, error) {
|
||||
}
|
||||
|
||||
// create statediff service
|
||||
logWithCommand.Info("Setting up Postgres DB")
|
||||
db, err := setupPostgres(nodeInfo)
|
||||
logWithCommand.Info("Setting up database")
|
||||
conf, err := getConfig(nodeInfo)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
logWithCommand.Info("Creating statediff indexer")
|
||||
indexer, err := ind.NewStateDiffIndexer(chainConf, db)
|
||||
_, indexer, err := ind.NewStateDiffIndexer(context.Background(), chainConf, nodeInfo, conf)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
@@ -72,20 +93,6 @@ func createStateDiffService() (sd.StateDiffService, error) {
|
||||
return sd.NewStateDiffService(lvlDBReader, indexer, sdConf)
|
||||
}
|
||||
|
||||
func setupPostgres(nodeInfo node.Info) (*postgres.DB, error) {
|
||||
p := GetDBParams()
|
||||
logWithCommand.Info("initializing DB connection pool")
|
||||
db, err := postgres.NewDB(postgres.DbConnectionString(p), GetDBConfig(), nodeInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if viper.GetBool("prom.dbStats") {
|
||||
logWithCommand.Info("registering DB collector")
|
||||
prom.RegisterDBCollector(p.Name, db.DB)
|
||||
}
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func setupPreRunRanges() []sd.RangeRequest {
|
||||
if !viper.GetBool("statediff.prerun") {
|
||||
return nil
|
||||
@@ -105,13 +112,6 @@ func setupPreRunRanges() []sd.RangeRequest {
|
||||
addrs[i] = common.HexToAddress(addrStr)
|
||||
}
|
||||
preRunParams.WatchedAddresses = addrs
|
||||
var storageKeyStrs []string
|
||||
viper.UnmarshalKey("prerun.params.watchedStorageKeys", &storageKeyStrs)
|
||||
keys := make([]common.Hash, len(storageKeyStrs))
|
||||
for i, keyStr := range storageKeyStrs {
|
||||
keys[i] = common.HexToHash(keyStr)
|
||||
}
|
||||
preRunParams.WatchedStorageSlots = keys
|
||||
var rawRanges []blockRange
|
||||
viper.UnmarshalKey("prerun.ranges", &rawRanges)
|
||||
blockRanges := make([]sd.RangeRequest, len(rawRanges))
|
||||
@@ -134,18 +134,3 @@ func setupPreRunRanges() []sd.RangeRequest {
|
||||
|
||||
return blockRanges
|
||||
}
|
||||
|
||||
func chainConfig(chainID uint64) (*params.ChainConfig, error) {
|
||||
switch chainID {
|
||||
case 1:
|
||||
return params.MainnetChainConfig, nil
|
||||
case 3:
|
||||
return params.RopstenChainConfig, nil // Ropsten
|
||||
case 4:
|
||||
return params.RinkebyChainConfig, nil
|
||||
case 5:
|
||||
return params.GoerliChainConfig, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("chain config for chainid %d not available", chainID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2021 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 (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
v "github.com/vulcanize/eth-statediff-service/version"
|
||||
)
|
||||
|
||||
// versionCmd represents the version command
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Prints the version of eth-statediff-service",
|
||||
Long: `Use this command to fetch the version of eth-statediff-service
|
||||
|
||||
Usage: ./eth-statediff-service version`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
subCommand = cmd.CalledAs()
|
||||
logWithCommand = *log.WithField("SubCommand", subCommand)
|
||||
logWithCommand.Infof("eth-statediff-service version: %s", v.Version)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
}
|
||||
+1
-1
@@ -6,7 +6,7 @@ services:
|
||||
context: ./
|
||||
cache_from:
|
||||
- alpine:latest
|
||||
- golang:1.13
|
||||
- golang:1.16
|
||||
dockerfile: ./Dockerfile
|
||||
args:
|
||||
USER: "vdbm"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
[leveldb]
|
||||
mode = "local"
|
||||
path = "/app/geth-rw/chaindata"
|
||||
ancient = "/app/geth-rw/chaindata/ancient"
|
||||
url = "http://127.0.0.1:8082/"
|
||||
|
||||
[server]
|
||||
ipcPath = ""
|
||||
@@ -23,21 +25,21 @@
|
||||
includeTD = true
|
||||
includeCode = true
|
||||
watchedAddresses = []
|
||||
watchedStorageKeys = []
|
||||
|
||||
[log]
|
||||
file = ""
|
||||
level = "info"
|
||||
|
||||
[eth]
|
||||
chainID = 1
|
||||
|
||||
[database]
|
||||
name = ""
|
||||
hostname = ""
|
||||
port = 5432
|
||||
user = ""
|
||||
password = ""
|
||||
type = "postgres"
|
||||
driver = "sqlx"
|
||||
dumpDestination = ""
|
||||
filePath = ""
|
||||
|
||||
[cache]
|
||||
database = 1024
|
||||
@@ -49,3 +51,11 @@
|
||||
http = true
|
||||
httpAddr = "0.0.0.0"
|
||||
httpPort = 9100
|
||||
|
||||
[ethereum]
|
||||
chainConfig = ""
|
||||
nodeID = ""
|
||||
clientName = "eth-statediff-service"
|
||||
genesisBlock = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
|
||||
networkID = 1
|
||||
chainID = 1
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
[leveldb]
|
||||
mode = "local"
|
||||
path = "/Users/user/Library/Ethereum/geth/chaindata"
|
||||
ancient = "/Users/user/Library/Ethereum/geth/chaindata/ancient"
|
||||
url = "http://127.0.0.1:8082/"
|
||||
|
||||
[server]
|
||||
ipcPath = ".ipc"
|
||||
@@ -25,21 +27,21 @@
|
||||
includeTD = true
|
||||
includeCode = true
|
||||
watchedAddresses = []
|
||||
watchedStorageKeys = []
|
||||
|
||||
[log]
|
||||
file = ""
|
||||
level = "info"
|
||||
|
||||
[eth]
|
||||
chainID = 1
|
||||
|
||||
[database]
|
||||
name = "vulcanize_test"
|
||||
hostname = "localhost"
|
||||
port = 5432
|
||||
user = "vulcanize"
|
||||
password = "..."
|
||||
type = "postgres"
|
||||
driver = "sqlx"
|
||||
dumpDestination = ""
|
||||
filePath = ""
|
||||
|
||||
[cache]
|
||||
database = 1024
|
||||
@@ -51,3 +53,11 @@
|
||||
http = true
|
||||
httpAddr = "localhost"
|
||||
httpPort = "8889"
|
||||
|
||||
[ethereum]
|
||||
chainConfig = ""
|
||||
nodeID = ""
|
||||
clientName = "eth-statediff-service"
|
||||
genesisBlock = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
|
||||
networkID = 1
|
||||
chainID = 1
|
||||
|
||||
@@ -1,15 +1,131 @@
|
||||
module github.com/vulcanize/eth-statediff-service
|
||||
|
||||
go 1.13
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/ethereum/go-ethereum v1.10.9
|
||||
github.com/ethereum/go-ethereum v1.10.18
|
||||
github.com/jmoiron/sqlx v1.2.0
|
||||
github.com/prometheus/client_golang v1.0.0
|
||||
github.com/prometheus/client_golang v1.4.0
|
||||
github.com/sirupsen/logrus v1.7.0
|
||||
github.com/spf13/cobra v1.1.1
|
||||
github.com/spf13/viper v1.7.1
|
||||
github.com/vulcanize/go-eth-state-node-iterator v0.0.1-alpha.0.20211014064906-d23d01ed8191
|
||||
github.com/spf13/cobra v1.3.0
|
||||
github.com/spf13/viper v1.10.1
|
||||
github.com/vulcanize/go-eth-state-node-iterator v1.0.3
|
||||
github.com/vulcanize/leveldb-ethdb-rpc v0.1.2
|
||||
)
|
||||
|
||||
replace github.com/ethereum/go-ethereum v1.10.9 => github.com/vulcanize/go-ethereum v1.10.11-statediff-0.0.27
|
||||
require (
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
|
||||
github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/deckarep/golang-set v1.8.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
||||
github.com/deepmap/oapi-codegen v1.8.2 // indirect
|
||||
github.com/edsrzf/mmap-go v1.0.0 // indirect
|
||||
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect
|
||||
github.com/georgysavva/scany v0.2.9 // indirect
|
||||
github.com/go-ole/go-ole v1.2.1 // indirect
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.3.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/graph-gophers/graphql-go v1.3.0 // indirect
|
||||
github.com/hashicorp/go-bexpr v0.1.10 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
|
||||
github.com/holiman/uint256 v1.2.0 // indirect
|
||||
github.com/huin/goupnp v1.0.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/influxdata/influxdb v1.8.3 // indirect
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.4.0 // indirect
|
||||
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 // indirect
|
||||
github.com/ipfs/bbloom v0.0.4 // indirect
|
||||
github.com/ipfs/go-block-format v0.0.2 // indirect
|
||||
github.com/ipfs/go-cid v0.0.7 // indirect
|
||||
github.com/ipfs/go-datastore v0.4.2 // indirect
|
||||
github.com/ipfs/go-ipfs-blockstore v1.0.1 // indirect
|
||||
github.com/ipfs/go-ipfs-ds-help v1.0.0 // indirect
|
||||
github.com/ipfs/go-ipfs-util v0.0.1 // indirect
|
||||
github.com/ipfs/go-ipld-format v0.2.0 // indirect
|
||||
github.com/ipfs/go-log v0.0.1 // indirect
|
||||
github.com/ipfs/go-metrics-interface v0.0.1 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.10.0 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.1.1 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||
github.com/jackc/pgtype v1.8.1 // indirect
|
||||
github.com/jackc/pgx/v4 v4.13.0 // indirect
|
||||
github.com/jackc/puddle v1.1.3 // indirect
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||
github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8 // indirect
|
||||
github.com/lib/pq v1.10.2 // indirect
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect
|
||||
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/mitchellh/pointerstructure v1.2.0 // indirect
|
||||
github.com/mr-tron/base58 v1.1.3 // indirect
|
||||
github.com/multiformats/go-base32 v0.0.3 // indirect
|
||||
github.com/multiformats/go-base36 v0.1.0 // indirect
|
||||
github.com/multiformats/go-multibase v0.0.3 // indirect
|
||||
github.com/multiformats/go-multihash v0.0.14 // indirect
|
||||
github.com/multiformats/go-varint v0.0.5 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 // indirect
|
||||
github.com/pganalyze/pg_query_go/v2 v2.1.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.9.1 // indirect
|
||||
github.com/prometheus/procfs v0.0.8 // indirect
|
||||
github.com/prometheus/tsdb v0.7.1 // indirect
|
||||
github.com/rjeczalik/notify v0.9.1 // indirect
|
||||
github.com/rs/cors v1.7.0 // indirect
|
||||
github.com/shirou/gopsutil v3.21.5+incompatible // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/spf13/afero v1.6.0 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 // indirect
|
||||
github.com/stretchr/objx v0.2.0 // indirect
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
|
||||
github.com/thoas/go-funk v0.9.2 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.6 // indirect
|
||||
github.com/tklauser/numcpus v0.2.2 // indirect
|
||||
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef // indirect
|
||||
github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/ini.v1 v1.66.2 // indirect
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
||||
gopkg.in/urfave/cli.v1 v1.20.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
||||
replace github.com/ethereum/go-ethereum v1.10.18 => github.com/vulcanize/go-ethereum v1.10.18-statediff-4.0.2-alpha
|
||||
|
||||
@@ -0,0 +1,449 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"id": 4,
|
||||
"iteration": 1635774999288,
|
||||
"links": [
|
||||
{
|
||||
"icon": "info",
|
||||
"tags": [],
|
||||
"targetBlank": true,
|
||||
"title": "Grafana Docs",
|
||||
"tooltip": "",
|
||||
"type": "link",
|
||||
"url": "https://grafana.com/docs/grafana/latest/"
|
||||
},
|
||||
{
|
||||
"icon": "info",
|
||||
"tags": [],
|
||||
"targetBlank": true,
|
||||
"title": "Prometheus Docs",
|
||||
"type": "link",
|
||||
"url": "http://prometheus.io/docs/introduction/overview/"
|
||||
}
|
||||
],
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "eth-statediff-service",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {},
|
||||
"links": [],
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"fill": 1,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 6,
|
||||
"w": 18,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 3,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"percentage": false,
|
||||
"pluginVersion": "7.1.5",
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "rate(eth_statediff_service_processed_height{instance=\"$instance\"}[1m])",
|
||||
"interval": "",
|
||||
"legendFormat": "",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Instance block processed (rate-1m)",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": "<span style=\"font-family: 'Open Sans', 'Helvetica Neue', Helvetica; font-size: 25px;vertical-align: text-top;color: #bbbfc2;margin-left: 10px;\">Prometheus</span>\n\n<p style=\"margin-top: 10px;\">You're using Prometheus, an open-source systems monitoring and alerting toolkit originally built at SoundCloud. For more information, check out the <a href=\"https://grafana.com/\">Grafana</a> and <a href=\"http://prometheus.io/\">Prometheus</a> projects.</p>",
|
||||
"datasource": null,
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 5,
|
||||
"w": 6,
|
||||
"x": 18,
|
||||
"y": 0
|
||||
},
|
||||
"id": 9,
|
||||
"links": [],
|
||||
"mode": "html",
|
||||
"options": {
|
||||
"content": "<span style=\"font-family: 'Open Sans', 'Helvetica Neue', Helvetica; font-size: 25px;vertical-align: text-top;color: #bbbfc2;margin-left: 10px;\">Prometheus</span>\n\n<p style=\"margin-top: 10px;\">You're using Prometheus, an open-source systems monitoring and alerting toolkit originally built at SoundCloud. For more information, check out the <a href=\"https://grafana.com/\">Grafana</a> and <a href=\"http://prometheus.io/\">Prometheus</a> projects.</p>",
|
||||
"mode": "html"
|
||||
},
|
||||
"pluginVersion": "7.1.0",
|
||||
"style": {},
|
||||
"title": "",
|
||||
"transparent": true,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"content": "#### Samples Ingested\nThis graph displays the count of samples ingested by the Prometheus server, as measured over the last 5 minutes, per time series in the range vector. When troubleshooting an issue on IRC or Github, this is often the first stat requested by the Prometheus team. ",
|
||||
"datasource": null,
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 6,
|
||||
"w": 4,
|
||||
"x": 18,
|
||||
"y": 5
|
||||
},
|
||||
"id": 8,
|
||||
"links": [],
|
||||
"mode": "markdown",
|
||||
"options": {
|
||||
"content": "#### Samples Ingested\nThis graph displays the count of samples ingested by the Prometheus server, as measured over the last 5 minutes, per time series in the range vector. When troubleshooting an issue on IRC or Github, this is often the first stat requested by the Prometheus team. ",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"pluginVersion": "7.1.0",
|
||||
"style": {},
|
||||
"title": "",
|
||||
"transparent": true,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"aliasColors": {
|
||||
"Value": "red",
|
||||
"aggr": "red"
|
||||
},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "eth-statediff-service",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {},
|
||||
"links": [],
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"fill": 1,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 6,
|
||||
"w": 18,
|
||||
"x": 0,
|
||||
"y": 6
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 17,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"percentage": false,
|
||||
"pluginVersion": "7.1.5",
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(increase(eth_statediff_service_processed_height{instance=~\".+\"}[1m]))",
|
||||
"interval": "",
|
||||
"legendFormat": "aggr",
|
||||
"refId": "C"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Aggregate block processed (rate-1m)",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": "#### Scrapes\nPrometheus scrapes metrics from instrumented jobs, either directly or via an intermediary push gateway for short-lived jobs. Target scrapes will show how frequently targets are scraped, as measured over the last 5 minutes, per time series in the range vector. Scrape Duration will show how long the scrapes are taking, with percentiles available as series. ",
|
||||
"datasource": null,
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 6,
|
||||
"x": 18,
|
||||
"y": 11
|
||||
},
|
||||
"id": 11,
|
||||
"links": [],
|
||||
"mode": "markdown",
|
||||
"options": {
|
||||
"content": "#### Scrapes\nPrometheus scrapes metrics from instrumented jobs, either directly or via an intermediary push gateway for short-lived jobs. Target scrapes will show how frequently targets are scraped, as measured over the last 5 minutes, per time series in the range vector. Scrape Duration will show how long the scrapes are taking, with percentiles available as series. ",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"pluginVersion": "7.1.0",
|
||||
"style": {},
|
||||
"title": "",
|
||||
"transparent": true,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"content": "#### Rule Evaluation Duration\nThis graph panel plots the duration for all evaluations to execute. The 50th percentile, 90th percentile and 99th percentile are shown as three separate series to help identify outliers that may be skewing the data.",
|
||||
"datasource": null,
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 6,
|
||||
"x": 18,
|
||||
"y": 18
|
||||
},
|
||||
"id": 15,
|
||||
"links": [],
|
||||
"mode": "markdown",
|
||||
"options": {
|
||||
"content": "#### Rule Evaluation Duration\nThis graph panel plots the duration for all evaluations to execute. The 50th percentile, 90th percentile and 99th percentile are shown as three separate series to help identify outliers that may be skewing the data.",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"pluginVersion": "7.1.0",
|
||||
"style": {},
|
||||
"title": "",
|
||||
"transparent": true,
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"refresh": false,
|
||||
"revision": "1.0",
|
||||
"schemaVersion": 26,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"allValue": null,
|
||||
"current": {
|
||||
"selected": true,
|
||||
"text": "localhost:8888",
|
||||
"value": "localhost:8888"
|
||||
},
|
||||
"datasource": "eth-statediff-service",
|
||||
"definition": "label_values(instance)",
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"label": "SelectInstance",
|
||||
"multi": false,
|
||||
"name": "instance",
|
||||
"options": [],
|
||||
"query": "label_values(instance)",
|
||||
"refresh": 2,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": 1,
|
||||
"tagValuesQuery": "",
|
||||
"tags": [],
|
||||
"tagsQuery": "",
|
||||
"type": "query",
|
||||
"useTags": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": {
|
||||
"from": "now-30m",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"now": true,
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
],
|
||||
"time_options": [
|
||||
"5m",
|
||||
"15m",
|
||||
"1h",
|
||||
"6h",
|
||||
"12h",
|
||||
"24h",
|
||||
"2d",
|
||||
"7d",
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"timezone": "browser",
|
||||
"title": "Prometheus Block Process Stats",
|
||||
"uid": "q1FUJHKnz",
|
||||
"version": 14
|
||||
}
|
||||
+1
-1
@@ -28,7 +28,7 @@ const APIName = "statediff"
|
||||
const APIVersion = "0.0.1"
|
||||
|
||||
// PublicStateDiffAPI provides an RPC interface
|
||||
// that can be used to fetch historical diffs from leveldb directly
|
||||
// that can be used to fetch historical diffs from LevelDB directly
|
||||
type PublicStateDiffAPI struct {
|
||||
sds StateDiffService
|
||||
}
|
||||
|
||||
+214
-114
@@ -32,7 +32,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
sd "github.com/ethereum/go-ethereum/statediff"
|
||||
sdtrie "github.com/ethereum/go-ethereum/statediff/trie"
|
||||
sdtrie "github.com/ethereum/go-ethereum/statediff/trie_helpers"
|
||||
sdtypes "github.com/ethereum/go-ethereum/statediff/types"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -42,16 +42,16 @@ import (
|
||||
|
||||
var (
|
||||
nullHashBytes = common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")
|
||||
emptyNode, _ = rlp.EncodeToBytes([]byte{})
|
||||
emptyNode, _ = rlp.EncodeToBytes(&[]byte{})
|
||||
emptyContractRoot = crypto.Keccak256Hash(emptyNode)
|
||||
nullCodeHash = crypto.Keccak256Hash([]byte{}).Bytes()
|
||||
)
|
||||
|
||||
// Builder interface exposes the method for building a state diff between two blocks
|
||||
type Builder interface {
|
||||
BuildStateDiffObject(args sd.Args, params sd.Params) (sd.StateObject, error)
|
||||
BuildStateTrieObject(current *types.Block) (sd.StateObject, error)
|
||||
WriteStateDiffObject(args sd.StateRoots, params sd.Params, output sdtypes.StateNodeSink, codeOutput sdtypes.CodeSink) error
|
||||
BuildStateDiffObject(args sd.Args, params sd.Params) (sdtypes.StateObject, error)
|
||||
BuildStateTrieObject(current *types.Block) (sdtypes.StateObject, error)
|
||||
WriteStateDiffObject(args sdtypes.StateRoots, params sd.Params, output sdtypes.StateNodeSink, codeOutput sdtypes.CodeSink) error
|
||||
}
|
||||
|
||||
type builder struct {
|
||||
@@ -120,17 +120,17 @@ func NewBuilder(stateCache state.Database, workers uint) (Builder, error) {
|
||||
}
|
||||
|
||||
// BuildStateTrieObject builds a state trie object from the provided block
|
||||
func (sdb *builder) BuildStateTrieObject(current *types.Block) (sd.StateObject, error) {
|
||||
func (sdb *builder) BuildStateTrieObject(current *types.Block) (sdtypes.StateObject, error) {
|
||||
currentTrie, err := sdb.stateCache.OpenTrie(current.Root())
|
||||
if err != nil {
|
||||
return sd.StateObject{}, fmt.Errorf("error creating trie for block %d: %v", current.Number(), err)
|
||||
return sdtypes.StateObject{}, fmt.Errorf("error creating trie for block %d: %v", current.Number(), err)
|
||||
}
|
||||
it := currentTrie.NodeIterator([]byte{})
|
||||
stateNodes, codeAndCodeHashes, err := sdb.buildStateTrie(it)
|
||||
if err != nil {
|
||||
return sd.StateObject{}, fmt.Errorf("error collecting state nodes for block %d: %v", current.Number(), err)
|
||||
return sdtypes.StateObject{}, fmt.Errorf("error collecting state nodes for block %d: %v", current.Number(), err)
|
||||
}
|
||||
return sd.StateObject{
|
||||
return sdtypes.StateObject{
|
||||
BlockNumber: current.Number(),
|
||||
BlockHash: current.Hash(),
|
||||
Nodes: stateNodes,
|
||||
@@ -163,12 +163,12 @@ func (sdb *builder) buildStateTrie(it trie.NodeIterator) ([]sdtypes.StateNode, [
|
||||
node.LeafKey = leafKey
|
||||
if !bytes.Equal(account.CodeHash, nullCodeHash) {
|
||||
var storageNodes []sdtypes.StorageNode
|
||||
err := sdb.buildStorageNodesEventual(account.Root, nil, true, storageNodeAppender(&storageNodes))
|
||||
err := sdb.buildStorageNodesEventual(account.Root, true, storageNodeAppender(&storageNodes))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed building eventual storage diffs for account %+v\r\nerror: %v", account, err)
|
||||
}
|
||||
node.StorageNodes = storageNodes
|
||||
// emit codehash => code mappings for cod
|
||||
// emit codehash => code mappings for code
|
||||
codeHash := common.BytesToHash(account.CodeHash)
|
||||
code, err := sdb.stateCache.ContractCode(common.Hash{}, codeHash)
|
||||
if err != nil {
|
||||
@@ -190,16 +190,16 @@ func (sdb *builder) buildStateTrie(it trie.NodeIterator) ([]sdtypes.StateNode, [
|
||||
}
|
||||
|
||||
// BuildStateDiffObject builds a statediff object from two blocks and the provided parameters
|
||||
func (sdb *builder) BuildStateDiffObject(args sd.Args, params sd.Params) (sd.StateObject, error) {
|
||||
func (sdb *builder) BuildStateDiffObject(args sd.Args, params sd.Params) (sdtypes.StateObject, error) {
|
||||
var stateNodes []sdtypes.StateNode
|
||||
var codeAndCodeHashes []sdtypes.CodeAndCodeHash
|
||||
err := sdb.WriteStateDiffObject(
|
||||
sd.StateRoots{OldStateRoot: args.OldStateRoot, NewStateRoot: args.NewStateRoot},
|
||||
sdtypes.StateRoots{OldStateRoot: args.OldStateRoot, NewStateRoot: args.NewStateRoot},
|
||||
params, stateNodeAppender(&stateNodes), codeMappingAppender(&codeAndCodeHashes))
|
||||
if err != nil {
|
||||
return sd.StateObject{}, err
|
||||
return sdtypes.StateObject{}, err
|
||||
}
|
||||
return sd.StateObject{
|
||||
return sdtypes.StateObject{
|
||||
BlockHash: args.BlockHash,
|
||||
BlockNumber: args.BlockNumber,
|
||||
Nodes: stateNodes,
|
||||
@@ -208,7 +208,7 @@ func (sdb *builder) BuildStateDiffObject(args sd.Args, params sd.Params) (sd.Sta
|
||||
}
|
||||
|
||||
// WriteStateDiffObject writes a statediff object to output callback
|
||||
func (sdb *builder) WriteStateDiffObject(args sd.StateRoots, params sd.Params, output sdtypes.StateNodeSink, codeOutput sdtypes.CodeSink) error {
|
||||
func (sdb *builder) WriteStateDiffObject(args sdtypes.StateRoots, params sd.Params, output sdtypes.StateNodeSink, codeOutput sdtypes.CodeSink) error {
|
||||
if len(params.WatchedAddresses) > 0 {
|
||||
// if we are watching only specific accounts then we are only diffing leaf nodes
|
||||
log.Info("Ignoring intermediate state nodes because WatchedAddresses was passed")
|
||||
@@ -218,16 +218,18 @@ func (sdb *builder) WriteStateDiffObject(args sd.StateRoots, params sd.Params, o
|
||||
// Load tries for old and new states
|
||||
oldTrie, err := sdb.stateCache.OpenTrie(args.OldStateRoot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating trie for old state root: %v", err)
|
||||
return fmt.Errorf("error creating trie for oldStateRoot: %v", err)
|
||||
}
|
||||
newTrie, err := sdb.stateCache.OpenTrie(args.NewStateRoot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating trie for new state root: %v", err)
|
||||
return fmt.Errorf("error creating trie for newStateRoot: %v", err)
|
||||
}
|
||||
|
||||
// Split old and new tries into corresponding subtrie iterators
|
||||
oldIterFac := iter.NewSubtrieIteratorFactory(oldTrie, sdb.numWorkers)
|
||||
newIterFac := iter.NewSubtrieIteratorFactory(newTrie, sdb.numWorkers)
|
||||
oldIters1 := iter.SubtrieIterators(oldTrie, sdb.numWorkers)
|
||||
oldIters2 := iter.SubtrieIterators(oldTrie, sdb.numWorkers)
|
||||
newIters1 := iter.SubtrieIterators(newTrie, sdb.numWorkers)
|
||||
newIters2 := iter.SubtrieIterators(newTrie, sdb.numWorkers)
|
||||
|
||||
// Create iterators ahead of time to avoid race condition in state.Trie access
|
||||
// We do two state iterations per subtrie: one for new/updated nodes,
|
||||
@@ -235,8 +237,8 @@ func (sdb *builder) WriteStateDiffObject(args sd.StateRoots, params sd.Params, o
|
||||
var iterPairs [][]iterPair
|
||||
for i := uint(0); i < sdb.numWorkers; i++ {
|
||||
iterPairs = append(iterPairs, []iterPair{
|
||||
{older: oldIterFac.IteratorAt(i), newer: newIterFac.IteratorAt(i)},
|
||||
{older: oldIterFac.IteratorAt(i), newer: newIterFac.IteratorAt(i)},
|
||||
{older: oldIters1[i], newer: newIters1[i]},
|
||||
{older: oldIters2[i], newer: newIters2[i]},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -297,7 +299,7 @@ func (sdb *builder) buildStateDiff(args []iterPair, params sd.Params, output sdt
|
||||
if params.IntermediateStateNodes {
|
||||
diffAccountsAtB, diffPathsAtB, err = sdb.createdAndUpdatedStateWithIntermediateNodes(args[0], output)
|
||||
} else {
|
||||
diffAccountsAtB, diffPathsAtB, err = sdb.createdAndUpdatedState(args[0], params.WatchedAddresses)
|
||||
diffAccountsAtB, diffPathsAtB, err = sdb.createdAndUpdatedState(args[0], params.WatchedAddressesLeafKeys())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -306,12 +308,12 @@ func (sdb *builder) buildStateDiff(args []iterPair, params sd.Params, output sdt
|
||||
|
||||
// collect a slice of all the nodes that existed at a path in A that doesn't exist in B
|
||||
// a map of their leafkey to all the accounts that were touched and exist at A
|
||||
diffAccountsAtA, err := sdb.deletedOrUpdatedState(args[1], diffPathsAtB, output)
|
||||
diffAccountsAtA, err := sdb.deletedOrUpdatedState(args[1], diffAccountsAtB, diffPathsAtB, params.WatchedAddressesLeafKeys(), params.IntermediateStorageNodes, output)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error collecting deletedOrUpdatedNodes: %v", err)
|
||||
}
|
||||
|
||||
// collect and sort the leafkey keys for both account mappings into a slice
|
||||
// collect and sort the leafkeys for both account mappings into a slice
|
||||
createKeys := sortKeys(diffAccountsAtB)
|
||||
deleteKeys := sortKeys(diffAccountsAtA)
|
||||
|
||||
@@ -323,13 +325,12 @@ func (sdb *builder) buildStateDiff(args []iterPair, params sd.Params, output sdt
|
||||
|
||||
// build the diff nodes for the updated accounts using the mappings at both A and B as directed by the keys found as the intersection of the two
|
||||
err = sdb.buildAccountUpdates(
|
||||
diffAccountsAtB, diffAccountsAtA, updatedKeys,
|
||||
params.WatchedStorageSlots, params.IntermediateStorageNodes, output)
|
||||
diffAccountsAtB, diffAccountsAtA, updatedKeys, params.IntermediateStorageNodes, output)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error building diff for updated accounts: %v", err)
|
||||
}
|
||||
// build the diff nodes for created accounts
|
||||
err = sdb.buildAccountCreations(diffAccountsAtB, params.WatchedStorageSlots, params.IntermediateStorageNodes, output, codeOutput)
|
||||
err = sdb.buildAccountCreations(diffAccountsAtB, params.IntermediateStorageNodes, output, codeOutput)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error building diff for created accounts: %v", err)
|
||||
}
|
||||
@@ -339,7 +340,7 @@ func (sdb *builder) buildStateDiff(args []iterPair, params sd.Params, output sdt
|
||||
// createdAndUpdatedState returns
|
||||
// a mapping of their leafkeys to all the accounts that exist in a different state at B than A
|
||||
// and a slice of the paths for all of the nodes included in both
|
||||
func (sdb *builder) createdAndUpdatedState(iters iterPair, watchedAddresses []common.Address) (AccountMap, map[string]bool, error) {
|
||||
func (sdb *builder) createdAndUpdatedState(iters iterPair, watchedAddressesLeafKeys map[common.Hash]struct{}) (AccountMap, map[string]bool, error) {
|
||||
diffPathsAtB := make(map[string]bool)
|
||||
diffAcountsAtB := make(AccountMap)
|
||||
it, _ := trie.NewDifferenceIterator(iters.older, iters.newer)
|
||||
@@ -347,7 +348,7 @@ func (sdb *builder) createdAndUpdatedState(iters iterPair, watchedAddresses []co
|
||||
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
|
||||
continue
|
||||
}
|
||||
node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB())
|
||||
node, nodeElements, err := sdtrie.ResolveNode(it, sdb.stateCache.TrieDB())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -363,7 +364,7 @@ func (sdb *builder) createdAndUpdatedState(iters iterPair, watchedAddresses []co
|
||||
valueNodePath := append(node.Path, partialPath...)
|
||||
encodedPath := trie.HexToCompact(valueNodePath)
|
||||
leafKey := encodedPath[1:]
|
||||
if isWatchedAddress(watchedAddresses, leafKey) {
|
||||
if isWatchedAddress(watchedAddressesLeafKeys, leafKey) {
|
||||
diffAcountsAtB[common.Bytes2Hex(leafKey)] = accountWrapper{
|
||||
NodeType: node.NodeType,
|
||||
Path: node.Path,
|
||||
@@ -435,14 +436,15 @@ func (sdb *builder) createdAndUpdatedStateWithIntermediateNodes(iters iterPair,
|
||||
|
||||
// deletedOrUpdatedState returns a slice of all the paths that are emptied at B
|
||||
// and a mapping of their leafkeys to all the accounts that exist in a different state at A than B
|
||||
func (sdb *builder) deletedOrUpdatedState(iters iterPair, diffPathsAtB map[string]bool, output sdtypes.StateNodeSink) (AccountMap, error) {
|
||||
func (sdb *builder) deletedOrUpdatedState(iters iterPair, diffAccountsAtB AccountMap, diffPathsAtB map[string]bool, watchedAddressesLeafKeys map[common.Hash]struct{}, intermediateStorageNodes bool, output sdtypes.StateNodeSink) (AccountMap, error) {
|
||||
diffAccountAtA := make(AccountMap)
|
||||
it, _ := trie.NewDifferenceIterator(iters.newer, iters.older)
|
||||
for it.Next(true) {
|
||||
// skip value nodes
|
||||
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
|
||||
continue
|
||||
}
|
||||
node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB())
|
||||
node, nodeElements, err := sdtrie.ResolveNode(it, sdb.stateCache.TrieDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -457,24 +459,48 @@ func (sdb *builder) deletedOrUpdatedState(iters iterPair, diffPathsAtB map[strin
|
||||
valueNodePath := append(node.Path, partialPath...)
|
||||
encodedPath := trie.HexToCompact(valueNodePath)
|
||||
leafKey := encodedPath[1:]
|
||||
diffAccountAtA[common.Bytes2Hex(leafKey)] = accountWrapper{
|
||||
NodeType: node.NodeType,
|
||||
Path: node.Path,
|
||||
NodeValue: node.NodeValue,
|
||||
LeafKey: leafKey,
|
||||
Account: &account,
|
||||
}
|
||||
// if this node's path did not show up in diffPathsAtB
|
||||
// that means the node at this path was deleted (or moved) in B
|
||||
// emit an empty "removed" diff to signify as such
|
||||
if _, ok := diffPathsAtB[common.Bytes2Hex(node.Path)]; !ok {
|
||||
if err := output(sdtypes.StateNode{
|
||||
if isWatchedAddress(watchedAddressesLeafKeys, leafKey) {
|
||||
diffAccountAtA[common.Bytes2Hex(leafKey)] = accountWrapper{
|
||||
NodeType: node.NodeType,
|
||||
Path: node.Path,
|
||||
NodeValue: []byte{},
|
||||
NodeType: sdtypes.Removed,
|
||||
NodeValue: node.NodeValue,
|
||||
LeafKey: leafKey,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
Account: &account,
|
||||
}
|
||||
// if this node's path did not show up in diffPathsAtB
|
||||
// that means the node at this path was deleted (or moved) in B
|
||||
if _, ok := diffPathsAtB[common.Bytes2Hex(node.Path)]; !ok {
|
||||
var diff sdtypes.StateNode
|
||||
// if this node's leaf key also did not show up in diffAccountsAtB
|
||||
// that means the node was deleted
|
||||
// in that case, emit an empty "removed" diff state node
|
||||
// include empty "removed" diff storage nodes for all the storage slots
|
||||
if _, ok := diffAccountsAtB[common.Bytes2Hex(leafKey)]; !ok {
|
||||
diff = sdtypes.StateNode{
|
||||
NodeType: sdtypes.Removed,
|
||||
Path: node.Path,
|
||||
LeafKey: leafKey,
|
||||
NodeValue: []byte{},
|
||||
}
|
||||
|
||||
var storageDiffs []sdtypes.StorageNode
|
||||
err := sdb.buildRemovedAccountStorageNodes(account.Root, intermediateStorageNodes, storageNodeAppender(&storageDiffs))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed building storage diffs for removed node %x\r\nerror: %v", node.Path, err)
|
||||
}
|
||||
diff.StorageNodes = storageDiffs
|
||||
} else {
|
||||
// emit an empty "removed" diff with empty leaf key if the account was moved
|
||||
diff = sdtypes.StateNode{
|
||||
NodeType: sdtypes.Removed,
|
||||
Path: node.Path,
|
||||
NodeValue: []byte{},
|
||||
}
|
||||
}
|
||||
|
||||
if err := output(diff); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
case sdtypes.Extension, sdtypes.Branch:
|
||||
@@ -502,7 +528,7 @@ func (sdb *builder) deletedOrUpdatedState(iters iterPair, diffPathsAtB map[strin
|
||||
// to generate the statediff node objects for all of the accounts that existed at both A and B but in different states
|
||||
// needs to be called before building account creations and deletions as this mutates
|
||||
// those account maps to remove the accounts which were updated
|
||||
func (sdb *builder) buildAccountUpdates(creations, deletions AccountMap, updatedKeys []string, watchedStorageKeys []common.Hash, intermediateStorageNodes bool, output sdtypes.StateNodeSink) error {
|
||||
func (sdb *builder) buildAccountUpdates(creations, deletions AccountMap, updatedKeys []string, intermediateStorageNodes bool, output sdtypes.StateNodeSink) error {
|
||||
var err error
|
||||
for _, key := range updatedKeys {
|
||||
createdAcc := creations[key]
|
||||
@@ -511,7 +537,7 @@ func (sdb *builder) buildAccountUpdates(creations, deletions AccountMap, updated
|
||||
if deletedAcc.Account != nil && createdAcc.Account != nil {
|
||||
oldSR := deletedAcc.Account.Root
|
||||
newSR := createdAcc.Account.Root
|
||||
err = sdb.buildStorageNodesIncremental(oldSR, newSR, watchedStorageKeys, intermediateStorageNodes, storageNodeAppender(&storageDiffs))
|
||||
err = sdb.buildStorageNodesIncremental(oldSR, newSR, intermediateStorageNodes, storageNodeAppender(&storageDiffs))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed building incremental storage diffs for account with leafkey %s\r\nerror: %v", key, err)
|
||||
}
|
||||
@@ -534,7 +560,7 @@ func (sdb *builder) buildAccountUpdates(creations, deletions AccountMap, updated
|
||||
|
||||
// buildAccountCreations returns the statediff node objects for all the accounts that exist at B but not at A
|
||||
// it also returns the code and codehash for created contract accounts
|
||||
func (sdb *builder) buildAccountCreations(accounts AccountMap, watchedStorageKeys []common.Hash, intermediateStorageNodes bool, output sdtypes.StateNodeSink, codeOutput sdtypes.CodeSink) error {
|
||||
func (sdb *builder) buildAccountCreations(accounts AccountMap, intermediateStorageNodes bool, output sdtypes.StateNodeSink, codeOutput sdtypes.CodeSink) error {
|
||||
for _, val := range accounts {
|
||||
diff := sdtypes.StateNode{
|
||||
NodeType: val.NodeType,
|
||||
@@ -545,7 +571,7 @@ func (sdb *builder) buildAccountCreations(accounts AccountMap, watchedStorageKey
|
||||
if !bytes.Equal(val.Account.CodeHash, nullCodeHash) {
|
||||
// For contract creations, any storage node contained is a diff
|
||||
var storageDiffs []sdtypes.StorageNode
|
||||
err := sdb.buildStorageNodesEventual(val.Account.Root, watchedStorageKeys, intermediateStorageNodes, storageNodeAppender(&storageDiffs))
|
||||
err := sdb.buildStorageNodesEventual(val.Account.Root, intermediateStorageNodes, storageNodeAppender(&storageDiffs))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed building eventual storage diffs for node %x\r\nerror: %v", val.Path, err)
|
||||
}
|
||||
@@ -573,7 +599,7 @@ func (sdb *builder) buildAccountCreations(accounts AccountMap, watchedStorageKey
|
||||
|
||||
// buildStorageNodesEventual builds the storage diff node objects for a created account
|
||||
// i.e. it returns all the storage nodes at this state, since there is no previous state
|
||||
func (sdb *builder) buildStorageNodesEventual(sr common.Hash, watchedStorageKeys []common.Hash, intermediateNodes bool, output sdtypes.StorageNodeSink) error {
|
||||
func (sdb *builder) buildStorageNodesEventual(sr common.Hash, intermediateNodes bool, output sdtypes.StorageNodeSink) error {
|
||||
if bytes.Equal(sr.Bytes(), emptyContractRoot.Bytes()) {
|
||||
return nil
|
||||
}
|
||||
@@ -584,7 +610,7 @@ func (sdb *builder) buildStorageNodesEventual(sr common.Hash, watchedStorageKeys
|
||||
return err
|
||||
}
|
||||
it := sTrie.NodeIterator(make([]byte, 0))
|
||||
err = sdb.buildStorageNodesFromTrie(it, watchedStorageKeys, intermediateNodes, output)
|
||||
err = sdb.buildStorageNodesFromTrie(it, intermediateNodes, output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -594,7 +620,7 @@ func (sdb *builder) buildStorageNodesEventual(sr common.Hash, watchedStorageKeys
|
||||
// buildStorageNodesFromTrie returns all the storage diff node objects in the provided node iterator
|
||||
// if any storage keys are provided it will only return those leaf nodes
|
||||
// including intermediate nodes can be turned on or off
|
||||
func (sdb *builder) buildStorageNodesFromTrie(it trie.NodeIterator, watchedStorageKeys []common.Hash, intermediateNodes bool, output sdtypes.StorageNodeSink) error {
|
||||
func (sdb *builder) buildStorageNodesFromTrie(it trie.NodeIterator, intermediateNodes bool, output sdtypes.StorageNodeSink) error {
|
||||
for it.Next(true) {
|
||||
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
|
||||
continue
|
||||
@@ -609,15 +635,13 @@ func (sdb *builder) buildStorageNodesFromTrie(it trie.NodeIterator, watchedStora
|
||||
valueNodePath := append(node.Path, partialPath...)
|
||||
encodedPath := trie.HexToCompact(valueNodePath)
|
||||
leafKey := encodedPath[1:]
|
||||
if isWatchedStorageKey(watchedStorageKeys, leafKey) {
|
||||
if err := output(sdtypes.StorageNode{
|
||||
NodeType: node.NodeType,
|
||||
Path: node.Path,
|
||||
NodeValue: node.NodeValue,
|
||||
LeafKey: leafKey,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := output(sdtypes.StorageNode{
|
||||
NodeType: node.NodeType,
|
||||
Path: node.Path,
|
||||
NodeValue: node.NodeValue,
|
||||
LeafKey: leafKey,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
case sdtypes.Extension, sdtypes.Branch:
|
||||
if intermediateNodes {
|
||||
@@ -636,8 +660,70 @@ func (sdb *builder) buildStorageNodesFromTrie(it trie.NodeIterator, watchedStora
|
||||
return it.Error()
|
||||
}
|
||||
|
||||
// buildRemovedAccountStorageNodes builds the "removed" diffs for all the storage nodes for a destroyed account
|
||||
func (sdb *builder) buildRemovedAccountStorageNodes(sr common.Hash, intermediateNodes bool, output sdtypes.StorageNodeSink) error {
|
||||
if bytes.Equal(sr.Bytes(), emptyContractRoot.Bytes()) {
|
||||
return nil
|
||||
}
|
||||
log.Debug("Storage Root For Removed Diffs", "root", sr.Hex())
|
||||
sTrie, err := sdb.stateCache.OpenTrie(sr)
|
||||
if err != nil {
|
||||
log.Info("error in build removed account storage diffs", "error", err)
|
||||
return err
|
||||
}
|
||||
it := sTrie.NodeIterator(make([]byte, 0))
|
||||
err = sdb.buildRemovedStorageNodesFromTrie(it, intermediateNodes, output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildRemovedStorageNodesFromTrie returns diffs for all the storage nodes in the provided node interator
|
||||
// including intermediate nodes can be turned on or off
|
||||
func (sdb *builder) buildRemovedStorageNodesFromTrie(it trie.NodeIterator, intermediateNodes bool, output sdtypes.StorageNodeSink) error {
|
||||
for it.Next(true) {
|
||||
// skip value nodes
|
||||
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
|
||||
continue
|
||||
}
|
||||
node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch node.NodeType {
|
||||
case sdtypes.Leaf:
|
||||
partialPath := trie.CompactToHex(nodeElements[0].([]byte))
|
||||
valueNodePath := append(node.Path, partialPath...)
|
||||
encodedPath := trie.HexToCompact(valueNodePath)
|
||||
leafKey := encodedPath[1:]
|
||||
if err := output(sdtypes.StorageNode{
|
||||
NodeType: sdtypes.Removed,
|
||||
Path: node.Path,
|
||||
NodeValue: []byte{},
|
||||
LeafKey: leafKey,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
case sdtypes.Extension, sdtypes.Branch:
|
||||
if intermediateNodes {
|
||||
if err := output(sdtypes.StorageNode{
|
||||
NodeType: sdtypes.Removed,
|
||||
Path: node.Path,
|
||||
NodeValue: []byte{},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unexpected node type %s", node.NodeType)
|
||||
}
|
||||
}
|
||||
return it.Error()
|
||||
}
|
||||
|
||||
// buildStorageNodesIncremental builds the storage diff node objects for all nodes that exist in a different state at B than A
|
||||
func (sdb *builder) buildStorageNodesIncremental(oldSR common.Hash, newSR common.Hash, watchedStorageKeys []common.Hash, intermediateNodes bool, output sdtypes.StorageNodeSink) error {
|
||||
func (sdb *builder) buildStorageNodesIncremental(oldSR common.Hash, newSR common.Hash, intermediateNodes bool, output sdtypes.StorageNodeSink) error {
|
||||
if bytes.Equal(newSR.Bytes(), oldSR.Bytes()) {
|
||||
return nil
|
||||
}
|
||||
@@ -651,19 +737,20 @@ func (sdb *builder) buildStorageNodesIncremental(oldSR common.Hash, newSR common
|
||||
return err
|
||||
}
|
||||
|
||||
diffPathsAtB, err := sdb.createdAndUpdatedStorage(oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{}), watchedStorageKeys, intermediateNodes, output)
|
||||
diffSlotsAtB, diffPathsAtB, err := sdb.createdAndUpdatedStorage(oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{}), intermediateNodes, output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = sdb.deletedOrUpdatedStorage(oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{}), diffPathsAtB, watchedStorageKeys, intermediateNodes, output)
|
||||
err = sdb.deletedOrUpdatedStorage(oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{}), diffSlotsAtB, diffPathsAtB, intermediateNodes, output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sdb *builder) createdAndUpdatedStorage(a, b trie.NodeIterator, watchedKeys []common.Hash, intermediateNodes bool, output sdtypes.StorageNodeSink) (map[string]bool, error) {
|
||||
func (sdb *builder) createdAndUpdatedStorage(a, b trie.NodeIterator, intermediateNodes bool, output sdtypes.StorageNodeSink) (map[string]bool, map[string]bool, error) {
|
||||
diffPathsAtB := make(map[string]bool)
|
||||
diffSlotsAtB := make(map[string]bool)
|
||||
it, _ := trie.NewDifferenceIterator(a, b)
|
||||
for it.Next(true) {
|
||||
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
|
||||
@@ -671,7 +758,7 @@ func (sdb *builder) createdAndUpdatedStorage(a, b trie.NodeIterator, watchedKeys
|
||||
}
|
||||
node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
switch node.NodeType {
|
||||
case sdtypes.Leaf:
|
||||
@@ -679,15 +766,14 @@ func (sdb *builder) createdAndUpdatedStorage(a, b trie.NodeIterator, watchedKeys
|
||||
valueNodePath := append(node.Path, partialPath...)
|
||||
encodedPath := trie.HexToCompact(valueNodePath)
|
||||
leafKey := encodedPath[1:]
|
||||
if isWatchedStorageKey(watchedKeys, leafKey) {
|
||||
if err := output(sdtypes.StorageNode{
|
||||
NodeType: node.NodeType,
|
||||
Path: node.Path,
|
||||
NodeValue: node.NodeValue,
|
||||
LeafKey: leafKey,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
diffSlotsAtB[common.Bytes2Hex(leafKey)] = true
|
||||
if err := output(sdtypes.StorageNode{
|
||||
NodeType: node.NodeType,
|
||||
Path: node.Path,
|
||||
NodeValue: node.NodeValue,
|
||||
LeafKey: leafKey,
|
||||
}); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
case sdtypes.Extension, sdtypes.Branch:
|
||||
if intermediateNodes {
|
||||
@@ -696,57 +782,75 @@ func (sdb *builder) createdAndUpdatedStorage(a, b trie.NodeIterator, watchedKeys
|
||||
Path: node.Path,
|
||||
NodeValue: node.NodeValue,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected node type %s", node.NodeType)
|
||||
return nil, nil, fmt.Errorf("unexpected node type %s", node.NodeType)
|
||||
}
|
||||
diffPathsAtB[common.Bytes2Hex(node.Path)] = true
|
||||
}
|
||||
return diffPathsAtB, it.Error()
|
||||
return diffSlotsAtB, diffPathsAtB, it.Error()
|
||||
}
|
||||
|
||||
func (sdb *builder) deletedOrUpdatedStorage(a, b trie.NodeIterator, diffPathsAtB map[string]bool, watchedKeys []common.Hash, intermediateNodes bool, output sdtypes.StorageNodeSink) error {
|
||||
func (sdb *builder) deletedOrUpdatedStorage(a, b trie.NodeIterator, diffSlotsAtB, diffPathsAtB map[string]bool, intermediateNodes bool, output sdtypes.StorageNodeSink) error {
|
||||
it, _ := trie.NewDifferenceIterator(b, a)
|
||||
for it.Next(true) {
|
||||
// skip value nodes
|
||||
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
|
||||
continue
|
||||
}
|
||||
node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB())
|
||||
node, nodeElements, err := sdtrie.ResolveNode(it, sdb.stateCache.TrieDB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if this node path showed up in diffPathsAtB
|
||||
// that means this node was updated at B and we already have the updated diff for it
|
||||
// otherwise that means this node was deleted in B and we need to add a "removed" diff to represent that event
|
||||
if _, ok := diffPathsAtB[common.Bytes2Hex(node.Path)]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
switch node.NodeType {
|
||||
case sdtypes.Leaf:
|
||||
partialPath := trie.CompactToHex(nodeElements[0].([]byte))
|
||||
valueNodePath := append(node.Path, partialPath...)
|
||||
encodedPath := trie.HexToCompact(valueNodePath)
|
||||
leafKey := encodedPath[1:]
|
||||
if isWatchedStorageKey(watchedKeys, leafKey) {
|
||||
if err := output(sdtypes.StorageNode{
|
||||
NodeType: sdtypes.Removed,
|
||||
Path: node.Path,
|
||||
NodeValue: []byte{},
|
||||
LeafKey: leafKey,
|
||||
}); err != nil {
|
||||
return err
|
||||
|
||||
// if this node's path did not show up in diffPathsAtB
|
||||
// that means the node at this path was deleted (or moved) in B
|
||||
if _, ok := diffPathsAtB[common.Bytes2Hex(node.Path)]; !ok {
|
||||
// if this node's leaf key also did not show up in diffSlotsAtB
|
||||
// that means the node was deleted
|
||||
// in that case, emit an empty "removed" diff storage node
|
||||
if _, ok := diffSlotsAtB[common.Bytes2Hex(leafKey)]; !ok {
|
||||
if err := output(sdtypes.StorageNode{
|
||||
NodeType: sdtypes.Removed,
|
||||
Path: node.Path,
|
||||
NodeValue: []byte{},
|
||||
LeafKey: leafKey,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// emit an empty "removed" diff with empty leaf key if the account was moved
|
||||
if err := output(sdtypes.StorageNode{
|
||||
NodeType: sdtypes.Removed,
|
||||
Path: node.Path,
|
||||
NodeValue: []byte{},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
case sdtypes.Extension, sdtypes.Branch:
|
||||
if intermediateNodes {
|
||||
if err := output(sdtypes.StorageNode{
|
||||
NodeType: sdtypes.Removed,
|
||||
Path: node.Path,
|
||||
NodeValue: []byte{},
|
||||
}); err != nil {
|
||||
return err
|
||||
// if this node's path did not show up in diffPathsAtB
|
||||
// that means the node at this path was deleted in B
|
||||
// in that case, emit an empty "removed" diff storage node
|
||||
if _, ok := diffPathsAtB[common.Bytes2Hex(node.Path)]; !ok {
|
||||
if intermediateNodes {
|
||||
if err := output(sdtypes.StorageNode{
|
||||
NodeType: sdtypes.Removed,
|
||||
Path: node.Path,
|
||||
NodeValue: []byte{},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
@@ -757,18 +861,14 @@ func (sdb *builder) deletedOrUpdatedStorage(a, b trie.NodeIterator, diffPathsAtB
|
||||
}
|
||||
|
||||
// isWatchedAddress is used to check if a state account corresponds to one of the addresses the builder is configured to watch
|
||||
func isWatchedAddress(watchedAddresses []common.Address, stateLeafKey []byte) bool {
|
||||
func isWatchedAddress(watchedAddressesLeafKeys map[common.Hash]struct{}, stateLeafKey []byte) bool {
|
||||
// If we aren't watching any specific addresses, we are watching everything
|
||||
if len(watchedAddresses) == 0 {
|
||||
if len(watchedAddressesLeafKeys) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, addr := range watchedAddresses {
|
||||
addrHashKey := crypto.Keccak256(addr.Bytes())
|
||||
if bytes.Equal(addrHashKey, stateLeafKey) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
_, ok := watchedAddressesLeafKeys[common.BytesToHash(stateLeafKey)]
|
||||
return ok
|
||||
}
|
||||
|
||||
// isWatchedStorageKey is used to check if a storage leaf corresponds to one of the storage slots the builder is configured to watch
|
||||
|
||||
+631
-439
File diff suppressed because it is too large
Load Diff
+17
-5
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/vulcanize/leveldb-ethdb-rpc/pkg/client"
|
||||
)
|
||||
|
||||
// Reader interface required by the statediffing service
|
||||
@@ -46,15 +47,26 @@ type LvlDBReader struct {
|
||||
|
||||
// LvLDBReaderConfig struct for initializing a LvlDBReader
|
||||
type LvLDBReaderConfig struct {
|
||||
TrieConfig *trie.Config
|
||||
ChainConfig *params.ChainConfig
|
||||
Path, AncientPath string
|
||||
DBCacheSize int
|
||||
TrieConfig *trie.Config
|
||||
ChainConfig *params.ChainConfig
|
||||
Mode string
|
||||
Path, AncientPath, Url string
|
||||
DBCacheSize int
|
||||
}
|
||||
|
||||
// NewLvlDBReader creates a new Read using LevelDB
|
||||
func NewLvlDBReader(conf LvLDBReaderConfig) (*LvlDBReader, error) {
|
||||
edb, err := rawdb.NewLevelDBDatabaseWithFreezer(conf.Path, conf.DBCacheSize, 256, conf.AncientPath, "eth-statediff-service", true)
|
||||
var edb ethdb.Database
|
||||
var err error
|
||||
|
||||
if conf.Mode == "local" {
|
||||
edb, err = rawdb.NewLevelDBDatabaseWithFreezer(conf.Path, conf.DBCacheSize, 256, conf.AncientPath, "eth-statediff-service", true)
|
||||
}
|
||||
|
||||
if conf.Mode == "remote" {
|
||||
edb, err = client.NewDatabaseClient(conf.Url)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ func StartHTTPEndpoint(endpoint string, apis []rpc.API, modules []string, cors [
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not register HTTP API: %w", err)
|
||||
}
|
||||
handler := node.NewHTTPHandlerStack(srv, cors, vhosts)
|
||||
handler := node.NewHTTPHandlerStack(srv, cors, vhosts, nil)
|
||||
|
||||
// start http server
|
||||
_, addr, err := node.StartHTTPEndpoint(endpoint, rpc.DefaultHTTPTimeouts, prom.HTTPMiddleware(handler))
|
||||
|
||||
+37
-17
@@ -29,11 +29,11 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
sd "github.com/ethereum/go-ethereum/statediff"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/interfaces"
|
||||
sdtypes "github.com/ethereum/go-ethereum/statediff/types"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/vulcanize/eth-statediff-service/pkg/prom"
|
||||
|
||||
ind "github.com/ethereum/go-ethereum/statediff/indexer"
|
||||
"github.com/vulcanize/eth-statediff-service/pkg/prom"
|
||||
)
|
||||
|
||||
const defaultQueueSize = 1024
|
||||
@@ -67,12 +67,12 @@ type StateDiffService interface {
|
||||
type Service struct {
|
||||
// Used to build the state diff objects
|
||||
Builder Builder
|
||||
// Used to read data from leveldb
|
||||
// Used to read data from LevelDB
|
||||
lvlDBReader Reader
|
||||
// Used to signal shutdown of the service
|
||||
quitChan chan struct{}
|
||||
// Interface for publishing statediffs as PG-IPLD objects
|
||||
indexer ind.Indexer
|
||||
indexer interfaces.StateDiffIndexer
|
||||
// range queue
|
||||
queue chan RangeRequest
|
||||
// number of ranges we can work over concurrently
|
||||
@@ -82,8 +82,8 @@ type Service struct {
|
||||
}
|
||||
|
||||
// NewStateDiffService creates a new Service
|
||||
func NewStateDiffService(lvlDBReader Reader, indexer ind.Indexer, conf Config) (*Service, error) {
|
||||
builder, err := NewBuilder(lvlDBReader.StateDB(), conf.TrieWorkers)
|
||||
func NewStateDiffService(lvlDBReader Reader, indexer interfaces.StateDiffIndexer, conf Config) (*Service, error) {
|
||||
b, err := NewBuilder(lvlDBReader.StateDB(), conf.TrieWorkers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -92,7 +92,7 @@ func NewStateDiffService(lvlDBReader Reader, indexer ind.Indexer, conf Config) (
|
||||
}
|
||||
return &Service{
|
||||
lvlDBReader: lvlDBReader,
|
||||
Builder: builder,
|
||||
Builder: b,
|
||||
indexer: indexer,
|
||||
workers: conf.ServiceWorkers,
|
||||
queue: make(chan RangeRequest, conf.WorkerQueueSize),
|
||||
@@ -193,7 +193,11 @@ func (sds *Service) StateDiffAt(blockNumber uint64, params sd.Params) (*sd.Paylo
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logrus.Info(fmt.Sprintf("sending state diff at block %d", blockNumber))
|
||||
logrus.Infof("sending state diff at block %d", blockNumber)
|
||||
|
||||
// compute leaf keys of watched addresses in the params
|
||||
params.ComputeWatchedAddressesLeafKeys()
|
||||
|
||||
if blockNumber == 0 {
|
||||
return sds.processStateDiff(currentBlock, common.Hash{}, params)
|
||||
}
|
||||
@@ -211,7 +215,11 @@ func (sds *Service) StateDiffFor(blockHash common.Hash, params sd.Params) (*sd.P
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logrus.Info(fmt.Sprintf("sending state diff at block %s", blockHash.Hex()))
|
||||
logrus.Infof("sending state diff at block %s", blockHash.Hex())
|
||||
|
||||
// compute leaf keys of watched addresses in the params
|
||||
params.ComputeWatchedAddressesLeafKeys()
|
||||
|
||||
if currentBlock.NumberU64() == 0 {
|
||||
return sds.processStateDiff(currentBlock, common.Hash{}, params)
|
||||
}
|
||||
@@ -233,7 +241,7 @@ func (sds *Service) processStateDiff(currentBlock *types.Block, parentRoot commo
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stateDiffRlp, err := rlp.EncodeToBytes(stateDiff)
|
||||
stateDiffRlp, err := rlp.EncodeToBytes(&stateDiff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -280,7 +288,11 @@ func (sds *Service) StateTrieAt(blockNumber uint64, params sd.Params) (*sd.Paylo
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logrus.Info(fmt.Sprintf("sending state trie at block %d", blockNumber))
|
||||
logrus.Infof("sending state trie at block %d", blockNumber)
|
||||
|
||||
// compute leaf keys of watched addresses in the params
|
||||
params.ComputeWatchedAddressesLeafKeys()
|
||||
|
||||
return sds.processStateTrie(currentBlock, params)
|
||||
}
|
||||
|
||||
@@ -289,7 +301,7 @@ func (sds *Service) processStateTrie(block *types.Block, params sd.Params) (*sd.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stateTrieRlp, err := rlp.EncodeToBytes(stateNodes)
|
||||
stateTrieRlp, err := rlp.EncodeToBytes(&stateNodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -314,12 +326,16 @@ func (sds *Service) Stop() error {
|
||||
// This operation cannot be performed back past the point of db pruning; it requires an archival node
|
||||
// for historical data
|
||||
func (sds *Service) WriteStateDiffAt(blockNumber uint64, params sd.Params) error {
|
||||
logrus.Info(fmt.Sprintf("Writing state diff at block %d", blockNumber))
|
||||
logrus.Infof("Writing state diff at block %d", blockNumber)
|
||||
t := time.Now()
|
||||
currentBlock, err := sds.lvlDBReader.GetBlockByNumber(blockNumber)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// compute leaf keys of watched addresses in the params
|
||||
params.ComputeWatchedAddressesLeafKeys()
|
||||
|
||||
parentRoot := common.Hash{}
|
||||
if blockNumber != 0 {
|
||||
parentBlock, err := sds.lvlDBReader.GetBlockByHash(currentBlock.ParentHash())
|
||||
@@ -335,12 +351,16 @@ func (sds *Service) WriteStateDiffAt(blockNumber uint64, params sd.Params) error
|
||||
// This operation cannot be performed back past the point of db pruning; it requires an archival node
|
||||
// for historical data
|
||||
func (sds *Service) WriteStateDiffFor(blockHash common.Hash, params sd.Params) error {
|
||||
logrus.Info(fmt.Sprintf("Writing state diff for block %s", blockHash.Hex()))
|
||||
logrus.Infof("Writing state diff for block %s", blockHash.Hex())
|
||||
t := time.Now()
|
||||
currentBlock, err := sds.lvlDBReader.GetBlockByHash(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// compute leaf keys of watched addresses in the params
|
||||
params.ComputeWatchedAddressesLeafKeys()
|
||||
|
||||
parentRoot := common.Hash{}
|
||||
if currentBlock.NumberU64() != 0 {
|
||||
parentBlock, err := sds.lvlDBReader.GetBlockByHash(currentBlock.ParentHash())
|
||||
@@ -379,20 +399,20 @@ func (sds *Service) writeStateDiff(block *types.Block, parentRoot common.Hash, p
|
||||
}
|
||||
// defer handling of commit/rollback for any return case
|
||||
output := func(node sdtypes.StateNode) error {
|
||||
return sds.indexer.PushStateNode(tx, node)
|
||||
return sds.indexer.PushStateNode(tx, node, block.Hash().String())
|
||||
}
|
||||
codeOutput := func(c sdtypes.CodeAndCodeHash) error {
|
||||
return sds.indexer.PushCodeAndCodeHash(tx, c)
|
||||
}
|
||||
prom.SetTimeMetric(prom.T_BLOCK_PROCESSING, time.Now().Sub(t))
|
||||
t = time.Now()
|
||||
err = sds.Builder.WriteStateDiffObject(sd.StateRoots{
|
||||
err = sds.Builder.WriteStateDiffObject(sdtypes.StateRoots{
|
||||
NewStateRoot: block.Root(),
|
||||
OldStateRoot: parentRoot,
|
||||
}, params, output, codeOutput)
|
||||
prom.SetTimeMetric(prom.T_STATE_PROCESSING, time.Now().Sub(t))
|
||||
t = time.Now()
|
||||
err = tx.Close(err)
|
||||
err = tx.Submit(err)
|
||||
prom.SetLastProcessedHeight(height)
|
||||
prom.SetTimeMetric(prom.T_POSTGRES_TX_COMMIT, time.Now().Sub(t))
|
||||
return err
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
// Copyright 2019 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package testhelpers
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// MakeChain creates a chain of n blocks starting at and including parent.
|
||||
// the returned hash chain is ordered head->parent.
|
||||
func MakeChain(n int, parent *types.Block, chainGen func(int, *core.BlockGen)) ([]*types.Block, *core.BlockChain) {
|
||||
config := params.TestChainConfig
|
||||
config.LondonBlock.Set(big.NewInt(10))
|
||||
blocks, _ := core.GenerateChain(config, parent, ethash.NewFaker(), Testdb, n, chainGen)
|
||||
chain, _ := core.NewBlockChain(Testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
return blocks, chain
|
||||
}
|
||||
|
||||
func TestSelfDestructChainGen(i int, block *core.BlockGen) {
|
||||
signer := types.HomesteadSigner{}
|
||||
switch i {
|
||||
case 0:
|
||||
// Block 1 is mined by Account1Addr
|
||||
// Account1Addr creates a new contract
|
||||
block.SetCoinbase(TestBankAddress)
|
||||
tx, _ := types.SignTx(types.NewContractCreation(0, big.NewInt(0), 1000000, big.NewInt(0), ContractCode), signer, TestBankKey)
|
||||
ContractAddr = crypto.CreateAddress(TestBankAddress, 0)
|
||||
block.AddTx(tx)
|
||||
case 1:
|
||||
// Block 2 is mined by Account1Addr
|
||||
// Account1Addr self-destructs the contract
|
||||
block.SetCoinbase(TestBankAddress)
|
||||
data := common.Hex2Bytes("43D726D6")
|
||||
tx, _ := types.SignTx(types.NewTransaction(1, ContractAddr, big.NewInt(0), 100000, nil, data), signer, TestBankKey)
|
||||
block.AddTx(tx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainGen(i int, block *core.BlockGen) {
|
||||
signer := types.HomesteadSigner{}
|
||||
switch i {
|
||||
case 0:
|
||||
// In block 1, the test bank sends account #1 some ether.
|
||||
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(TestBankAddress), Account1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, TestBankKey)
|
||||
block.AddTx(tx)
|
||||
case 1:
|
||||
// In block 2, the test bank sends some more ether to account #1.
|
||||
// Account1Addr passes it on to account #2.
|
||||
// Account1Addr creates a test contract.
|
||||
tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(TestBankAddress), Account1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, TestBankKey)
|
||||
nonce := block.TxNonce(Account1Addr)
|
||||
tx2, _ := types.SignTx(types.NewTransaction(nonce, Account2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, Account1Key)
|
||||
nonce++
|
||||
tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, big.NewInt(0), ContractCode), signer, Account1Key)
|
||||
ContractAddr = crypto.CreateAddress(Account1Addr, nonce)
|
||||
block.AddTx(tx1)
|
||||
block.AddTx(tx2)
|
||||
block.AddTx(tx3)
|
||||
case 2:
|
||||
// Block 3 has a single tx from the bankAccount to the contract, that transfers no value
|
||||
// Block 3 is mined by Account2Addr
|
||||
block.SetCoinbase(Account2Addr)
|
||||
//put function: c16431b9
|
||||
//close function: 43d726d6
|
||||
data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003")
|
||||
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(TestBankAddress), ContractAddr, big.NewInt(0), 100000, nil, data), signer, TestBankKey)
|
||||
block.AddTx(tx)
|
||||
case 3:
|
||||
// Block 4 has three txs from bankAccount to the contract, that transfer no value
|
||||
// Two set the two original slot positions to 0 and one sets another position to a new value
|
||||
// Block 4 is mined by Account2Addr
|
||||
block.SetCoinbase(Account2Addr)
|
||||
data1 := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
|
||||
data2 := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000")
|
||||
data3 := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000009")
|
||||
|
||||
nonce := block.TxNonce(TestBankAddress)
|
||||
tx1, _ := types.SignTx(types.NewTransaction(nonce, ContractAddr, big.NewInt(0), 100000, nil, data1), signer, TestBankKey)
|
||||
nonce++
|
||||
tx2, _ := types.SignTx(types.NewTransaction(nonce, ContractAddr, big.NewInt(0), 100000, nil, data2), signer, TestBankKey)
|
||||
nonce++
|
||||
tx3, _ := types.SignTx(types.NewTransaction(nonce, ContractAddr, big.NewInt(0), 100000, nil, data3), signer, TestBankKey)
|
||||
block.AddTx(tx1)
|
||||
block.AddTx(tx2)
|
||||
block.AddTx(tx3)
|
||||
case 4:
|
||||
// Block 5 has one tx from bankAccount to the contract, that transfers no value
|
||||
// It sets the remaining storage value to zero
|
||||
// Block 5 is mined by Account1Addr
|
||||
block.SetCoinbase(Account1Addr)
|
||||
data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000")
|
||||
nonce := block.TxNonce(TestBankAddress)
|
||||
tx, _ := types.SignTx(types.NewTransaction(nonce, ContractAddr, big.NewInt(0), 100000, nil, data), signer, TestBankKey)
|
||||
block.AddTx(tx)
|
||||
case 5:
|
||||
// Block 6 has a tx from Account1Key which self-destructs the contract, it transfers no value
|
||||
// Block 6 is mined by Account2Addr
|
||||
block.SetCoinbase(Account2Addr)
|
||||
data := common.Hex2Bytes("43D726D6")
|
||||
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(Account1Addr), ContractAddr, big.NewInt(0), 100000, nil, data), signer, Account1Key)
|
||||
block.AddTx(tx)
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
// Copyright 2019 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package testhelpers
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// AddressToLeafKey hashes an returns an address
|
||||
func AddressToLeafKey(address common.Address) []byte {
|
||||
return crypto.Keccak256(address[:])
|
||||
}
|
||||
|
||||
// AddressToEncodedPath hashes an address and appends the even-number leaf flag to it
|
||||
func AddressToEncodedPath(address common.Address) []byte {
|
||||
addrHash := crypto.Keccak256(address[:])
|
||||
decodedPath := append(EvenLeafFlag, addrHash...)
|
||||
return decodedPath
|
||||
}
|
||||
|
||||
// Test variables
|
||||
var (
|
||||
EvenLeafFlag = []byte{byte(2) << 4}
|
||||
NullCodeHash = crypto.Keccak256Hash([]byte{})
|
||||
NullHash = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000")
|
||||
|
||||
Testdb = rawdb.NewMemoryDatabase()
|
||||
TestBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
TestBankAddress = crypto.PubkeyToAddress(TestBankKey.PublicKey) //0x71562b71999873DB5b286dF957af199Ec94617F7
|
||||
BankLeafKey = AddressToLeafKey(TestBankAddress)
|
||||
TestBankFunds = big.NewInt(100000000)
|
||||
Genesis = core.GenesisBlockForTesting(Testdb, TestBankAddress, TestBankFunds)
|
||||
|
||||
Account1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||
Account2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
||||
Account1Addr = crypto.PubkeyToAddress(Account1Key.PublicKey) //0x703c4b2bD70c169f5717101CaeE543299Fc946C7
|
||||
Account2Addr = crypto.PubkeyToAddress(Account2Key.PublicKey) //0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e
|
||||
Account1LeafKey = AddressToLeafKey(Account1Addr)
|
||||
Account2LeafKey = AddressToLeafKey(Account2Addr)
|
||||
ContractCode = common.Hex2Bytes("608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506040518060200160405280600160ff16815250600190600161007492919061007a565b506100e4565b82606481019282156100ae579160200282015b828111156100ad578251829060ff1690559160200191906001019061008d565b5b5090506100bb91906100bf565b5090565b6100e191905b808211156100dd5760008160009055506001016100c5565b5090565b90565b6101ca806100f36000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806343d726d61461003b578063c16431b914610045575b600080fd5b61004361007d565b005b61007b6004803603604081101561005b57600080fd5b81019080803590602001909291908035906020019092919050505061015c565b005b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610122576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806101746022913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b806001836064811061016a57fe5b0181905550505056fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6e2ea265627a7a72305820e3747183708fb6bff3f6f7a80fb57dcc1c19f83f9cb25457a3ed5c0424bde66864736f6c634300050a0032")
|
||||
ByteCodeAfterDeployment = common.Hex2Bytes("608060405234801561001057600080fd5b50600436106100365760003560e01c806343d726d61461003b578063c16431b914610045575b600080fd5b61004361007d565b005b61007b6004803603604081101561005b57600080fd5b81019080803590602001909291908035906020019092919050505061015c565b005b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610122576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806101746022913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b806001836064811061016a57fe5b0181905550505056fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6e2ea265627a7a72305820e3747183708fb6bff3f6f7a80fb57dcc1c19f83f9cb25457a3ed5c0424bde66864736f6c634300050a0032")
|
||||
CodeHash = common.HexToHash("0xaaea5efba4fd7b45d7ec03918ac5d8b31aa93b48986af0e6b591f0f087c80127")
|
||||
ContractAddr common.Address
|
||||
|
||||
EmptyRootNode, _ = rlp.EncodeToBytes([]byte{})
|
||||
EmptyContractRoot = crypto.Keccak256Hash(EmptyRootNode)
|
||||
)
|
||||
@@ -0,0 +1,30 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2021 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 version
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
major = 3 // major version component of the current release
|
||||
minor = 0 // minor version component of the current release
|
||||
patch = 0 // patch version component of the current release
|
||||
)
|
||||
|
||||
// Version holds the textual version string.
|
||||
var Version = func() string {
|
||||
return fmt.Sprintf("%d.%d.%d", major, minor, patch)
|
||||
}()
|
||||
Reference in New Issue
Block a user