Compare commits
136
Commits
v0.1.0
...
v4.1.0-alpha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f898f60c3 | ||
|
|
0c56037e1f | ||
|
|
9aa683442e | ||
|
|
adf3dd4b6f | ||
|
|
5ea4b6766a | ||
|
|
2c4fd6f099 | ||
|
|
f964b53fe3 | ||
|
|
b8dad6a09b | ||
|
|
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 | ||
|
|
efb334aef3 | ||
|
|
c83e8835ab | ||
|
|
728642afec | ||
|
|
5875d1ec82 | ||
|
|
8d6919ab2b | ||
|
|
abf72725ee | ||
|
|
8a55469403 | ||
|
|
a3857bffe4 | ||
|
|
3956081874 | ||
|
|
e1abb399cd | ||
|
|
3d00e3ed05 | ||
|
|
84fff0aa03 | ||
|
|
79cb508d83 | ||
|
|
f51d89e873 | ||
|
|
a7ae868dd8 | ||
|
|
ee59ff3650 | ||
|
|
8571f35031 | ||
|
|
f679082c3b | ||
|
|
aace7793ba | ||
|
|
b49e30d8e9 | ||
|
|
f207284e3a | ||
|
|
3d3e68c0ae | ||
|
|
66835c4c3e | ||
|
|
0d092cc535 | ||
|
|
a057940716 | ||
|
|
7dc6c52dec | ||
|
|
ca9d2a7bec | ||
|
|
072f035a02 | ||
|
|
5a35f84de3 | ||
|
|
0f81dc32d2 | ||
|
|
6a912eef41 | ||
|
|
88f2eaed7f | ||
|
|
26a88f06e7 | ||
|
|
0dcec4ec8e | ||
|
|
4cbaa630c7 | ||
|
|
fb37ee9881 | ||
|
|
01d0c21fd5 | ||
|
|
64e80eeb6a | ||
|
|
05b90107ca | ||
|
|
c4b95cc1b7 | ||
|
|
7b9ca9b519 | ||
|
|
b09e58c22a | ||
|
|
450ff197a2 | ||
|
|
41e2ae176f | ||
|
|
fa0be51041 | ||
|
|
0315a31dd2 | ||
|
|
017daffb5a | ||
|
|
d3bed86fff | ||
|
|
d61113ad12 | ||
|
|
7a6b866163 | ||
|
|
bd5d2992fa | ||
|
|
d886579f3d | ||
|
|
a93eb3ce35 | ||
|
|
abff9bf045 | ||
|
|
ca8b3066d1 | ||
|
|
294568da4e | ||
|
|
a5eea3f428 | ||
|
|
05328a1763 | ||
|
|
4901b3d35b | ||
|
|
f0a631109e |
@@ -0,0 +1,69 @@
|
||||
name: Publish Docker image
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
pull_request:
|
||||
jobs:
|
||||
pre_job:
|
||||
# continue-on-error: true # Uncomment once integration is finished
|
||||
runs-on: ubuntu-latest
|
||||
# Map a step output to a job output
|
||||
outputs:
|
||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
||||
steps:
|
||||
- id: skip_check
|
||||
uses: fkirc/skip-duplicate-actions@v4
|
||||
with:
|
||||
# All of these options are optional, so you can remove them if you are happy with the defaults
|
||||
concurrent_skipping: "never"
|
||||
skip_after_successful_duplicate: "true"
|
||||
do_not_skip: '["workflow_dispatch", "schedule"]'
|
||||
run-tests:
|
||||
if: ${{ needs.pre_job.outputs.should_skip != 'true' }}
|
||||
needs: pre_job
|
||||
uses: ./.github/workflows/tests.yml
|
||||
build:
|
||||
name: Run docker build
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
always() &&
|
||||
(needs.run-tests.result == 'success' || needs.run-tests.result == 'skipped') &&
|
||||
github.event_name == 'release'
|
||||
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
|
||||
if: |
|
||||
always() &&
|
||||
(needs.build.result == 'success') &&
|
||||
github.event_name == 'release'
|
||||
needs: build
|
||||
steps:
|
||||
- name: Get the version
|
||||
id: vars
|
||||
run: |
|
||||
echo ::set-output name=sha::$(echo ${GITHUB_SHA:0:7})
|
||||
echo ::set-output name=tag::$(echo ${GITHUB_REF#refs/tags/})
|
||||
- name: Docker Login to Github Registry
|
||||
run: echo ${{ secrets.GITHUB_TOKEN }} | docker login https://docker.pkg.github.com -u vulcanize --password-stdin
|
||||
- name: Docker Pull
|
||||
run: docker pull docker.pkg.github.com/vulcanize/eth-statediff-service/eth-statediff-service:${{steps.vars.outputs.sha}}
|
||||
- name: Docker Login to Docker Registry
|
||||
run: echo ${{ secrets.VULCANIZEJENKINS_PAT }} | docker login -u vulcanizejenkins --password-stdin
|
||||
- name: Tag docker image
|
||||
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,2 +1,3 @@
|
||||
.idea/
|
||||
eth-statediff-service
|
||||
.vscode
|
||||
|
||||
+10
-5
@@ -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
|
||||
@@ -6,16 +6,21 @@ RUN apk add busybox-extras
|
||||
|
||||
# Get and build ipfs-blockchain-watcher
|
||||
ADD . /go/src/github.com/vulcanize/eth-statediff-service
|
||||
#RUN git clone https://github.com/vulcanize/eth-statediff-service.git /go/src/github.com/vulcanize/eth-statediff-service
|
||||
|
||||
WORKDIR /go/src/github.com/vulcanize/eth-statediff-service
|
||||
RUN GO111MODULE=on GCO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o eth-statediff-service .
|
||||
|
||||
# app container
|
||||
FROM alpine
|
||||
|
||||
ARG USER
|
||||
ARG CONFIG_FILE
|
||||
ARG EXPOSE_PORT
|
||||
RUN adduser -Du 5000 $USER
|
||||
ARG USER="vdm"
|
||||
ARG CONFIG_FILE="./environments/config.toml"
|
||||
ARG EXPOSE_PORT=8545
|
||||
|
||||
RUN adduser -Du 5000 $USER adm
|
||||
RUN adduser $USER adm; apk --no-cache add sudo; echo '%adm ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
|
||||
|
||||
WORKDIR /app
|
||||
RUN chown $USER /app
|
||||
USER $USER
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
## Build docker image
|
||||
.PHONY: docker-build
|
||||
docker-build:
|
||||
docker build -t vulcanize/eth-statediff-service .
|
||||
|
||||
.PHONY: test
|
||||
test: | $(GOOSE)
|
||||
go test -p 1 ./pkg/... -v
|
||||
|
||||
build:
|
||||
go fmt ./...
|
||||
go build
|
||||
@@ -1,34 +1,185 @@
|
||||
## eth-statediff-service
|
||||
# eth-statediff-service
|
||||
|
||||
[](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
|
||||
|
||||
Usage:
|
||||
## Setup
|
||||
|
||||
`./eth-statediff-service serve --config={path to toml config file}`
|
||||
Build the binary:
|
||||
|
||||
Config:
|
||||
```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`
|
||||
|
||||
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()`
|
||||
|
||||
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"`
|
||||
|
||||
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
|
||||
|
||||
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"
|
||||
|
||||
[statediff]
|
||||
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 = ""
|
||||
level = "info"
|
||||
|
||||
[eth]
|
||||
chainID = 1
|
||||
|
||||
[database]
|
||||
name = "vulcanize_test"
|
||||
hostname = "localhost"
|
||||
port = 5432
|
||||
user = "vulcanize"
|
||||
password = "..."
|
||||
type = "postgres"
|
||||
driver = "sqlx"
|
||||
dumpDestination = ""
|
||||
fileMode = "csv"
|
||||
fileCsvDir = ""
|
||||
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
|
||||
```
|
||||
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const (
|
||||
ETH_NODE_ID = "ETH_NODE_ID"
|
||||
ETH_CLIENT_NAME = "ETH_CLIENT_NAME"
|
||||
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"
|
||||
STATEDIFF_SERVICE_WORKERS = "STATEDIFF_SERVICE_WORKERS"
|
||||
STATEDIFF_WORKER_QUEUE_SIZE = "STATEDIFF_WORKER_QUEUE_SIZE"
|
||||
|
||||
SERVICE_IPC_PATH = "SERVICE_IPC_PATH"
|
||||
SERVICE_HTTP_PATH = "SERVICE_HTTP_PATH"
|
||||
|
||||
PROM_METRICS = "PROM_METRICS"
|
||||
PROM_HTTP = "PROM_HTTP"
|
||||
PROM_HTTP_ADDR = "PROM_HTTP_ADDR"
|
||||
PROM_HTTP_PORT = "PROM_HTTP_PORT"
|
||||
PROM_DB_STATS = "PROM_DB_STATS"
|
||||
|
||||
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_FILE_MODE = "DATABASE_FILE_MODE"
|
||||
DATABASE_FILE_CSV_DIR = "DATABASE_FILE_CSV_DIR"
|
||||
|
||||
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
|
||||
func init() {
|
||||
viper.BindEnv("server.ipcPath", SERVICE_IPC_PATH)
|
||||
viper.BindEnv("server.httpPath", SERVICE_HTTP_PATH)
|
||||
|
||||
viper.BindEnv("ethereum.nodeID", ETH_NODE_ID)
|
||||
viper.BindEnv("ethereum.clientName", ETH_CLIENT_NAME)
|
||||
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", 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.fileMode", DATABASE_FILE_MODE)
|
||||
viper.BindEnv("database.filePath", DATABASE_FILE_PATH)
|
||||
viper.BindEnv("database.fileCsvDir", DATABASE_FILE_CSV_DIR)
|
||||
|
||||
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)
|
||||
viper.BindEnv("prom.httpAddr", PROM_HTTP_ADDR)
|
||||
viper.BindEnv("prom.httpPort", PROM_HTTP_PORT)
|
||||
viper.BindEnv("prom.dbStats", PROM_DB_STATS)
|
||||
|
||||
viper.BindEnv("statediff.serviceWorkers", STATEDIFF_SERVICE_WORKERS)
|
||||
viper.BindEnv("statediff.trieWorkers", STATEDIFF_TRIE_WORKERS)
|
||||
viper.BindEnv("statediff.workerQueueSize", STATEDIFF_WORKER_QUEUE_SIZE)
|
||||
|
||||
viper.BindEnv("statediff.prerun", STATEDIFF_PRERUN)
|
||||
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)
|
||||
}
|
||||
+286
-5
@@ -18,12 +18,23 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"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/shared"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/vulcanize/eth-statediff-service/pkg/prom"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -45,7 +56,7 @@ func Execute() {
|
||||
}
|
||||
|
||||
func initFuncs(cmd *cobra.Command, args []string) {
|
||||
logfile := viper.GetString("logfile")
|
||||
logfile := viper.GetString("log.file")
|
||||
if logfile != "" {
|
||||
file, err := os.OpenFile(logfile,
|
||||
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
@@ -62,10 +73,24 @@ func initFuncs(cmd *cobra.Command, args []string) {
|
||||
if err := logLevel(); err != nil {
|
||||
log.Fatal("Could not set log level: ", err)
|
||||
}
|
||||
|
||||
if viper.GetBool("prom.metrics") {
|
||||
log.Info("initializing prometheus metrics")
|
||||
prom.Init()
|
||||
}
|
||||
|
||||
if viper.GetBool("prom.http") {
|
||||
addr := fmt.Sprintf(
|
||||
"%s:%s",
|
||||
viper.GetString("prom.httpAddr"),
|
||||
viper.GetString("prom.httpPort"),
|
||||
)
|
||||
log.Info("starting prometheus server")
|
||||
prom.Listen(addr)
|
||||
}
|
||||
}
|
||||
|
||||
func logLevel() error {
|
||||
viper.BindEnv("log.level", "LOGRUS_LEVEL")
|
||||
lvl, err := log.ParseLevel(viper.GetString("log.level"))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -83,14 +108,129 @@ func init() {
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
viper.AutomaticEnv()
|
||||
|
||||
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().Int("workers", 0, "number of concurrent workers to use")
|
||||
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")
|
||||
rootCmd.PersistentFlags().Int("trie-workers", 0, "number of workers to use for trie traversal and processing")
|
||||
rootCmd.PersistentFlags().Int("worker-queue-size", 0, "size of the range request queue for service workers")
|
||||
|
||||
rootCmd.PersistentFlags().String("database-name", "vulcanize_public", "database name")
|
||||
rootCmd.PersistentFlags().Int("database-port", 5432, "database port")
|
||||
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-mode", "csv", "mode for writing file (for database-type=file; options: csv, sql)")
|
||||
rootCmd.PersistentFlags().String("database-file-csv-dir", "", "full directory path (for database-file-mode=csv)")
|
||||
rootCmd.PersistentFlags().String("database-file-path", "", "full file path (for database-file-mode=sql)")
|
||||
|
||||
rootCmd.PersistentFlags().String("eth-node-id", "", "eth node id")
|
||||
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")
|
||||
rootCmd.PersistentFlags().String("eth-chain-id", "1", "eth chain id")
|
||||
|
||||
rootCmd.PersistentFlags().Int("cache-db", 1024, "megabytes of memory allocated to database cache")
|
||||
rootCmd.PersistentFlags().Int("cache-trie", 1024, "Megabytes of memory allocated to trie cache")
|
||||
|
||||
rootCmd.PersistentFlags().Bool("prom-http", false, "enable prometheus http service")
|
||||
rootCmd.PersistentFlags().String("prom-http-addr", "127.0.0.1", "prometheus http host")
|
||||
rootCmd.PersistentFlags().String("prom-http-port", "8080", "prometheus http port")
|
||||
rootCmd.PersistentFlags().Bool("prom-db-stats", false, "enables prometheus db stats")
|
||||
rootCmd.PersistentFlags().Bool("prom-metrics", false, "enable prometheus metrics")
|
||||
|
||||
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.workers", rootCmd.PersistentFlags().Lookup("workers"))
|
||||
|
||||
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.fileMode", rootCmd.PersistentFlags().Lookup("database-file-mode"))
|
||||
viper.BindPFlag("database.fileCsvDir", rootCmd.PersistentFlags().Lookup("database-file-csv-dir"))
|
||||
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())
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
@@ -105,3 +245,144 @@ func initConfig() {
|
||||
log.Warn("No config file passed with --config flag")
|
||||
}
|
||||
}
|
||||
|
||||
func getEthNodeInfo() node.Info {
|
||||
var nodeID, genesisBlock, networkID, clientName string
|
||||
var chainID uint64
|
||||
if !viper.IsSet("ethereum.nodeID") {
|
||||
nodeID = randSeq(12)
|
||||
} else {
|
||||
nodeID = viper.GetString("ethereum.nodeID")
|
||||
}
|
||||
genesisBlock = viper.GetString("ethereum.genesisBlock")
|
||||
if !viper.IsSet("ethereum.genesisBlock") {
|
||||
genesisBlock = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
|
||||
} else {
|
||||
genesisBlock = viper.GetString("ethereum.genesisBlock")
|
||||
}
|
||||
if !viper.IsSet("ethereum.chainID") {
|
||||
chainID = 1
|
||||
} else {
|
||||
chainID = viper.GetUint64("ethereum.chainID")
|
||||
}
|
||||
if !viper.IsSet("ethereum.networkID") {
|
||||
networkID = "1"
|
||||
} else {
|
||||
networkID = viper.GetString("ethereum.networkID")
|
||||
}
|
||||
if !viper.IsSet("ethereum.clientName") {
|
||||
clientName = "eth-statediff-service"
|
||||
} else {
|
||||
clientName = viper.GetString("ethereum.clientName")
|
||||
}
|
||||
return node.Info{
|
||||
ID: nodeID,
|
||||
ClientName: clientName,
|
||||
GenesisBlock: genesisBlock,
|
||||
NetworkID: networkID,
|
||||
ChainID: chainID,
|
||||
}
|
||||
}
|
||||
|
||||
var characters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||
|
||||
func randSeq(n int) string {
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
b[i] = characters[rand.Intn(len(characters))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
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")
|
||||
|
||||
fileModeStr := viper.GetString("database.fileMode")
|
||||
fileMode, err := file.ResolveFileMode(fileModeStr)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
filePathStr := viper.GetString("database.filePath")
|
||||
if fileMode == file.SQL && filePathStr == "" {
|
||||
logWithCommand.Fatal("when operating in sql file writing mode a file path must be provided")
|
||||
}
|
||||
|
||||
fileCsvDirStr := viper.GetString("database.fileCsvDir")
|
||||
if fileMode == file.CSV && fileCsvDirStr == "" {
|
||||
logWithCommand.Fatal("when operating in csv file writing mode a directory path must be provided")
|
||||
}
|
||||
|
||||
indexerConfig = file.Config{
|
||||
Mode: fileMode,
|
||||
OutputDir: fileCsvDirStr,
|
||||
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
|
||||
}
|
||||
|
||||
+28
-64
@@ -16,24 +16,23 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
sd "github.com/vulcanize/eth-statediff-service/pkg"
|
||||
srpc "github.com/vulcanize/eth-statediff-service/pkg/rpc"
|
||||
)
|
||||
|
||||
// serveCmd represents the serve command
|
||||
var serveCmd = &cobra.Command{
|
||||
Use: "serve",
|
||||
Short: "Standup 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}`,
|
||||
@@ -44,72 +43,48 @@ var serveCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(serveCmd)
|
||||
}
|
||||
|
||||
func serve() {
|
||||
logWithCommand.Info("starting statediff RPC service")
|
||||
logWithCommand.Info("Running eth-statediff-service serve command")
|
||||
|
||||
// load params
|
||||
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")
|
||||
}
|
||||
chainID := viper.GetUint64("eth.chainID")
|
||||
config, err := chainConfig(chainID)
|
||||
statediffService, err := createStateDiffService()
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
// create leveldb reader
|
||||
logWithCommand.Info("creating leveldb reader")
|
||||
lvlDBReader, err := sd.NewLvlDBReader(path, ancientPath, config)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
// create statediff service
|
||||
logWithCommand.Info("creating statediff service")
|
||||
statediffService, err := sd.NewStateDiffService(
|
||||
lvlDBReader, sd.Config{Workers: viper.GetUint("statediff.workers")})
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
// short circuit if we only want to perform prerun
|
||||
if viper.GetBool("prerun.only") {
|
||||
if err := statediffService.Run(nil); err != nil {
|
||||
logWithCommand.Fatal("unable to perform prerun: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// start service and servers
|
||||
logWithCommand.Info("starting statediff service")
|
||||
logWithCommand.Info("Starting statediff service")
|
||||
wg := new(sync.WaitGroup)
|
||||
go statediffService.Loop(wg)
|
||||
logWithCommand.Info("starting rpc servers")
|
||||
if err := statediffService.Loop(wg); err != nil {
|
||||
logWithCommand.Fatalf("unable to start statediff service: %v", err)
|
||||
}
|
||||
logWithCommand.Info("Starting RPC servers")
|
||||
if err := startServers(statediffService); err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
logWithCommand.Info("rpc servers successfully spun up; awaiting requests")
|
||||
logWithCommand.Info("RPC servers successfully spun up; awaiting requests")
|
||||
|
||||
// clean shutdown
|
||||
shutdown := make(chan os.Signal)
|
||||
signal.Notify(shutdown, os.Interrupt)
|
||||
<-shutdown
|
||||
logWithCommand.Info("received interrupt signal, shutting down")
|
||||
logWithCommand.Info("Received interrupt signal, shutting down")
|
||||
statediffService.Stop()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(serveCmd)
|
||||
|
||||
serveCmd.PersistentFlags().String("leveldb-path", "", "path to primary datastore")
|
||||
serveCmd.PersistentFlags().String("ancient-path", "", "path to ancient datastore")
|
||||
serveCmd.PersistentFlags().Uint("chain-id", 1, "ethereum chain id (mainnet = 1)")
|
||||
serveCmd.PersistentFlags().String("http-path", "", "vdb server http path")
|
||||
serveCmd.PersistentFlags().String("ipc-path", "", "vdb server ipc path")
|
||||
|
||||
viper.BindPFlag("leveldb.path", serveCmd.PersistentFlags().Lookup("leveldb-path"))
|
||||
viper.BindPFlag("leveldb.ancient", serveCmd.PersistentFlags().Lookup("ancient-path"))
|
||||
viper.BindPFlag("eth.chainID", serveCmd.PersistentFlags().Lookup("chain-id"))
|
||||
viper.BindPFlag("server.httpPath", serveCmd.PersistentFlags().Lookup("http-path"))
|
||||
viper.BindPFlag("server.ipcPath", serveCmd.PersistentFlags().Lookup("ipc-path"))
|
||||
}
|
||||
|
||||
func startServers(serv sd.IService) error {
|
||||
func startServers(serv sd.StateDiffService) error {
|
||||
ipcPath := viper.GetString("server.ipcPath")
|
||||
httpPath := viper.GetString("server.httpPath")
|
||||
if ipcPath == "" && httpPath == "" {
|
||||
@@ -117,31 +92,20 @@ func startServers(serv sd.IService) error {
|
||||
}
|
||||
if ipcPath != "" {
|
||||
logWithCommand.Info("starting up IPC server")
|
||||
_, _, err := rpc.StartIPCEndpoint(ipcPath, serv.APIs())
|
||||
_, _, err := srpc.StartIPCEndpoint(ipcPath, serv.APIs())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if httpPath != "" {
|
||||
logWithCommand.Info("starting up HTTP server")
|
||||
if _, _, err := rpc.StartHTTPEndpoint(httpPath, serv.APIs(), []string{sd.APIName}, nil, nil, rpc.HTTPTimeouts{}); err != nil {
|
||||
_, err := srpc.StartHTTPEndpoint(httpPath, serv.APIs(), []string{"statediff"}, nil, []string{"*"}, rpc.HTTPTimeouts{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
logWithCommand.Info("HTTP server is disabled")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func chainConfig(chainID uint64) (*params.ChainConfig, error) {
|
||||
switch chainID {
|
||||
case 1:
|
||||
return params.MainnetChainConfig, nil
|
||||
case 3:
|
||||
return params.TestnetChainConfig, 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)
|
||||
}
|
||||
}
|
||||
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"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/trie"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
sd "github.com/vulcanize/eth-statediff-service/pkg"
|
||||
)
|
||||
|
||||
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")
|
||||
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)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
// create LevelDB reader
|
||||
logWithCommand.Info("Creating LevelDB reader")
|
||||
readerConf := sd.LvLDBReaderConfig{
|
||||
TrieConfig: &trie.Config{
|
||||
Cache: viper.GetInt("cache.trie"),
|
||||
Journal: "",
|
||||
Preimages: false,
|
||||
},
|
||||
ChainConfig: chainConf,
|
||||
Mode: mode,
|
||||
Path: path,
|
||||
AncientPath: ancientPath,
|
||||
Url: url,
|
||||
DBCacheSize: viper.GetInt("cache.database"),
|
||||
}
|
||||
lvlDBReader, err := sd.NewLvlDBReader(readerConf)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
// create statediff service
|
||||
logWithCommand.Info("Setting up database")
|
||||
conf, err := getConfig(nodeInfo)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
logWithCommand.Info("Creating statediff indexer")
|
||||
_, indexer, err := ind.NewStateDiffIndexer(context.Background(), chainConf, nodeInfo, conf)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
logWithCommand.Info("Creating statediff service")
|
||||
sdConf := sd.Config{
|
||||
ServiceWorkers: viper.GetUint("statediff.serviceWorkers"),
|
||||
TrieWorkers: viper.GetUint("statediff.trieWorkers"),
|
||||
WorkerQueueSize: viper.GetUint("statediff.workerQueueSize"),
|
||||
PreRuns: setupPreRunRanges(),
|
||||
}
|
||||
return sd.NewStateDiffService(lvlDBReader, indexer, sdConf)
|
||||
}
|
||||
|
||||
func setupPreRunRanges() []sd.RangeRequest {
|
||||
if !viper.GetBool("statediff.prerun") {
|
||||
return nil
|
||||
}
|
||||
preRunParams := gethsd.Params{
|
||||
IntermediateStateNodes: viper.GetBool("prerun.params.intermediateStateNodes"),
|
||||
IntermediateStorageNodes: viper.GetBool("prerun.params.intermediateStorageNodes"),
|
||||
IncludeBlock: viper.GetBool("prerun.params.includeBlock"),
|
||||
IncludeReceipts: viper.GetBool("prerun.params.includeReceipts"),
|
||||
IncludeTD: viper.GetBool("prerun.params.includeTD"),
|
||||
IncludeCode: viper.GetBool("prerun.params.includeCode"),
|
||||
}
|
||||
var addrStrs []string
|
||||
viper.UnmarshalKey("prerun.params.watchedAddresses", &addrStrs)
|
||||
addrs := make([]common.Address, len(addrStrs))
|
||||
for i, addrStr := range addrStrs {
|
||||
addrs[i] = common.HexToAddress(addrStr)
|
||||
}
|
||||
preRunParams.WatchedAddresses = addrs
|
||||
var rawRanges []blockRange
|
||||
viper.UnmarshalKey("prerun.ranges", &rawRanges)
|
||||
blockRanges := make([]sd.RangeRequest, len(rawRanges))
|
||||
for i, rawRange := range rawRanges {
|
||||
blockRanges[i] = sd.RangeRequest{
|
||||
Start: rawRange[0],
|
||||
Stop: rawRange[1],
|
||||
Params: preRunParams,
|
||||
}
|
||||
}
|
||||
if viper.IsSet("prerun.start") && viper.IsSet("prerun.stop") {
|
||||
hardStart := viper.GetInt("prerun.start")
|
||||
hardStop := viper.GetInt("prerun.stop")
|
||||
blockRanges = append(blockRanges, sd.RangeRequest{
|
||||
Start: uint64(hardStart),
|
||||
Stop: uint64(hardStop),
|
||||
Params: preRunParams,
|
||||
})
|
||||
}
|
||||
|
||||
return blockRanges
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
[leveldb]
|
||||
mode = "local"
|
||||
path = "/app/geth-rw/chaindata"
|
||||
ancient = "/app/geth-rw/chaindata/ancient"
|
||||
url = "http://127.0.0.1:8082/"
|
||||
|
||||
[server]
|
||||
ipcPath = ""
|
||||
httpPath = "0.0.0.0:8545"
|
||||
|
||||
[statediff]
|
||||
prerun = true
|
||||
serviceWorkers = 1
|
||||
workerQueueSize = 1024
|
||||
trieWorkers = 16
|
||||
|
||||
[prerun]
|
||||
only = true
|
||||
ranges = []
|
||||
[prerun.params]
|
||||
intermediateStateNodes = true
|
||||
intermediateStorageNodes = true
|
||||
includeBlock = true
|
||||
includeReceipts = true
|
||||
includeTD = true
|
||||
includeCode = true
|
||||
watchedAddresses = []
|
||||
|
||||
[log]
|
||||
file = ""
|
||||
level = "info"
|
||||
|
||||
[database]
|
||||
name = ""
|
||||
hostname = ""
|
||||
port = 5432
|
||||
user = ""
|
||||
password = ""
|
||||
type = "postgres"
|
||||
driver = "sqlx"
|
||||
dumpDestination = ""
|
||||
fileMode = "csv"
|
||||
fileCsvDir = ""
|
||||
filePath = ""
|
||||
|
||||
[cache]
|
||||
database = 1024
|
||||
trie = 4096
|
||||
|
||||
[prom]
|
||||
dbStats = false
|
||||
metrics = true
|
||||
http = true
|
||||
httpAddr = "0.0.0.0"
|
||||
httpPort = 9100
|
||||
|
||||
[ethereum]
|
||||
chainConfig = ""
|
||||
nodeID = ""
|
||||
clientName = "eth-statediff-service"
|
||||
genesisBlock = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
|
||||
networkID = 1
|
||||
chainID = 1
|
||||
@@ -1,17 +1,63 @@
|
||||
[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"
|
||||
httpPath = "127.0.0.1:8545"
|
||||
|
||||
[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 = ""
|
||||
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]
|
||||
chainConfig = ""
|
||||
nodeID = ""
|
||||
clientName = "eth-statediff-service"
|
||||
genesisBlock = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
|
||||
networkID = 1
|
||||
chainID = 1
|
||||
|
||||
@@ -1,13 +1,131 @@
|
||||
module github.com/vulcanize/eth-statediff-service
|
||||
|
||||
go 1.13
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/ethereum/go-ethereum v1.9.11
|
||||
github.com/sirupsen/logrus v1.6.0
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/spf13/viper v1.7.1
|
||||
github.com/vulcanize/go-eth-state-node-iterator v0.0.1-alpha
|
||||
github.com/ethereum/go-ethereum v1.10.19
|
||||
github.com/jmoiron/sqlx v1.2.0
|
||||
github.com/prometheus/client_golang v1.4.0
|
||||
github.com/sirupsen/logrus v1.7.0
|
||||
github.com/spf13/cobra v1.3.0
|
||||
github.com/spf13/viper v1.10.1
|
||||
github.com/vulcanize/go-eth-state-node-iterator v1.1.1
|
||||
github.com/vulcanize/leveldb-ethdb-rpc v0.1.3
|
||||
)
|
||||
|
||||
replace github.com/ethereum/go-ethereum v1.9.11 => github.com/vulcanize/go-ethereum v1.9.11-statediff-0.0.8
|
||||
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.19 => github.com/vulcanize/go-ethereum v1.10.19-statediff-4.1.0-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
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
global:
|
||||
scrape_interval: 10s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: 'eth-statediff-service'
|
||||
static_configs:
|
||||
- targets: ['localhost:8100']
|
||||
+13
-3
@@ -28,13 +28,13 @@ 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 IService
|
||||
sds StateDiffService
|
||||
}
|
||||
|
||||
// NewPublicStateDiffAPI creates an rpc interface for the underlying statediff service
|
||||
func NewPublicStateDiffAPI(sds IService) *PublicStateDiffAPI {
|
||||
func NewPublicStateDiffAPI(sds StateDiffService) *PublicStateDiffAPI {
|
||||
return &PublicStateDiffAPI{
|
||||
sds: sds,
|
||||
}
|
||||
@@ -49,3 +49,13 @@ func (api *PublicStateDiffAPI) StateDiffAt(ctx context.Context, blockNumber uint
|
||||
func (api *PublicStateDiffAPI) StateTrieAt(ctx context.Context, blockNumber uint64, params sd.Params) (*sd.Payload, error) {
|
||||
return api.sds.StateTrieAt(blockNumber, params)
|
||||
}
|
||||
|
||||
// WriteStateDiffAt writes a state diff object directly to DB at the specific blockheight
|
||||
func (api *PublicStateDiffAPI) WriteStateDiffAt(ctx context.Context, blockNumber uint64, params sd.Params) error {
|
||||
return api.sds.WriteStateDiffAt(blockNumber, params)
|
||||
}
|
||||
|
||||
// WriteStateDiffsInRange writes the state diff objects for the provided block range, with the provided params
|
||||
func (api *PublicStateDiffAPI) WriteStateDiffsInRange(ctx context.Context, start, stop uint64, params sd.Params) error {
|
||||
return api.sds.WriteStateDiffsInRange(start, stop, params)
|
||||
}
|
||||
|
||||
+82
-631
@@ -20,47 +20,25 @@
|
||||
package statediff
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/bits"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
|
||||
sd "github.com/ethereum/go-ethereum/statediff"
|
||||
sdtypes "github.com/ethereum/go-ethereum/statediff/types"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
iter "github.com/vulcanize/go-eth-state-node-iterator"
|
||||
)
|
||||
|
||||
var (
|
||||
nullHashBytes = common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")
|
||||
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)
|
||||
}
|
||||
|
||||
type builder struct {
|
||||
stateCache state.Database
|
||||
sd.StateDiffBuilder
|
||||
numWorkers uint
|
||||
}
|
||||
|
||||
type iterPair struct {
|
||||
older, newer trie.NodeIterator
|
||||
}
|
||||
|
||||
// NewBuilder is used to create a statediff builder
|
||||
func NewBuilder(stateCache state.Database, workers uint) (Builder, error) {
|
||||
func NewBuilder(stateCache state.Database, workers uint) (sd.Builder, error) {
|
||||
if workers == 0 {
|
||||
workers = 1
|
||||
}
|
||||
@@ -68,199 +46,24 @@ func NewBuilder(stateCache state.Database, workers uint) (Builder, error) {
|
||||
return nil, fmt.Errorf("workers must be a power of 2")
|
||||
}
|
||||
return &builder{
|
||||
stateCache: stateCache, // state cache is safe for concurrent reads
|
||||
StateDiffBuilder: sd.StateDiffBuilder{
|
||||
StateCache: stateCache,
|
||||
},
|
||||
numWorkers: workers,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BuildStateTrieObject builds a state trie object from the provided block
|
||||
func (sdb *builder) BuildStateTrieObject(current *types.Block) (sd.StateObject, error) {
|
||||
currentTrie, err := sdb.stateCache.OpenTrie(current.Root())
|
||||
// BuildStateDiffObject builds a statediff object from two blocks and the provided parameters
|
||||
func (sdb *builder) BuildStateDiffObject(args sd.Args, params sd.Params) (sdtypes.StateObject, error) {
|
||||
var stateNodes []sdtypes.StateNode
|
||||
var codeAndCodeHashes []sdtypes.CodeAndCodeHash
|
||||
err := sdb.WriteStateDiffObject(
|
||||
sdtypes.StateRoots{OldStateRoot: args.OldStateRoot, NewStateRoot: args.NewStateRoot},
|
||||
params, sd.StateNodeAppender(&stateNodes), sd.CodeMappingAppender(&codeAndCodeHashes))
|
||||
if err != nil {
|
||||
return sd.StateObject{}, fmt.Errorf("error creating trie for block %d: %v", current.Number(), err)
|
||||
return sdtypes.StateObject{}, 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 sd.StateObject{
|
||||
BlockNumber: current.Number(),
|
||||
BlockHash: current.Hash(),
|
||||
Nodes: stateNodes,
|
||||
CodeAndCodeHashes: codeAndCodeHashes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func resolveNode(it trie.NodeIterator, trieDB *trie.Database) (sd.StateNode, []interface{}, error) {
|
||||
nodePath := make([]byte, len(it.Path()))
|
||||
copy(nodePath, it.Path())
|
||||
node, err := trieDB.Node(it.Hash())
|
||||
if err != nil {
|
||||
return sd.StateNode{}, nil, err
|
||||
}
|
||||
var nodeElements []interface{}
|
||||
if err := rlp.DecodeBytes(node, &nodeElements); err != nil {
|
||||
return sd.StateNode{}, nil, err
|
||||
}
|
||||
ty, err := sd.CheckKeyType(nodeElements)
|
||||
if err != nil {
|
||||
return sd.StateNode{}, nil, err
|
||||
}
|
||||
return sd.StateNode{
|
||||
NodeType: ty,
|
||||
Path: nodePath,
|
||||
NodeValue: node,
|
||||
}, nodeElements, nil
|
||||
}
|
||||
|
||||
func (sdb *builder) buildStateTrie(it trie.NodeIterator) ([]sd.StateNode, []sd.CodeAndCodeHash, error) {
|
||||
stateNodes := make([]sd.StateNode, 0)
|
||||
codeAndCodeHashes := make([]sd.CodeAndCodeHash, 0)
|
||||
for it.Next(true) {
|
||||
// skip value nodes and null nodes
|
||||
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
|
||||
continue
|
||||
}
|
||||
nodePath := make([]byte, len(it.Path()))
|
||||
copy(nodePath, it.Path())
|
||||
node, err := sdb.stateCache.TrieDB().Node(it.Hash())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var nodeElements []interface{}
|
||||
if err := rlp.DecodeBytes(node, &nodeElements); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
ty, err := sd.CheckKeyType(nodeElements)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
switch ty {
|
||||
case sd.Leaf:
|
||||
var account state.Account
|
||||
if err := rlp.DecodeBytes(nodeElements[1].([]byte), &account); err != nil {
|
||||
return nil, nil, fmt.Errorf("error decoding account for leaf node at path %x nerror: %v", nodePath, err)
|
||||
}
|
||||
partialPath := trie.CompactToHex(nodeElements[0].([]byte))
|
||||
valueNodePath := append(nodePath, partialPath...)
|
||||
encodedPath := trie.HexToCompact(valueNodePath)
|
||||
leafKey := encodedPath[1:]
|
||||
node := sd.StateNode{
|
||||
NodeType: ty,
|
||||
Path: nodePath,
|
||||
LeafKey: leafKey,
|
||||
NodeValue: node,
|
||||
}
|
||||
if !bytes.Equal(account.CodeHash, nullCodeHash) {
|
||||
storageNodes, err := sdb.buildStorageNodesEventual(account.Root, nil, true)
|
||||
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
|
||||
codeHash := common.BytesToHash(account.CodeHash)
|
||||
code, err := sdb.stateCache.ContractCode(common.Hash{}, codeHash)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to retrieve code for codehash %s\r\n error: %v", codeHash.String(), err)
|
||||
}
|
||||
codeAndCodeHashes = append(codeAndCodeHashes, sd.CodeAndCodeHash{
|
||||
Hash: codeHash,
|
||||
Code: code,
|
||||
})
|
||||
}
|
||||
stateNodes = append(stateNodes, node)
|
||||
case sd.Extension, sd.Branch:
|
||||
stateNodes = append(stateNodes, sd.StateNode{
|
||||
NodeType: ty,
|
||||
Path: nodePath,
|
||||
NodeValue: node,
|
||||
})
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("unexpected node type %s", ty)
|
||||
}
|
||||
}
|
||||
return stateNodes, codeAndCodeHashes, it.Error()
|
||||
}
|
||||
|
||||
// BuildStateDiff builds a statediff object from two blocks and the provided parameters
|
||||
func (sdb *builder) BuildStateDiffObject(args sd.Args, params sd.Params) (sd.StateObject, 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")
|
||||
params.IntermediateStateNodes = false
|
||||
}
|
||||
|
||||
// Load tries for old and new states
|
||||
oldTrie, err := sdb.stateCache.OpenTrie(args.OldStateRoot)
|
||||
if err != nil {
|
||||
return sd.StateObject{}, fmt.Errorf("error creating trie for old state root: %v", err)
|
||||
}
|
||||
newTrie, err := sdb.stateCache.OpenTrie(args.NewStateRoot)
|
||||
if err != nil {
|
||||
return sd.StateObject{}, fmt.Errorf("error creating trie for new state root: %v", err)
|
||||
}
|
||||
|
||||
// Split old and new tries into corresponding subtrie iterators
|
||||
oldIterFac := iter.NewSubtrieIteratorFactory(oldTrie, sdb.numWorkers)
|
||||
newIterFac := iter.NewSubtrieIteratorFactory(newTrie, sdb.numWorkers)
|
||||
iterChan := make(chan []iterPair, sdb.numWorkers)
|
||||
|
||||
// Create iterators ahead of time to avoid race condition in state.Trie access
|
||||
for i := uint(0); i < sdb.numWorkers; i++ {
|
||||
// two state iterations per diff build
|
||||
iterChan <- []iterPair{
|
||||
iterPair{
|
||||
older: oldIterFac.IteratorAt(i),
|
||||
newer: newIterFac.IteratorAt(i),
|
||||
},
|
||||
iterPair{
|
||||
older: oldIterFac.IteratorAt(i),
|
||||
newer: newIterFac.IteratorAt(i),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type packet struct {
|
||||
nodes []sd.StateNode
|
||||
codes []sd.CodeAndCodeHash
|
||||
}
|
||||
|
||||
packetChan := make(chan packet)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for w := uint(0); w < sdb.numWorkers; w++ {
|
||||
wg.Add(1)
|
||||
go func(iterChan <-chan []iterPair) error {
|
||||
defer wg.Done()
|
||||
if iters, more := <-iterChan; more {
|
||||
subtrieNodes, subtrieCodes, err := sdb.buildStateDiff(iters, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
packetChan <- packet{
|
||||
nodes: subtrieNodes,
|
||||
codes: subtrieCodes,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}(iterChan)
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer close(packetChan)
|
||||
defer close(iterChan)
|
||||
wg.Wait()
|
||||
}()
|
||||
|
||||
stateNodes := make([]sd.StateNode, 0)
|
||||
codeAndCodeHashes := make([]sd.CodeAndCodeHash, 0)
|
||||
for packet := range packetChan {
|
||||
stateNodes = append(stateNodes, packet.nodes...)
|
||||
codeAndCodeHashes = append(codeAndCodeHashes, packet.codes...)
|
||||
}
|
||||
|
||||
return sd.StateObject{
|
||||
return sdtypes.StateObject{
|
||||
BlockHash: args.BlockHash,
|
||||
BlockNumber: args.BlockNumber,
|
||||
Nodes: stateNodes,
|
||||
@@ -268,437 +71,85 @@ func (sdb *builder) BuildStateDiffObject(args sd.Args, params sd.Params) (sd.Sta
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (sdb *builder) buildStateDiff(args []iterPair, params sd.Params) ([]sd.StateNode, []sd.CodeAndCodeHash, error) {
|
||||
// collect a slice of all the intermediate nodes that were touched and exist at B
|
||||
// a map of their leafkey to all the accounts that were touched and exist at B
|
||||
// and a slice of all the paths for the nodes in both of the above sets
|
||||
createdOrUpdatedIntermediateNodes, diffAccountsAtB, diffPathsAtB, err := sdb.createdAndUpdatedState(
|
||||
args[0], params.WatchedAddresses, params.IntermediateStateNodes)
|
||||
// WriteStateDiffObject writes a statediff object to output callback
|
||||
func (sdb *builder) WriteStateDiffObject(args sdtypes.StateRoots, params sd.Params, output sdtypes.StateNodeSink, codeOutput sdtypes.CodeSink) error {
|
||||
// Load tries for old and new states
|
||||
oldTrie, err := sdb.StateCache.OpenTrie(args.OldStateRoot)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error collecting createdAndUpdatedNodes: %v", err)
|
||||
return fmt.Errorf("error creating trie for oldStateRoot: %v", err)
|
||||
}
|
||||
|
||||
// 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
|
||||
emptiedPaths, diffAccountsAtA, err := sdb.deletedOrUpdatedState(args[1], diffPathsAtB)
|
||||
newTrie, err := sdb.StateCache.OpenTrie(args.NewStateRoot)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error collecting deletedOrUpdatedNodes: %v", err)
|
||||
return fmt.Errorf("error creating trie for newStateRoot: %v", err)
|
||||
}
|
||||
|
||||
// collect and sort the leafkey keys for both account mappings into a slice
|
||||
createKeys := sortKeys(diffAccountsAtB)
|
||||
deleteKeys := sortKeys(diffAccountsAtA)
|
||||
// Split old and new tries into corresponding subtrie iterators
|
||||
oldIters1 := iter.SubtrieIterators(oldTrie, sdb.numWorkers)
|
||||
oldIters2 := iter.SubtrieIterators(oldTrie, sdb.numWorkers)
|
||||
newIters1 := iter.SubtrieIterators(newTrie, sdb.numWorkers)
|
||||
newIters2 := iter.SubtrieIterators(newTrie, sdb.numWorkers)
|
||||
|
||||
// and then find the intersection of these keys
|
||||
// these are the leafkeys for the accounts which exist at both A and B but are different
|
||||
// this also mutates the passed in createKeys and deleteKeys, removing the intersection keys
|
||||
// and leaving the truly created or deleted keys in place
|
||||
updatedKeys := findIntersection(createKeys, deleteKeys)
|
||||
|
||||
// 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
|
||||
updatedAccounts, err := sdb.buildAccountUpdates(
|
||||
diffAccountsAtB, diffAccountsAtA, updatedKeys,
|
||||
params.WatchedStorageSlots, params.IntermediateStorageNodes)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error building diff for updated accounts: %v", err)
|
||||
}
|
||||
// build the diff nodes for created accounts
|
||||
createdAccounts, codeAndCodeHashes, err := sdb.buildAccountCreations(
|
||||
diffAccountsAtB, params.WatchedStorageSlots, params.IntermediateStorageNodes)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error building diff for created accounts: %v", err)
|
||||
}
|
||||
|
||||
// assemble all of the nodes into the statediff object, including the intermediate nodes
|
||||
nodes := append(
|
||||
append(
|
||||
append(updatedAccounts, createdAccounts...),
|
||||
createdOrUpdatedIntermediateNodes...,
|
||||
),
|
||||
emptiedPaths...)
|
||||
|
||||
return nodes, codeAndCodeHashes, nil
|
||||
}
|
||||
|
||||
// createdAndUpdatedState returns
|
||||
// a slice of all the intermediate nodes that exist in a different state at B than A
|
||||
// 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, intermediates bool) ([]sd.StateNode, AccountMap, map[string]bool, error) {
|
||||
createdOrUpdatedIntermediateNodes := make([]sd.StateNode, 0)
|
||||
diffPathsAtB := make(map[string]bool)
|
||||
diffAcountsAtB := make(AccountMap)
|
||||
it, _ := trie.NewDifferenceIterator(iters.older, iters.newer)
|
||||
for it.Next(true) {
|
||||
// skip value nodes and null nodes
|
||||
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
|
||||
continue
|
||||
}
|
||||
node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB())
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
switch node.NodeType {
|
||||
case sd.Leaf:
|
||||
// created vs updated is important for leaf nodes since we need to diff their storage
|
||||
// so we need to map all changed accounts at B to their leafkey, since account can change pathes but not leafkey
|
||||
var account state.Account
|
||||
if err := rlp.DecodeBytes(nodeElements[1].([]byte), &account); err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("error decoding account for leaf node at path %x nerror: %v", node.Path, err)
|
||||
}
|
||||
partialPath := trie.CompactToHex(nodeElements[0].([]byte))
|
||||
valueNodePath := append(node.Path, partialPath...)
|
||||
encodedPath := trie.HexToCompact(valueNodePath)
|
||||
leafKey := encodedPath[1:]
|
||||
if isWatchedAddress(watchedAddresses, leafKey) {
|
||||
diffAcountsAtB[common.Bytes2Hex(leafKey)] = accountWrapper{
|
||||
NodeType: node.NodeType,
|
||||
Path: node.Path,
|
||||
NodeValue: node.NodeValue,
|
||||
LeafKey: leafKey,
|
||||
Account: &account,
|
||||
}
|
||||
}
|
||||
case sd.Extension, sd.Branch:
|
||||
// create a diff for any intermediate node that has changed at b
|
||||
// created vs updated makes no difference for intermediate nodes since we do not need to diff storage
|
||||
if intermediates {
|
||||
createdOrUpdatedIntermediateNodes = append(createdOrUpdatedIntermediateNodes, sd.StateNode{
|
||||
NodeType: node.NodeType,
|
||||
Path: node.Path,
|
||||
NodeValue: node.NodeValue,
|
||||
})
|
||||
}
|
||||
default:
|
||||
return nil, nil, nil, fmt.Errorf("unexpected node type %s", node.NodeType)
|
||||
}
|
||||
// add both intermediate and leaf node paths to the list of diffPathsAtB
|
||||
diffPathsAtB[common.Bytes2Hex(node.Path)] = true
|
||||
}
|
||||
return createdOrUpdatedIntermediateNodes, diffAcountsAtB, diffPathsAtB, it.Error()
|
||||
}
|
||||
|
||||
// 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) ([]sd.StateNode, AccountMap, error) {
|
||||
emptiedPaths := make([]sd.StateNode, 0)
|
||||
diffAccountAtA := make(AccountMap)
|
||||
it, _ := trie.NewDifferenceIterator(iters.newer, iters.older)
|
||||
for it.Next(true) {
|
||||
// skip value nodes and null nodes
|
||||
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
|
||||
continue
|
||||
}
|
||||
node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// if this nodePath 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 {
|
||||
emptiedPaths = append(emptiedPaths, sd.StateNode{
|
||||
Path: node.Path,
|
||||
NodeValue: []byte{},
|
||||
NodeType: sd.Removed,
|
||||
})
|
||||
}
|
||||
switch node.NodeType {
|
||||
case sd.Leaf:
|
||||
// map all different accounts at A to their leafkey
|
||||
var account state.Account
|
||||
if err := rlp.DecodeBytes(nodeElements[1].([]byte), &account); err != nil {
|
||||
return nil, nil, fmt.Errorf("error decoding account for leaf node at path %x nerror: %v", node.Path, err)
|
||||
}
|
||||
partialPath := trie.CompactToHex(nodeElements[0].([]byte))
|
||||
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,
|
||||
}
|
||||
case sd.Extension, sd.Branch:
|
||||
// fall through, we did everything we need to do with these node types
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("unexpected node type %s", node.NodeType)
|
||||
}
|
||||
}
|
||||
return emptiedPaths, diffAccountAtA, it.Error()
|
||||
}
|
||||
|
||||
// buildAccountUpdates uses the account diffs maps for A => B and B => A and the known intersection of their leafkeys
|
||||
// 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) ([]sd.StateNode, error) {
|
||||
updatedAccounts := make([]sd.StateNode, 0, len(updatedKeys))
|
||||
var err error
|
||||
for _, key := range updatedKeys {
|
||||
createdAcc := creations[key]
|
||||
deletedAcc := deletions[key]
|
||||
var storageDiffs []sd.StorageNode
|
||||
if deletedAcc.Account != nil && createdAcc.Account != nil {
|
||||
oldSR := deletedAcc.Account.Root
|
||||
newSR := createdAcc.Account.Root
|
||||
storageDiffs, err = sdb.buildStorageNodesIncremental(oldSR, newSR, watchedStorageKeys, intermediateStorageNodes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed building incremental storage diffs for account with leafkey %s\r\nerror: %v", key, err)
|
||||
}
|
||||
}
|
||||
updatedAccounts = append(updatedAccounts, sd.StateNode{
|
||||
NodeType: createdAcc.NodeType,
|
||||
Path: createdAcc.Path,
|
||||
NodeValue: createdAcc.NodeValue,
|
||||
LeafKey: createdAcc.LeafKey,
|
||||
StorageNodes: storageDiffs,
|
||||
// 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,
|
||||
// one for deleted/updated nodes; prepare 2 iterator instances for each task
|
||||
var iterPairs [][]sd.IterPair
|
||||
for i := uint(0); i < sdb.numWorkers; i++ {
|
||||
iterPairs = append(iterPairs, []sd.IterPair{
|
||||
{Older: oldIters1[i], Newer: newIters1[i]},
|
||||
{Older: oldIters2[i], Newer: newIters2[i]},
|
||||
})
|
||||
delete(creations, key)
|
||||
delete(deletions, key)
|
||||
}
|
||||
|
||||
return updatedAccounts, nil
|
||||
}
|
||||
// Dispatch workers to process trie data; sync and collect results here via channels
|
||||
nodeChan := make(chan sdtypes.StateNode)
|
||||
codeChan := make(chan sdtypes.CodeAndCodeHash)
|
||||
|
||||
// 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) ([]sd.StateNode, []sd.CodeAndCodeHash, error) {
|
||||
accountDiffs := make([]sd.StateNode, 0, len(accounts))
|
||||
codeAndCodeHashes := make([]sd.CodeAndCodeHash, 0)
|
||||
for _, val := range accounts {
|
||||
diff := sd.StateNode{
|
||||
NodeType: val.NodeType,
|
||||
Path: val.Path,
|
||||
LeafKey: val.LeafKey,
|
||||
NodeValue: val.NodeValue,
|
||||
go func() {
|
||||
nodeSender := func(node sdtypes.StateNode) error { nodeChan <- node; return nil }
|
||||
codeSender := func(code sdtypes.CodeAndCodeHash) error { codeChan <- code; return nil }
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for w := uint(0); w < sdb.numWorkers; w++ {
|
||||
wg.Add(1)
|
||||
go func(worker uint) {
|
||||
defer wg.Done()
|
||||
var err error
|
||||
if !params.IntermediateStateNodes {
|
||||
err = sdb.BuildStateDiffWithoutIntermediateStateNodes(iterPairs[worker], params, nodeSender, codeSender)
|
||||
} else {
|
||||
err = sdb.BuildStateDiffWithIntermediateStateNodes(iterPairs[worker], params, nodeSender, codeSender)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logrus.Errorf("buildStateDiff error for worker %d, params %+v", worker, params)
|
||||
}
|
||||
}(w)
|
||||
}
|
||||
if !bytes.Equal(val.Account.CodeHash, nullCodeHash) {
|
||||
// For contract creations, any storage node contained is a diff
|
||||
storageDiffs, err := sdb.buildStorageNodesEventual(val.Account.Root, watchedStorageKeys, intermediateStorageNodes)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed building eventual storage diffs for node %x\r\nerror: %v", val.Path, err)
|
||||
wg.Wait()
|
||||
close(nodeChan)
|
||||
close(codeChan)
|
||||
}()
|
||||
|
||||
for nodeChan != nil || codeChan != nil {
|
||||
select {
|
||||
case node, more := <-nodeChan:
|
||||
if more {
|
||||
if err := output(node); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
nodeChan = nil
|
||||
}
|
||||
diff.StorageNodes = storageDiffs
|
||||
// emit codehash => code mappings for new contracts
|
||||
codeHash := common.BytesToHash(val.Account.CodeHash)
|
||||
code, err := sdb.stateCache.ContractCode(common.Hash{}, codeHash)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to retrieve code for codehash %s\r\n error: %v", codeHash.String(), err)
|
||||
case codeAndCodeHash, more := <-codeChan:
|
||||
if more {
|
||||
if err := codeOutput(codeAndCodeHash); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
codeChan = nil
|
||||
}
|
||||
codeAndCodeHashes = append(codeAndCodeHashes, sd.CodeAndCodeHash{
|
||||
Hash: codeHash,
|
||||
Code: code,
|
||||
})
|
||||
}
|
||||
accountDiffs = append(accountDiffs, diff)
|
||||
}
|
||||
|
||||
return accountDiffs, codeAndCodeHashes, nil
|
||||
}
|
||||
|
||||
// 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) ([]sd.StorageNode, error) {
|
||||
if bytes.Equal(sr.Bytes(), emptyContractRoot.Bytes()) {
|
||||
return nil, nil
|
||||
}
|
||||
log.Debug("Storage Root For Eventual Diff", "root", sr, sr.Hex())
|
||||
sTrie, err := sdb.stateCache.OpenTrie(sr)
|
||||
if err != nil {
|
||||
log.Info("error in build storage diff eventual", "error", err)
|
||||
return nil, err
|
||||
}
|
||||
it := sTrie.NodeIterator(make([]byte, 0))
|
||||
return sdb.buildStorageNodesFromTrie(it, watchedStorageKeys, intermediateNodes)
|
||||
}
|
||||
|
||||
// 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) ([]sd.StorageNode, error) {
|
||||
storageDiffs := make([]sd.StorageNode, 0)
|
||||
for it.Next(true) {
|
||||
// skip value nodes and null nodes
|
||||
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
|
||||
continue
|
||||
}
|
||||
node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch node.NodeType {
|
||||
case sd.Leaf:
|
||||
partialPath := trie.CompactToHex(nodeElements[0].([]byte))
|
||||
valueNodePath := append(node.Path, partialPath...)
|
||||
encodedPath := trie.HexToCompact(valueNodePath)
|
||||
leafKey := encodedPath[1:]
|
||||
if isWatchedStorageKey(watchedStorageKeys, leafKey) {
|
||||
storageDiffs = append(storageDiffs, sd.StorageNode{
|
||||
NodeType: node.NodeType,
|
||||
Path: node.Path,
|
||||
NodeValue: node.NodeValue,
|
||||
LeafKey: leafKey,
|
||||
})
|
||||
}
|
||||
case sd.Extension, sd.Branch:
|
||||
if intermediateNodes {
|
||||
storageDiffs = append(storageDiffs, sd.StorageNode{
|
||||
NodeType: node.NodeType,
|
||||
Path: node.Path,
|
||||
NodeValue: node.NodeValue,
|
||||
})
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected node type %s", node.NodeType)
|
||||
}
|
||||
}
|
||||
return storageDiffs, 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) ([]sd.StorageNode, error) {
|
||||
if bytes.Equal(newSR.Bytes(), oldSR.Bytes()) {
|
||||
return nil, nil
|
||||
}
|
||||
log.Debug("Storage Roots for Incremental Diff", "old", oldSR.Hex(), "new", newSR.Hex())
|
||||
oldTrie, err := sdb.stateCache.OpenTrie(oldSR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newTrie, err := sdb.stateCache.OpenTrie(newSR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
createdOrUpdatedStorage, diffPathsAtB, err := sdb.createdAndUpdatedStorage(oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{}), watchedStorageKeys, intermediateNodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deletedStorage, err := sdb.deletedOrUpdatedStorage(oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{}), diffPathsAtB, watchedStorageKeys, intermediateNodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(createdOrUpdatedStorage, deletedStorage...), nil
|
||||
}
|
||||
|
||||
func (sdb *builder) createdAndUpdatedStorage(a, b trie.NodeIterator, watchedKeys []common.Hash, intermediateNodes bool) ([]sd.StorageNode, map[string]bool, error) {
|
||||
createdOrUpdatedStorage := make([]sd.StorageNode, 0)
|
||||
diffPathsAtB := make(map[string]bool)
|
||||
it, _ := trie.NewDifferenceIterator(a, b)
|
||||
for it.Next(true) {
|
||||
// skip value nodes and null nodes
|
||||
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
|
||||
continue
|
||||
}
|
||||
node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
switch node.NodeType {
|
||||
case sd.Leaf:
|
||||
partialPath := trie.CompactToHex(nodeElements[0].([]byte))
|
||||
valueNodePath := append(node.Path, partialPath...)
|
||||
encodedPath := trie.HexToCompact(valueNodePath)
|
||||
leafKey := encodedPath[1:]
|
||||
if isWatchedStorageKey(watchedKeys, leafKey) {
|
||||
createdOrUpdatedStorage = append(createdOrUpdatedStorage, sd.StorageNode{
|
||||
NodeType: node.NodeType,
|
||||
Path: node.Path,
|
||||
NodeValue: node.NodeValue,
|
||||
LeafKey: leafKey,
|
||||
})
|
||||
}
|
||||
case sd.Extension, sd.Branch:
|
||||
if intermediateNodes {
|
||||
createdOrUpdatedStorage = append(createdOrUpdatedStorage, sd.StorageNode{
|
||||
NodeType: node.NodeType,
|
||||
Path: node.Path,
|
||||
NodeValue: node.NodeValue,
|
||||
})
|
||||
}
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("unexpected node type %s", node.NodeType)
|
||||
}
|
||||
diffPathsAtB[common.Bytes2Hex(node.Path)] = true
|
||||
}
|
||||
return createdOrUpdatedStorage, diffPathsAtB, it.Error()
|
||||
}
|
||||
|
||||
func (sdb *builder) deletedOrUpdatedStorage(a, b trie.NodeIterator, diffPathsAtB map[string]bool, watchedKeys []common.Hash, intermediateNodes bool) ([]sd.StorageNode, error) {
|
||||
deletedStorage := make([]sd.StorageNode, 0)
|
||||
it, _ := trie.NewDifferenceIterator(b, a)
|
||||
for it.Next(true) {
|
||||
// skip value nodes and null nodes
|
||||
if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) {
|
||||
continue
|
||||
}
|
||||
node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB())
|
||||
if err != nil {
|
||||
return nil, 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 sd.Leaf:
|
||||
partialPath := trie.CompactToHex(nodeElements[0].([]byte))
|
||||
valueNodePath := append(node.Path, partialPath...)
|
||||
encodedPath := trie.HexToCompact(valueNodePath)
|
||||
leafKey := encodedPath[1:]
|
||||
if isWatchedStorageKey(watchedKeys, leafKey) {
|
||||
deletedStorage = append(deletedStorage, sd.StorageNode{
|
||||
NodeType: sd.Removed,
|
||||
Path: node.Path,
|
||||
NodeValue: []byte{},
|
||||
})
|
||||
}
|
||||
case sd.Extension, sd.Branch:
|
||||
if intermediateNodes {
|
||||
deletedStorage = append(deletedStorage, sd.StorageNode{
|
||||
NodeType: sd.Removed,
|
||||
Path: node.Path,
|
||||
NodeValue: []byte{},
|
||||
})
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected node type %s", node.NodeType)
|
||||
}
|
||||
}
|
||||
return deletedStorage, it.Error()
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// If we aren't watching any specific addresses, we are watching everything
|
||||
if len(watchedAddresses) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, addr := range watchedAddresses {
|
||||
addrHashKey := crypto.Keccak256(addr.Bytes())
|
||||
if bytes.Equal(addrHashKey, stateLeafKey) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isWatchedStorageKey is used to check if a storage leaf corresponds to one of the storage slots the builder is configured to watch
|
||||
func isWatchedStorageKey(watchedKeys []common.Hash, storageLeafKey []byte) bool {
|
||||
// If we aren't watching any specific addresses, we are watching everything
|
||||
if len(watchedKeys) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, hashKey := range watchedKeys {
|
||||
if bytes.Equal(hashKey.Bytes(), storageLeafKey) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
+927
-637
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
package statediff
|
||||
|
||||
// Config holds config params for the statediffing service
|
||||
type Config struct {
|
||||
ServiceWorkers uint
|
||||
TrieWorkers uint
|
||||
WorkerQueueSize uint
|
||||
PreRuns []RangeRequest
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
// 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 prom
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
namespace = "eth_statediff_service"
|
||||
subsystem = "connections"
|
||||
)
|
||||
|
||||
// DBStatsGetter is an interface that gets sql.DBStats.
|
||||
type DBStatsGetter interface {
|
||||
Stats() sql.DBStats
|
||||
}
|
||||
|
||||
// DBStatsCollector implements the prometheus.Collector interface.
|
||||
type DBStatsCollector struct {
|
||||
sg DBStatsGetter
|
||||
|
||||
// descriptions of exported metrics
|
||||
maxOpenDesc *prometheus.Desc
|
||||
openDesc *prometheus.Desc
|
||||
inUseDesc *prometheus.Desc
|
||||
idleDesc *prometheus.Desc
|
||||
waitedForDesc *prometheus.Desc
|
||||
blockedSecondsDesc *prometheus.Desc
|
||||
closedMaxIdleDesc *prometheus.Desc
|
||||
closedMaxLifetimeDesc *prometheus.Desc
|
||||
}
|
||||
|
||||
// NewDBStatsCollector creates a new DBStatsCollector.
|
||||
func NewDBStatsCollector(dbName string, sg DBStatsGetter) *DBStatsCollector {
|
||||
labels := prometheus.Labels{"db_name": dbName}
|
||||
return &DBStatsCollector{
|
||||
sg: sg,
|
||||
maxOpenDesc: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "max_open"),
|
||||
"Maximum number of open connections to the database.",
|
||||
nil,
|
||||
labels,
|
||||
),
|
||||
openDesc: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "open"),
|
||||
"The number of established connections both in use and idle.",
|
||||
nil,
|
||||
labels,
|
||||
),
|
||||
inUseDesc: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "in_use"),
|
||||
"The number of connections currently in use.",
|
||||
nil,
|
||||
labels,
|
||||
),
|
||||
idleDesc: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "idle"),
|
||||
"The number of idle connections.",
|
||||
nil,
|
||||
labels,
|
||||
),
|
||||
waitedForDesc: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "waited_for"),
|
||||
"The total number of connections waited for.",
|
||||
nil,
|
||||
labels,
|
||||
),
|
||||
blockedSecondsDesc: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "blocked_seconds"),
|
||||
"The total time blocked waiting for a new connection.",
|
||||
nil,
|
||||
labels,
|
||||
),
|
||||
closedMaxIdleDesc: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "closed_max_idle"),
|
||||
"The total number of connections closed due to SetMaxIdleConns.",
|
||||
nil,
|
||||
labels,
|
||||
),
|
||||
closedMaxLifetimeDesc: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "closed_max_lifetime"),
|
||||
"The total number of connections closed due to SetConnMaxLifetime.",
|
||||
nil,
|
||||
labels,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// Describe implements the prometheus.Collector interface.
|
||||
func (c DBStatsCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||
ch <- c.maxOpenDesc
|
||||
ch <- c.openDesc
|
||||
ch <- c.inUseDesc
|
||||
ch <- c.idleDesc
|
||||
ch <- c.waitedForDesc
|
||||
ch <- c.blockedSecondsDesc
|
||||
ch <- c.closedMaxIdleDesc
|
||||
ch <- c.closedMaxLifetimeDesc
|
||||
}
|
||||
|
||||
// Collect implements the prometheus.Collector interface.
|
||||
func (c DBStatsCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
stats := c.sg.Stats()
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.maxOpenDesc,
|
||||
prometheus.GaugeValue,
|
||||
float64(stats.MaxOpenConnections),
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.openDesc,
|
||||
prometheus.GaugeValue,
|
||||
float64(stats.OpenConnections),
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.inUseDesc,
|
||||
prometheus.GaugeValue,
|
||||
float64(stats.InUse),
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.idleDesc,
|
||||
prometheus.GaugeValue,
|
||||
float64(stats.Idle),
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.waitedForDesc,
|
||||
prometheus.CounterValue,
|
||||
float64(stats.WaitCount),
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.blockedSecondsDesc,
|
||||
prometheus.CounterValue,
|
||||
stats.WaitDuration.Seconds(),
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.closedMaxIdleDesc,
|
||||
prometheus.CounterValue,
|
||||
float64(stats.MaxIdleClosed),
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.closedMaxLifetimeDesc,
|
||||
prometheus.CounterValue,
|
||||
float64(stats.MaxLifetimeClosed),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2020 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 prom
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
// HTTPMiddleware http connection metric reader
|
||||
func HTTPMiddleware(next http.Handler) http.Handler {
|
||||
if !metrics {
|
||||
return next
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
httpCount.Inc()
|
||||
|
||||
start := time.Now()
|
||||
next.ServeHTTP(w, r)
|
||||
duration := time.Now().Sub(start)
|
||||
httpDuration.Observe(float64(duration.Seconds()))
|
||||
})
|
||||
}
|
||||
|
||||
// IPCMiddleware unix-socket connection counter
|
||||
func IPCMiddleware(server *rpc.Server, client rpc.Conn) {
|
||||
if metrics {
|
||||
ipcCount.Inc()
|
||||
}
|
||||
server.ServeCodec(rpc.NewCodec(client), 0)
|
||||
if metrics {
|
||||
ipcCount.Dec()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
// 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 prom
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
const (
|
||||
statsSubsystem = "stats"
|
||||
subsystemHTTP = "http"
|
||||
subsystemIPC = "ipc"
|
||||
)
|
||||
|
||||
var (
|
||||
metrics bool
|
||||
|
||||
queuedRanges prometheus.Gauge
|
||||
lastLoadedHeight prometheus.Gauge
|
||||
lastProcessedHeight prometheus.Gauge
|
||||
|
||||
tBlockLoad prometheus.Histogram
|
||||
tBlockProcessing prometheus.Histogram
|
||||
tStateProcessing prometheus.Histogram
|
||||
tTxCommit prometheus.Histogram
|
||||
|
||||
httpCount prometheus.Counter
|
||||
httpDuration prometheus.Histogram
|
||||
ipcCount prometheus.Gauge
|
||||
)
|
||||
|
||||
const (
|
||||
RANGES_QUEUED = "ranges_queued"
|
||||
LOADED_HEIGHT = "loaded_height"
|
||||
PROCESSED_HEIGHT = "processed_height"
|
||||
T_BLOCK_LOAD = "t_block_load"
|
||||
T_BLOCK_PROCESSING = "t_block_processing"
|
||||
T_STATE_PROCESSING = "t_state_processing"
|
||||
T_POSTGRES_TX_COMMIT = "t_postgres_tx_commit"
|
||||
)
|
||||
|
||||
// Init module initialization
|
||||
func Init() {
|
||||
metrics = true
|
||||
|
||||
queuedRanges = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Name: RANGES_QUEUED,
|
||||
Help: "Number of range requests currently queued",
|
||||
})
|
||||
lastLoadedHeight = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Name: LOADED_HEIGHT,
|
||||
Help: "The last block that was loaded for processing",
|
||||
})
|
||||
lastProcessedHeight = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Name: PROCESSED_HEIGHT,
|
||||
Help: "The last block that was processed",
|
||||
})
|
||||
|
||||
tBlockLoad = promauto.NewHistogram(prometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: statsSubsystem,
|
||||
Name: T_BLOCK_LOAD,
|
||||
Help: "Block loading time",
|
||||
})
|
||||
tBlockProcessing = promauto.NewHistogram(prometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: statsSubsystem,
|
||||
Name: T_BLOCK_PROCESSING,
|
||||
Help: "Block (header, uncles, txs, rcts, tx trie, rct trie) processing time",
|
||||
})
|
||||
tStateProcessing = promauto.NewHistogram(prometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: statsSubsystem,
|
||||
Name: T_STATE_PROCESSING,
|
||||
Help: "State (state trie, storage tries, and code) processing time",
|
||||
})
|
||||
tTxCommit = promauto.NewHistogram(prometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: statsSubsystem,
|
||||
Name: T_POSTGRES_TX_COMMIT,
|
||||
Help: "Postgres tx commit time",
|
||||
})
|
||||
|
||||
httpCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystemHTTP,
|
||||
Name: "count",
|
||||
Help: "http request count",
|
||||
})
|
||||
httpDuration = promauto.NewHistogram(prometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystemHTTP,
|
||||
Name: "duration",
|
||||
Help: "http request duration",
|
||||
})
|
||||
ipcCount = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystemIPC,
|
||||
Name: "count",
|
||||
Help: "unix socket connection count",
|
||||
})
|
||||
}
|
||||
|
||||
// RegisterDBCollector create metric collector for given connection
|
||||
func RegisterDBCollector(name string, db *sqlx.DB) {
|
||||
if metrics {
|
||||
prometheus.Register(NewDBStatsCollector(name, db))
|
||||
}
|
||||
}
|
||||
|
||||
// IncQueuedRanges increments the number of queued range requests
|
||||
func IncQueuedRanges() {
|
||||
if metrics {
|
||||
queuedRanges.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
// DecQueuedRanges decrements the number of queued range requests
|
||||
func DecQueuedRanges() {
|
||||
if metrics {
|
||||
queuedRanges.Dec()
|
||||
}
|
||||
}
|
||||
|
||||
// SetLastLoadedHeight sets last loaded height
|
||||
func SetLastLoadedHeight(height int64) {
|
||||
if metrics {
|
||||
lastLoadedHeight.Set(float64(height))
|
||||
}
|
||||
}
|
||||
|
||||
// SetLastProcessedHeight sets last processed height
|
||||
func SetLastProcessedHeight(height int64) {
|
||||
if metrics {
|
||||
lastProcessedHeight.Set(float64(height))
|
||||
}
|
||||
}
|
||||
|
||||
// SetTimeMetric time metric observation
|
||||
func SetTimeMetric(name string, t time.Duration) {
|
||||
if !metrics {
|
||||
return
|
||||
}
|
||||
tAsF64 := t.Seconds()
|
||||
switch name {
|
||||
case T_BLOCK_LOAD:
|
||||
tBlockLoad.Observe(tAsF64)
|
||||
case T_BLOCK_PROCESSING:
|
||||
tBlockProcessing.Observe(tAsF64)
|
||||
case T_STATE_PROCESSING:
|
||||
tStateProcessing.Observe(tAsF64)
|
||||
case T_POSTGRES_TX_COMMIT:
|
||||
tTxCommit.Observe(tAsF64)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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 prom
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Listen start listening http
|
||||
func Listen(addr string) *http.Server {
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/metrics", promhttp.Handler())
|
||||
srv := http.Server{
|
||||
Addr: addr,
|
||||
Handler: mux,
|
||||
}
|
||||
go func() {
|
||||
if err := srv.ListenAndServe(); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
}()
|
||||
return &srv
|
||||
}
|
||||
+36
-6
@@ -25,25 +25,55 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"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"
|
||||
)
|
||||
|
||||
// LvlDBReader exposes the necessary read functions on lvldb
|
||||
// Reader interface required by the statediffing service
|
||||
type Reader interface {
|
||||
GetBlockByHash(hash common.Hash) (*types.Block, error)
|
||||
GetBlockByNumber(number uint64) (*types.Block, error)
|
||||
GetReceiptsByHash(hash common.Hash) (types.Receipts, error)
|
||||
GetTdByHash(hash common.Hash) (*big.Int, error)
|
||||
StateDB() state.Database
|
||||
}
|
||||
|
||||
// LvlDBReader exposes the necessary Reader methods on lvldb
|
||||
type LvlDBReader struct {
|
||||
ethDB ethdb.Database
|
||||
stateDB state.Database
|
||||
chainConfig *params.ChainConfig
|
||||
}
|
||||
|
||||
// NewLvlDBReader creates a new LvlDBReader
|
||||
func NewLvlDBReader(path, ancient string, chainConfig *params.ChainConfig) (*LvlDBReader, error) {
|
||||
edb, err := rawdb.NewLevelDBDatabaseWithFreezer(path, 1024, 256, ancient, "eth-statediff-service")
|
||||
// LvLDBReaderConfig struct for initializing a LvlDBReader
|
||||
type LvLDBReaderConfig struct {
|
||||
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) {
|
||||
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
|
||||
}
|
||||
return &LvlDBReader{
|
||||
ethDB: edb,
|
||||
stateDB: state.NewDatabase(edb),
|
||||
chainConfig: chainConfig,
|
||||
stateDB: state.NewDatabaseWithConfig(edb, conf.TrieConfig),
|
||||
chainConfig: conf.ChainConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2020 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 rpc
|
||||
|
||||
import "github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
// checkModuleAvailability check that all names given in modules are actually
|
||||
// available API services.
|
||||
func checkModuleAvailability(modules []string, apis []rpc.API) (bad, available []string) {
|
||||
availableSet := make(map[string]struct{})
|
||||
for _, api := range apis {
|
||||
if _, ok := availableSet[api.Namespace]; !ok {
|
||||
availableSet[api.Namespace] = struct{}{}
|
||||
available = append(available, api.Namespace)
|
||||
}
|
||||
}
|
||||
for _, name := range modules {
|
||||
if _, ok := availableSet[name]; !ok {
|
||||
bad = append(bad, name)
|
||||
}
|
||||
}
|
||||
return bad, available
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2020 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 rpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/vulcanize/eth-statediff-service/pkg/prom"
|
||||
)
|
||||
|
||||
// StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules.
|
||||
func StartHTTPEndpoint(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string, timeouts rpc.HTTPTimeouts) (*rpc.Server, error) {
|
||||
|
||||
srv := rpc.NewServer()
|
||||
err := node.RegisterApis(apis, modules, srv, false)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not register HTTP API: %w", err)
|
||||
}
|
||||
handler := node.NewHTTPHandlerStack(srv, cors, vhosts, nil)
|
||||
|
||||
// start http server
|
||||
_, addr, err := node.StartHTTPEndpoint(endpoint, rpc.DefaultHTTPTimeouts, prom.HTTPMiddleware(handler))
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not start RPC api: %v", err)
|
||||
}
|
||||
extapiURL := fmt.Sprintf("http://%v/", addr)
|
||||
log.Infof("HTTP endpoint opened %s", extapiURL)
|
||||
|
||||
return srv, err
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2020 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 rpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/vulcanize/eth-statediff-service/pkg/prom"
|
||||
)
|
||||
|
||||
var (
|
||||
// On Linux, sun_path is 108 bytes in size
|
||||
// see http://man7.org/linux/man-pages/man7/unix.7.html
|
||||
maxPathSize = 108
|
||||
)
|
||||
|
||||
// ipcListen will create a Unix socket on the given endpoint.
|
||||
func ipcListen(endpoint string) (net.Listener, error) {
|
||||
if len(endpoint) > int(maxPathSize) {
|
||||
log.Warn(fmt.Sprintf("The ipc endpoint is longer than %d characters. ", maxPathSize),
|
||||
"endpoint", endpoint)
|
||||
}
|
||||
|
||||
// Ensure the IPC path exists and remove any previous leftover
|
||||
if err := os.MkdirAll(filepath.Dir(endpoint), 0751); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
os.Remove(endpoint)
|
||||
l, err := net.Listen("unix", endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
os.Chmod(endpoint, 0600)
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func ipcServe(srv *rpc.Server, listener net.Listener) {
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if netutil.IsTemporaryError(err) {
|
||||
log.WithError(err).Warn("rpc accept error")
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("unknown error")
|
||||
continue
|
||||
}
|
||||
log.WithField("addr", conn.RemoteAddr()).Trace("accepted ipc connection")
|
||||
go prom.IPCMiddleware(srv, conn)
|
||||
}
|
||||
}
|
||||
|
||||
// StartIPCEndpoint starts an IPC endpoint.
|
||||
func StartIPCEndpoint(ipcEndpoint string, apis []rpc.API) (net.Listener, *rpc.Server, error) {
|
||||
// Register all the APIs exposed by the services.
|
||||
handler := rpc.NewServer()
|
||||
for _, api := range apis {
|
||||
if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
log.Debug("IPC registered", "namespace", api.Namespace)
|
||||
}
|
||||
// All APIs registered, start the IPC listener.
|
||||
listener, err := ipcListen(ipcEndpoint)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
go ipcServe(handler, listener)
|
||||
return listener, handler, nil
|
||||
}
|
||||
+263
-48
@@ -20,65 +20,83 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"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"
|
||||
)
|
||||
|
||||
// lvlDBReader are the db interfaces required by the statediffing service
|
||||
type lvlDBReader interface {
|
||||
GetBlockByHash(hash common.Hash) (*types.Block, error)
|
||||
GetBlockByNumber(number uint64) (*types.Block, error)
|
||||
GetReceiptsByHash(hash common.Hash) (types.Receipts, error)
|
||||
GetTdByHash(hash common.Hash) (*big.Int, error)
|
||||
StateDB() state.Database
|
||||
}
|
||||
const defaultQueueSize = 1024
|
||||
|
||||
// IService is the state-diffing service interface
|
||||
type IService interface {
|
||||
// APIs(), Protocols(), Start() and Stop()
|
||||
node.Service
|
||||
// Main event loop for processing state diffs
|
||||
Loop(wg *sync.WaitGroup)
|
||||
// Method to get state diff object at specific block
|
||||
// StateDiffService is the state-diffing service interface
|
||||
type StateDiffService interface {
|
||||
// Lifecycle Start() and Stop()
|
||||
node.Lifecycle
|
||||
// APIs and Protocols() interface for node service registration
|
||||
APIs() []rpc.API
|
||||
Protocols() []p2p.Protocol
|
||||
// Loop is the main event loop for processing state diffs
|
||||
Loop(wg *sync.WaitGroup) error
|
||||
// Run is a one-off command to run on a predefined set of ranges
|
||||
Run(ranges []RangeRequest) error
|
||||
// StateDiffAt method to get state diff object at specific block
|
||||
StateDiffAt(blockNumber uint64, params sd.Params) (*sd.Payload, error)
|
||||
// Method to get state trie object at specific block
|
||||
// StateDiffFor method to get state diff object at specific block
|
||||
StateDiffFor(blockHash common.Hash, params sd.Params) (*sd.Payload, error)
|
||||
// StateTrieAt method to get state trie object at specific block
|
||||
StateTrieAt(blockNumber uint64, params sd.Params) (*sd.Payload, error)
|
||||
}
|
||||
|
||||
// Server configuration
|
||||
type Config struct {
|
||||
// Number of goroutines to use
|
||||
Workers uint
|
||||
// WriteStateDiffAt method to write state diff object directly to DB
|
||||
WriteStateDiffAt(blockNumber uint64, params sd.Params) error
|
||||
// WriteStateDiffFor method to get state trie object at specific block
|
||||
WriteStateDiffFor(blockHash common.Hash, params sd.Params) error
|
||||
// WriteStateDiffsInRange method to wrtie state diff objects within the range directly to the DB
|
||||
WriteStateDiffsInRange(start, stop uint64, params sd.Params) error
|
||||
}
|
||||
|
||||
// Service is the underlying struct for the state diffing service
|
||||
type Service struct {
|
||||
// Used to build the state diff objects
|
||||
Builder Builder
|
||||
// Used to read data from leveldb
|
||||
lvlDBReader lvlDBReader
|
||||
Builder sd.Builder
|
||||
// Used to read data from LevelDB
|
||||
lvlDBReader Reader
|
||||
// Used to signal shutdown of the service
|
||||
QuitChan chan bool
|
||||
quitChan chan struct{}
|
||||
// Interface for publishing statediffs as PG-IPLD objects
|
||||
indexer interfaces.StateDiffIndexer
|
||||
// range queue
|
||||
queue chan RangeRequest
|
||||
// number of ranges we can work over concurrently
|
||||
workers uint
|
||||
// ranges configured locally
|
||||
preruns []RangeRequest
|
||||
}
|
||||
|
||||
// NewStateDiffService creates a new Service
|
||||
func NewStateDiffService(lvlDBReader lvlDBReader, cfg Config) (*Service, error) {
|
||||
builder, err := NewBuilder(lvlDBReader.StateDB(), cfg.Workers)
|
||||
func NewStateDiffService(lvlDBReader Reader, indexer interfaces.StateDiffIndexer, conf Config) (*Service, error) {
|
||||
b, err := NewBuilder(lvlDBReader.StateDB(), conf.TrieWorkers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if conf.WorkerQueueSize == 0 {
|
||||
conf.WorkerQueueSize = defaultQueueSize
|
||||
}
|
||||
return &Service{
|
||||
lvlDBReader: lvlDBReader,
|
||||
Builder: builder,
|
||||
QuitChan: make(chan bool),
|
||||
Builder: b,
|
||||
indexer: indexer,
|
||||
workers: conf.ServiceWorkers,
|
||||
queue: make(chan RangeRequest, conf.WorkerQueueSize),
|
||||
preruns: conf.PreRuns,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -99,17 +117,73 @@ func (sds *Service) APIs() []rpc.API {
|
||||
}
|
||||
}
|
||||
|
||||
// Loop is an empty service loop for awaiting rpc requests
|
||||
func (sds *Service) Loop(wg *sync.WaitGroup) {
|
||||
wg.Add(1)
|
||||
for {
|
||||
select {
|
||||
case <-sds.QuitChan:
|
||||
logrus.Info("closing the statediff service loop")
|
||||
wg.Done()
|
||||
return
|
||||
// Run does a one-off processing run on the provided RangeRequests + any pre-runs, exiting afterwards
|
||||
func (sds *Service) Run(rngs []RangeRequest) error {
|
||||
for _, preRun := range sds.preruns {
|
||||
logrus.Infof("processing prerun range (%d, %d)", preRun.Start, preRun.Stop)
|
||||
for i := preRun.Start; i <= preRun.Stop; i++ {
|
||||
if err := sds.WriteStateDiffAt(i, preRun.Params); err != nil {
|
||||
return fmt.Errorf("error writing statediff at height %d in range (%d, %d) : %v", i, preRun.Start, preRun.Stop, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
sds.preruns = nil
|
||||
for _, rng := range rngs {
|
||||
logrus.Infof("processing prerun range (%d, %d)", rng.Start, rng.Stop)
|
||||
for i := rng.Start; i <= rng.Stop; i++ {
|
||||
if err := sds.WriteStateDiffAt(i, rng.Params); err != nil {
|
||||
return fmt.Errorf("error writing statediff at height %d in range (%d, %d) : %v", i, rng.Start, rng.Stop, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Loop is an empty service loop for awaiting rpc requests
|
||||
func (sds *Service) Loop(wg *sync.WaitGroup) error {
|
||||
if sds.quitChan != nil {
|
||||
return fmt.Errorf("service loop is already running")
|
||||
}
|
||||
|
||||
sds.quitChan = make(chan struct{})
|
||||
for i := 0; i < int(sds.workers); i++ {
|
||||
wg.Add(1)
|
||||
go func(id int) {
|
||||
defer wg.Done()
|
||||
for {
|
||||
select {
|
||||
case blockRange := <-sds.queue:
|
||||
logrus.Infof("service worker %d received range (%d, %d) off of work queue, beginning processing", id, blockRange.Start, blockRange.Stop)
|
||||
prom.DecQueuedRanges()
|
||||
for j := blockRange.Start; j <= blockRange.Stop; j++ {
|
||||
if err := sds.WriteStateDiffAt(j, blockRange.Params); err != nil {
|
||||
logrus.Errorf("service worker %d error writing statediff at height %d in range (%d, %d) : %v", id, j, blockRange.Start, blockRange.Stop, err)
|
||||
}
|
||||
select {
|
||||
case <-sds.quitChan:
|
||||
logrus.Infof("closing service worker %d\n"+
|
||||
"working in range (%d, %d)\n"+
|
||||
"last processed height: %d", id, blockRange.Start, blockRange.Stop, j)
|
||||
return
|
||||
default:
|
||||
logrus.Infof("service worker %d finished processing statediff height %d in range (%d, %d)", id, j, blockRange.Start, blockRange.Stop)
|
||||
}
|
||||
}
|
||||
logrus.Infof("service worker %d finished processing range (%d, %d)", id, blockRange.Start, blockRange.Stop)
|
||||
case <-sds.quitChan:
|
||||
logrus.Infof("closing the statediff service loop worker %d", id)
|
||||
return
|
||||
}
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
for _, preRun := range sds.preruns {
|
||||
if err := sds.WriteStateDiffsInRange(preRun.Start, preRun.Stop, preRun.Params); err != nil {
|
||||
close(sds.quitChan)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StateDiffAt returns a state diff object payload at the specific blockheight
|
||||
@@ -119,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 paths of watched addresses in the params
|
||||
params.ComputeWatchedAddressesLeafPaths()
|
||||
|
||||
if blockNumber == 0 {
|
||||
return sds.processStateDiff(currentBlock, common.Hash{}, params)
|
||||
}
|
||||
@@ -130,6 +208,28 @@ func (sds *Service) StateDiffAt(blockNumber uint64, params sd.Params) (*sd.Paylo
|
||||
return sds.processStateDiff(currentBlock, parentBlock.Root(), params)
|
||||
}
|
||||
|
||||
// StateDiffFor returns a state diff object payload for the specific blockhash
|
||||
// This operation cannot be performed back past the point of db pruning; it requires an archival node for historical data
|
||||
func (sds *Service) StateDiffFor(blockHash common.Hash, params sd.Params) (*sd.Payload, error) {
|
||||
currentBlock, err := sds.lvlDBReader.GetBlockByHash(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logrus.Infof("sending state diff at block %s", blockHash.Hex())
|
||||
|
||||
// compute leaf paths of watched addresses in the params
|
||||
params.ComputeWatchedAddressesLeafPaths()
|
||||
|
||||
if currentBlock.NumberU64() == 0 {
|
||||
return sds.processStateDiff(currentBlock, common.Hash{}, params)
|
||||
}
|
||||
parentBlock, err := sds.lvlDBReader.GetBlockByHash(currentBlock.ParentHash())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sds.processStateDiff(currentBlock, parentBlock.Root(), params)
|
||||
}
|
||||
|
||||
// processStateDiff method builds the state diff payload from the current block, parent state root, and provided params
|
||||
func (sds *Service) processStateDiff(currentBlock *types.Block, parentRoot common.Hash, params sd.Params) (*sd.Payload, error) {
|
||||
stateDiff, err := sds.Builder.BuildStateDiffObject(sd.Args{
|
||||
@@ -141,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
|
||||
}
|
||||
@@ -188,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 paths of watched addresses in the params
|
||||
params.ComputeWatchedAddressesLeafPaths()
|
||||
|
||||
return sds.processStateTrie(currentBlock, params)
|
||||
}
|
||||
|
||||
@@ -197,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
|
||||
}
|
||||
@@ -206,15 +310,126 @@ func (sds *Service) processStateTrie(block *types.Block, params sd.Params) (*sd.
|
||||
}
|
||||
|
||||
// Start is used to begin the service
|
||||
func (sds *Service) Start(*p2p.Server) error {
|
||||
func (sds *Service) Start() error {
|
||||
logrus.Info("starting statediff service")
|
||||
go sds.Loop(new(sync.WaitGroup))
|
||||
return nil
|
||||
return sds.Loop(new(sync.WaitGroup))
|
||||
}
|
||||
|
||||
// Stop is used to close down the service
|
||||
func (sds *Service) Stop() error {
|
||||
logrus.Info("stopping statediff service")
|
||||
close(sds.QuitChan)
|
||||
close(sds.quitChan)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteStateDiffAt writes a state diff at the specific blockheight directly to the database
|
||||
// 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.Infof("Writing state diff at block %d", blockNumber)
|
||||
t := time.Now()
|
||||
currentBlock, err := sds.lvlDBReader.GetBlockByNumber(blockNumber)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// compute leaf paths of watched addresses in the params
|
||||
params.ComputeWatchedAddressesLeafPaths()
|
||||
|
||||
parentRoot := common.Hash{}
|
||||
if blockNumber != 0 {
|
||||
parentBlock, err := sds.lvlDBReader.GetBlockByHash(currentBlock.ParentHash())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parentRoot = parentBlock.Root()
|
||||
}
|
||||
return sds.writeStateDiff(currentBlock, parentRoot, params, t)
|
||||
}
|
||||
|
||||
// WriteStateDiffFor writes a state diff for the specific blockHash directly to the database
|
||||
// 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.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 paths of watched addresses in the params
|
||||
params.ComputeWatchedAddressesLeafPaths()
|
||||
|
||||
parentRoot := common.Hash{}
|
||||
if currentBlock.NumberU64() != 0 {
|
||||
parentBlock, err := sds.lvlDBReader.GetBlockByHash(currentBlock.ParentHash())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parentRoot = parentBlock.Root()
|
||||
}
|
||||
return sds.writeStateDiff(currentBlock, parentRoot, params, t)
|
||||
}
|
||||
|
||||
// Writes a state diff from the current block, parent state root, and provided params
|
||||
func (sds *Service) writeStateDiff(block *types.Block, parentRoot common.Hash, params sd.Params, t time.Time) error {
|
||||
var totalDifficulty *big.Int
|
||||
var receipts types.Receipts
|
||||
var err error
|
||||
if params.IncludeTD {
|
||||
totalDifficulty, err = sds.lvlDBReader.GetTdByHash(block.Hash())
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if params.IncludeReceipts {
|
||||
receipts, err = sds.lvlDBReader.GetReceiptsByHash(block.Hash())
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
height := block.Number().Int64()
|
||||
prom.SetLastLoadedHeight(height)
|
||||
prom.SetTimeMetric(prom.T_BLOCK_LOAD, time.Now().Sub(t))
|
||||
t = time.Now()
|
||||
tx, err := sds.indexer.PushBlock(block, receipts, totalDifficulty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// defer handling of commit/rollback for any return case
|
||||
output := func(node sdtypes.StateNode) error {
|
||||
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(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.Submit(err)
|
||||
prom.SetLastProcessedHeight(height)
|
||||
prom.SetTimeMetric(prom.T_POSTGRES_TX_COMMIT, time.Now().Sub(t))
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteStateDiffsInRange adds a RangeRequest to the work queue
|
||||
func (sds *Service) WriteStateDiffsInRange(start, stop uint64, params sd.Params) error {
|
||||
if stop < start {
|
||||
return fmt.Errorf("invalid block range (%d, %d): stop height must be greater or equal to start height", start, stop)
|
||||
}
|
||||
blocked := time.NewTimer(30 * time.Second)
|
||||
select {
|
||||
case sds.queue <- RangeRequest{Start: start, Stop: stop, Params: params}:
|
||||
prom.IncQueuedRanges()
|
||||
logrus.Infof("added range (%d, %d) to the worker queue", start, stop)
|
||||
return nil
|
||||
case <-blocked.C:
|
||||
return fmt.Errorf("unable to add range (%d, %d) to the worker queue", start, stop)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,124 +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
|
||||
blocks, _ := core.GenerateChain(config, parent, ethash.NewFaker(), Testdb, n, chainGen)
|
||||
chain, _ := core.NewBlockChain(Testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, 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,128 +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 mocks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
)
|
||||
|
||||
// BlockChain is a mock blockchain for testing
|
||||
type BlockChain struct {
|
||||
HashesLookedUp []common.Hash
|
||||
blocksToReturnByHash map[common.Hash]*types.Block
|
||||
blocksToReturnByNumber map[uint64]*types.Block
|
||||
callCount int
|
||||
ChainEvents []core.ChainEvent
|
||||
Receipts map[common.Hash]types.Receipts
|
||||
TDByHash map[common.Hash]*big.Int
|
||||
}
|
||||
|
||||
// SetBlocksForHashes mock method
|
||||
func (blockChain *BlockChain) SetBlocksForHashes(blocks map[common.Hash]*types.Block) {
|
||||
if blockChain.blocksToReturnByHash == nil {
|
||||
blockChain.blocksToReturnByHash = make(map[common.Hash]*types.Block)
|
||||
}
|
||||
blockChain.blocksToReturnByHash = blocks
|
||||
}
|
||||
|
||||
// GetBlockByHash mock method
|
||||
func (blockChain *BlockChain) GetBlockByHash(hash common.Hash) *types.Block {
|
||||
blockChain.HashesLookedUp = append(blockChain.HashesLookedUp, hash)
|
||||
|
||||
var block *types.Block
|
||||
if len(blockChain.blocksToReturnByHash) > 0 {
|
||||
block = blockChain.blocksToReturnByHash[hash]
|
||||
}
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
// SetChainEvents mock method
|
||||
func (blockChain *BlockChain) SetChainEvents(chainEvents []core.ChainEvent) {
|
||||
blockChain.ChainEvents = chainEvents
|
||||
}
|
||||
|
||||
// SubscribeChainEvent mock method
|
||||
func (blockChain *BlockChain) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
|
||||
subErr := errors.New("subscription error")
|
||||
|
||||
var eventCounter int
|
||||
subscription := event.NewSubscription(func(quit <-chan struct{}) error {
|
||||
for _, chainEvent := range blockChain.ChainEvents {
|
||||
if eventCounter > 1 {
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
return subErr
|
||||
}
|
||||
select {
|
||||
case ch <- chainEvent:
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
eventCounter++
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return subscription
|
||||
}
|
||||
|
||||
// SetReceiptsForHash test method
|
||||
func (blockChain *BlockChain) SetReceiptsForHash(hash common.Hash, receipts types.Receipts) {
|
||||
if blockChain.Receipts == nil {
|
||||
blockChain.Receipts = make(map[common.Hash]types.Receipts)
|
||||
}
|
||||
blockChain.Receipts[hash] = receipts
|
||||
}
|
||||
|
||||
// GetReceiptsByHash mock method
|
||||
func (blockChain *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||
return blockChain.Receipts[hash]
|
||||
}
|
||||
|
||||
// SetBlockForNumber test method
|
||||
func (blockChain *BlockChain) SetBlockForNumber(block *types.Block, number uint64) {
|
||||
if blockChain.blocksToReturnByNumber == nil {
|
||||
blockChain.blocksToReturnByNumber = make(map[uint64]*types.Block)
|
||||
}
|
||||
blockChain.blocksToReturnByNumber[number] = block
|
||||
}
|
||||
|
||||
// GetBlockByNumber mock method
|
||||
func (blockChain *BlockChain) GetBlockByNumber(number uint64) *types.Block {
|
||||
return blockChain.blocksToReturnByNumber[number]
|
||||
}
|
||||
|
||||
// GetTdByHash mock method
|
||||
func (blockChain *BlockChain) GetTdByHash(hash common.Hash) *big.Int {
|
||||
return blockChain.TDByHash[hash]
|
||||
}
|
||||
|
||||
func (blockChain *BlockChain) SetTdByHash(hash common.Hash, td *big.Int) {
|
||||
if blockChain.TDByHash == nil {
|
||||
blockChain.TDByHash = make(map[common.Hash]*big.Int)
|
||||
}
|
||||
blockChain.TDByHash[hash] = td
|
||||
}
|
||||
|
||||
func (blockChain *BlockChain) UnlockTrie(root common.Hash) {}
|
||||
@@ -1,57 +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 mocks
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
statediff "github.com/vulcanize/eth-statediff-service/pkg"
|
||||
)
|
||||
|
||||
// Builder is a mock state diff builder
|
||||
type Builder struct {
|
||||
Args statediff.Args
|
||||
Params statediff.Params
|
||||
stateDiff statediff.StateObject
|
||||
block *types.Block
|
||||
stateTrie statediff.StateObject
|
||||
builderError error
|
||||
}
|
||||
|
||||
// BuildStateDiffObject mock method
|
||||
func (builder *Builder) BuildStateDiffObject(args statediff.Args, params statediff.Params) (statediff.StateObject, error) {
|
||||
builder.Args = args
|
||||
builder.Params = params
|
||||
|
||||
return builder.stateDiff, builder.builderError
|
||||
}
|
||||
|
||||
// BuildStateTrieObject mock method
|
||||
func (builder *Builder) BuildStateTrieObject(block *types.Block) (statediff.StateObject, error) {
|
||||
builder.block = block
|
||||
|
||||
return builder.stateTrie, builder.builderError
|
||||
}
|
||||
|
||||
// SetStateDiffToBuild mock method
|
||||
func (builder *Builder) SetStateDiffToBuild(stateDiff statediff.StateObject) {
|
||||
builder.stateDiff = stateDiff
|
||||
}
|
||||
|
||||
// SetBuilderError mock method
|
||||
func (builder *Builder) SetBuilderError(err error) {
|
||||
builder.builderError = err
|
||||
}
|
||||
@@ -1,73 +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"
|
||||
"math/rand"
|
||||
|
||||
"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}
|
||||
BlockNumber = big.NewInt(rand.Int63())
|
||||
BlockHash = "0xfa40fbe2d98d98b3363a778d52f2bcd29d6790b9b3f3cab2b167fd12d3550f73"
|
||||
NullCodeHash = crypto.Keccak256Hash([]byte{})
|
||||
StoragePath = common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes()
|
||||
StorageKey = common.HexToHash("0000000000000000000000000000000000000000000000000000000000000001").Bytes()
|
||||
StorageValue = common.Hex2Bytes("0x03")
|
||||
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)
|
||||
)
|
||||
+10
-3
@@ -20,8 +20,9 @@
|
||||
package statediff
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
sd "github.com/ethereum/go-ethereum/statediff"
|
||||
sdTypes "github.com/ethereum/go-ethereum/statediff/types"
|
||||
)
|
||||
|
||||
// AccountMap is a mapping of hex encoded path => account wrapper
|
||||
@@ -29,9 +30,15 @@ type AccountMap map[string]accountWrapper
|
||||
|
||||
// accountWrapper is used to temporary associate the unpacked node with its raw values
|
||||
type accountWrapper struct {
|
||||
Account *state.Account
|
||||
NodeType sd.NodeType
|
||||
Account *types.StateAccount
|
||||
NodeType sdTypes.NodeType
|
||||
Path []byte
|
||||
NodeValue []byte
|
||||
LeafKey []byte
|
||||
}
|
||||
|
||||
// RangeRequest holds range quest work params
|
||||
type RangeRequest struct {
|
||||
Start, Stop uint64
|
||||
Params sd.Params
|
||||
}
|
||||
|
||||
Regular → Executable
+12
-3
@@ -4,8 +4,17 @@ set -e
|
||||
set +x
|
||||
|
||||
# Check the database variables are set
|
||||
test $VDB_COMMAND
|
||||
test "$VDB_COMMAND"
|
||||
set +e
|
||||
|
||||
echo "Running the statediff service"
|
||||
./eth-statediff-service ${VDB_COMMAND} --config=config.toml
|
||||
# docker must be run in privilaged mode for mounts to work
|
||||
echo "Setting up /app/geth-rw overlayed /app/geth-ro"
|
||||
mkdir -p /tmp/overlay && \
|
||||
sudo mount -t tmpfs tmpfs /tmp/overlay && \
|
||||
mkdir -p /tmp/overlay/upper && \
|
||||
mkdir -p /tmp/overlay/work && \
|
||||
mkdir -p /app/geth-rw && \
|
||||
sudo mount -t overlay overlay -o lowerdir=/app/geth-ro,upperdir=/tmp/overlay/upper,workdir=/tmp/overlay/work /app/geth-rw && \
|
||||
|
||||
echo "Running the statediff service" && \
|
||||
sudo ./eth-statediff-service "$VDB_COMMAND" --config=config.toml
|
||||
|
||||
@@ -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