Compare commits

..
Author SHA1 Message Date
telackey 768357293c Add progress counter to prometheus output (#76)
* Add a progress counter by checking the distance already traversed from the startPath to endPath in a bounded iterator vs the estimated number of iterations.
2023-05-23 11:23:58 -05:00
Ian Norden dd86f02997 Merge pull request #75 from cerc-io/ian/v5_dev
fix overshadowing of snap.Tx
2023-05-18 09:37:26 -05:00
i-norden 891fca89bd fix overshadowing of snap.Tx 2023-05-17 08:16:50 -05:00
Ian Norden 0cab03b98e Merge pull request #74 from cerc-io/ian/v5_dev
update geth and ipld-eth-db deps
2023-05-15 11:38:24 -05:00
i-norden 81e286399b update geth and ipld-eth-db deps 2023-05-15 11:32:29 -05:00
Ian Norden 7e426b2ca5 Merge pull request #71 from cerc-io/ian/v5_dev
v5 upgrade
2023-05-12 11:29:14 -05:00
i-norden 98015c4c87 fix NUMERIC formattign error 2023-05-12 11:11:00 -05:00
i-norden 6de5b9e96c update misc 2023-05-12 10:41:18 -05:00
i-norden 2dd9221467 update go mod 2023-05-12 10:27:20 -05:00
i-norden ea4c1042c4 update service_test with new fixtures 2023-05-12 10:07:28 -05:00
i-norden 7af17b1851 update test helpers 2023-05-12 10:07:12 -05:00
i-norden 4081787b03 fix service.go: prevent panics on subtrie iterators with odd-length prefix-paths 2023-05-12 10:06:54 -05:00
i-norden 3d8064ccbb finish updating test fixtures 2023-05-12 09:58:46 -05:00
i-norden fdb105b769 update compose 2023-05-10 13:11:42 -05:00
i-norden 32b637671d use v5.0.2 statediffing geth 2023-05-10 13:11:23 -05:00
i-norden 4ee75a3371 update publisher interface 2023-05-10 13:11:12 -05:00
telackey 82176ea41c Add Docker files (#73) 2023-05-09 10:33:32 -05:00
i-norden 0a04baab17 bump db version in docker compose 2023-04-12 13:13:04 -05:00
i-norden 4245b80a4a go mod tidy 2023-04-12 13:13:04 -05:00
i-norden 22ecd4065a update service 2023-04-12 13:13:04 -05:00
i-norden 382ad92701 removei in-place snapshot stuff 2023-04-12 13:13:04 -05:00
i-norden 14b1180161 update Block1 and Chain2_Block32 test fixtures 2023-04-12 13:13:04 -05:00
i-norden ead007f159 update csv file publisher 2023-04-12 13:13:04 -05:00
i-norden f1a980f37c update direct pg publisher 2023-04-12 13:13:04 -05:00
i-norden f83ab82424 use types/models exported from vdb geth 2023-04-12 13:13:04 -05:00
Ian Norden fb5a95d874 Merge pull request #72 from cerc-io/ian/v5_rebase
Rebase
2023-04-12 13:12:35 -05:00
roysc 97ee99a449 Update schema (#61)
Updates table to add header_cids.uncles_hash
2023-04-12 13:09:58 -05:00
Ian Norden b6d7695536 Merge pull request #70 from cerc-io/ian/v4_dev
update db version
2023-04-11 09:06:03 -05:00
i-norden 3f3e77cbac update db version 2023-04-11 09:01:50 -05:00
Ian Norden e63ffbf0ad Merge pull request #69 from cerc-io/ian/v4_dev
Update to use v4 vdb geth v1.11.5
2023-03-31 12:50:23 -05:00
i-norden 6ede522ae0 missing .meta files in fixtures 2023-03-31 12:36:30 -05:00
i-norden 5c13d59515 refactor to work with v4 vdb geth v1.11.5 2023-03-31 10:39:27 -05:00
i-norden 6fe54aa8b4 use go1.19 2023-03-31 10:39:13 -05:00
i-norden c9dd85488b bump vdb v4 geth to v1.11.5 2023-03-31 10:39:00 -05:00
Michael ac85fe29eb dependency updates for geth 1.10.26 (#67) 2022-11-08 14:11:11 -05:00
roysc c270f39de9 Update schema (#61)
Updates table to add header_cids.uncles_hash
2022-10-13 13:19:47 -05:00
Michael 9ea78c4ecf dependency updates for geth 1.10.25 (#65) 2022-09-26 09:14:15 -04:00
Michael 989bd1c0f1 Cerc refactor (#64)
* cerc refactor waiting on unpublished dependencies

* more cerc_refactor with published ipld-eth-db unstable

* TearDownDB refactor

* missed second publisher_test TearDownDB
2022-09-20 13:47:34 -04:00
Michael 3f93a989dc Merge pull request #63 from vulcanize/rebase-1.10.23-wip
updates for geth rebase on 1.10.23
2022-09-02 15:39:23 -04:00
Michael Shaw 321e37584d updates for geth rebase on 1.10.23 2022-09-02 15:21:29 -04:00
prathamesh0 be544a3424 Add helper scripts for data dump correction (#57)
* Add a script to find bad data in CSV file dumps

* Add a script to delete bad rows from CSV file dumps

* Add instructions to run the scripts

* Reorganize instructions
2022-08-17 15:14:14 +05:30
Ian Nordenandprathamesh 05aeeab581 Account selective snapshot (#46)
* snapshotter ignores nodes not along a path along those derived from a list of account addresses if one is provided

* config and env updates

* cmd update

* Encode watched address path bytes to hex for comparison

* actually ignore the subtries that are not along the paths of interest

* Fixes for account selective snapshot

* Use non-concurrent iterator when having a single worker

* Only index root node when starting path of an iterator is nil

* Upgrade deps

* Avoid tracking iterators and skip recovery test

* Fix recovery mechanism, use sync Map instead of buffered channels

* Add test for account selective snapshot

* Continue traversal with concurrent iterators with starting path nil

* Use errgroup to simplify error handling with concurrent iterators

* Check if all the nodes are indexed in the recovery test

* Use concurrency safe sync Map in account selective snapshot test

* Only track concurrent iterators and refactor code

* Fix node and recovered path comparison

* Revert back to using buffered channels for tracking iterators

* Add a metric to monitor number of active iterators

* Update docs

* Update seeked path after node is processed

* Return error on context cancellation from subtrie iteration

* Add tests for account selective snapshot recovery

* Explicity enforce concurrent iterator bounds to avoid duplicate nodes

* Update full snapshot test to check nodes being indexed

* Refactor code to simplify snapshot logic

* Remove unnecessary function argument

* Use ctx cancellation for handling signals

* Add descriptive comments

Co-authored-by: prathamesh0 <prathamesh.musale0@gmail.com>
2022-08-03 17:05:04 +05:30
prathamesh0 5002c82038 Upgrade dependencies (#56) 2022-08-03 16:11:38 +05:30
Michael 289fb63568 Merge pull request #54 from vulcanize/release-v4.1.1-alpha
update for go-ethereum 1.10.20
2022-07-20 17:18:58 -04:00
Michael Shaw 257fac1a0c update for go-ethereum 1.10.20 2022-07-19 15:12:44 -04:00
prathamesh0 c0a7fdf9e8 Fix typo in config format in README (#53) 2022-07-18 16:48:58 +05:30
prathamesh0 2af26ad583 Add instructions to import snapshot data into database (#52)
* Add instructions to import snapshot data into database

* Add monitoring and update data processing in README

* Update instructions to import snapshot
2022-07-18 15:30:23 +05:30
prathamesh0andnabarun ac00ee280f Upgrade geth (#51)
* Fix Makefile for installing mockgen tool

* Upgrade geth

* Add sleep in the failing recovery test

Co-authored-by: nabarun <nabarun@deepstacksoft.com>
2022-07-13 11:39:36 +05:30
prathamesh0andnabarun b241bf05eb Add command to take an in-place snapshot (#42)
* Add command to take an in-place snapshot

* Add test data for in place snapshot unit test

* Implement unit test for inplace snapshot

* Add check for storage IPLD

* Run unit tests sequentially

* Add github workflow for unit test

* Add missing checks for state and storage cid fields

* Add more storage nodes to test

* Update ipld-eth-db version for tests

* Add comments for inplace snapshot test data

* Add in-place snapshot cmd in readme

* Implement defer pattern for db transaction

* Log transaction commit or rollback error

Co-authored-by: nabarun <nabarun@deepstacksoft.com>
2022-06-15 12:51:26 +05:30
Ashwin Phatak c2ecea6d61 Merge pull request #45 from vulcanize/ng-merge-fix-iterator
Merge changes to fix iterator and update
2022-06-10 14:24:33 +05:30
nabarun baef795834 Merge changes to fix iterator and update 2022-06-10 14:12:34 +05:30
nabarun 4979e4090d Merge pull request #43 from roysc/fix-iterator-and-update
Fix iterator and update
2022-06-10 14:03:43 +05:30
nabarun 59011cba3c Update go-eth-state-node-iterator version 2022-06-10 14:02:13 +05:30
roysc 8371344756 clean up logging 2022-06-08 20:12:22 +08:00
roysc ef391d8d31 simplify tracker, prevent send to closed chan 2022-06-08 20:12:22 +08:00
roysc 838dabcba6 update iterator mod 2022-06-08 20:12:22 +08:00
roysc 8572c36ee7 decrement path from restored iterator
to cover node gaps
2022-06-08 20:12:22 +08:00
Ashwin Phatak 74f82a641d Fix dependency 2022-06-08 20:05:38 +08:00
roysc 3e24972a89 try to fix hanging iterator 2022-06-08 20:05:38 +08:00
Ashwin Phatak 7129044eae Log progress info 2022-06-08 18:32:10 +08:00
Abdul Rabbani c9d398b5af Merge pull request #40 from vulcanize/feature/update-go-geth-sharding
Update go and geth versions
2022-06-01 09:22:04 -04:00
Abdul Rabbani 8aab6a5137 Update geth versions 2022-06-01 09:20:45 -04:00
Abdul Rabbani cd2559e434 Merge branch 'master' into feature/update-go-geth-sharding 2022-06-01 09:19:18 -04:00
Abdul Rabbani d213644ff4 Merge pull request #39 from vulcanize/feature/update-go-geth
Updating Go and Geth Version
2022-06-01 09:10:35 -04:00
Abdul Rabbani a04777cb78 Updating Go and Geth Version
Ran Tests:

```
09:07:50:~/GitHub/cerc/ipld-eth-state-snapshot go test ./... -v
# github.com/vulcanize/ipld-eth-state-snapshot/cmd
cmd/stateSnapshot.go:47:3: (*github.com/sirupsen/logrus.Entry).Fatal call has possible formatting directive %v
?       github.com/vulcanize/ipld-eth-state-snapshot    [no test files]
=== RUN   TestCreateSnapshot
=== RUN   TestCreateSnapshot/case
time="2022-06-01T09:08:03-04:00" level=info msg="Creating snapshot at height 1"
time="2022-06-01T09:08:03-04:00" level=info msg="head hash: 0xa7684ac44d48494670b2e0d9085b7750e7341620f0a271db146ed5e70c1db854 head height: 1"
=== RUN   TestCreateSnapshot/case#01
time="2022-06-01T09:08:03-04:00" level=info msg="Creating snapshot at height 1"
time="2022-06-01T09:08:03-04:00" level=info msg="head hash: 0xa7684ac44d48494670b2e0d9085b7750e7341620f0a271db146ed5e70c1db854 head height: 1"
=== RUN   TestCreateSnapshot/case#02
time="2022-06-01T09:08:03-04:00" level=info msg="Creating snapshot at height 1"
time="2022-06-01T09:08:03-04:00" level=info msg="head hash: 0xa7684ac44d48494670b2e0d9085b7750e7341620f0a271db146ed5e70c1db854 head height: 1"
=== RUN   TestCreateSnapshot/case#03
time="2022-06-01T09:08:03-04:00" level=info msg="Creating snapshot at height 1"
time="2022-06-01T09:08:03-04:00" level=info msg="head hash: 0xa7684ac44d48494670b2e0d9085b7750e7341620f0a271db146ed5e70c1db854 head height: 1"
--- PASS: TestCreateSnapshot (0.20s)
    --- PASS: TestCreateSnapshot/case (0.15s)
    --- PASS: TestCreateSnapshot/case#01 (0.01s)
    --- PASS: TestCreateSnapshot/case#02 (0.02s)
    --- PASS: TestCreateSnapshot/case#03 (0.03s)
=== RUN   TestRecovery
=== RUN   TestRecovery/case
time="2022-06-01T09:08:03-04:00" level=info msg="Creating snapshot at height 1"
time="2022-06-01T09:08:03-04:00" level=info msg="head hash: 0xa7684ac44d48494670b2e0d9085b7750e7341620f0a271db146ed5e70c1db854 head height: 1"
time="2022-06-01T09:08:03-04:00" level=info msg="Dumping recovery state to: /var/folders/xf/cg688d9j7f39ktmd_4ttwxqh0000gn/T/TestRecoverycase3574218035/001/recover.csv"
time="2022-06-01T09:08:03-04:00" level=info msg="Creating snapshot at height 1"
time="2022-06-01T09:08:03-04:00" level=info msg="head hash: 0xa7684ac44d48494670b2e0d9085b7750e7341620f0a271db146ed5e70c1db854 head height: 1"
=== RUN   TestRecovery/case#01
time="2022-06-01T09:08:03-04:00" level=info msg="Creating snapshot at height 1"
time="2022-06-01T09:08:03-04:00" level=info msg="head hash: 0xa7684ac44d48494670b2e0d9085b7750e7341620f0a271db146ed5e70c1db854 head height: 1"
time="2022-06-01T09:08:03-04:00" level=info msg="Dumping recovery state to: /var/folders/xf/cg688d9j7f39ktmd_4ttwxqh0000gn/T/TestRecoverycase#011814952517/001/recover.csv"
time="2022-06-01T09:08:03-04:00" level=info msg="Creating snapshot at height 1"
time="2022-06-01T09:08:03-04:00" level=info msg="head hash: 0xa7684ac44d48494670b2e0d9085b7750e7341620f0a271db146ed5e70c1db854 head height: 1"
=== RUN   TestRecovery/case#02
time="2022-06-01T09:08:03-04:00" level=info msg="Creating snapshot at height 1"
time="2022-06-01T09:08:03-04:00" level=info msg="head hash: 0xa7684ac44d48494670b2e0d9085b7750e7341620f0a271db146ed5e70c1db854 head height: 1"
time="2022-06-01T09:08:03-04:00" level=info msg="Dumping recovery state to: /var/folders/xf/cg688d9j7f39ktmd_4ttwxqh0000gn/T/TestRecoverycase#021650290358/001/recover.csv"
time="2022-06-01T09:08:03-04:00" level=info msg="Creating snapshot at height 1"
time="2022-06-01T09:08:03-04:00" level=info msg="head hash: 0xa7684ac44d48494670b2e0d9085b7750e7341620f0a271db146ed5e70c1db854 head height: 1"
--- PASS: TestRecovery (0.40s)
    --- PASS: TestRecovery/case (0.12s)
    --- PASS: TestRecovery/case#01 (0.19s)
    --- PASS: TestRecovery/case#02 (0.09s)
PASS
ok      github.com/vulcanize/ipld-eth-state-snapshot/pkg/snapshot       1.109s
=== RUN   TestWriting
--- PASS: TestWriting (0.00s)
=== RUN   TestPgCopy
    publisher_test.go:84: set TEST_WITH_DB to enable test
--- SKIP: TestPgCopy (0.00s)
PASS
ok      github.com/vulcanize/ipld-eth-state-snapshot/pkg/snapshot/file  0.279s
=== RUN   TestBasic
    publisher_test.go:47: set TEST_WITH_DB to enable test
--- SKIP: TestBasic (0.00s)
PASS
ok      github.com/vulcanize/ipld-eth-state-snapshot/pkg/snapshot/pg    0.647s
```
2022-06-01 09:09:19 -04:00
Ashwin Phatak c785c12274 Merge pull request #35 from vulcanize/ng-file-ethereum-nodeId
Add ethereum config to fix db import in file mode
2022-05-25 18:38:09 +05:30
nabarun 23c4d22885 Add ethereum config to fix db import in file mode 2022-05-25 18:15:08 +05:30
Ashwin Phatak d3a149899b Merge pull request #34 from deep-stack/pm-merge-metrics
Merge changes to add env bindings and prometheus metrics
2022-05-23 18:14:06 +05:30
prathamesh dc27487b66 Merge changes to add env bindings and prometheus metrics 2022-05-23 17:11:51 +05:30
prathamesh0 cce45bda57 Add prometheus metrics collection (#33)
* Upgrade geth

* Add prometheus metrics collection

* Update README
2022-05-23 16:56:48 +05:30
Ian Norden 751e064f8f Merge pull request #28 from vulcanize/release-v3.0.2
add env bindings
2022-05-17 22:36:08 -05:00
i-norden 004f3e6665 constants for CLI binding strings 2022-05-17 22:33:20 -05:00
i-norden 31105a009c add env bindings 2022-04-04 14:35:18 -05:00
74 changed files with 4052 additions and 1647 deletions
+30
View File
@@ -0,0 +1,30 @@
name: Docker Build
on: [pull_request]
jobs:
test:
name: Run unit tests
runs-on: ubuntu-latest
env:
GOPATH: /tmp/go
GO111MODULE: on
steps:
- name: Create GOPATH
run: mkdir -p /tmp/go
- uses: actions/setup-go@v3
with:
go-version: ">=1.19.0"
check-latest: true
- name: Checkout code
uses: actions/checkout@v2
- name: Run database
run: docker-compose up -d
- name: Run unit tests
run: |
sleep 45
make dbtest
+6
View File
@@ -1,2 +1,8 @@
.idea/
.vscode/
ipld-eth-state-snapshot
mocks/
.vscode
output_dir*/
log_file
recovery_file
+27
View File
@@ -0,0 +1,27 @@
FROM golang:1.20-alpine as builder
RUN apk --update --no-cache add make git g++ linux-headers
# DEBUG
RUN apk add busybox-extras
# Get and build ipfs-blockchain-watcher
ADD . /go/src/github.com/cerc-io/ipld-eth-state-snapshot
#RUN git clone https://github.com/cerc-io/ipld-eth-state-snapshot.git /go/src/github.com/vulcanize/ipld-eth-state-snapshot
WORKDIR /go/src/github.com/cerc-io/ipld-eth-state-snapshot
RUN GO111MODULE=on GCO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o ipld-eth-state-snapshot .
# app container
FROM alpine
RUN apk --no-cache add su-exec bash
WORKDIR /app
COPY --from=builder /go/src/github.com/cerc-io/ipld-eth-state-snapshot/startup_script.sh .
COPY --from=builder /go/src/github.com/cerc-io/ipld-eth-state-snapshot/environments environments
# keep binaries immutable
COPY --from=builder /go/src/github.com/cerc-io/ipld-eth-state-snapshot/ipld-eth-state-snapshot ipld-eth-state-snapshot
ENTRYPOINT ["/app/startup_script.sh"]
+20 -4
View File
@@ -1,12 +1,28 @@
BIN = $(GOPATH)/bin
## Mockgen tool
MOCKGEN = $(BIN)/mockgen
$(BIN)/mockgen:
go install github.com/golang/mock/mockgen@v1.6.0
MOCKS_DIR = $(CURDIR)/mocks
mockgen_cmd=mockgen
.PHONY: mocks
.PHONY: mocks test
mocks: mocks/snapshot/publisher.go
mocks: $(MOCKGEN) mocks/snapshot/publisher.go
mocks/snapshot/publisher.go: pkg/types/publisher.go
$(mockgen_cmd) -package snapshot_mock -destination $@ -source $< Publisher Tx
$(MOCKGEN) -package snapshot_mock -destination $@ -source $< Publisher Tx
clean:
rm -f mocks/snapshot/publisher.go
build:
go fmt ./...
go build
test: mocks
go clean -testcache && go test -p 1 -v ./...
dbtest: mocks
go clean -testcache && TEST_WITH_DB=true go test -p 1 -v ./...
+183 -15
View File
@@ -4,32 +4,200 @@
[![Go Report Card](https://goreportcard.com/badge/github.com/vulcanize/ipld-eth-state-snapshot)](https://goreportcard.com/report/github.com/vulcanize/ipld-eth-state-snapshot)
## Usage
## Setup
./ipld-eth-state-snapshot stateSnapshot --config={path to toml config file}
* Build the binary:
### Config
```bash
make build
```
## Configuration
Config format:
```toml
[snapshot]
mode = "file" # indicates output mode ("postgres" or "file")
workers = 4 # degree of concurrency, the state trie is subdivided into sectiosn that are traversed and processed concurrently
blockHeight = -1 # blockheight to perform the snapshot at (-1 indicates to use the latest blockheight found in leveldb)
recoveryFile = "recovery_file" # specifies a file to output recovery information on error or premature closure
mode = "file" # indicates output mode <postgres | file>
workers = 4 # degree of concurrency, the state trie is subdivided into sections that are traversed and processed concurrently
blockHeight = -1 # blockheight to perform the snapshot at (-1 indicates to use the latest blockheight found in leveldb)
recoveryFile = "recovery_file" # specifies a file to output recovery information on error or premature closure
accounts = [] # list of accounts (addresses) to take the snapshot for # SNAPSHOT_ACCOUNTS
[leveldb]
path = "/Users/user/Library/Ethereum/geth/chaindata" # path to geth leveldb
ancient = "/Users/user/Library/Ethereum/geth/chaindata/ancient" # path to geth ancient database
# path to geth leveldb
path = "/Users/user/Library/Ethereum/geth/chaindata" # LVL_DB_PATH
# path to geth ancient database
ancient = "/Users/user/Library/Ethereum/geth/chaindata/ancient" # ANCIENT_DB_PATH
[database]
name = "vulcanize_public" # postgres database name
hostname = "localhost" # postgres host
port = 5432 # postgres port
user = "postgres" # postgres user
password = "" # postgres password
# when operating in 'postgres' output mode
# db credentials
name = "vulcanize_public" # DATABASE_NAME
hostname = "localhost" # DATABASE_HOSTNAME
port = 5432 # DATABASE_PORT
user = "postgres" # DATABASE_USER
password = "" # DATABASE_PASSWORD
[file]
outputDir = "output_dir/" # when operating in 'file' output mode, this is the directory the files are written to
# when operating in 'file' output mode
# directory the CSV files are written to
outputDir = "output_dir/" # FILE_OUTPUT_DIR
[log]
level = "info" # log level (trace, debug, info, warn, error, fatal, panic) (default: info)
file = "log_file" # file path for logging, leave unset to log to stdout
[prom]
# prometheus metrics
metrics = true # enable prometheus metrics (default: false)
http = true # enable prometheus http service (default: false)
httpAddr = "0.0.0.0" # prometheus http host (default: 127.0.0.1)
httpPort = 9101 # prometheus http port (default: 8086)
dbStats = true # enable prometheus db stats (default: false)
[ethereum]
# node info
clientName = "Geth" # ETH_CLIENT_NAME
nodeID = "arch1" # ETH_NODE_ID
networkID = "1" # ETH_NETWORK_ID
chainID = "1" # ETH_CHAIN_ID
genesisBlock = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" # ETH_GENESIS_BLOCK
```
## Usage
* For state snapshot from LevelDB:
```bash
./ipld-eth-state-snapshot stateSnapshot --config={path to toml config file}
```
* Account selective snapshot: To restrict the snapshot to a list of accounts (addresses), provide the addresses in config parameter `snapshot.accounts` or env variable `SNAPSHOT_ACCOUNTS`. Only nodes related to provided addresses will be indexed.
Example:
```toml
[snapshot]
accounts = [
"0x825a6eec09e44Cb0fa19b84353ad0f7858d7F61a"
]
```
## Monitoring
* Enable metrics using config parameters `prom.metrics` and `prom.http`.
* `ipld-eth-state-snapshot` exposes following prometheus metrics at `/metrics` endpoint:
* `state_node_count`: Number of state nodes processed.
* `storage_node_count`: Number of storage nodes processed.
* `code_node_count`: Number of code nodes processed.
* DB stats if operating in `postgres` mode.
## Tests
* Run unit tests:
```bash
# setup db
docker-compose up -d
# run tests after db migrations are run
make dbtest
# tear down db
docker-compose down -v --remove-orphans
```
## Import output data in file mode into a database
* When `ipld-eth-state-snapshot stateSnapshot` is run in file mode (`database.type`), the output is in form of CSV files.
* Assuming the output files are located in host's `./output_dir` directory.
* Data post-processing:
* Create a directory to store post-processed output:
```bash
mkdir -p output_dir/processed_output
```
* Combine output from multiple workers and copy to post-processed output directory:
```bash
# public.blocks
cat {output_dir,output_dir/*}/public.blocks.csv > output_dir/processed_output/combined-public.blocks.csv
# eth.state_cids
cat output_dir/*/eth.state_cids.csv > output_dir/processed_output/combined-eth.state_cids.csv
# eth.storage_cids
cat output_dir/*/eth.storage_cids.csv > output_dir/processed_output/combined-eth.storage_cids.csv
# public.nodes
cp output_dir/public.nodes.csv output_dir/processed_output/public.nodes.csv
# eth.header_cids
cp output_dir/eth.header_cids.csv output_dir/processed_output/eth.header_cids.csv
```
* De-duplicate data:
```bash
# public.blocks
sort -u output_dir/processed_output/combined-public.blocks.csv -o output_dir/processed_output/deduped-combined-public.blocks.csv
# eth.header_cids
sort -u output_dir/processed_output/eth.header_cids.csv -o output_dir/processed_output/deduped-eth.header_cids.csv
# eth.state_cids
sort -u output_dir/processed_output/combined-eth.state_cids.csv -o output_dir/processed_output/deduped-combined-eth.state_cids.csv
# eth.storage_cids
sort -u output_dir/processed_output/combined-eth.storage_cids.csv -o output_dir/processed_output/deduped-combined-eth.storage_cids.csv
```
* Copy over the post-processed output files to the DB server (say in `/output_dir`).
* Start `psql` to run the import commands:
```bash
psql -U <DATABASE_USER> -h <DATABASE_HOSTNAME> -p <DATABASE_PORT> <DATABASE_NAME>
```
* Run the following to import data:
```bash
# public.nodes
COPY public.nodes FROM '/output_dir/processed_output/public.nodes.csv' CSV;
# public.blocks
COPY public.blocks FROM '/output_dir/processed_output/deduped-combined-public.blocks.csv' CSV;
# eth.header_cids
COPY eth.header_cids FROM '/output_dir/processed_output/deduped-eth.header_cids.csv' CSV;
# eth.state_cids
COPY eth.state_cids FROM '/output_dir/processed_output/deduped-combined-eth.state_cids.csv' CSV FORCE NOT NULL state_leaf_key;
# eth.storage_cids
COPY eth.storage_cids FROM '/output_dir/processed_output/deduped-combined-eth.storage_cids.csv' CSV FORCE NOT NULL storage_leaf_key;
```
* NOTE: `COPY` command on CSVs inserts empty strings as `NULL` in the DB. Passing `FORCE_NOT_NULL <COLUMN_NAME>` forces it to insert empty strings instead. This is required to maintain compatibility of the imported snapshot data with the data generated by statediffing. Reference: https://www.postgresql.org/docs/14/sql-copy.html
### Troubleshooting
* Run the following command to find any rows (in data dumps in `file` mode) having unexpected number of columns:
```bash
./scripts/find-bad-rows.sh -i <input-file> -c <expected-columns> -o [output-file] -d true
```
* Run the following command to select rows (from data dumps in `file` mode) other than the ones having unexpected number of columns:
```bash
./scripts/filter-bad-rows.sh -i <input-file> -c <expected-columns> -o <output-file>
```
* See [scripts](./scripts) for more details.
+47 -16
View File
@@ -24,6 +24,9 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cerc-io/ipld-eth-state-snapshot/pkg/prom"
"github.com/cerc-io/ipld-eth-state-snapshot/pkg/snapshot"
)
var (
@@ -46,7 +49,7 @@ func Execute() {
}
func initFuncs(cmd *cobra.Command, args []string) {
logfile := viper.GetString("logfile")
logfile := viper.GetString(snapshot.LOGRUS_FILE_TOML)
if logfile != "" {
file, err := os.OpenFile(logfile,
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
@@ -63,10 +66,25 @@ func initFuncs(cmd *cobra.Command, args []string) {
if err := logLevel(); err != nil {
log.Fatal("Could not set log level: ", err)
}
if viper.GetBool(snapshot.PROM_METRICS_TOML) {
log.Info("initializing prometheus metrics")
prom.Init()
}
if viper.GetBool(snapshot.PROM_HTTP_TOML) {
addr := fmt.Sprintf(
"%s:%s",
viper.GetString(snapshot.PROM_HTTP_ADDR_TOML),
viper.GetString(snapshot.PROM_HTTP_PORT_TOML),
)
log.Info("starting prometheus server")
prom.Serve(addr)
}
}
func logLevel() error {
lvl, err := log.ParseLevel(viper.GetString("log.level"))
lvl, err := log.ParseLevel(viper.GetString(snapshot.LOGRUS_LEVEL_TOML))
if err != nil {
return err
}
@@ -75,6 +93,7 @@ func logLevel() error {
log.SetReportCaller(true)
}
log.Info("Log level set to ", lvl.String())
return nil
}
@@ -84,21 +103,33 @@ func init() {
viper.AutomaticEnv()
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file location")
rootCmd.PersistentFlags().String("logfile", "", "file path for logging")
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().String("log-level", log.InfoLevel.String(), "Log level (trace, debug, info, warn, error, fatal, panic")
rootCmd.PersistentFlags().String(snapshot.LOGRUS_FILE_CLI, "", "file path for logging")
rootCmd.PersistentFlags().String(snapshot.DATABASE_NAME_CLI, "vulcanize_public", "database name")
rootCmd.PersistentFlags().Int(snapshot.DATABASE_PORT_CLI, 5432, "database port")
rootCmd.PersistentFlags().String(snapshot.DATABASE_HOSTNAME_CLI, "localhost", "database hostname")
rootCmd.PersistentFlags().String(snapshot.DATABASE_USER_CLI, "", "database user")
rootCmd.PersistentFlags().String(snapshot.DATABASE_PASSWORD_CLI, "", "database password")
rootCmd.PersistentFlags().String(snapshot.LOGRUS_LEVEL_CLI, log.InfoLevel.String(), "log level (trace, debug, info, warn, error, fatal, panic)")
viper.BindPFlag("logfile", rootCmd.PersistentFlags().Lookup("logfile"))
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("log.level", rootCmd.PersistentFlags().Lookup("log-level"))
rootCmd.PersistentFlags().Bool(snapshot.PROM_METRICS_CLI, false, "enable prometheus metrics")
rootCmd.PersistentFlags().Bool(snapshot.PROM_HTTP_CLI, false, "enable prometheus http service")
rootCmd.PersistentFlags().String(snapshot.PROM_HTTP_ADDR_CLI, "127.0.0.1", "prometheus http host")
rootCmd.PersistentFlags().String(snapshot.PROM_HTTP_PORT_CLI, "8086", "prometheus http port")
rootCmd.PersistentFlags().Bool(snapshot.PROM_DB_STATS_CLI, false, "enables prometheus db stats")
viper.BindPFlag(snapshot.LOGRUS_FILE_TOML, rootCmd.PersistentFlags().Lookup(snapshot.LOGRUS_FILE_CLI))
viper.BindPFlag(snapshot.DATABASE_NAME_TOML, rootCmd.PersistentFlags().Lookup(snapshot.DATABASE_NAME_CLI))
viper.BindPFlag(snapshot.DATABASE_PORT_TOML, rootCmd.PersistentFlags().Lookup(snapshot.DATABASE_PORT_CLI))
viper.BindPFlag(snapshot.DATABASE_HOSTNAME_TOML, rootCmd.PersistentFlags().Lookup(snapshot.DATABASE_HOSTNAME_CLI))
viper.BindPFlag(snapshot.DATABASE_USER_TOML, rootCmd.PersistentFlags().Lookup(snapshot.DATABASE_USER_CLI))
viper.BindPFlag(snapshot.DATABASE_PASSWORD_TOML, rootCmd.PersistentFlags().Lookup(snapshot.DATABASE_PASSWORD_CLI))
viper.BindPFlag(snapshot.LOGRUS_LEVEL_TOML, rootCmd.PersistentFlags().Lookup(snapshot.LOGRUS_LEVEL_CLI))
viper.BindPFlag(snapshot.PROM_METRICS_TOML, rootCmd.PersistentFlags().Lookup(snapshot.PROM_METRICS_CLI))
viper.BindPFlag(snapshot.PROM_HTTP_TOML, rootCmd.PersistentFlags().Lookup(snapshot.PROM_HTTP_CLI))
viper.BindPFlag(snapshot.PROM_HTTP_ADDR_TOML, rootCmd.PersistentFlags().Lookup(snapshot.PROM_HTTP_ADDR_CLI))
viper.BindPFlag(snapshot.PROM_HTTP_PORT_TOML, rootCmd.PersistentFlags().Lookup(snapshot.PROM_HTTP_PORT_CLI))
viper.BindPFlag(snapshot.PROM_DB_STATS_TOML, rootCmd.PersistentFlags().Lookup(snapshot.PROM_DB_STATS_CLI))
}
func initConfig() {
+25 -24
View File
@@ -22,7 +22,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/vulcanize/ipld-eth-state-snapshot/pkg/snapshot"
"github.com/cerc-io/ipld-eth-state-snapshot/pkg/snapshot"
)
// stateSnapshotCmd represents the stateSnapshot command
@@ -40,11 +40,11 @@ var stateSnapshotCmd = &cobra.Command{
}
func stateSnapshot() {
modeStr := viper.GetString("snapshot.mode")
modeStr := viper.GetString(snapshot.SNAPSHOT_MODE_TOML)
mode := snapshot.SnapshotMode(modeStr)
config, err := snapshot.NewConfig(mode)
if err != nil {
logWithCommand.Fatal("unable to initialize config: %v", err)
logWithCommand.Fatalf("unable to initialize config: %v", err)
}
logWithCommand.Infof("opening levelDB and ancient data at %s and %s",
config.Eth.LevelDBPath, config.Eth.AncientDBPath)
@@ -52,11 +52,11 @@ func stateSnapshot() {
if err != nil {
logWithCommand.Fatal(err)
}
height := viper.GetInt64("snapshot.blockHeight")
recoveryFile := viper.GetString("snapshot.recoveryFile")
height := viper.GetInt64(snapshot.SNAPSHOT_BLOCK_HEIGHT_TOML)
recoveryFile := viper.GetString(snapshot.SNAPSHOT_RECOVERY_FILE_TOML)
if recoveryFile == "" {
recoveryFile = fmt.Sprintf("./%d_snapshot_recovery", height)
logWithCommand.Infof("no recovery file set, creating default: %s", recoveryFile)
logWithCommand.Infof("no recovery file set, using default: %s", recoveryFile)
}
pub, err := snapshot.NewPublisher(mode, config)
@@ -68,14 +68,13 @@ func stateSnapshot() {
if err != nil {
logWithCommand.Fatal(err)
}
workers := viper.GetUint("snapshot.workers")
workers := viper.GetUint(snapshot.SNAPSHOT_WORKERS_TOML)
if height < 0 {
if err := snapshotService.CreateLatestSnapshot(workers); err != nil {
if err := snapshotService.CreateLatestSnapshot(workers, config.Service.AllowedAccounts); err != nil {
logWithCommand.Fatal(err)
}
} else {
params := snapshot.SnapshotParams{Workers: workers, Height: uint64(height)}
params := snapshot.SnapshotParams{Workers: workers, Height: uint64(height), WatchedAddresses: config.Service.AllowedAccounts}
if err := snapshotService.CreateSnapshot(params); err != nil {
logWithCommand.Fatal(err)
}
@@ -86,19 +85,21 @@ func stateSnapshot() {
func init() {
rootCmd.AddCommand(stateSnapshotCmd)
stateSnapshotCmd.PersistentFlags().String("leveldb-path", "", "path to primary datastore")
stateSnapshotCmd.PersistentFlags().String("ancient-path", "", "path to ancient datastore")
stateSnapshotCmd.PersistentFlags().String("block-height", "", "blockheight to extract state at")
stateSnapshotCmd.PersistentFlags().Int("workers", 1, "number of concurrent workers to use")
stateSnapshotCmd.PersistentFlags().String("recovery-file", "", "file to recover from a previous iteration")
stateSnapshotCmd.PersistentFlags().String("snapshot-mode", "postgres", "output mode for snapshot ('file' or 'postgres')")
stateSnapshotCmd.PersistentFlags().String("output-dir", "", "directory for writing ouput to while operating in 'file' mode")
stateSnapshotCmd.PersistentFlags().String(snapshot.LVL_DB_PATH_CLI, "", "path to primary datastore")
stateSnapshotCmd.PersistentFlags().String(snapshot.ANCIENT_DB_PATH_CLI, "", "path to ancient datastore")
stateSnapshotCmd.PersistentFlags().String(snapshot.SNAPSHOT_BLOCK_HEIGHT_CLI, "", "block height to extract state at")
stateSnapshotCmd.PersistentFlags().Int(snapshot.SNAPSHOT_WORKERS_CLI, 1, "number of concurrent workers to use")
stateSnapshotCmd.PersistentFlags().String(snapshot.SNAPSHOT_RECOVERY_FILE_CLI, "", "file to recover from a previous iteration")
stateSnapshotCmd.PersistentFlags().String(snapshot.SNAPSHOT_MODE_CLI, "postgres", "output mode for snapshot ('file' or 'postgres')")
stateSnapshotCmd.PersistentFlags().String(snapshot.FILE_OUTPUT_DIR_CLI, "", "directory for writing ouput to while operating in 'file' mode")
stateSnapshotCmd.PersistentFlags().StringArray(snapshot.SNAPSHOT_ACCOUNTS_CLI, nil, "list of account addresses to limit snapshot to")
viper.BindPFlag("leveldb.path", stateSnapshotCmd.PersistentFlags().Lookup("leveldb-path"))
viper.BindPFlag("leveldb.ancient", stateSnapshotCmd.PersistentFlags().Lookup("ancient-path"))
viper.BindPFlag("snapshot.blockHeight", stateSnapshotCmd.PersistentFlags().Lookup("block-height"))
viper.BindPFlag("snapshot.workers", stateSnapshotCmd.PersistentFlags().Lookup("workers"))
viper.BindPFlag("snapshot.recoveryFile", stateSnapshotCmd.PersistentFlags().Lookup("recovery-file"))
viper.BindPFlag("snapshot.mode", stateSnapshotCmd.PersistentFlags().Lookup("snapshot-mode"))
viper.BindPFlag("file.outputDir", stateSnapshotCmd.PersistentFlags().Lookup("output-dir"))
viper.BindPFlag(snapshot.LVL_DB_PATH_TOML, stateSnapshotCmd.PersistentFlags().Lookup(snapshot.LVL_DB_PATH_CLI))
viper.BindPFlag(snapshot.ANCIENT_DB_PATH_TOML, stateSnapshotCmd.PersistentFlags().Lookup(snapshot.ANCIENT_DB_PATH_CLI))
viper.BindPFlag(snapshot.SNAPSHOT_BLOCK_HEIGHT_TOML, stateSnapshotCmd.PersistentFlags().Lookup(snapshot.SNAPSHOT_BLOCK_HEIGHT_CLI))
viper.BindPFlag(snapshot.SNAPSHOT_WORKERS_TOML, stateSnapshotCmd.PersistentFlags().Lookup(snapshot.SNAPSHOT_WORKERS_CLI))
viper.BindPFlag(snapshot.SNAPSHOT_RECOVERY_FILE_TOML, stateSnapshotCmd.PersistentFlags().Lookup(snapshot.SNAPSHOT_RECOVERY_FILE_CLI))
viper.BindPFlag(snapshot.SNAPSHOT_MODE_TOML, stateSnapshotCmd.PersistentFlags().Lookup(snapshot.SNAPSHOT_MODE_CLI))
viper.BindPFlag(snapshot.FILE_OUTPUT_DIR_TOML, stateSnapshotCmd.PersistentFlags().Lookup(snapshot.FILE_OUTPUT_DIR_CLI))
viper.BindPFlag(snapshot.SNAPSHOT_ACCOUNTS_TOML, stateSnapshotCmd.PersistentFlags().Lookup(snapshot.SNAPSHOT_ACCOUNTS_CLI))
}
+27
View File
@@ -0,0 +1,27 @@
version: '3.2'
services:
migrations:
restart: on-failure
depends_on:
- ipld-eth-db
image: git.vdb.to/cerc-io/ipld-eth-db/ipld-eth-db:v5.0.2-alpha
environment:
DATABASE_USER: "vdbm"
DATABASE_NAME: "cerc_testing"
DATABASE_PASSWORD: "password"
DATABASE_HOSTNAME: "ipld-eth-db"
DATABASE_PORT: 5432
ipld-eth-db:
image: timescale/timescaledb:latest-pg14
restart: always
command: ["postgres", "-c", "log_statement=all"]
environment:
POSTGRES_USER: "vdbm"
POSTGRES_DB: "cerc_testing"
POSTGRES_PASSWORD: "password"
ports:
- "127.0.0.1:8077:5432"
volumes:
- /tmp:/tmp
+28 -6
View File
@@ -1,18 +1,40 @@
[database]
name = "vulcanize_public"
name = "cerc_testing"
hostname = "localhost"
port = 5432
user = "postgres"
port = 8077
user = "vdbm"
password = "password"
[leveldb]
path = "/Users/user/Library/Ethereum/geth/chaindata"
ancient = "/Users/user/Library/Ethereum/geth/chaindata/ancient"
path = "/Users/user/go/src/github.com/cerc-io/ipld-eth-state-snapshot/fixture/chain2data"
ancient = "/Users/user/go/src/github.com/cerc-io/ipld-eth-state-snapshot/fixture/chain2data/ancient"
[log]
level = "info"
file = "log_file"
[prom]
metrics = true
http = true
httpAddr = "0.0.0.0"
httpPort = 9101
dbStats = true
[snapshot]
mode = "file"
workers = 4
blockHeight = -1
blockHeight = 32
recoveryFile = "recovery_file"
startHeight = 1
endHeight = 12
[file]
outputDir = "output_dir/"
[ethereum]
clientName = "Geth" # $ETH_CLIENT_NAME
nodeID = "arch1" # $ETH_NODE_ID
networkID = "1" # $ETH_NETWORK_ID
chainID = "1" # $ETH_CHAIN_ID
genesisBlock = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" # $ETH_GENESIS_BLOCK
+7
View File
@@ -0,0 +1,7 @@
*/*.log
*/CURRENT*
*/LOCK
*/LOG
*/MANIFEST-*
*/ancient/FLOCK
*/ancient/*.meta
Binary file not shown.
Binary file not shown.
View File
+1
View File
@@ -0,0 +1 @@
MANIFEST-000005
View File
+15
View File
@@ -0,0 +1,15 @@
=============== Mar 31, 2023 (CDT) ===============
12:22:01.281499 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
12:22:01.281586 table@recovery F·2
12:22:01.281592 table@recovery recovering @2
12:22:01.281901 table@recovery recovered @2 Gk·13 Ck·0 Cb·0 S·918 Q·13
12:22:01.281909 table@recovery recovering @4
12:22:01.282725 table@recovery recovered @4 Gk·416 Ck·0 Cb·0 S·35872 Q·430
12:22:01.282737 table@recovery recovered F·2 N·429 Gk·429 Ck·0 Q·430
12:22:01.298688 version@stat F·[2] S·35KiB[35KiB] Sc·[0.50]
12:22:01.308338 db@open opening
12:22:01.308738 version@stat F·[2] S·35KiB[35KiB] Sc·[0.50]
12:22:01.314746 db@janitor F·4 G·0
12:22:01.314778 db@open done T·6.427167ms
12:22:01.317510 db@close closing
12:22:01.317545 db@close done T·34.833µs
Binary file not shown.
View File
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
��
+1
View File
@@ -0,0 +1 @@
��
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
��
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
��
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
��
+13 -13
View File
@@ -3,25 +3,25 @@ package fixture
import (
"os"
"path/filepath"
"runtime"
)
// TODO: embed some mainnet data
// import "embed"
//_go:embed mainnet_data.tar.gz
var (
ChaindataPath, AncientdataPath string
)
// GetChainDataPath returns the absolute paths to chain data in 'fixture/' given the chain (chain | chain2)
func GetChainDataPath(chain string) (string, string) {
path := filepath.Join("..", "..", "fixture", chain)
func init() {
_, path, _, _ := runtime.Caller(0)
wd := filepath.Dir(path)
ChaindataPath = filepath.Join(wd, "..", "fixture", "chaindata")
AncientdataPath = filepath.Join(ChaindataPath, "ancient")
if _, err := os.Stat(ChaindataPath); err != nil {
panic("must populate chaindata at " + ChaindataPath)
chaindataPath, err := filepath.Abs(path)
if err != nil {
panic("cannot resolve path " + path)
}
ancientdataPath := filepath.Join(chaindataPath, "ancient")
if _, err := os.Stat(chaindataPath); err != nil {
panic("must populate chaindata at " + chaindataPath)
}
return chaindataPath, ancientdataPath
}
-6
View File
@@ -1,6 +0,0 @@
*.log
CURRENT*
LOCK
LOG
MANIFEST-*
ancient/FLOCK
View File
+1
View File
@@ -0,0 +1 @@
MANIFEST-000003
View File
+13
View File
@@ -0,0 +1,13 @@
=============== Mar 31, 2023 (CDT) ===============
12:22:01.090151 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
12:22:01.090565 table@recovery F·1
12:22:01.090576 table@recovery recovering @2
12:22:01.106640 table@recovery recovered @2 Gk·146827 Ck·0 Cb·0 S·5765300 Q·146827
12:22:01.106661 table@recovery recovered F·1 N·146827 Gk·146827 Ck·0 Q·146827
12:22:01.133566 version@stat F·[1] S·5MiB[5MiB] Sc·[0.25]
12:22:01.145740 db@open opening
12:22:01.146636 version@stat F·[1] S·5MiB[5MiB] Sc·[0.25]
12:22:01.151049 db@janitor F·3 G·0
12:22:01.151090 db@open done T·5.315792ms
12:22:01.161329 db@close closing
12:22:01.161389 db@close done T·58.667µs
Binary file not shown.
View File
+1
View File
@@ -0,0 +1 @@
��
+1
View File
@@ -0,0 +1 @@
��
+1
View File
@@ -0,0 +1 @@
��
+1
View File
@@ -0,0 +1 @@
��
+1
View File
@@ -0,0 +1 @@
��
+264
View File
@@ -0,0 +1,264 @@
package fixture
var Block1_StateNodeLeafKeys = []string{
"0x291b6bbca879d684337ee5e2bca5d18cfb2d3b7a97c8187e703309564280295e",
"0x021fe3360ba8c02e194f8e7facdeb9088b3cf433b6498bd6900d50df0266ffe3",
"0x7820b41394d7f30f2b02f24f8a33afe8b98cd68827ec651aec3ba33e41206fe2",
"0xf908d785cef1abc9fcea55c07490d16b4825e8a6a7c8fb157ed2900bf4af58b9",
"0xd207b05b684ba18361df8e81e846d10365ffcf04f7840403c5bacf77add4c50f",
"0x5037b71f063bfe647233f570aa06602b1e6b5124db0b1fe998975f93b0fb1084",
"0xb02c4d58a3800d93f95f2eb4850b3d242756bf1ca3099bc43907af6dff0bf337",
"0x300d2dbe83a6ba7fd75737c8d7453d984e7938ba7ae113d3da2ad7433061157b",
"0x80382280e2f4a8ea9001c8e22332a3dc3d3a692b549d3c90f65796270e1eb770",
"0x087fb108e6836a088e06156b1a26210d1f5284296b30b0bb0b3d1c2a7e01ac11",
"0x71a3c7f8d0fe8abc9fbab1860ad576eddff2144763937195ea4f012fc625b053",
"0xb8864cb7c6215e99ffe5db7b22b3129e93b9a38b9b54193a0e3d7c8251657173",
"0x881a506039e25a818f35aa0e3ec58c64df5c106245dc25ef6fa4cf1eb1aab56a",
"0x40637a529320dace54763c2e6ac52c522c8a446ba9b6f54daf84c0e442124b93",
"0xa092c3f36135f79c57aa5683d97e5d5750c37d0bd0ada215e9a96856c56d7e91",
"0x684272547bac4c083b7380f054d08a5030862a5f06a2367f3b8e880c4b7370ed",
"0x18dcd435bf7d1820085f6c46d587cae669ca7c2d3ad4cea9db320a0b3c8bd21d",
"0xc00c28a6bfe2829613432a2c21eefc25da5bad983c5430ca28bc1a92d2b65ec5",
"0xf03db4f61547c9d8c58b4f37f84f74eff767c24cf81b957662e96622f2ac4d19",
"0x38401b4e97e67c4a6d8095979b4c1ecb9eaf5726fc8a12798975a7653e980872",
"0x992470b3e52c94f7bd5d4962a1798a41425bea74ad087a7f53aebb785401df3d",
"0xe015d2e4c085291611298460602b913b5f19272715fcaa48258efe24cf21885e",
"0x2092b5602121cc484fa55b90cea0be17d931184263925a864e0d5d43a20d67b0",
"0xe83c2c56f351d8c25863a55ece938ffcb6cdf2978eb34ea1a19591884d3885f3",
"0x489fe686547f4a999d2818aedbc18b735ed2cd502c30124a0b8af861b2251f91",
"0x90b0d289ea211dca8e020c9cc8c5d6ba2f416fe15fa692b47184a4b946b2214d",
"0x6042501054831920bc9d50a33d6be1b5a8214c31ba9416958f8996cdb6ddb05c",
"0x125adc67efe8bf6808c02bd3ae20262953b5e45dab4eb9386441cf6ec7381844",
"0xd81c5d559ba870e90a2b617cf0542695e9a626619b59148b40295a0ba55ca66e",
"0x5839262442bd87bdce9bcd8e97628a84fd07aaf55ba8c4c29e1ae91cb17cb222",
"0xa876da518a393dbd067dc72abfa08d475ed6447fca96d92ec3f9e7eba503ca61",
"0xc93e183a9dd09cdd44ca2a9eb6b94f970f8fa309b0f9cfd823572c23aad58fb2",
"0x75422a3d36c93fcbccad2e502775226f375e264e35fc55ac20df1e9f050e8b8c",
"0x20ebfef43639215aeb35e1e00bc96174f52155afb781659f34628d1f3f7ae018",
"0x20ebfef43639215aeb35e1e00bc96174f52155afb781659f34628d1f3f7ae018",
"0x005e54f1867fd030f90673b8b625ac8f0656e44a88cfc0b3af3e3f3c3d486960",
"0x1a6d9674aec5c8329252cb634022308bf4c98e70edec613d925ab781483445a4",
"0x5095b76023974440e6c155e26b3f7084f0b1901a0df2183dcfebe01f326d5e96",
"0x58bb8d7ba2f52228853238b26bfe3eb9607cea944e7020458981618efe8280a1",
"0xf92f9911e6d5b40e46f574db0a406d360f91c8de842ded48c8c699080985b4cf",
"0x785a32bf0f42bd6c986b900070bd70ff4ed5eaad448e57cf5d997224c3f87c2c",
"0x9546dea22f2479752816b65aa9db1b835efefbc1ed11804699683897cda4227e",
"0x39c8bb92227ba91a2657f29c5498f5c5b07ae36369c8d9283444f477930c79be",
"0x75422a3d36c93fcbccad2e502775226f375e264e35fc55ac20df1e9f050e8b8c",
"0x9ee40dcbc49746e31a754155515aaa232bf0bdbfd5a7ab77ea9cd39f4b7282a1",
"0xe8c07bab8822eeeb875236e148f781341157f9bfc56c1c53972489ff4009695b",
"0xb0fe18e510f695dbf24ad56fb3b647aa09f8e19021abe80686a66a2e653cb12d",
"0xf5b693085beb21b0878c7e5fab7519ce0d6105354cf6744dbfb73e3745e929e4",
"0xa0a22226f85371b5e5e4173b33a1b9629d745961f36e9b0510c591f11e3dd95b",
"0x08b3f9a96baba12f47430f2b0e2851f407e8ca355bb8a9a69242bc99aecc2cf3",
"0xc01318ac6e0d2cc08468abc1df6f2c9ec3fc5cfbfe5efec83aab250cfc83ef3f",
"0x8938be2ad9950390d6d92213ca102fc21a4304c88354d0e74f4b3ff696ce5791",
"0x623f300bf600b7b050b9b70fa57140c59a5f0b9fc5b9d0bdab11d108a71fc1a3",
"0xb8f37a61c26d388306ad9dee25e72aa86a49903a1fbbe6205211b47d1f39f77f",
"0x127ec70ea0d8a7b6244ae7d951b2204f75336cf21f49df6370ab920534c95728",
"0x68492f20c1d7597425c8a53a7f5e380fbce058d1bfea2521a7e8af03c387e1a0",
"0xf93d447fcf86f847e6609bfb3100aa5d07df37f2c72cbc4961e3a0f86a7c24b7",
"0x3a2c7d5164f6a1d8cbc5d9e228253eae4a66d8b0d550d07c53ec84d47ed17172",
"0xf93d447fcf86f847e6609bfb3100aa5d07df37f2c72cbc4961e3a0f86a7c24b7",
"0x4b6029773e7175f32398422fd18c6cb095e5278ac7be41ca4a5a6c0a6dda1a71",
"0xd222125724b9df1f4a92e1284dbc871a9156c253572df0a38ae146ac03b62af4",
"0x5a4be2322eb50cfea1051978c284526cd5a6e28caca681855736a5d3c31d9bad",
"0x89d04bcba396d12de1ed36f41df60ce0ae0449bae83f14c740451cf19362ae8f",
"0xd8ffcbc0be38306e539da616161c64ef7bc0f0281d62e5296bc8eb6ea709cb0b",
"0x96ad0b27ef10fa7a9940299df122d31222cc911bd2c91d2740f25a0dc931f4c0",
"0x40f85cb8136e95238cea4cb951a2ee645a4998c108831d5abe1d8f48696864c3",
"0xcb44297958c82fe80fb882f2c8a085a38556247b3c3f665eca382d27e26e3bbc",
"0x6298deb7c736dc50b4590ca066b72173d4e2c3f75bb109396f32eaaf9ad11bc8",
"0x3019c0a91ba30d346a55890b1b07287d8aae35baa8c4068ef8f1de66084aca75",
"0xb9af72fc0534a4a5a94eb1f64cb1bd6c425acba3249fb8441e7411e9be9ec1f3",
"0x80be858ab09789d26cf1128775756a8abe1c8ffc4218d3e990b3d25a82583255",
"0x0999f18de77e9451867cb1935f9acd56f79cbc11d04826c3da6b420a4f4bbe21",
"0xb14d9fdc657d712208d6e7c2cfbf62f7ad7ba1bfff2e63e6caee2b0bf5bad0fd",
"0xa93db8611e8d0c159eb724d2aea72763dfb5a6909a8516467c79d0386d61a511",
"0xc01b3857946cb77af9fb09394c4a359fba6df202c77d3e07e53177f3f18a55f2",
"0x79deb2f042f1717cc882c5a38bbb6c524932352ff25baeb8b8fd2225095e29ae",
"0x50ff4d47a46c9cd35b029e95ef9c957e57da987491f6e7be242e72bc5d3e8086",
"0xe8c6b410e2984be3bed9a84b76fbbd647f5fa56e65e36c264903275aa22cdb0d",
"0xe1e23690e890cdec6eb19b06f5d31d422b5c17bd4c435afb2a2532180011453f",
"0x9f0dbaecc6581cfbb57cd6c6f50e24c23ad4fcdf228140c0cb51a8a4edf5d0b0",
"0xf67f470df15c88b9beaf3fad72890a64f8851d01ece546b0c9fb3bdf92041a35",
"0x1d50131be868de2741cb775d0ff800631f96065f40b60845bce72e782718af23",
"0xa0f1ddc6e3aad69109e0e8c5d15e8800e220b192f85f369e0bef5cf858dccf9a",
"0x689802d6ed1a28b049e9d4fe5334c5902fd9bc00c42821c82f82ee2da10be908",
"0x1318a4c2bc731718857238ec2c42e4a95c0aeb7290a60c8abb110c2e638341c2",
"0x21120e1be03d384ee32a9bd7268578d8829f271540c848c97a495990bc48f582",
"0x297d3acffb942c7e6f3fb029b735338ac34e39c4ce9e852f64d50fb57cc97ec0",
"0x7744be2427cacf17062cb030d30e34c3b543b28a83707df04e64d3f7ddb471b8",
"0x028e62cb4665fce19ae1fc13a604618d7d20be037fc68b63beb3384dfa5ab776",
"0xcce46204baa86f733abf208b358ba8b807081d6d46a407745238aef2c133fd4d",
"0xaa546817dabffc9c4249830a0af5cf43c6ece6562720990736199e570f621d7c",
"0xfb1d6797796ad9edd1b8b41030035a003eb6286ae2fc1a8296b509891acfa033",
"0xb25939867994cd8e5a18f341628420fa8ab88b2f5d05ffe51291654922f5454f",
"0x2b7afbb14b4c902f37163b15f70fe8335dec4e26fbae4718c03f19da5aecbe8b",
"0x1e22733f173fbc95fa079ed50ec24c498a8aad2515d827e5ff8200c240e9cdc9",
"0x4b945ef2e8c78482bbffcbdf4921cd375065e3b5024a3bed469a9f34b2e773e7",
"0x13b8f5750961b5588bc69fbd0b701461854e110fd78c14767515bdd882725975",
"0x41351b958be1e95cdc37f7d2d654315e33ff7f45bac9179f9242fdc828c8d2e3",
"0x9f4e60123db23e3f3055f75087248fd5923d469e76d344d28b4f24a449ca6398",
"0x7d1009331195f6a3c2eee6ac4b4664f61c777500c163ee84769913578a33bdf2",
"0xbb62e8d8d0eb3d7432758c244131f1649874367cc52a4fa4fd0609def9e57389",
"0xe22d496c425479acb445c4040bdb5e17fa461de175eaaa77aa79e23b5b19f761",
"0xd24e8fff20c5317074c54fd54ca9f1fc8fef36bb70c44b55f90a70c621b91f9a",
"0xd90bcdecb44a3402e2a1b5d3a321b51e415e298ebaca3c95d91443adc03f6cec",
"0x826ce1d60ac9f0ab5944acf1c06700a585f8407d670495c7811b97cf8263b434",
"0x2139fa1aaa3d60e53d070d5a1f1ac9c8211f41b2dd5a1813416c8447a7759557",
"0x96e16af9aa5f894c43514d940687dafaa5ad04ea78a3b0080319370e454602d2",
"0x6400fc32c5b2d3580c0e19889487cf9e057d6b82a99bf30eb6e539b7cde52655",
"0x02cb51767354e1fe6bd4a49b64b3721ffddbc95fed1b8ead005c39bbc07bc4d8",
"0x3a82e1e58ae6f14fbbeb35b6d833330c4fb5b3cb770391f5cc340949c6254e83",
"0xa216c0a47b12c20d5ac5c381464a89521d8b0c1a5cb328aeabb29b70bc3347d4",
"0xea674e603f60ba1e252419d86d0485fd3f46ca6abbf4c65afee0a9ba29059655",
"0x8ae586aa1ec2c29aced4feb5adf9336541769b93cd5b8cc1b64b7d831c7b0ec2",
"0x323e393774eeec8e9d596258267d0133a5b2f01c567822a4ff018488671ecb6e",
"0x51ee1e08db014fc699587667b7256e7b9090e1755c587420cff5fbe0e20194fc",
"0x6a1c4e8b7c710531c9e66166d0629ff443df19cefa081eb4fffcc7d267ce7c92",
"0x09af9e7ea2370ab3e3ee7f0a465a7dacb6c9b7e65196dc496d5570a0d8020bda",
"0x5b70e80538acdabd6137353b0f9d8d149f4dba91e8be2e7946e409bfdbe685b9",
"0xc0f4fde474181fca3d1fb6e0dc118a71457fe7cc5d4f1e426c92e1767eb885a0",
"0x4bce4e0299e5362578a95a27bcb779b9a89232296388db803909696f90635581",
"0x1ebfd7dbb6804351804443df01a482e6451912b884cd26b31504bb77bbec9862",
"0x03089e01be9eb2af5ff5fa1c5983c6c6fb78dd734658d1f8f11d4f8d27a23fd5",
"0xb31ba6d576dbde0542d30a9264e2c71be270e3eb4f5910567a8c1c0619a2e5ec",
"0xaa8d9c947771632a645c67655595b61da72837bfa97f30f417dbaed82f2f11c3",
"0x32fc4fd3fc836c6db0058fd20ff7e8ca611885eb7215b39f97d2b4e797dca254",
"0x5c33820c3d76f4de8d1e0eca24c7d2fbef76189b6a43a7afea4dfda5b50edb96",
"0x6aefc729e28e1bbd2e63dd0803314deb808ab044909af882b7935dd05f5939a4",
"0x0c3a3c99f34afe9d2c8cecc90524ef63afc9fc13384d5d931281f414d5d487c0",
"0x9fa95d10732f34365f34f5a2f7702a9dae40c583105c40921a5052dca1f9f793",
"0xeacbb86ec518baa710dea5ed986162f21df2a98d2119f6cacbb01e8d9a77a629",
"0x7d1d5f3a14c0ab133b65698c2d62c818c65f13306ccbfd61aa4b7590bb78660c",
"0xfb6ab511cf5bcb157f833241e74ead7128a21818c2b5080be21ee9f85ff5d523",
"0x21ea8bdf8a4ea6dc9e204591d88c3b5b19533d33bfbc59c08cca266c3c87a960",
"0x9782bb006174ed014c59e6eee381c6e10bc9304226edb99edd6f8e9041e9d9a7",
"0xbc0cb591a67822e88dcebb8991bca5b7305b5858d65bc2b5e6605e481a650588",
"0x8484452e4f0c1a9f115c52b4ac78a0de2f1b0ab1a4aae8390426ac71e03f8779",
"0x2bd9aebde44794e6db317be6d83c5a266e1a96e18e04bc7073998bb779cfe2fe",
"0xd9ed35852650f28d7a8f73c7ba49bcd72d97edf8d731797e5a2728559b865552",
"0x1468288056310c82aa4c01a7e12a10f8111a0560e72b700555479031b86c357d",
"0xfc39bbcf08818ec3329e5769dff3fbdad66e6312a961acb32c1b203edf70aeba",
"0x5d9114e8c0b1e858bf7322eec122e3fc4b0988f146620def0fde9dbd322f3a03",
"0x4e09550da9ca55ba375e56c525b49b27814d20165528e4e7f309878221b2878b",
"0xe2c8bbf962c30afe9c35e9d7e036ef496b52cb770c645a76f2cee5f8811a9830",
"0xbddec651652e0aa17e056de1790554aeca5fa248cecc4131d189dacd26c4ca55",
"0x51f0e21d8c8aeef60da6ae58d10dcf1f42e7d9a0cb8ee2b4a97caa566a152b61",
"0x421df1fa259221d02aa4956eb0d35ace318ca24c0a33a64c1af96cf67cf245b6",
"0xd37476a178ee68b02b3de0edee1ebb57e1638eebf58b3e256322e0da1189f272",
"0xa3b41c13f6e16fd2242a43b041cfd1f02cbb4cf06943d14365bb8f0a30ff507a",
"0x3ac3fe84c663607ebbe01c6b008d02b92ed4feab26ed8171014853c0f9419cc8",
"0xad00cf4d7db523f20b5000bd1469a11d70b73feadd36578abdb9f3558bdd3674",
"0x8c3ab0970b73895b8c9959bae685c3a19f45eb5ad89d42b52a340ec4ac204d19",
"0x2d15e1e82ca50b7334be8990b93a8b043e771340cc9398baa23845763c696d22",
"0x331d99c6bec3aecbec962be74b25448511983b013c0876f6f4e6029773dfad61",
"0x258487f1d48945c7743c412b0f026d722a4641d5978f11a0b44dbf7c1d274d41",
"0x528b55564e8518548e42b534da3a526179b820f264ee7c6929d00b0b6a31cfc2",
"0xc2911b75be7d3fdb9442b900ca8f7205d6d52fdb3505e77ea8676f811a6d5ea1",
"0xda0d7fc05c32bbba16518f9f2936fd75140d5c99b95ae536205ba9caf1868378",
"0x26dbcce1b599ebed46e2569579562031255ea9a1c1c575fdddb0003f4d389165",
"0x8cfefa9806c99db8bea1ed8e534f6dcdb835cd1410f1f287ea4293e29a3eacc0",
"0xbe017710d6bfb0ac7a8d140333127571809eaf2b590900f168545daea3865f60",
"0x42b45de6853d406783b6b50c378bfb61d6ce5271b3bf813cc2b959055fc12f98",
"0x2df378e8ecee785e1d65200d738178432cbc9ad55c49ea5af905629d348416c2",
"0xe36c0f154a365e086c687050cee74bddde9997a1dedeb0e55217368c0ab6baa4",
"0xa62e9dc15a35c1ab9d0ac4345b1f02fd8e8070883a33651c0c143b2582708268",
"0x04242954a5cb9748d3f66bcd4583fd3830287aa585bebd9dd06fa6625976be49",
"0xb41089e529c1ff6c4d6583ecfca7ea6419bf39e0d418686bb7a24424c670b332",
"0xd3a7e59f888a7ac07a211c717c07b82970d94597f6365cf8c93d51ac9d45c597",
"0xaddb0d4b14749bb4b5cd858f29a4b4225138c3953090ce45204458dcd53749e4",
"0x34275c313c5286317794f0154f8e4ffacfc80d9d0c5b184d2482e7f9211ffc3e",
"0xfce7321f278a2973bf5c67757ea7690649295d53967daa3ae858695be336abe2",
"0x1f99af2df2da9c0176bdb3e995bdfa47ffbfe6a3aacf8e54401f4dac2ece9338",
"0x6b2c03103b55a45cc1e4550889a9396e7e331b5cf1729e4871ff4dee701139e9",
"0x640528e5cf143f173d29cd8b4f896a1c6128f3416c887c10d4a11898841b4a82",
"0xe4295c5aa043a60d279854f7424e21250d5c1e1f9959549e90bc926d70e97068",
"0xae49f4417048cacc01716a2d804f699d8ec34ead735e1c61604996ac37f35157",
"0x52a6fb56e13d45fe16456e828c8c375b0decaae2d644b76f8a9b0a741de1ed89",
"0x5f9e4ee172469cfda38a7457ef192b6efb169fedb73943e4c987d4eb910189d4",
"0x43926296aa3c8efab7e2ff9bc644236193fa657726127559ba0668734d1b2790",
"0xd52688a8f926c816ca1e079067caba944f158e764817b83fc43594370ca9cf62",
"0xb53f6bc81ecfaf0cf4c3dcd9527e3c0f6910383acf964ed14b194fa660d30b10",
"0x0d21eb4f7c202eeccd6d87627f3d512e630d4d05578585847168758fd1aaed22",
"0x1ffff1a455b66d52107a26231feefb0f3565067e9544cf9470d975f01111b1cf",
"0xdc8829261b6bfd53ab7daac305c2ad0ed15423234128bdc5b86a2e9eaf6157e4",
"0x6bcfd58769c6966e31aabe28addce6151f2dd2ea1d9c307e09c6c9db95b6b103",
"0x7d766503b9158ee06b0a587859f6e24974c78c3a64b6e4d8aefbcb8ad3bc2127",
"0xeb885fa37ef7e686a7f8dd3d9004de555526310e78dcbf857c9b0a31e40b9736",
"0x34447afb686b4074d3b93b99076a60b5f5a902fa32df18d85fc1d13ada8cdb71",
"0x2f1b6a8f228675a171126d1781aeb52756d6fe5685af8c7ec23dc8187d783d1f",
"0x6545d98b7c7dd9e023753a64c515e69e19e72b18f163831c08c7f559dd8134b5",
"0x15b381b518c6b754185d6903caf17b4f8dc30babfedf8072b118bbc3eec3013f",
"0x06339935111b4a563a28a91c253b638af868b3af7c372cab497cac4f6cb2c0aa",
"0x45b0937bfe39b0fdadcaa8974daf324193624b47f4918234645819823dbec07f",
"0x8dd81a60355138e9a80054603fe23d7896544254f66e236d478a9b033c3a0330",
"0x3ba5ac70a18399817ccbe8bd4d9c78115136cc61699d51af87608af520226c32",
"0x0e8271a44c634b7fedcd00cb09afea2a70ac8380b6bf5f61ff07e793ee3f6d4c",
"0x34562e3ace608bdd75a10eb153cbc2c00e69b935d42f57c193c38ef6d674abac",
"0x06d9e4b9abc0b8978120d451eb4cca4f72ff1d97321713e0ef379a157b8f2b60",
"0x65a1c5667776dd9a726a67038e45e040b1fa463ed55f753c2156b355616f2c82",
"0xdcbad7acc8ff4bb5562dc1a37e87244df11ed754f8f25ebb6fc23320e5268b3f",
"0xe55b734df35ab6d576607e01c42f21d275cd9c0f86fbb3100241a14ba0c7672f",
"0xb7c8c273af675612bc8ee68c7a419b671e678e63fb9ed62646358d70adb82434",
"0x15c53c582284ec93075084bae0536b5ab7ae44a44ee2718489b72be3aa78c3c7",
"0xfced34088582b4a78f611c7366195e3e94f9080637f4104bbbd0d07ffb783d49",
"0xbe03f50d58fee6912ddb6cdfd5c26302db94c986ad69eb4e7fb32768636bc9e6",
"0x6be4eb2fb308fd641346533a4acb9c4cca3e2d478e9075522646c972a2062985",
"0xebd4d964df19c1598d3ce8354498f4f0cadb39d700af15ba6a42f8f6444c27d0",
"0x5380c7b7ae81a58eb98d9c78de4a1fd7fd9535fc953ed2be602daaa41767312a",
"0x7e833b9b867caae3d5e2b64c13a744f0e5cc1bff50fdb3713e3c505114a07de1",
"0xaeb868cc8cd000167af8b0f4dadd2e348e543a019cd0b1312dbedeb4ecdc592a",
"0xb7fc8b1cc055dd94a0f598f5ce4f8e121a8e3e4ac752b027ba3e2e99ed10c7b8",
"0x3c9bd268aedc8dbb36db0a4664e17b83abcfd87e404901eee7bd5704a95064ee",
"0xc2cf8e05450be6d2c98edc5df219a4cbd86dc60667b4a7810257177dc73217ed",
"0x07c57780db2d0b81258ad3be5df2c3a9e89e4c06bfaddc3a1f4e3b9401215947",
"0x7eb6fdadc6703ad64a7ccd5feddc788dc92efac7e066e01d5eb7176e91a6331d",
"0x6d2032b002dda8c5280bf48213d7b981476a9a8e53a69a39c625e5c8129e9287",
"0x65ca73153cb8f02233c0d12f996e3dc1584f0426285f63bf41bec91a86d3c36e",
"0xaebe9973fc4849a27521c2a71aab7673ddab83cc88e91abec675c1c471abf927",
"0xe690b239ba3aaf993e443ae14aeffc44cf8d9931a79baed9fa141d0e4506e131",
"0xbe8f0a3d1c2d7a0fc2342f9a4c78e00fe38a7f26da345758cb2b8efd052bcd55",
"0xeca6cdd9768090f90960cf9bad47705ca3b19a54c8ec8186e2987ffdd3a8435b",
"0x15fefe8613a1e673567aac83294fa0da6d048e5c7642227017441c9f899fed1b",
"0xfdfacf5483fdda2fc98d08bcce1a68d5d308d3ad88ee21b50004fd694ba6f805",
"0x36b3096c912adc7674ca92b68ddb3b0494e9b988fbef1bed7938e8fac4c2df7a",
"0x8f8755e5d8418704774f38ac79dd944c223bae9ae361c5efdd614d09c60fdcfc",
"0xdd7ad9673f823444e5194d5ab0eca9a1fb7bd3770d0fdc62ce3e180037158773",
"0x46f3f8cc486e8047567212c0f011c065466bcb76647dcf87d42756d7fbbfcb4b",
"0x546690cf9510b4b7732d2c99c7c2d994f6668b7d7f75a1d28c0eba53f784fe1e",
"0xc49241829446ad570b5b97d38a504782a6522204c67d5f29b61515246d0410dc",
"0x8fd56ff474602c98136f4a9389e3e2b5d7e78ec55d5edb63fb477d7281424515",
"0x471703c5eda8644a64cec152c58f5aacec93d72fb0bfa705f0473f9043a8357c",
"0xddc48afbdb1661c8789b447f581ae07eb1863b24e50865dfacacf0aef7a4f9dc",
"0xe6c05c2ebac68df110b9966cbf92414a6ddcf3b67fc77901a5fa049da0cfb109",
"0x3da06a67e7fee9c938155c50e6cec9da70b66679e777da6f02149c6674cd9105",
"0xaf03175444dc43cc33391c4c4010eac15d401b2eb5ffc7bc778abe372f28ce37",
"0xee317a72c6604e9e4abcba1ad9d6159a00d9168558be819e4393936aa1eee432",
"0x162386be0a525c5a9e6110c7244a375038c525fe102df224286161b1289bc716",
"0x54c901f415ecf89c46159b356a4a95e9e5666bc88f8bf4bf5d9c15aac8709e0a",
"0xbeda4b34674f3b9d0aaf6c3946c05b73b4e7ac680c7084d41dc8f0bd3bb38031",
"0x65d07b4d81508c24f55172afab2935db210689baba6270991f0218356705dc1c",
"0xfe6a58207750197f48cb90864096850259845c2c8e90c74433325c0b144bf8bb",
"0xc57975ae690f9a6554015459db6c13e8c6dbf6e4cc0f5cf986afd1210c233abf",
"0xee9d69f02d1245985cab3ed816bc86177c83e0701cc5515de9932aa065886407",
"0x6dc809012cc91335a5ea4e186bd04479aac7dae1bcbd023bf660213a69f4fdbd",
"0x3e400fa3269849dec0cbf4edbd663657c73bdc8c97c07aa166dd8b49f0b14028",
"0x56d423ba59df7075b27e5d573ad8399ac6ab8a7541efb9a1e646a67244301fed",
"0xe748320edc6a9c30d249d75af74a19ade7f69c56e5e0d3a9adb17fee1b0a4bef",
"0xde6e6fcaefc39f05e5912014093f38926987bb7b125e51b49ddfb49b03e36c50",
"0xfffa0eae268038cfa984647a1d0635beb86eda9fb7b500688f3189520cfa9ee5",
"0x660f4f93865ad80d0c9f1159bc17ecaa398a0f4af3a374dd033b20029378980a",
"0xe7cf18b96a1c12507c9a95b7f48c3e012651793a196ce2b81aaa6cb185dc03b6",
"0xc60f70699042477727e7230f64b5c94d8dcb2c7815f8c2542722fcef900dd238",
"0x6688c7b8c02428a3ba7e0b83237978a46faf9a632fc615dfcf7226473c244004",
"0x3e9a637c5898b70506a347ae7b5768d1a84022f9702e8268e8a5bade8194dd47",
"0x3eea7e8e2ca3e385fe39d605086b049b99df1adab85a1c6569873424f488c5ad",
"0xc6474110edeef59c48841b35aed37057feb211e738124285a233e77e53152a57",
"0x3eef5a1ec65e8ae1c87be5edc45099ac9e7783d4b8075810ba688eb479e7a730",
"0xc6e7f2ef75851a2b752eb247b5701aa5f24d0c867534330c74c51c1ae91319f7",
"0xc705619eb7a97a01d24e21cda98066d40aeb6d6ce6fb9e9a4880d86fe4432b13",
"0xc7caa90744f6b0e1fadfc5c21ccc5de0057447d6160835e35e25a82fc391d051",
"0xc7dd5ab81d775d5c8b283ddf787bc88c61e766b7b418c0d6375683f59fe6e5a9",
}
-359
View File
@@ -1,359 +0,0 @@
package fixture
var Block1_StateNodePaths = [][]byte{
[]byte{},
[]byte{0},
[]byte{0, 0},
[]byte{0, 2},
[]byte{0, 2, 1},
[]byte{0, 2, 8},
[]byte{0, 2, 12},
[]byte{0, 3},
[]byte{0, 4},
[]byte{0, 6},
[]byte{0, 6, 3},
[]byte{0, 6, 13},
[]byte{0, 7},
[]byte{0, 8},
[]byte{0, 8, 7},
[]byte{0, 8, 11},
[]byte{0, 9},
[]byte{0, 9, 9},
[]byte{0, 9, 10},
[]byte{0, 12},
[]byte{0, 13},
[]byte{0, 14},
[]byte{1},
[]byte{1, 2},
[]byte{1, 2, 5},
[]byte{1, 2, 7},
[]byte{1, 3},
[]byte{1, 3, 1},
[]byte{1, 3, 11},
[]byte{1, 4},
[]byte{1, 5},
[]byte{1, 5, 11},
[]byte{1, 5, 12},
[]byte{1, 5, 15},
[]byte{1, 6},
[]byte{1, 8},
[]byte{1, 10},
[]byte{1, 13},
[]byte{1, 14},
[]byte{1, 14, 2},
[]byte{1, 14, 11},
[]byte{1, 15},
[]byte{1, 15, 9},
[]byte{1, 15, 15},
[]byte{2},
[]byte{2, 0},
[]byte{2, 0, 9},
[]byte{2, 0, 14},
[]byte{2, 1},
[]byte{2, 1, 1},
[]byte{2, 1, 3},
[]byte{2, 1, 14},
[]byte{2, 5},
[]byte{2, 6},
[]byte{2, 9},
[]byte{2, 9, 1},
[]byte{2, 9, 7},
[]byte{2, 11},
[]byte{2, 11, 7},
[]byte{2, 11, 13},
[]byte{2, 13},
[]byte{2, 13, 1},
[]byte{2, 13, 15},
[]byte{2, 15},
[]byte{3},
[]byte{3, 0},
[]byte{3, 0, 0},
[]byte{3, 0, 1},
[]byte{3, 2},
[]byte{3, 2, 3},
[]byte{3, 2, 15},
[]byte{3, 3},
[]byte{3, 4},
[]byte{3, 4, 2},
[]byte{3, 4, 4},
[]byte{3, 4, 5},
[]byte{3, 6},
[]byte{3, 8},
[]byte{3, 9},
[]byte{3, 10},
[]byte{3, 10, 2},
[]byte{3, 10, 8},
[]byte{3, 10, 12},
[]byte{3, 11},
[]byte{3, 12},
[]byte{3, 13},
[]byte{3, 14},
[]byte{3, 14, 4},
[]byte{3, 14, 9},
[]byte{3, 14, 14},
[]byte{3, 14, 14, 10},
[]byte{3, 14, 14, 15},
[]byte{4},
[]byte{4, 0},
[]byte{4, 0, 6},
[]byte{4, 0, 15},
[]byte{4, 1},
[]byte{4, 2},
[]byte{4, 2, 1},
[]byte{4, 2, 11},
[]byte{4, 3},
[]byte{4, 5},
[]byte{4, 6},
[]byte{4, 7},
[]byte{4, 8},
[]byte{4, 11},
[]byte{4, 11, 6},
[]byte{4, 11, 9},
[]byte{4, 11, 12},
[]byte{4, 14},
[]byte{5},
[]byte{5, 0},
[]byte{5, 0, 3},
[]byte{5, 0, 9},
[]byte{5, 0, 15},
[]byte{5, 1},
[]byte{5, 1, 14},
[]byte{5, 1, 15},
[]byte{5, 2},
[]byte{5, 2, 8},
[]byte{5, 2, 10},
[]byte{5, 3},
[]byte{5, 4},
[]byte{5, 4, 6},
[]byte{5, 4, 12},
[]byte{5, 6},
[]byte{5, 8},
[]byte{5, 8, 3},
[]byte{5, 8, 11},
[]byte{5, 10},
[]byte{5, 11},
[]byte{5, 12},
[]byte{5, 13},
[]byte{5, 15},
[]byte{6},
[]byte{6, 0},
[]byte{6, 2},
[]byte{6, 2, 3},
[]byte{6, 2, 9},
[]byte{6, 4},
[]byte{6, 4, 0},
[]byte{6, 4, 0, 0},
[]byte{6, 4, 0, 5},
[]byte{6, 5},
[]byte{6, 5, 4},
[]byte{6, 5, 10},
[]byte{6, 5, 12},
[]byte{6, 5, 13},
[]byte{6, 6},
[]byte{6, 6, 0},
[]byte{6, 6, 8},
[]byte{6, 8},
[]byte{6, 8, 4},
[]byte{6, 8, 4, 2},
[]byte{6, 8, 4, 9},
[]byte{6, 8, 9},
[]byte{6, 10},
[]byte{6, 10, 1},
[]byte{6, 10, 14},
[]byte{6, 11},
[]byte{6, 11, 2},
[]byte{6, 11, 12},
[]byte{6, 11, 14},
[]byte{6, 13},
[]byte{6, 13, 2},
[]byte{6, 13, 12},
[]byte{7},
[]byte{7, 1},
[]byte{7, 5},
[]byte{7, 7},
[]byte{7, 8},
[]byte{7, 8, 2},
[]byte{7, 8, 5},
[]byte{7, 9},
[]byte{7, 13},
[]byte{7, 13, 1},
[]byte{7, 13, 1, 0},
[]byte{7, 13, 1, 13},
[]byte{7, 13, 7},
[]byte{7, 14},
[]byte{7, 14, 8},
[]byte{7, 14, 11},
[]byte{8},
[]byte{8, 0},
[]byte{8, 0, 3},
[]byte{8, 0, 11},
[]byte{8, 2},
[]byte{8, 4},
[]byte{8, 8},
[]byte{8, 9},
[]byte{8, 9, 3},
[]byte{8, 9, 13},
[]byte{8, 10},
[]byte{8, 12},
[]byte{8, 12, 3},
[]byte{8, 12, 15},
[]byte{8, 13},
[]byte{8, 15},
[]byte{8, 15, 8},
[]byte{8, 15, 13},
[]byte{9},
[]byte{9, 0},
[]byte{9, 5},
[]byte{9, 6},
[]byte{9, 6, 10},
[]byte{9, 6, 14},
[]byte{9, 7},
[]byte{9, 9},
[]byte{9, 14},
[]byte{9, 15},
[]byte{9, 15, 0},
[]byte{9, 15, 4},
[]byte{9, 15, 10},
[]byte{10},
[]byte{10, 0},
[]byte{10, 0, 9},
[]byte{10, 0, 10},
[]byte{10, 0, 15},
[]byte{10, 2},
[]byte{10, 3},
[]byte{10, 6},
[]byte{10, 8},
[]byte{10, 9},
[]byte{10, 10},
[]byte{10, 10, 5},
[]byte{10, 10, 8},
[]byte{10, 13},
[]byte{10, 13, 0},
[]byte{10, 13, 13},
[]byte{10, 14},
[]byte{10, 14, 4},
[]byte{10, 14, 11},
[]byte{10, 14, 11, 8},
[]byte{10, 14, 11, 14},
[]byte{10, 15},
[]byte{11},
[]byte{11, 0},
[]byte{11, 0, 2},
[]byte{11, 0, 15},
[]byte{11, 1},
[]byte{11, 2},
[]byte{11, 3},
[]byte{11, 4},
[]byte{11, 5},
[]byte{11, 7},
[]byte{11, 7, 12},
[]byte{11, 7, 15},
[]byte{11, 8},
[]byte{11, 8, 8},
[]byte{11, 8, 15},
[]byte{11, 9},
[]byte{11, 11},
[]byte{11, 12},
[]byte{11, 13},
[]byte{11, 14},
[]byte{11, 14, 0},
[]byte{11, 14, 0, 1},
[]byte{11, 14, 0, 3},
[]byte{11, 14, 8},
[]byte{11, 14, 13},
[]byte{12},
[]byte{12, 0},
[]byte{12, 0, 0},
[]byte{12, 0, 1},
[]byte{12, 0, 1, 3},
[]byte{12, 0, 1, 11},
[]byte{12, 0, 15},
[]byte{12, 2},
[]byte{12, 2, 9},
[]byte{12, 2, 12},
[]byte{12, 4},
[]byte{12, 5},
[]byte{12, 6},
[]byte{12, 6, 0},
[]byte{12, 6, 4},
[]byte{12, 6, 14},
[]byte{12, 7},
[]byte{12, 7, 0},
[]byte{12, 7, 12},
[]byte{12, 7, 13},
[]byte{12, 9},
[]byte{12, 11},
[]byte{12, 12},
[]byte{13},
[]byte{13, 2},
[]byte{13, 2, 0},
[]byte{13, 2, 2},
[]byte{13, 2, 4},
[]byte{13, 3},
[]byte{13, 3, 7},
[]byte{13, 3, 10},
[]byte{13, 5},
[]byte{13, 8},
[]byte{13, 8, 1},
[]byte{13, 8, 15},
[]byte{13, 9},
[]byte{13, 9, 0},
[]byte{13, 9, 14},
[]byte{13, 10},
[]byte{13, 12},
[]byte{13, 12, 8},
[]byte{13, 12, 11},
[]byte{13, 13},
[]byte{13, 13, 7},
[]byte{13, 13, 12},
[]byte{13, 14},
[]byte{14},
[]byte{14, 0},
[]byte{14, 1},
[]byte{14, 2},
[]byte{14, 2, 2},
[]byte{14, 2, 12},
[]byte{14, 3},
[]byte{14, 4},
[]byte{14, 5},
[]byte{14, 6},
[]byte{14, 6, 9},
[]byte{14, 6, 12},
[]byte{14, 7},
[]byte{14, 7, 4},
[]byte{14, 7, 12},
[]byte{14, 8},
[]byte{14, 8, 3},
[]byte{14, 8, 12},
[]byte{14, 8, 12, 0},
[]byte{14, 8, 12, 6},
[]byte{14, 10},
[]byte{14, 10, 6},
[]byte{14, 10, 12},
[]byte{14, 11},
[]byte{14, 11, 8},
[]byte{14, 11, 13},
[]byte{14, 12},
[]byte{14, 14},
[]byte{14, 14, 3},
[]byte{14, 14, 9},
[]byte{15},
[]byte{15, 0},
[]byte{15, 5},
[]byte{15, 6},
[]byte{15, 9},
[]byte{15, 9, 0},
[]byte{15, 9, 2},
[]byte{15, 9, 3},
[]byte{15, 11},
[]byte{15, 11, 1},
[]byte{15, 11, 6},
[]byte{15, 12},
[]byte{15, 12, 3},
[]byte{15, 12, 14},
[]byte{15, 12, 14, 7},
[]byte{15, 12, 14, 13},
[]byte{15, 13},
[]byte{15, 14},
[]byte{15, 15},
}
+492 -7
View File
@@ -3,10 +3,13 @@ package fixture
import (
"math/big"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/statediff/indexer/ipld"
"github.com/ethereum/go-ethereum/statediff/indexer/models"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
snapt "github.com/vulcanize/ipld-eth-state-snapshot/pkg/types"
)
var Block1_Header = types.Header{
@@ -28,9 +31,491 @@ var Block1_Header = types.Header{
BaseFee: nil,
}
var Block1_StateNode0 = snapt.Node{
NodeType: 0,
Path: []byte{12, 0},
Key: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
Value: []byte{248, 113, 160, 147, 141, 92, 6, 119, 63, 191, 125, 121, 193, 230, 153, 223, 49, 102, 109, 236, 50, 44, 161, 215, 28, 224, 171, 111, 118, 230, 79, 99, 18, 99, 4, 160, 117, 126, 95, 187, 60, 115, 90, 36, 51, 167, 59, 86, 20, 175, 63, 118, 94, 230, 107, 202, 41, 253, 234, 165, 214, 221, 181, 45, 9, 202, 244, 148, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 160, 247, 170, 155, 102, 71, 245, 140, 90, 255, 89, 193, 131, 99, 31, 85, 161, 78, 90, 0, 204, 46, 253, 15, 71, 120, 19, 109, 123, 255, 0, 188, 27, 128},
var block1_stateNodeRLP = []byte{248, 113, 160, 147, 141, 92, 6, 119, 63, 191, 125, 121, 193, 230, 153, 223, 49, 102, 109, 236, 50, 44, 161, 215, 28, 224, 171, 111, 118, 230, 79, 99, 18, 99, 4, 160, 117, 126, 95, 187, 60, 115, 90, 36, 51, 167, 59, 86, 20, 175, 63, 118, 94, 230, 107, 202, 41, 253, 234, 165, 214, 221, 181, 45, 9, 202, 244, 148, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 160, 247, 170, 155, 102, 71, 245, 140, 90, 255, 89, 193, 131, 99, 31, 85, 161, 78, 90, 0, 204, 46, 253, 15, 71, 120, 19, 109, 123, 255, 0, 188, 27, 128}
var block1_stateNodeCID = ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(block1_stateNodeRLP))
var block_stateNodeLeafKey = "0x39fc293fc702e42b9c023f094826545db42fc0fdf2ba031bb522d5ef917a6edb"
var Block1_StateNodeIPLD = models.IPLDModel{
BlockNumber: Block1_Header.Number.String(),
Key: block1_stateNodeCID.String(),
Data: block1_stateNodeRLP,
}
var Block1_EmptyRootNodeRLP, _ = rlp.EncodeToBytes([]byte{})
var Block1_StateNode0 = models.StateNodeModel{
BlockNumber: Block1_Header.Number.String(),
HeaderID: Block1_Header.Hash().Hex(),
CID: block1_stateNodeCID.String(),
Diff: false,
Balance: "1000",
Nonce: 1,
CodeHash: crypto.Keccak256Hash([]byte{}).Hex(),
StorageRoot: crypto.Keccak256Hash(Block1_EmptyRootNodeRLP).Hex(),
Removed: false,
StateKey: block_stateNodeLeafKey,
}
var block1_storageNodeRLP = []byte{3, 111, 15, 5, 141, 92, 6, 120, 63, 191, 125, 121, 193, 230, 153, 7, 49, 102, 109, 236, 50, 44, 161, 215, 28, 224, 171, 111, 118, 230, 79, 99, 18, 99, 4, 160, 117, 126, 95, 187, 60, 115, 90, 36, 51, 167, 59, 86, 20, 175, 63, 118, 94, 2, 107, 202, 41, 253, 234, 165, 214, 221, 181, 45, 9, 202, 244, 148, 128, 128, 32, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 160, 247, 170, 155, 102, 245, 71, 140, 90, 255, 89, 131, 99, 99, 31, 85, 161, 78, 90, 0, 204, 46, 253, 15, 71, 120, 19, 109, 123, 255, 0, 188, 27, 128}
var block1_storageNodeCID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(block1_storageNodeRLP))
var Block1_StorageNodeIPLD = models.IPLDModel{
BlockNumber: Block1_Header.Number.String(),
Key: block1_storageNodeCID.String(),
Data: block1_storageNodeRLP,
}
var Block1_StorageNode0 = models.StorageNodeModel{
BlockNumber: Block1_Header.Number.String(),
HeaderID: Block1_Header.Hash().Hex(),
StateKey: block_stateNodeLeafKey,
StorageKey: "0x33153abc667e873b6036c8a46bdd847e2ade3f89b9331c78ef2553fea194c50d",
Removed: false,
CID: block1_storageNodeCID.String(),
Diff: false,
Value: []byte{1},
}
// Header for last block at height 32
var Chain2_Block32_Header = types.Header{
ParentHash: common.HexToHash("0x6983c921c053d1f637449191379f61ba844013c71e5ebfacaff77f8a8bd97042"),
UncleHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
Coinbase: common.HexToAddress("0x0000000000000000000000000000000000000000"),
Root: common.HexToHash("0xeaa5866eb37e33fc3cfe1376b2ad7f465e7213c14e6834e1cfcef9552b2e5d5d"),
TxHash: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"),
ReceiptHash: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"),
Bloom: types.Bloom{},
Difficulty: big.NewInt(2),
Number: big.NewInt(32),
GasLimit: 8253773,
GasUsed: 0,
Time: 1658408469,
Extra: []byte{216, 131, 1, 10, 19, 132, 103, 101, 116, 104, 136, 103, 111, 49, 46, 49, 56, 46, 50, 133, 108, 105, 110, 117, 120, 0, 0, 0, 0, 0, 0, 0, 113, 250, 240, 25, 148, 32, 193, 94, 196, 10, 99, 63, 251, 130, 170, 0, 176, 201, 149, 55, 230, 58, 218, 112, 84, 153, 122, 83, 134, 52, 176, 99, 53, 54, 63, 12, 226, 81, 38, 176, 57, 117, 92, 205, 237, 81, 203, 232, 220, 228, 166, 254, 206, 136, 7, 253, 2, 61, 47, 217, 235, 24, 140, 92, 1},
MixDigest: common.Hash{},
Nonce: types.BlockNonce{},
BaseFee: nil,
}
// State nodes for all paths at height 32
// Total 7
var Chain2_Block32_stateNode0RLP = []byte{248, 145, 128, 128, 128, 160, 151, 6, 152, 177, 246, 151, 39, 79, 71, 219, 192, 153, 253, 0, 46, 66, 56, 238, 116, 176, 237, 244, 79, 132, 49, 29, 30, 82, 108, 53, 191, 204, 128, 128, 160, 46, 224, 200, 157, 30, 24, 225, 92, 222, 131, 123, 169, 124, 86, 228, 124, 79, 136, 236, 83, 185, 22, 67, 136, 5, 73, 46, 110, 136, 138, 101, 63, 128, 128, 160, 104, 220, 31, 84, 240, 26, 100, 148, 110, 49, 52, 120, 81, 119, 30, 251, 196, 107, 11, 134, 124, 238, 93, 61, 109, 109, 181, 208, 10, 189, 17, 92, 128, 128, 160, 171, 149, 11, 254, 75, 39, 224, 164, 133, 151, 153, 47, 109, 134, 15, 169, 139, 206, 132, 93, 220, 210, 0, 225, 235, 118, 121, 247, 173, 12, 135, 133, 128, 128, 128, 128}
var Chain2_Block32_stateNode0CID = ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(Chain2_Block32_stateNode0RLP))
var Chain2_Block32_stateNode1RLP = []byte{248, 81, 128, 128, 128, 160, 209, 34, 171, 171, 30, 147, 168, 199, 137, 152, 249, 118, 14, 166, 1, 169, 116, 224, 82, 196, 237, 83, 255, 188, 228, 197, 7, 178, 144, 137, 77, 55, 128, 128, 128, 128, 128, 160, 135, 96, 108, 173, 177, 63, 201, 196, 26, 204, 72, 118, 17, 30, 76, 117, 155, 63, 68, 187, 4, 249, 78, 69, 161, 82, 178, 234, 164, 48, 158, 173, 128, 128, 128, 128, 128, 128, 128}
var Chain2_Block32_stateNode1CID = ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(Chain2_Block32_stateNode1RLP))
var Chain2_Block32_stateNode2RLP = []byte{248, 105, 160, 32, 21, 58, 188, 102, 126, 135, 59, 96, 54, 200, 164, 107, 221, 132, 126, 42, 222, 63, 137, 185, 51, 28, 120, 239, 37, 83, 254, 161, 148, 197, 13, 184, 70, 248, 68, 1, 128, 160, 168, 127, 48, 6, 204, 116, 51, 247, 216, 182, 191, 182, 185, 124, 223, 202, 239, 15, 67, 91, 253, 165, 42, 2, 54, 10, 211, 250, 242, 149, 205, 139, 160, 224, 22, 140, 8, 116, 27, 79, 113, 64, 185, 215, 180, 38, 38, 236, 164, 5, 87, 211, 15, 88, 153, 138, 185, 94, 186, 125, 137, 164, 198, 141, 192}
var Chain2_Block32_stateNode2CID = ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(Chain2_Block32_stateNode2RLP))
var Chain2_Block32_stateNode3RLP = []byte{248, 105, 160, 32, 252, 41, 63, 199, 2, 228, 43, 156, 2, 63, 9, 72, 38, 84, 93, 180, 47, 192, 253, 242, 186, 3, 27, 181, 34, 213, 239, 145, 122, 110, 219, 184, 70, 248, 68, 1, 128, 160, 25, 80, 158, 144, 166, 222, 32, 247, 189, 42, 34, 60, 40, 240, 56, 105, 251, 184, 132, 209, 219, 59, 60, 16, 221, 204, 228, 74, 76, 113, 37, 226, 160, 224, 22, 140, 8, 116, 27, 79, 113, 64, 185, 215, 180, 38, 38, 236, 164, 5, 87, 211, 15, 88, 153, 138, 185, 94, 186, 125, 137, 164, 198, 141, 192}
var Chain2_Block32_stateNode3CID = ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(Chain2_Block32_stateNode3RLP))
var Chain2_Block32_stateNode4RLP = []byte{248, 118, 160, 55, 171, 60, 13, 215, 117, 244, 72, 175, 127, 180, 18, 67, 65, 94, 214, 251, 151, 93, 21, 48, 162, 216, 40, 246, 155, 234, 115, 70, 35, 26, 215, 184, 83, 248, 81, 10, 141, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112}
var Chain2_Block32_stateNode4CID = ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(Chain2_Block32_stateNode4RLP))
var Chain2_Block32_stateNode5RLP = []byte{248, 105, 160, 51, 151, 227, 61, 237, 218, 71, 99, 174, 161, 67, 252, 97, 81, 235, 205, 154, 147, 246, 45, 183, 166, 165, 86, 212, 108, 88, 93, 130, 173, 42, 252, 184, 70, 248, 68, 1, 128, 160, 54, 174, 96, 33, 243, 186, 113, 120, 188, 222, 254, 210, 63, 40, 4, 130, 154, 156, 66, 247, 130, 93, 88, 113, 144, 78, 47, 252, 174, 140, 130, 45, 160, 29, 80, 58, 104, 206, 141, 36, 93, 124, 217, 67, 93, 183, 43, 71, 98, 114, 126, 124, 105, 229, 48, 218, 194, 109, 83, 20, 76, 13, 102, 156, 130}
var Chain2_Block32_stateNode5CID = ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(Chain2_Block32_stateNode5RLP))
var Chain2_Block32_stateNode6RLP = []byte{248, 105, 160, 58, 188, 94, 219, 48, 85, 131, 227, 63, 102, 50, 44, 238, 228, 48, 136, 170, 153, 39, 125, 167, 114, 254, 181, 5, 53, 18, 208, 58, 10, 112, 43, 184, 70, 248, 68, 1, 128, 160, 54, 174, 96, 33, 243, 186, 113, 120, 188, 222, 254, 210, 63, 40, 4, 130, 154, 156, 66, 247, 130, 93, 88, 113, 144, 78, 47, 252, 174, 140, 130, 45, 160, 29, 80, 58, 104, 206, 141, 36, 93, 124, 217, 67, 93, 183, 43, 71, 98, 114, 126, 124, 105, 229, 48, 218, 194, 109, 83, 20, 76, 13, 102, 156, 130}
var Chain2_Block32_stateNode6CID = ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(Chain2_Block32_stateNode6RLP))
var Chain2_Block32_StateIPLDs = []models.IPLDModel{
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: Chain2_Block32_stateNode0CID.String(),
Data: Chain2_Block32_stateNode0RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: Chain2_Block32_stateNode1CID.String(),
Data: Chain2_Block32_stateNode1RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: Chain2_Block32_stateNode2CID.String(),
Data: Chain2_Block32_stateNode2RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: Chain2_Block32_stateNode3CID.String(),
Data: Chain2_Block32_stateNode3RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: Chain2_Block32_stateNode4CID.String(),
Data: Chain2_Block32_stateNode4RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: Chain2_Block32_stateNode5CID.String(),
Data: Chain2_Block32_stateNode5RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: Chain2_Block32_stateNode6CID.String(),
Data: Chain2_Block32_stateNode6RLP,
},
}
var Chain2_Block32_StateNodes = []models.StateNodeModel{
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
CID: Chain2_Block32_stateNode2CID.String(),
Diff: false,
Balance: "0",
Nonce: 1,
CodeHash: common.HexToHash("0xe0168c08741b4f7140b9d7b42626eca40557d30f58998ab95eba7d89a4c68dc0").Hex(),
StorageRoot: common.HexToHash("0xa87f3006cc7433f7d8b6bfb6b97cdfcaef0f435bfda52a02360ad3faf295cd8b").Hex(),
Removed: false,
StateKey: "0x33153abc667e873b6036c8a46bdd847e2ade3f89b9331c78ef2553fea194c50d",
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
CID: Chain2_Block32_stateNode3CID.String(),
Diff: false,
Balance: "1000",
Nonce: 1,
CodeHash: crypto.Keccak256Hash([]byte{}).Hex(),
StorageRoot: crypto.Keccak256Hash(Block1_EmptyRootNodeRLP).Hex(),
Removed: false,
StateKey: "0x39fc293fc702e42b9c023f094826545db42fc0fdf2ba031bb522d5ef917a6edb",
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
CID: Chain2_Block32_stateNode4CID.String(),
Diff: false,
Balance: "1000",
Nonce: 1,
CodeHash: crypto.Keccak256Hash([]byte{}).Hex(),
StorageRoot: crypto.Keccak256Hash(Block1_EmptyRootNodeRLP).Hex(),
Removed: false,
StateKey: "0x67ab3c0dd775f448af7fb41243415ed6fb975d1530a2d828f69bea7346231ad7",
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
CID: Chain2_Block32_stateNode5CID.String(),
Diff: false,
Balance: "1000",
Nonce: 1,
CodeHash: crypto.Keccak256Hash([]byte{}).Hex(),
StorageRoot: crypto.Keccak256Hash(Block1_EmptyRootNodeRLP).Hex(),
Removed: false,
StateKey: "0x9397e33dedda4763aea143fc6151ebcd9a93f62db7a6a556d46c585d82ad2afc",
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
CID: Chain2_Block32_stateNode6CID.String(),
Diff: false,
Balance: "0",
Nonce: 1,
CodeHash: common.HexToHash("0x1d503a68ce8d245d7cd9435db72b4762727e7c69e530dac26d53144c0d669c82").Hex(),
StorageRoot: common.HexToHash("0x36ae6021f3ba7178bcdefed23f2804829a9c42f7825d5871904e2ffcae8c822d").Hex(),
Removed: false,
StateKey: "0xcabc5edb305583e33f66322ceee43088aa99277da772feb5053512d03a0a702b",
},
}
// Storage nodes for all paths at height 32
// Total 18
var chain2_Block32_storageNode0RLP = []byte{248, 145, 128, 128, 128, 128, 160, 46, 77, 227, 140, 57, 224, 108, 238, 40, 82, 145, 79, 210, 174, 54, 248, 0, 145, 137, 64, 229, 230, 148, 145, 250, 132, 89, 198, 8, 249, 245, 133, 128, 160, 146, 250, 117, 217, 106, 75, 51, 124, 196, 244, 29, 16, 47, 173, 5, 90, 86, 19, 15, 48, 179, 174, 60, 171, 112, 154, 92, 70, 232, 164, 141, 165, 128, 160, 107, 250, 27, 137, 190, 180, 7, 172, 62, 97, 13, 157, 215, 114, 55, 219, 14, 244, 163, 155, 192, 255, 34, 143, 154, 149, 33, 227, 166, 135, 164, 93, 128, 128, 128, 160, 173, 131, 221, 2, 30, 147, 11, 230, 58, 166, 18, 25, 90, 56, 198, 126, 196, 130, 131, 1, 213, 112, 129, 155, 96, 143, 121, 231, 218, 97, 216, 200, 128, 128, 128, 128}
var chain2_Block32_storageNode0CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode0RLP))
var chain2_Block32_storageNode1RLP = []byte{248, 81, 160, 167, 145, 134, 15, 219, 140, 96, 62, 101, 242, 176, 129, 164, 160, 200, 221, 13, 1, 246, 167, 156, 45, 205, 192, 88, 236, 235, 80, 105, 178, 123, 2, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 160, 18, 136, 22, 150, 26, 170, 67, 152, 182, 246, 95, 49, 193, 199, 219, 163, 97, 25, 243, 70, 126, 235, 163, 59, 44, 16, 37, 37, 247, 50, 229, 70, 128, 128}
var chain2_Block32_storageNode1CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode1RLP))
var chain2_Block32_storageNode2RLP = []byte{236, 160, 32, 87, 135, 250, 18, 168, 35, 224, 242, 183, 99, 28, 196, 27, 59, 168, 130, 139, 51, 33, 202, 129, 17, 17, 250, 117, 205, 58, 163, 187, 90, 206, 138, 137, 54, 53, 201, 173, 197, 222, 160, 0, 0}
var chain2_Block32_storageNode2CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode2RLP))
var chain2_Block32_storageNode3RLP = []byte{226, 160, 32, 44, 236, 111, 71, 132, 84, 126, 80, 66, 161, 99, 128, 134, 227, 24, 137, 41, 243, 79, 60, 0, 5, 248, 222, 195, 102, 201, 110, 129, 149, 172, 100}
var chain2_Block32_storageNode3CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode3RLP))
var chain2_Block32_storageNode4RLP = []byte{236, 160, 58, 160, 42, 17, 221, 77, 37, 151, 49, 139, 113, 212, 147, 177, 69, 221, 246, 174, 8, 23, 169, 211, 148, 127, 69, 213, 41, 166, 167, 95, 43, 239, 138, 137, 54, 53, 201, 173, 197, 222, 159, 255, 156}
var chain2_Block32_storageNode4CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode4RLP))
var chain2_Block32_storageNode5RLP = []byte{248, 67, 160, 58, 53, 172, 251, 193, 95, 248, 26, 57, 174, 125, 52, 79, 215, 9, 242, 142, 134, 0, 180, 170, 140, 101, 198, 182, 75, 254, 127, 227, 107, 209, 155, 161, 160, 71, 76, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6}
var chain2_Block32_storageNode5CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode5RLP))
var chain2_Block32_storageNode6RLP = []byte{248, 67, 160, 58, 53, 172, 251, 193, 95, 248, 26, 57, 174, 125, 52, 79, 215, 9, 242, 142, 134, 0, 180, 170, 140, 101, 198, 182, 75, 254, 127, 227, 107, 209, 155, 161, 160, 71, 76, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6}
var chain2_Block32_storageNode6CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode6RLP))
var chain2_Block32_storageNode7RLP = []byte{248, 67, 160, 50, 87, 90, 14, 158, 89, 60, 0, 249, 89, 248, 201, 47, 18, 219, 40, 105, 195, 57, 90, 59, 5, 2, 208, 94, 37, 22, 68, 111, 113, 248, 91, 161, 160, 71, 111, 108, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}
var chain2_Block32_storageNode7CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode7RLP))
var chain2_Block32_storageNode8RLP = []byte{248, 67, 160, 50, 87, 90, 14, 158, 89, 60, 0, 249, 89, 248, 201, 47, 18, 219, 40, 105, 195, 57, 90, 59, 5, 2, 208, 94, 37, 22, 68, 111, 113, 248, 91, 161, 160, 71, 111, 108, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}
var chain2_Block32_storageNode8CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode8RLP))
var chain2_Block32_storageNode9RLP = []byte{248, 145, 128, 128, 128, 128, 160, 145, 86, 15, 219, 52, 36, 164, 68, 160, 227, 156, 111, 1, 245, 112, 184, 187, 242, 26, 138, 8, 98, 129, 35, 57, 212, 165, 21, 204, 151, 229, 43, 128, 160, 250, 205, 84, 126, 141, 108, 126, 228, 162, 8, 238, 234, 141, 159, 232, 175, 70, 112, 207, 55, 165, 209, 107, 153, 54, 183, 60, 172, 194, 251, 66, 61, 128, 160, 107, 250, 27, 137, 190, 180, 7, 172, 62, 97, 13, 157, 215, 114, 55, 219, 14, 244, 163, 155, 192, 255, 34, 143, 154, 149, 33, 227, 166, 135, 164, 93, 128, 128, 128, 160, 173, 131, 221, 2, 30, 147, 11, 230, 58, 166, 18, 25, 90, 56, 198, 126, 196, 130, 131, 1, 213, 112, 129, 155, 96, 143, 121, 231, 218, 97, 216, 200, 128, 128, 128, 128}
var chain2_Block32_storageNode9CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode9RLP))
var chain2_Block32_storageNode10RLP = []byte{236, 160, 48, 87, 135, 250, 18, 168, 35, 224, 242, 183, 99, 28, 196, 27, 59, 168, 130, 139, 51, 33, 202, 129, 17, 17, 250, 117, 205, 58, 163, 187, 90, 206, 138, 137, 54, 53, 201, 173, 197, 222, 160, 0, 0}
var chain2_Block32_storageNode10CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode10RLP))
var chain2_Block32_storageNode11RLP = []byte{236, 160, 58, 160, 42, 17, 221, 77, 37, 151, 49, 139, 113, 212, 147, 177, 69, 221, 246, 174, 8, 23, 169, 211, 148, 127, 69, 213, 41, 166, 167, 95, 43, 239, 138, 137, 54, 53, 201, 173, 197, 222, 160, 0, 0}
var chain2_Block32_storageNode11CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode11RLP))
var chain2_Block32_storageNode12RLP = []byte{248, 81, 128, 128, 160, 79, 197, 241, 58, 178, 249, 186, 12, 45, 168, 139, 1, 81, 171, 14, 124, 244, 216, 93, 8, 204, 164, 92, 205, 146, 60, 106, 183, 99, 35, 235, 40, 128, 128, 128, 128, 128, 128, 128, 128, 160, 82, 154, 228, 80, 107, 126, 132, 72, 3, 170, 88, 197, 100, 216, 50, 21, 226, 183, 86, 42, 208, 239, 184, 183, 152, 93, 188, 113, 224, 234, 218, 43, 128, 128, 128, 128, 128}
var chain2_Block32_storageNode12CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode12RLP))
var chain2_Block32_storageNode13RLP = []byte{248, 81, 128, 128, 160, 79, 197, 241, 58, 178, 249, 186, 12, 45, 168, 139, 1, 81, 171, 14, 124, 244, 216, 93, 8, 204, 164, 92, 205, 146, 60, 106, 183, 99, 35, 235, 40, 128, 128, 128, 128, 128, 128, 128, 128, 160, 82, 154, 228, 80, 107, 126, 132, 72, 3, 170, 88, 197, 100, 216, 50, 21, 226, 183, 86, 42, 208, 239, 184, 183, 152, 93, 188, 113, 224, 234, 218, 43, 128, 128, 128, 128, 128}
var chain2_Block32_storageNode13CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode13RLP))
var chain2_Block32_storageNode14RLP = []byte{226, 160, 57, 13, 236, 217, 84, 139, 98, 168, 214, 3, 69, 169, 136, 56, 111, 200, 75, 166, 188, 149, 72, 64, 8, 246, 54, 47, 147, 22, 14, 243, 229, 99, 1}
var chain2_Block32_storageNode14CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode14RLP))
var chain2_Block32_storageNode15RLP = []byte{226, 160, 57, 13, 236, 217, 84, 139, 98, 168, 214, 3, 69, 169, 136, 56, 111, 200, 75, 166, 188, 149, 72, 64, 8, 246, 54, 47, 147, 22, 14, 243, 229, 99, 1}
var chain2_Block32_storageNode15CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode15RLP))
var chain2_Block32_storageNode16RLP = []byte{226, 160, 49, 14, 45, 82, 118, 18, 7, 59, 38, 238, 205, 253, 113, 126, 106, 50, 12, 244, 75, 74, 250, 194, 176, 115, 45, 159, 203, 226, 183, 250, 12, 246, 4}
var chain2_Block32_storageNode16CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode16RLP))
var chain2_Block32_storageNode17RLP = []byte{226, 160, 49, 14, 45, 82, 118, 18, 7, 59, 38, 238, 205, 253, 113, 126, 106, 50, 12, 244, 75, 74, 250, 194, 176, 115, 45, 159, 203, 226, 183, 250, 12, 246, 4}
var chain2_Block32_storageNode17CID = ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(chain2_Block32_storageNode17RLP))
var Chain2_Block32_StorageIPLDs = []models.IPLDModel{
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode0CID.String(),
Data: chain2_Block32_storageNode0RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode1CID.String(),
Data: chain2_Block32_storageNode1RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode2CID.String(),
Data: chain2_Block32_storageNode2RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode3CID.String(),
Data: chain2_Block32_storageNode3RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode4CID.String(),
Data: chain2_Block32_storageNode4RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode5CID.String(),
Data: chain2_Block32_storageNode5RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode6CID.String(),
Data: chain2_Block32_storageNode6RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode7CID.String(),
Data: chain2_Block32_storageNode7RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode8CID.String(),
Data: chain2_Block32_storageNode8RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode9CID.String(),
Data: chain2_Block32_storageNode9RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode10CID.String(),
Data: chain2_Block32_storageNode10RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode11CID.String(),
Data: chain2_Block32_storageNode11RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode12CID.String(),
Data: chain2_Block32_storageNode12RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode13CID.String(),
Data: chain2_Block32_storageNode13RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode14CID.String(),
Data: chain2_Block32_storageNode14RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode15CID.String(),
Data: chain2_Block32_storageNode15RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode16CID.String(),
Data: chain2_Block32_storageNode16RLP,
},
{
BlockNumber: Chain2_Block32_Header.Number.String(),
Key: chain2_Block32_storageNode17CID.String(),
Data: chain2_Block32_storageNode17RLP,
},
}
var Chain2_Block32_StorageNodes = []models.StorageNodeModel{
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
Diff: false,
Removed: false,
StorageKey: "0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace",
CID: chain2_Block32_storageNode2CID.String(),
Value: []byte{137, 54, 53, 201, 173, 197, 222, 160, 0, 0},
StateKey: "0x33153abc667e873b6036c8a46bdd847e2ade3f89b9331c78ef2553fea194c50d",
}, // 0
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
Diff: false,
Removed: false,
StorageKey: "0x4e2cec6f4784547e5042a1638086e3188929f34f3c0005f8dec366c96e8195ac",
CID: chain2_Block32_storageNode3CID.String(),
Value: []byte{100},
StateKey: "0x33153abc667e873b6036c8a46bdd847e2ade3f89b9331c78ef2553fea194c50d",
}, // 1
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
Diff: false,
Removed: false,
StorageKey: "0x6aa02a11dd4d2597318b71d493b145ddf6ae0817a9d3947f45d529a6a75f2bef",
CID: chain2_Block32_storageNode4CID.String(),
Value: []byte{137, 54, 53, 201, 173, 197, 222, 159, 255, 156},
StateKey: "0x33153abc667e873b6036c8a46bdd847e2ade3f89b9331c78ef2553fea194c50d",
}, // 2
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
Diff: false,
Removed: false,
StorageKey: "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b",
CID: chain2_Block32_storageNode5CID.String(),
Value: []byte{},
StateKey: "0x39fc293fc702e42b9c023f094826545db42fc0fdf2ba031bb522d5ef917a6edb'",
}, // 3
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
Diff: false,
Removed: false,
StorageKey: "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b",
CID: chain2_Block32_storageNode6CID.String(),
Value: []byte{160, 71, 76, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6},
StateKey: "0x33153abc667e873b6036c8a46bdd847e2ade3f89b9331c78ef2553fea194c50d",
}, // 4
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
Diff: false,
Removed: false,
StorageKey: "0xc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b",
CID: chain2_Block32_storageNode7CID.String(),
Value: []byte{},
StateKey: "0x39fc293fc702e42b9c023f094826545db42fc0fdf2ba031bb522d5ef917a6edb'",
}, // 5
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
Diff: false,
Removed: false,
StorageKey: "0xc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b",
CID: chain2_Block32_storageNode8CID.String(),
Value: []byte{160, 71, 111, 108, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8},
StateKey: "0x33153abc667e873b6036c8a46bdd847e2ade3f89b9331c78ef2553fea194c50d",
}, // 6
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
Diff: false,
Removed: false,
StorageKey: "0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace",
CID: chain2_Block32_storageNode10CID.String(),
Value: []byte{},
StateKey: "0x39fc293fc702e42b9c023f094826545db42fc0fdf2ba031bb522d5ef917a6edb'",
}, // 7
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
Diff: false,
Removed: false,
StorageKey: "0x6aa02a11dd4d2597318b71d493b145ddf6ae0817a9d3947f45d529a6a75f2bef",
CID: chain2_Block32_storageNode11CID.String(),
Value: []byte{},
StateKey: "0x39fc293fc702e42b9c023f094826545db42fc0fdf2ba031bb522d5ef917a6edb'",
}, // 8
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
Diff: false,
Removed: false,
StorageKey: "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563",
CID: chain2_Block32_storageNode14CID.String(),
Value: []byte{'\x01'},
StateKey: "0xcabc5edb305583e33f66322ceee43088aa99277da772feb5053512d03a0a702b",
}, // 9
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
Diff: false,
Removed: false,
StorageKey: "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563",
CID: chain2_Block32_storageNode15CID.String(),
Value: []byte{},
StateKey: "0x9397e33dedda4763aea143fc6151ebcd9a93f62db7a6a556d46c585d82ad2afc",
}, // 10
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
Diff: false,
Removed: false,
StorageKey: "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6",
CID: chain2_Block32_storageNode16CID.String(),
Value: []byte{'\x04'},
StateKey: "0xcabc5edb305583e33f66322ceee43088aa99277da772feb5053512d03a0a702b",
}, // 11
{
BlockNumber: Chain2_Block32_Header.Number.String(),
HeaderID: Chain2_Block32_Header.Hash().Hex(),
Diff: false,
Removed: false,
StorageKey: "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6",
CID: chain2_Block32_storageNode17CID.String(),
Value: []byte{},
StateKey: "0x9397e33dedda4763aea143fc6151ebcd9a93f62db7a6a556d46c585d82ad2afc",
}, // 12
}
// Contracts used in chain2
/*
pragma solidity ^0.8.0;
contract Test {
uint256 private count;
uint256 private count2;
event Increment(uint256 count);
constructor() {
count2 = 4;
}
function incrementCount() public returns (uint256) {
count = count + 1;
emit Increment(count);
return count;
}
function destroy() public {
selfdestruct(payable(msg.sender));
}
function deleteCount2() public {
count2 = 0;
}
}
*/
/*
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract GLDToken is ERC20 {
constructor(uint256 initialSupply) ERC20("Gold", "GLD") {
_mint(msg.sender, initialSupply);
}
}
*/
+96 -14
View File
@@ -1,20 +1,102 @@
module github.com/vulcanize/ipld-eth-state-snapshot
module github.com/cerc-io/ipld-eth-state-snapshot
go 1.15
go 1.19
require (
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
github.com/ethereum/go-ethereum v1.10.17
github.com/ethereum/go-ethereum v1.11.5
github.com/golang/mock v1.6.0
github.com/ipfs/go-cid v0.1.0
github.com/ipfs/go-ipfs-blockstore v1.1.2
github.com/ipfs/go-ipfs-ds-help v1.1.0
github.com/jackc/pgx/v4 v4.15.0
github.com/multiformats/go-multihash v0.1.0
github.com/sirupsen/logrus v1.6.0
github.com/spf13/cobra v1.0.0
github.com/spf13/viper v1.7.0
github.com/vulcanize/go-eth-state-node-iterator v1.0.2
github.com/ipfs/go-cid v0.2.0
github.com/lib/pq v1.10.7
github.com/prometheus/client_golang v1.14.0
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.5.0
github.com/spf13/viper v1.12.0
golang.org/x/sync v0.1.0
)
replace github.com/ethereum/go-ethereum v1.10.17 => github.com/vulcanize/go-ethereum v1.10.17-statediff-4.0.1-alpha
require (
github.com/DataDog/zstd v1.5.2 // 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.2.0 // indirect
github.com/cockroachdb/errors v1.9.1 // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 // indirect
github.com/cockroachdb/redact v1.1.3 // indirect
github.com/deckarep/golang-set/v2 v2.1.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/georgysavva/scany v1.2.1 // indirect
github.com/getsentry/sentry-go v0.18.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/gofrs/flock v0.8.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
github.com/holiman/uint256 v1.2.2-0.20230321075855-87b91420868c // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.11.0 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.2.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.10.0 // indirect
github.com/jackc/pgx/v4 v4.15.0 // indirect
github.com/jackc/puddle v1.2.1 // indirect
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/klauspost/compress v1.15.15 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect
github.com/minio/sha256-simd v1.0.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.0.4 // 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.1.0 // indirect
github.com/multiformats/go-varint v0.0.6 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.39.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.3.0 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect
github.com/thoas/go-funk v0.9.2 // indirect
github.com/tklauser/go-sysconf v0.3.11 // indirect
github.com/tklauser/numcpus v0.6.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.6.0 // indirect
golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/blake3 v1.1.7 // indirect
)
replace github.com/ethereum/go-ethereum v1.11.5 => github.com/cerc-io/go-ethereum v1.11.5-statediff-5.0.4-alpha
+511 -484
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -18,7 +18,7 @@ package main
import (
"github.com/sirupsen/logrus"
"github.com/vulcanize/ipld-eth-state-snapshot/cmd"
"github.com/cerc-io/ipld-eth-state-snapshot/cmd"
)
func main() {
+157
View File
@@ -0,0 +1,157 @@
// VulcanizeDB
// Copyright © 2022 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package prom
import (
"github.com/prometheus/client_golang/prometheus"
mets "github.com/ethereum/go-ethereum/statediff/indexer/database/metrics"
)
// DBStatsGetter is an interface that gets sql.DBStats.
type DBStatsGetter interface {
Stats() mets.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, connSubsystem, "max_open"),
"Maximum number of open connections to the database.",
nil,
labels,
),
openDesc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, connSubsystem, "open"),
"The number of established connections both in use and idle.",
nil,
labels,
),
inUseDesc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, connSubsystem, "in_use"),
"The number of connections currently in use.",
nil,
labels,
),
idleDesc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, connSubsystem, "idle"),
"The number of idle connections.",
nil,
labels,
),
waitedForDesc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, connSubsystem, "waited_for"),
"The total number of connections waited for.",
nil,
labels,
),
blockedSecondsDesc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, connSubsystem, "blocked_seconds"),
"The total time blocked waiting for a new connection.",
nil,
labels,
),
closedMaxIdleDesc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, connSubsystem, "closed_max_idle"),
"The total number of connections closed due to SetMaxIdleConns.",
nil,
labels,
),
closedMaxLifetimeDesc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, connSubsystem, "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.MaxOpen()),
)
ch <- prometheus.MustNewConstMetric(
c.openDesc,
prometheus.GaugeValue,
float64(stats.Open()),
)
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()),
)
}
+127
View File
@@ -0,0 +1,127 @@
// VulcanizeDB
// Copyright © 2022 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package prom
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
const (
namespace = "ipld_eth_state_snapshot"
connSubsystem = "connections"
statsSubsystem = "stats"
)
var (
metrics bool
stateNodeCount prometheus.Counter
storageNodeCount prometheus.Counter
codeNodeCount prometheus.Counter
activeIteratorCount prometheus.Gauge
)
func Init() {
metrics = true
stateNodeCount = promauto.NewCounter(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: statsSubsystem,
Name: "state_node_count",
Help: "Number of state nodes processed",
})
storageNodeCount = promauto.NewCounter(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: statsSubsystem,
Name: "storage_node_count",
Help: "Number of storage nodes processed",
})
codeNodeCount = promauto.NewCounter(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: statsSubsystem,
Name: "code_node_count",
Help: "Number of code nodes processed",
})
activeIteratorCount = promauto.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Subsystem: statsSubsystem,
Name: "active_iterator_count",
Help: "Number of active iterators",
})
}
func RegisterGaugeFunc(name string, function func() float64) {
promauto.NewGaugeFunc(
prometheus.GaugeOpts{
Namespace: namespace,
Subsystem: statsSubsystem,
Name: name,
Help: name,
}, function)
}
// RegisterDBCollector create metric collector for given connection
func RegisterDBCollector(name string, db DBStatsGetter) {
if metrics {
prometheus.Register(NewDBStatsCollector(name, db))
}
}
// IncStateNodeCount increments the number of state nodes processed
func IncStateNodeCount() {
if metrics {
stateNodeCount.Inc()
}
}
// IncStorageNodeCount increments the number of storage nodes processed
func IncStorageNodeCount() {
if metrics {
storageNodeCount.Inc()
}
}
// IncCodeNodeCount increments the number of code nodes processed
func IncCodeNodeCount() {
if metrics {
codeNodeCount.Inc()
}
}
// IncActiveIterCount increments the number of active iterators
func IncActiveIterCount() {
if metrics {
activeIteratorCount.Inc()
}
}
// DecActiveIterCount decrements the number of active iterators
func DecActiveIterCount() {
if metrics {
activeIteratorCount.Dec()
}
}
func Enabled() bool {
return metrics
}
+47
View File
@@ -0,0 +1,47 @@
// VulcanizeDB
// Copyright © 2022 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package prom
import (
"errors"
"net/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/sirupsen/logrus"
)
var errPromHTTP = errors.New("can't start http server for prometheus")
// Serve start listening http
func Serve(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.
WithError(err).
WithField("module", "prom").
WithField("addr", addr).
Fatal(errPromHTTP)
}
}()
return &srv
}
+82 -44
View File
@@ -19,6 +19,8 @@ import (
"fmt"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/sirupsen/logrus"
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres"
@@ -26,15 +28,6 @@ import (
"github.com/spf13/viper"
)
const (
ANCIENT_DB_PATH = "ANCIENT_DB_PATH"
ETH_CLIENT_NAME = "ETH_CLIENT_NAME"
ETH_GENESIS_BLOCK = "ETH_GENESIS_BLOCK"
ETH_NETWORK_ID = "ETH_NETWORK_ID"
ETH_NODE_ID = "ETH_NODE_ID"
LVL_DB_PATH = "LVL_DB_PATH"
)
// SnapshotMode specifies the snapshot data output method
type SnapshotMode string
@@ -47,9 +40,10 @@ const (
// Config contains params for both databases the service uses
type Config struct {
Eth *EthConfig
DB *DBConfig
File *FileConfig
Eth *EthConfig
DB *DBConfig
File *FileConfig
Service *ServiceConfig
}
// EthConfig is config parameters for the chain.
@@ -69,35 +63,54 @@ type FileConfig struct {
OutputDir string
}
type ServiceConfig struct {
AllowedAccounts map[common.Address]struct{}
}
func NewConfig(mode SnapshotMode) (*Config, error) {
ret := &Config{
&EthConfig{},
&DBConfig{},
&FileConfig{},
&ServiceConfig{},
}
return ret, ret.Init(mode)
}
func NewInPlaceSnapshotConfig() *Config {
ret := &Config{
&EthConfig{},
&DBConfig{},
&FileConfig{},
&ServiceConfig{},
}
ret.DB.Init()
return ret
}
// Init Initialises config
func (c *Config) Init(mode SnapshotMode) error {
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(LOGRUS_FILE_TOML, LOGRUS_FILE)
viper.BindEnv(ETH_NODE_ID_TOML, ETH_NODE_ID)
viper.BindEnv(ETH_CLIENT_NAME_TOML, ETH_CLIENT_NAME)
viper.BindEnv(ETH_GENESIS_BLOCK_TOML, ETH_GENESIS_BLOCK)
viper.BindEnv(ETH_NETWORK_ID_TOML, ETH_NETWORK_ID)
viper.BindEnv(ETH_CHAIN_ID_TOML, ETH_CHAIN_ID)
c.Eth.NodeInfo = ethNode.Info{
ID: viper.GetString("ethereum.nodeID"),
ClientName: viper.GetString("ethereum.clientName"),
GenesisBlock: viper.GetString("ethereum.genesisBlock"),
NetworkID: viper.GetString("ethereum.networkID"),
ChainID: viper.GetUint64("ethereum.chainID"),
ID: viper.GetString(ETH_NODE_ID_TOML),
ClientName: viper.GetString(ETH_CLIENT_NAME_TOML),
GenesisBlock: viper.GetString(ETH_GENESIS_BLOCK_TOML),
NetworkID: viper.GetString(ETH_NETWORK_ID_TOML),
ChainID: viper.GetUint64(ETH_CHAIN_ID_TOML),
}
viper.BindEnv("leveldb.ancient", ANCIENT_DB_PATH)
viper.BindEnv("leveldb.path", LVL_DB_PATH)
viper.BindEnv(ANCIENT_DB_PATH_TOML, ANCIENT_DB_PATH)
viper.BindEnv(LVL_DB_PATH_TOML, LVL_DB_PATH)
c.Eth.AncientDBPath = viper.GetString("leveldb.ancient")
c.Eth.LevelDBPath = viper.GetString("leveldb.path")
c.Eth.AncientDBPath = viper.GetString(ANCIENT_DB_PATH_TOML)
c.Eth.LevelDBPath = viper.GetString(LVL_DB_PATH_TOML)
switch mode {
case FileSnapshot:
@@ -107,41 +120,66 @@ func (c *Config) Init(mode SnapshotMode) error {
default:
return fmt.Errorf("no output mode specified")
}
return nil
return c.Service.Init()
}
func (c *DBConfig) Init() {
viper.BindEnv("database.name", "DATABASE_NAME")
viper.BindEnv("database.hostname", "DATABASE_HOSTNAME")
viper.BindEnv("database.port", "DATABASE_PORT")
viper.BindEnv("database.user", "DATABASE_USER")
viper.BindEnv("database.password", "DATABASE_PASSWORD")
viper.BindEnv("database.maxIdle", "DATABASE_MAX_IDLE_CONNECTIONS")
viper.BindEnv("database.maxOpen", "DATABASE_MAX_OPEN_CONNECTIONS")
viper.BindEnv("database.maxLifetime", "DATABASE_MAX_CONN_LIFETIME")
viper.BindEnv(DATABASE_NAME_TOML, DATABASE_NAME)
viper.BindEnv(DATABASE_HOSTNAME_TOML, DATABASE_HOSTNAME)
viper.BindEnv(DATABASE_PORT_TOML, DATABASE_PORT)
viper.BindEnv(DATABASE_USER_TOML, DATABASE_USER)
viper.BindEnv(DATABASE_PASSWORD_TOML, DATABASE_PASSWORD)
viper.BindEnv(DATABASE_MAX_IDLE_CONNECTIONS_TOML, DATABASE_MAX_IDLE_CONNECTIONS)
viper.BindEnv(DATABASE_MAX_OPEN_CONNECTIONS_TOML, DATABASE_MAX_OPEN_CONNECTIONS)
viper.BindEnv(DATABASE_MAX_CONN_LIFETIME_TOML, DATABASE_MAX_CONN_LIFETIME)
dbParams := postgres.Config{}
// DB params
dbParams.DatabaseName = viper.GetString("database.name")
dbParams.Hostname = viper.GetString("database.hostname")
dbParams.Port = viper.GetInt("database.port")
dbParams.Username = viper.GetString("database.user")
dbParams.Password = viper.GetString("database.password")
dbParams.DatabaseName = viper.GetString(DATABASE_NAME_TOML)
dbParams.Hostname = viper.GetString(DATABASE_HOSTNAME_TOML)
dbParams.Port = viper.GetInt(DATABASE_PORT_TOML)
dbParams.Username = viper.GetString(DATABASE_USER_TOML)
dbParams.Password = viper.GetString(DATABASE_PASSWORD_TOML)
// Connection config
dbParams.MaxIdle = viper.GetInt("database.maxIdle")
dbParams.MaxConns = viper.GetInt("database.maxOpen")
dbParams.MaxConnLifetime = time.Duration(viper.GetInt("database.maxLifetime")) * time.Second
dbParams.MaxIdle = viper.GetInt(DATABASE_MAX_IDLE_CONNECTIONS_TOML)
dbParams.MaxConns = viper.GetInt(DATABASE_MAX_OPEN_CONNECTIONS_TOML)
dbParams.MaxConnLifetime = time.Duration(viper.GetInt(DATABASE_MAX_CONN_LIFETIME_TOML)) * time.Second
c.ConnConfig = dbParams
c.URI = dbParams.DbConnectionString()
}
func (c *FileConfig) Init() error {
viper.BindEnv("file.outputDir", "FILE_OUTPUT_DIR")
c.OutputDir = viper.GetString("file.outputDir")
viper.BindEnv(FILE_OUTPUT_DIR_TOML, FILE_OUTPUT_DIR)
c.OutputDir = viper.GetString(FILE_OUTPUT_DIR_TOML)
if c.OutputDir == "" {
logrus.Infof("no output directory set, using default: %s", defaultOutputDir)
c.OutputDir = defaultOutputDir
}
return nil
}
func (c *ServiceConfig) Init() error {
viper.BindEnv(SNAPSHOT_BLOCK_HEIGHT_TOML, SNAPSHOT_BLOCK_HEIGHT)
viper.BindEnv(SNAPSHOT_START_HEIGHT_TOML, SNAPSHOT_START_HEIGHT)
viper.BindEnv(SNAPSHOT_END_HEIGHT_TOML, SNAPSHOT_END_HEIGHT)
viper.BindEnv(SNAPSHOT_MODE_TOML, SNAPSHOT_MODE)
viper.BindEnv(SNAPSHOT_WORKERS_TOML, SNAPSHOT_WORKERS)
viper.BindEnv(PROM_HTTP_TOML, PROM_HTTP)
viper.BindEnv(PROM_HTTP_ADDR_TOML, PROM_HTTP_ADDR)
viper.BindEnv(PROM_METRICS_TOML, PROM_METRICS)
viper.BindEnv(SNAPSHOT_ACCOUNTS_TOML, SNAPSHOT_ACCOUNTS)
var allowedAccounts []string
viper.UnmarshalKey(SNAPSHOT_ACCOUNTS_TOML, &allowedAccounts)
accountsLen := len(allowedAccounts)
if accountsLen != 0 {
c.AllowedAccounts = make(map[common.Address]struct{}, accountsLen)
for _, allowedAccount := range allowedAccounts {
c.AllowedAccounts[common.HexToAddress(allowedAccount)] = struct{}{}
}
} else {
logrus.Infof("no snapshot addresses specified, will perform snapshot of entire trie(s)")
}
return nil
}
+136
View File
@@ -0,0 +1,136 @@
// Copyright © 2020 Vulcanize, Inc
//
// 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 snapshot
// ENV variables
const (
SNAPSHOT_BLOCK_HEIGHT = "SNAPSHOT_BLOCK_HEIGHT"
SNAPSHOT_WORKERS = "SNAPSHOT_WORKERS"
SNAPSHOT_RECOVERY_FILE = "SNAPSHOT_RECOVERY_FILE"
SNAPSHOT_MODE = "SNAPSHOT_MODE"
SNAPSHOT_START_HEIGHT = "SNAPSHOT_START_HEIGHT"
SNAPSHOT_END_HEIGHT = "SNAPSHOT_END_HEIGHT"
SNAPSHOT_ACCOUNTS = "SNAPSHOT_ACCOUNTS"
LOGRUS_LEVEL = "LOGRUS_LEVEL"
LOGRUS_FILE = "LOGRUS_FILE"
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"
FILE_OUTPUT_DIR = "FILE_OUTPUT_DIR"
ANCIENT_DB_PATH = "ANCIENT_DB_PATH"
LVL_DB_PATH = "LVL_DB_PATH"
ETH_CLIENT_NAME = "ETH_CLIENT_NAME"
ETH_GENESIS_BLOCK = "ETH_GENESIS_BLOCK"
ETH_NETWORK_ID = "ETH_NETWORK_ID"
ETH_NODE_ID = "ETH_NODE_ID"
ETH_CHAIN_ID = "ETH_CHAIN_ID"
DATABASE_NAME = "DATABASE_NAME"
DATABASE_HOSTNAME = "DATABASE_HOSTNAME"
DATABASE_PORT = "DATABASE_PORT"
DATABASE_USER = "DATABASE_USER"
DATABASE_PASSWORD = "DATABASE_PASSWORD"
DATABASE_MAX_IDLE_CONNECTIONS = "DATABASE_MAX_IDLE_CONNECTIONS"
DATABASE_MAX_OPEN_CONNECTIONS = "DATABASE_MAX_OPEN_CONNECTIONS"
DATABASE_MAX_CONN_LIFETIME = "DATABASE_MAX_CONN_LIFETIME"
)
// TOML bindings
const (
SNAPSHOT_BLOCK_HEIGHT_TOML = "snapshot.blockHeight"
SNAPSHOT_WORKERS_TOML = "snapshot.workers"
SNAPSHOT_RECOVERY_FILE_TOML = "snapshot.recoveryFile"
SNAPSHOT_MODE_TOML = "snapshot.mode"
SNAPSHOT_START_HEIGHT_TOML = "snapshot.startHeight"
SNAPSHOT_END_HEIGHT_TOML = "snapshot.endHeight"
SNAPSHOT_ACCOUNTS_TOML = "snapshot.accounts"
LOGRUS_LEVEL_TOML = "log.level"
LOGRUS_FILE_TOML = "log.file"
PROM_METRICS_TOML = "prom.metrics"
PROM_HTTP_TOML = "prom.http"
PROM_HTTP_ADDR_TOML = "prom.httpAddr"
PROM_HTTP_PORT_TOML = "prom.httpPort"
PROM_DB_STATS_TOML = "prom.dbStats"
FILE_OUTPUT_DIR_TOML = "file.outputDir"
ANCIENT_DB_PATH_TOML = "leveldb.ancient"
LVL_DB_PATH_TOML = "leveldb.path"
ETH_CLIENT_NAME_TOML = "ethereum.clientName"
ETH_GENESIS_BLOCK_TOML = "ethereum.genesisBlock"
ETH_NETWORK_ID_TOML = "ethereum.networkID"
ETH_NODE_ID_TOML = "ethereum.nodeID"
ETH_CHAIN_ID_TOML = "ethereum.chainID"
DATABASE_NAME_TOML = "database.name"
DATABASE_HOSTNAME_TOML = "database.hostname"
DATABASE_PORT_TOML = "database.port"
DATABASE_USER_TOML = "database.user"
DATABASE_PASSWORD_TOML = "database.password"
DATABASE_MAX_IDLE_CONNECTIONS_TOML = "database.maxIdle"
DATABASE_MAX_OPEN_CONNECTIONS_TOML = "database.maxOpen"
DATABASE_MAX_CONN_LIFETIME_TOML = "database.maxLifetime"
)
// CLI flags
const (
SNAPSHOT_BLOCK_HEIGHT_CLI = "block-height"
SNAPSHOT_WORKERS_CLI = "workers"
SNAPSHOT_RECOVERY_FILE_CLI = "recovery-file"
SNAPSHOT_MODE_CLI = "snapshot-mode"
SNAPSHOT_START_HEIGHT_CLI = "start-height"
SNAPSHOT_END_HEIGHT_CLI = "end-height"
SNAPSHOT_ACCOUNTS_CLI = "snapshot-accounts"
LOGRUS_LEVEL_CLI = "log-level"
LOGRUS_FILE_CLI = "log-file"
PROM_METRICS_CLI = "prom-metrics"
PROM_HTTP_CLI = "prom-http"
PROM_HTTP_ADDR_CLI = "prom-httpAddr"
PROM_HTTP_PORT_CLI = "prom-httpPort"
PROM_DB_STATS_CLI = "prom-dbStats"
FILE_OUTPUT_DIR_CLI = "output-dir"
ANCIENT_DB_PATH_CLI = "ancient-path"
LVL_DB_PATH_CLI = "leveldb-path"
ETH_CLIENT_NAME_CLI = "ethereum-client-name"
ETH_GENESIS_BLOCK_CLI = "ethereum-genesis-block"
ETH_NETWORK_ID_CLI = "ethereum-network-id"
ETH_NODE_ID_CLI = "ethereum-node-id"
ETH_CHAIN_ID_CLI = "ethereum-chain-id"
DATABASE_NAME_CLI = "database-name"
DATABASE_HOSTNAME_CLI = "database-hostname"
DATABASE_PORT_CLI = "database-port"
DATABASE_USER_CLI = "database-user"
DATABASE_PASSWORD_CLI = "database-password"
DATABASE_MAX_IDLE_CONNECTIONS_CLI = "database-max-idle"
DATABASE_MAX_OPEN_CONNECTIONS_CLI = "database-max-open"
DATABASE_MAX_CONN_LIFETIME_CLI = "database-max-lifetime"
)
+70 -84
View File
@@ -21,37 +21,39 @@ import (
"math/big"
"os"
"path/filepath"
"strconv"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/lib/pq"
"github.com/ipfs/go-cid"
blockstore "github.com/ipfs/go-ipfs-blockstore"
dshelp "github.com/ipfs/go-ipfs-ds-help"
"github.com/multiformats/go-multihash"
"github.com/sirupsen/logrus"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/statediff/indexer/ipld"
"github.com/ethereum/go-ethereum/statediff/indexer/models"
nodeinfo "github.com/ethereum/go-ethereum/statediff/indexer/node"
"github.com/ethereum/go-ethereum/statediff/indexer/shared"
snapt "github.com/vulcanize/ipld-eth-state-snapshot/pkg/types"
"github.com/ethereum/go-ethereum/statediff/indexer/shared/schema"
"github.com/cerc-io/ipld-eth-state-snapshot/pkg/prom"
snapt "github.com/cerc-io/ipld-eth-state-snapshot/pkg/types"
)
var _ snapt.Publisher = (*publisher)(nil)
var (
// tables written once per block
perBlockTables = []*snapt.Table{
&snapt.TableIPLDBlock,
&snapt.TableNodeInfo,
&snapt.TableHeader,
perBlockTables = []*schema.Table{
&schema.TableIPLDBlock,
&schema.TableNodeInfo,
&schema.TableHeader,
}
// tables written during state iteration
perNodeTables = []*snapt.Table{
&snapt.TableIPLDBlock,
&snapt.TableStateNode,
&snapt.TableStorageNode,
perNodeTables = []*schema.Table{
&schema.TableIPLDBlock,
&schema.TableStateNode,
&schema.TableStorageNode,
}
)
@@ -100,12 +102,12 @@ func newFileWriter(path string) (ret fileWriter, err error) {
return
}
func (tx fileWriters) write(tbl *snapt.Table, args ...interface{}) error {
func (tx fileWriters) write(tbl *schema.Table, args ...interface{}) error {
row := tbl.ToCsvRow(args...)
return tx[tbl.Name].Write(row)
}
func makeFileWriters(dir string, tables []*snapt.Table) (fileWriters, error) {
func makeFileWriters(dir string, tables []*schema.Table) (fileWriters, error) {
if err := os.MkdirAll(dir, 0755); err != nil {
return nil, err
}
@@ -158,22 +160,14 @@ func (p *publisher) BeginTx() (snapt.Tx, error) {
return fileTx{writers}, nil
}
// PublishRaw derives a cid from raw bytes and provided codec and multihash type, and writes it to the db tx
// returns the CID and blockstore prefixed multihash key
func (tx fileWriters) publishRaw(codec uint64, raw []byte, height *big.Int) (cid, prefixedKey string, err error) {
c, err := ipld.RawdataToCid(codec, raw, multihash.KECCAK_256)
if err != nil {
return
}
cid = c.String()
prefixedKey, err = tx.publishIPLD(c, raw, height)
return
func (tx fileWriters) publishIPLD(c cid.Cid, raw []byte, height *big.Int) error {
return tx.write(&schema.TableIPLDBlock, height.String(), c.String(), raw)
}
func (tx fileWriters) publishIPLD(c cid.Cid, raw []byte, height *big.Int) (string, error) {
dbKey := dshelp.MultihashToDsKey(c.Hash())
prefixedKey := blockstore.BlockPrefix.String() + dbKey.String()
return prefixedKey, tx.write(&snapt.TableIPLDBlock, height.String(), prefixedKey, raw)
// PublishIPLD writes an IPLD to the ipld.blocks blockstore
func (p *publisher) PublishIPLD(c cid.Cid, raw []byte, height *big.Int, snapTx snapt.Tx) error {
tx := snapTx.(fileTx)
return tx.publishIPLD(c, raw, height)
}
// PublishHeader writes the header to the ipfs backing pg datastore and adds secondary
@@ -183,96 +177,88 @@ func (p *publisher) PublishHeader(header *types.Header) error {
if err != nil {
return err
}
if _, err = p.writers.publishIPLD(headerNode.Cid(), headerNode.RawData(), header.Number); err != nil {
if err := p.writers.publishIPLD(headerNode.Cid(), headerNode.RawData(), header.Number); err != nil {
return err
}
mhKey := shared.MultihashKeyFromCID(headerNode.Cid())
err = p.writers.write(&snapt.TableNodeInfo, p.nodeInfo.GenesisBlock, p.nodeInfo.NetworkID, p.nodeInfo.ID,
err = p.writers.write(&schema.TableNodeInfo, p.nodeInfo.GenesisBlock, p.nodeInfo.NetworkID, p.nodeInfo.ID,
p.nodeInfo.ClientName, p.nodeInfo.ChainID)
if err != nil {
return err
}
err = p.writers.write(&snapt.TableHeader, header.Number.String(), header.Hash().Hex(), header.ParentHash.Hex(),
headerNode.Cid().String(), 0, p.nodeInfo.ID, 0, header.Root.Hex(), header.TxHash.Hex(),
header.ReceiptHash.Hex(), header.UncleHash.Hex(), header.Bloom.Bytes(), header.Time, mhKey,
0, header.Coinbase.String())
err = p.writers.write(&schema.TableHeader,
header.Number.String(),
header.Hash().Hex(),
header.ParentHash.Hex(),
headerNode.Cid().String(),
"0",
pq.StringArray([]string{p.nodeInfo.ID}),
"0",
header.Root.Hex(),
header.TxHash.Hex(),
header.ReceiptHash.Hex(),
header.UncleHash.Hex(),
header.Bloom.Bytes(),
strconv.FormatUint(header.Time, 10),
header.Coinbase.String())
if err != nil {
return err
}
return p.writers.Commit()
}
// PublishStateNode writes the state node to the ipfs backing datastore and adds secondary indexes
// in the state_cids table
func (p *publisher) PublishStateNode(node *snapt.Node, headerID string, height *big.Int, snapTx snapt.Tx) error {
var stateKey string
if !snapt.IsNullHash(node.Key) {
stateKey = node.Key.Hex()
}
// PublishStateLeafNode writes the state node eth.state_cids
func (p *publisher) PublishStateLeafNode(stateNode *models.StateNodeModel, snapTx snapt.Tx) error {
tx := snapTx.(fileTx)
stateCIDStr, mhKey, err := tx.publishRaw(ipld.MEthStateTrie, node.Value, height)
if err != nil {
return err
}
err = tx.write(&snapt.TableStateNode, height.String(), headerID, stateKey, stateCIDStr, node.Path,
node.NodeType, false, mhKey)
err := tx.write(&schema.TableStateNode,
stateNode.BlockNumber,
stateNode.HeaderID,
stateNode.StateKey,
stateNode.CID,
false,
stateNode.Balance,
strconv.FormatUint(stateNode.Nonce, 10),
stateNode.CodeHash,
stateNode.StorageRoot,
false)
if err != nil {
return err
}
// increment state node counter.
atomic.AddUint64(&p.stateNodeCounter, 1)
prom.IncStateNodeCount()
// increment current batch size counter
p.currBatchSize += 2
return err
}
// PublishStorageNode writes the storage node to the ipfs backing pg datastore and adds secondary
// indexes in the storage_cids table
func (p *publisher) PublishStorageNode(node *snapt.Node, headerID string, height *big.Int, statePath []byte, snapTx snapt.Tx) error {
var storageKey string
if !snapt.IsNullHash(node.Key) {
storageKey = node.Key.Hex()
}
// PublishStorageLeafNode writes the storage node to eth.storage_cids
func (p *publisher) PublishStorageLeafNode(storageNode *models.StorageNodeModel, snapTx snapt.Tx) error {
tx := snapTx.(fileTx)
storageCIDStr, mhKey, err := tx.publishRaw(ipld.MEthStorageTrie, node.Value, height)
if err != nil {
return err
}
err = tx.write(&snapt.TableStorageNode, height.String(), headerID, statePath, storageKey, storageCIDStr, node.Path,
node.NodeType, false, mhKey)
err := tx.write(&schema.TableStorageNode,
storageNode.BlockNumber,
storageNode.HeaderID,
storageNode.StateKey,
storageNode.StorageKey,
storageNode.CID,
false,
storageNode.Value,
false)
if err != nil {
return err
}
// increment storage node counter.
atomic.AddUint64(&p.storageNodeCounter, 1)
prom.IncStorageNodeCount()
// increment current batch size counter
p.currBatchSize += 2
return nil
}
// PublishCode writes code to the ipfs backing pg datastore
func (p *publisher) PublishCode(height *big.Int, codeHash common.Hash, codeBytes []byte, snapTx snapt.Tx) error {
// no codec for code, doesn't matter though since blockstore key is multihash-derived
mhKey, err := shared.MultihashKeyFromKeccak256(codeHash)
if err != nil {
return fmt.Errorf("error deriving multihash key from codehash: %v", err)
}
tx := snapTx.(fileTx)
if err = tx.write(&snapt.TableIPLDBlock, height.String(), mhKey, codeBytes); err != nil {
return fmt.Errorf("error publishing code IPLD: %v", err)
}
// increment code node counter.
atomic.AddUint64(&p.codeNodeCounter, 1)
p.currBatchSize++
return nil
}
func (p *publisher) PrepareTxForBatch(tx snapt.Tx, maxBatchSize uint) (snapt.Tx, error) {
return tx, nil
}
+21 -24
View File
@@ -9,24 +9,26 @@ import (
"path/filepath"
"testing"
"github.com/ethereum/go-ethereum/statediff/indexer/ipld"
"github.com/jackc/pgx/v4"
"github.com/ethereum/go-ethereum/statediff/indexer/shared/schema"
fixt "github.com/vulcanize/ipld-eth-state-snapshot/fixture"
snapt "github.com/vulcanize/ipld-eth-state-snapshot/pkg/types"
"github.com/vulcanize/ipld-eth-state-snapshot/test"
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres"
"github.com/ethereum/go-ethereum/statediff/indexer/ipld"
"github.com/ethereum/go-ethereum/statediff/indexer/test_helpers"
fixt "github.com/cerc-io/ipld-eth-state-snapshot/fixture"
"github.com/cerc-io/ipld-eth-state-snapshot/test"
)
var (
pgConfig = test.DefaultPgConfig
nodeInfo = test.DefaultNodeInfo
// tables ordered according to fkey depedencies
allTables = []*snapt.Table{
&snapt.TableIPLDBlock,
&snapt.TableNodeInfo,
&snapt.TableHeader,
&snapt.TableStateNode,
&snapt.TableStorageNode,
allTables = []*schema.Table{
&schema.TableIPLDBlock,
&schema.TableNodeInfo,
&schema.TableHeader,
&schema.TableStateNode,
&schema.TableStorageNode,
}
)
@@ -37,8 +39,7 @@ func writeFiles(t *testing.T, dir string) *publisher {
tx, err := pub.BeginTx()
test.NoError(t, err)
headerID := fixt.Block1_Header.Hash().String()
test.NoError(t, pub.PublishStateNode(&fixt.Block1_StateNode0, headerID, fixt.Block1_Header.Number, tx))
test.NoError(t, pub.PublishStateLeafNode(&fixt.Block1_StateNode0, tx))
test.NoError(t, tx.Commit())
return pub
@@ -46,7 +47,7 @@ func writeFiles(t *testing.T, dir string) *publisher {
// verify that we can parse the csvs
// TODO check actual data
func verifyFileData(t *testing.T, path string, tbl *snapt.Table) {
func verifyFileData(t *testing.T, path string, tbl *schema.Table) {
file, err := os.Open(path)
test.NoError(t, err)
r := csv.NewReader(file)
@@ -88,27 +89,23 @@ func TestPgCopy(t *testing.T) {
pub := writeFiles(t, dir)
ctx := context.Background()
conn, err := pgx.Connect(ctx, pgConfig.DbConnectionString())
driver, err := postgres.NewSQLXDriver(ctx, pgConfig, nodeInfo)
test.NoError(t, err)
db := postgres.NewPostgresDB(driver, false)
// clear existing test data
pgDeleteTable := `DELETE FROM %s`
for _, tbl := range allTables {
_, err = conn.Exec(ctx, fmt.Sprintf(pgDeleteTable, tbl.Name))
test.NoError(t, err)
}
test_helpers.TearDownDB(t, db)
// copy from files
pgCopyStatement := `COPY %s FROM '%s' CSV`
for _, tbl := range perBlockTables {
stm := fmt.Sprintf(pgCopyStatement, tbl.Name, TableFile(pub.dir, tbl.Name))
_, err = conn.Exec(ctx, stm)
_, err = db.Exec(ctx, stm)
test.NoError(t, err)
}
for i := uint32(0); i < pub.txCounter; i++ {
for _, tbl := range perNodeTables {
stm := fmt.Sprintf(pgCopyStatement, tbl.Name, TableFile(pub.txDir(i), tbl.Name))
_, err = conn.Exec(ctx, stm)
_, err = db.Exec(ctx, stm)
test.NoError(t, err)
}
}
@@ -122,7 +119,7 @@ func TestPgCopy(t *testing.T) {
BlockHash string
}
var header res
err = conn.QueryRow(ctx, pgQueryHeader, fixt.Block1_Header.Number.Uint64()).Scan(
err = db.QueryRow(ctx, pgQueryHeader, fixt.Block1_Header.Number.Uint64()).Scan(
&header.CID, &header.BlockHash)
test.NoError(t, err)
+64 -78
View File
@@ -17,24 +17,25 @@ package pg
import (
"context"
"fmt"
"math/big"
"strconv"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ipfs/go-cid"
blockstore "github.com/ipfs/go-ipfs-blockstore"
dshelp "github.com/ipfs/go-ipfs-ds-help"
"github.com/multiformats/go-multihash"
"github.com/lib/pq"
"github.com/sirupsen/logrus"
log "github.com/sirupsen/logrus"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql"
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres"
"github.com/ethereum/go-ethereum/statediff/indexer/ipld"
"github.com/ethereum/go-ethereum/statediff/indexer/shared"
snapt "github.com/vulcanize/ipld-eth-state-snapshot/pkg/types"
"github.com/ethereum/go-ethereum/statediff/indexer/models"
"github.com/ethereum/go-ethereum/statediff/indexer/shared/schema"
"github.com/cerc-io/ipld-eth-state-snapshot/pkg/prom"
snapt "github.com/cerc-io/ipld-eth-state-snapshot/pkg/types"
)
var _ snapt.Publisher = (*publisher)(nil)
@@ -86,23 +87,15 @@ func (p *publisher) BeginTx() (snapt.Tx, error) {
}}, nil
}
// PublishRaw derives a cid from raw bytes and provided codec and multihash type, and writes it to the db tx
// returns the CID and blockstore prefixed multihash key
func (tx pubTx) publishRaw(codec uint64, raw []byte, height *big.Int) (cid, prefixedKey string, err error) {
c, err := ipld.RawdataToCid(codec, raw, multihash.KECCAK_256)
if err != nil {
return
}
cid = c.String()
prefixedKey, err = tx.publishIPLD(c, raw, height)
return
func (tx pubTx) publishIPLD(c cid.Cid, raw []byte, height *big.Int) error {
_, err := tx.Exec(schema.TableIPLDBlock.ToInsertStatement(false), height.Uint64(), c.String(), raw)
return err
}
func (tx pubTx) publishIPLD(c cid.Cid, raw []byte, height *big.Int) (string, error) {
dbKey := dshelp.MultihashToDsKey(c.Hash())
prefixedKey := blockstore.BlockPrefix.String() + dbKey.String()
_, err := tx.Exec(snapt.TableIPLDBlock.ToInsertStatement(), height.Uint64(), prefixedKey, raw)
return prefixedKey, err
// PublishIPLD writes an IPLD to the ipld.blocks blockstore
func (p *publisher) PublishIPLD(c cid.Cid, raw []byte, height *big.Int, snapTx snapt.Tx) error {
tx := snapTx.(pubTx)
return tx.publishIPLD(c, raw, height)
}
// PublishHeader writes the header to the ipfs backing pg datastore and adds secondary indexes in the header_cids table
@@ -117,92 +110,85 @@ func (p *publisher) PublishHeader(header *types.Header) (err error) {
return err
}
tx := pubTx{snapTx, nil}
defer func() { err = snapt.CommitOrRollback(tx, err) }()
defer func() {
err = snapt.CommitOrRollback(tx, err)
if err != nil {
logrus.Errorf("CommitOrRollback failed: %s", err)
}
}()
if _, err = tx.publishIPLD(headerNode.Cid(), headerNode.RawData(), header.Number); err != nil {
if err := tx.publishIPLD(headerNode.Cid(), headerNode.RawData(), header.Number); err != nil {
return err
}
mhKey := shared.MultihashKeyFromCID(headerNode.Cid())
_, err = tx.Exec(snapt.TableHeader.ToInsertStatement(), header.Number.Uint64(), header.Hash().Hex(),
header.ParentHash.Hex(), headerNode.Cid().String(), "0", p.db.NodeID(), "0",
header.Root.Hex(), header.TxHash.Hex(), header.ReceiptHash.Hex(), header.UncleHash.Hex(),
header.Bloom.Bytes(), header.Time, mhKey, 0, header.Coinbase.String())
_, err = tx.Exec(schema.TableHeader.ToInsertStatement(false),
header.Number.Uint64(),
header.Hash().Hex(),
header.ParentHash.Hex(),
headerNode.Cid().String(),
"0",
pq.StringArray([]string{p.db.NodeID()}),
"0",
header.Root.Hex(),
header.TxHash.Hex(),
header.ReceiptHash.Hex(),
header.UncleHash.Hex(),
header.Bloom.Bytes(),
strconv.FormatUint(header.Time, 10),
header.Coinbase.String())
return err
}
// PublishStateNode writes the state node to the ipfs backing datastore and adds secondary indexes in the state_cids table
func (p *publisher) PublishStateNode(node *snapt.Node, headerID string, height *big.Int, snapTx snapt.Tx) error {
var stateKey string
if !snapt.IsNullHash(node.Key) {
stateKey = node.Key.Hex()
}
// PublishStateLeafNode writes the state leaf node to eth.state_cids
func (p *publisher) PublishStateLeafNode(stateNode *models.StateNodeModel, snapTx snapt.Tx) error {
tx := snapTx.(pubTx)
stateCIDStr, mhKey, err := tx.publishRaw(ipld.MEthStateTrie, node.Value, height)
if err != nil {
return err
}
_, err = tx.Exec(snapt.TableStateNode.ToInsertStatement(),
height.Uint64(), headerID, stateKey, stateCIDStr, node.Path, node.NodeType, false, mhKey)
_, err := tx.Exec(schema.TableStateNode.ToInsertStatement(false),
stateNode.BlockNumber,
stateNode.HeaderID,
stateNode.StateKey,
stateNode.CID,
false,
stateNode.Balance,
stateNode.Nonce,
stateNode.CodeHash,
stateNode.StorageRoot,
false)
if err != nil {
return err
}
// increment state node counter.
atomic.AddUint64(&p.stateNodeCounter, 1)
prom.IncStateNodeCount()
// increment current batch size counter
p.currBatchSize += 2
return err
}
// PublishStorageNode writes the storage node to the ipfs backing pg datastore and adds secondary indexes in the storage_cids table
func (p *publisher) PublishStorageNode(node *snapt.Node, headerID string, height *big.Int, statePath []byte, snapTx snapt.Tx) error {
var storageKey string
if !snapt.IsNullHash(node.Key) {
storageKey = node.Key.Hex()
}
// PublishStorageLeafNode writes the storage leaf node to eth.storage_cids
func (p *publisher) PublishStorageLeafNode(storageNode *models.StorageNodeModel, snapTx snapt.Tx) error {
tx := snapTx.(pubTx)
storageCIDStr, mhKey, err := tx.publishRaw(ipld.MEthStorageTrie, node.Value, height)
if err != nil {
return err
}
_, err = tx.Exec(snapt.TableStorageNode.ToInsertStatement(),
height.Uint64(), headerID, statePath, storageKey, storageCIDStr, node.Path, node.NodeType, false, mhKey)
_, err := tx.Exec(schema.TableStorageNode.ToInsertStatement(false),
storageNode.BlockNumber,
storageNode.HeaderID,
storageNode.StateKey,
storageNode.StorageKey,
storageNode.CID,
false,
storageNode.Value,
false)
if err != nil {
return err
}
// increment storage node counter.
atomic.AddUint64(&p.storageNodeCounter, 1)
prom.IncStorageNodeCount()
// increment current batch size counter
p.currBatchSize += 2
return err
}
// PublishCode writes code to the ipfs backing pg datastore
func (p *publisher) PublishCode(height *big.Int, codeHash common.Hash, codeBytes []byte, snapTx snapt.Tx) error {
// no codec for code, doesn't matter though since blockstore key is multihash-derived
mhKey, err := shared.MultihashKeyFromKeccak256(codeHash)
if err != nil {
return fmt.Errorf("error deriving multihash key from codehash: %v", err)
}
tx := snapTx.(pubTx)
if _, err = tx.Exec(snapt.TableIPLDBlock.ToInsertStatement(), height.Uint64(), mhKey, codeBytes); err != nil {
return fmt.Errorf("error publishing code IPLD: %v", err)
}
// increment code node counter.
atomic.AddUint64(&p.codeNodeCounter, 1)
p.currBatchSize++
return nil
}
func (p *publisher) PrepareTxForBatch(tx snapt.Tx, maxBatchSize uint) (snapt.Tx, error) {
var err error
// maximum batch size reached, commit the current transaction and begin a new transaction.
+19 -26
View File
@@ -2,41 +2,38 @@ package pg
import (
"context"
"fmt"
"testing"
"github.com/ethereum/go-ethereum/statediff/indexer/shared/schema"
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres"
"github.com/ethereum/go-ethereum/statediff/indexer/ipld"
"github.com/jackc/pgx/v4"
"github.com/ethereum/go-ethereum/statediff/indexer/test_helpers"
fixt "github.com/vulcanize/ipld-eth-state-snapshot/fixture"
snapt "github.com/vulcanize/ipld-eth-state-snapshot/pkg/types"
"github.com/vulcanize/ipld-eth-state-snapshot/test"
fixt "github.com/cerc-io/ipld-eth-state-snapshot/fixture"
"github.com/cerc-io/ipld-eth-state-snapshot/test"
)
var (
pgConfig = test.DefaultPgConfig
nodeInfo = test.DefaultNodeInfo
// tables ordered according to fkey depedencies
allTables = []*snapt.Table{
&snapt.TableIPLDBlock,
&snapt.TableNodeInfo,
&snapt.TableHeader,
&snapt.TableStateNode,
&snapt.TableStorageNode,
allTables = []*schema.Table{
&schema.TableIPLDBlock,
&schema.TableNodeInfo,
&schema.TableHeader,
&schema.TableStateNode,
&schema.TableStorageNode,
}
)
func writeData(t *testing.T) *publisher {
driver, err := postgres.NewPGXDriver(context.Background(), pgConfig, nodeInfo)
test.NoError(t, err)
pub := NewPublisher(postgres.NewPostgresDB(driver))
func writeData(t *testing.T, db *postgres.DB) *publisher {
pub := NewPublisher(db)
test.NoError(t, pub.PublishHeader(&fixt.Block1_Header))
tx, err := pub.BeginTx()
test.NoError(t, err)
headerID := fixt.Block1_Header.Hash().String()
test.NoError(t, pub.PublishStateNode(&fixt.Block1_StateNode0, headerID, fixt.Block1_Header.Number, tx))
test.NoError(t, pub.PublishStateLeafNode(&fixt.Block1_StateNode0, tx))
test.NoError(t, tx.Commit())
return pub
@@ -47,17 +44,13 @@ func TestBasic(t *testing.T) {
test.NeedsDB(t)
ctx := context.Background()
conn, err := pgx.Connect(ctx, pgConfig.DbConnectionString())
driver, err := postgres.NewSQLXDriver(ctx, pgConfig, nodeInfo)
test.NoError(t, err)
db := postgres.NewPostgresDB(driver, false)
// clear existing test data
pgDeleteTable := `DELETE FROM %s`
for _, tbl := range allTables {
_, err = conn.Exec(ctx, fmt.Sprintf(pgDeleteTable, tbl.Name))
test.NoError(t, err)
}
test_helpers.TearDownDB(t, db)
_ = writeData(t)
_ = writeData(t, db)
// check header was successfully committed
pgQueryHeader := `SELECT cid, block_hash
@@ -68,7 +61,7 @@ func TestBasic(t *testing.T) {
BlockHash string
}
var header res
err = conn.QueryRow(ctx, pgQueryHeader, fixt.Block1_Header.Number.Uint64()).Scan(
err = db.QueryRow(ctx, pgQueryHeader, fixt.Block1_Header.Number.Uint64()).Scan(
&header.CID, &header.BlockHash)
test.NoError(t, err)
+437 -187
View File
@@ -17,10 +17,10 @@ package snapshot
import (
"bytes"
"context"
"errors"
"fmt"
"math/big"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
@@ -29,11 +29,15 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/statediff/indexer/ipld"
"github.com/ethereum/go-ethereum/statediff/indexer/models"
"github.com/ethereum/go-ethereum/trie"
iter "github.com/ethereum/go-ethereum/trie/concurrent_iterator"
log "github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup"
iter "github.com/vulcanize/go-eth-state-node-iterator"
. "github.com/vulcanize/ipld-eth-state-snapshot/pkg/types"
"github.com/cerc-io/ipld-eth-state-snapshot/pkg/prom"
. "github.com/cerc-io/ipld-eth-state-snapshot/pkg/types"
)
var (
@@ -47,20 +51,18 @@ var (
// Service holds ethDB and stateDB to read data from lvldb and Publisher
// to publish trie in postgres DB.
type Service struct {
ethDB ethdb.Database
stateDB state.Database
ipfsPublisher Publisher
maxBatchSize uint
tracker iteratorTracker
recoveryFile string
watchingAddresses bool
ethDB ethdb.Database
stateDB state.Database
ipfsPublisher Publisher
maxBatchSize uint
tracker iteratorTracker
recoveryFile string
}
func NewLevelDB(con *EthConfig) (ethdb.Database, error) {
println(con.LevelDBPath)
println(con.AncientDBPath)
edb, err := rawdb.NewLevelDBDatabaseWithFreezer(
con.LevelDBPath, 1024, 256, con.AncientDBPath, "ipld-eth-state-snapshot", true,
)
kvdb, _ := rawdb.NewLevelDBDatabase(con.LevelDBPath, 1024, 256, "ipld-eth-state-snapshot", true)
edb, err := rawdb.NewDatabaseWithFreezer(kvdb, con.AncientDBPath, "ipld-eth-state-snapshot", true)
if err != nil {
return nil, fmt.Errorf("unable to create NewLevelDBDatabaseWithFreezer: %s", err)
}
@@ -79,11 +81,17 @@ func NewSnapshotService(edb ethdb.Database, pub Publisher, recoveryFile string)
}
type SnapshotParams struct {
Height uint64
Workers uint
WatchedAddresses map[common.Address]struct{}
Height uint64
Workers uint
}
func (s *Service) CreateSnapshot(params SnapshotParams) error {
paths := make([][]byte, 0, len(params.WatchedAddresses))
for addr := range params.WatchedAddresses {
paths = append(paths, keybytesToHex(crypto.Keccak256(addr.Bytes())))
}
s.watchingAddresses = len(paths) > 0
// extract header from lvldb and publish to PG-IPFS
// hold onto the headerID so that we can link the state nodes to this header
log.Infof("Creating snapshot at height %d", params.Height)
@@ -104,197 +112,385 @@ func (s *Service) CreateSnapshot(params SnapshotParams) error {
if err != nil {
return err
}
headerID := header.Hash().String()
ctx, cancelCtx := context.WithCancel(context.Background())
s.tracker = newTracker(s.recoveryFile, int(params.Workers))
go s.tracker.run()
go s.tracker.captureSignal()
s.tracker.captureSignal(cancelCtx)
var iters []trie.NodeIterator
// attempt to restore from recovery file if it exists
iters, err = s.tracker.restore(tree)
if err != nil {
log.Errorf("restore error: %s", err.Error())
return err
}
if iters != nil {
log.Debugf("restored iterators; count: %d", len(iters))
if params.Workers < uint(len(iters)) {
return fmt.Errorf(
"number of recovered workers (%d) is greater than number configured (%d)",
len(iters), params.Workers,
)
}
} else { // nothing to restore
} else {
// nothing to restore
log.Debugf("no iterators to restore")
if params.Workers > 1 {
iters = iter.SubtrieIterators(tree, params.Workers)
} else {
iters = []trie.NodeIterator{tree.NodeIterator(nil)}
}
for i, it := range iters {
iters[i] = s.tracker.tracked(it)
// recovered path is nil for fresh iterators
iters[i] = s.tracker.tracked(it, nil)
}
}
defer func() {
err := s.tracker.haltAndDump()
if err != nil {
log.Error("failed to write recovery file: ", err)
log.Errorf("failed to write recovery file: %v", err)
}
}()
if len(iters) > 0 {
return s.createSnapshotAsync(iters, headerID, new(big.Int).SetUint64(params.Height))
} else {
return s.createSnapshot(iters[0], headerID, new(big.Int).SetUint64(params.Height))
switch {
case len(iters) > 1:
return s.createSnapshotAsync(ctx, iters, headerID, new(big.Int).SetUint64(params.Height), paths)
case len(iters) == 1:
return s.createSnapshot(ctx, iters[0], headerID, new(big.Int).SetUint64(params.Height), paths)
default:
return nil
}
}
// Create snapshot up to head (ignores height param)
func (s *Service) CreateLatestSnapshot(workers uint) error {
// CreateLatestSnapshot snapshot at head (ignores height param)
func (s *Service) CreateLatestSnapshot(workers uint, watchedAddresses map[common.Address]struct{}) error {
log.Info("Creating snapshot at head")
hash := rawdb.ReadHeadHeaderHash(s.ethDB)
height := rawdb.ReadHeaderNumber(s.ethDB, hash)
if height == nil {
return fmt.Errorf("unable to read header height for header hash %s", hash.String())
}
return s.CreateSnapshot(SnapshotParams{Height: *height, Workers: workers})
return s.CreateSnapshot(SnapshotParams{Height: *height, Workers: workers, WatchedAddresses: watchedAddresses})
}
type nodeResult struct {
node Node
elements []interface{}
// Full-trie concurrent snapshot
func (s *Service) createSnapshotAsync(ctx context.Context, iters []trie.NodeIterator, headerID string, height *big.Int, seekingPaths [][]byte) error {
// use errgroup with a context to stop all concurrent iterators if one runs into an error
// each concurrent iterator completes processing it's current node before stopping
g, ctx := errgroup.WithContext(ctx)
for _, it := range iters {
func(it trie.NodeIterator) {
g.Go(func() error {
return s.createSnapshot(ctx, it, headerID, height, seekingPaths)
})
}(it)
}
return g.Wait()
}
func resolveNode(it trie.NodeIterator, trieDB *trie.Database) (*nodeResult, error) {
// "leaf" nodes are actually "value" nodes, whose parents are the actual leaves
if it.Leaf() {
return nil, nil
}
if IsNullHash(it.Hash()) {
return nil, nil
}
path := make([]byte, len(it.Path()))
copy(path, it.Path())
n, err := trieDB.Node(it.Hash())
if err != nil {
return nil, err
}
var elements []interface{}
if err := rlp.DecodeBytes(n, &elements); err != nil {
return nil, err
}
ty, err := CheckKeyType(elements)
if err != nil {
return nil, err
}
return &nodeResult{
node: Node{
NodeType: ty,
Path: path,
Value: n,
},
elements: elements,
}, nil
}
func (s *Service) createSnapshot(it trie.NodeIterator, headerID string, height *big.Int) error {
// createSnapshot performs traversal using the given iterator and indexes the nodes
// optionally filtering them according to a list of paths
func (s *Service) createSnapshot(ctx context.Context, it trie.NodeIterator, headerID string, height *big.Int, seekingPaths [][]byte) error {
tx, err := s.ipfsPublisher.BeginTx()
if err != nil {
return err
}
defer func() { err = CommitOrRollback(tx, err) }()
for it.Next(true) {
res, err := resolveNode(it, s.stateDB.TrieDB())
if err != nil {
return err
}
if res == nil {
continue
}
tx, err = s.ipfsPublisher.PrepareTxForBatch(tx, s.maxBatchSize)
if err != nil {
return err
}
switch res.node.NodeType {
case Leaf:
// if the node is a leaf, decode the account and publish the associated storage trie
// nodes if there are any
var account types.StateAccount
if err := rlp.DecodeBytes(res.elements[1].([]byte), &account); err != nil {
return fmt.Errorf(
"error decoding account for leaf node at path %x nerror: %v", res.node.Path, err)
}
partialPath := trie.CompactToHex(res.elements[0].([]byte))
valueNodePath := append(res.node.Path, partialPath...)
encodedPath := trie.HexToCompact(valueNodePath)
leafKey := encodedPath[1:]
res.node.Key = common.BytesToHash(leafKey)
err := s.ipfsPublisher.PublishStateNode(&res.node, headerID, height, tx)
if err != nil {
return err
}
// publish any non-nil code referenced by codehash
if !bytes.Equal(account.CodeHash, emptyCodeHash) {
codeHash := common.BytesToHash(account.CodeHash)
codeBytes := rawdb.ReadCode(s.ethDB, codeHash)
if len(codeBytes) == 0 {
log.Error("Code is missing", "account", common.BytesToHash(it.LeafKey()))
return errors.New("missing code")
}
if err = s.ipfsPublisher.PublishCode(height, codeHash, codeBytes, tx); err != nil {
return err
}
}
if tx, err = s.storageSnapshot(account.Root, headerID, height, res.node.Path, tx); err != nil {
return fmt.Errorf("failed building storage snapshot for account %+v\r\nerror: %w", account, err)
}
case Extension, Branch:
res.node.Key = common.BytesToHash([]byte{})
if err := s.ipfsPublisher.PublishStateNode(&res.node, headerID, height, tx); err != nil {
return err
}
default:
return errors.New("unexpected node type")
}
}
return it.Error()
}
// Full-trie concurrent snapshot
func (s *Service) createSnapshotAsync(iters []trie.NodeIterator, headerID string, height *big.Int) error {
errors := make(chan error)
var wg sync.WaitGroup
for _, it := range iters {
wg.Add(1)
go func(it trie.NodeIterator) {
defer wg.Done()
if err := s.createSnapshot(it, headerID, height); err != nil {
errors <- err
}
}(it)
}
done := make(chan struct{})
go func() {
wg.Wait()
done <- struct{}{}
defer func() {
err = CommitOrRollback(tx, err)
}()
var err error
select {
case err = <-errors:
case <-done:
close(errors)
// path (from recovery dump) to be seeked on recovery
// nil in case of a fresh iterator
var recoveredPath []byte
// latest path seeked from the concurrent iterator
// (updated after a node processed)
// nil in case of a fresh iterator; initially holds the recovered path in case of a recovered iterator
var seekedPath *[]byte
// end path for the concurrent iterator
var endPath []byte
if i, ok := it.(*trackedIter); ok {
seekedPath = &i.seekedPath
recoveredPath = append(recoveredPath, *seekedPath...)
endPath = i.endPath
} else {
err = errors.New("untracked iterator")
return err
}
tx, err = s.createSubTrieSnapshot(ctx, tx, nil, it, recoveredPath, seekedPath, endPath, headerID, height, seekingPaths)
return err
}
func (s *Service) storageSnapshot(sr common.Hash, headerID string, height *big.Int, statePath []byte, tx Tx) (Tx, error) {
// createSubTrieSnapshot processes nodes at the next level of a trie using the given subtrie iterator
// continually updating seekedPath with path of the latest processed node
func (s *Service) createSubTrieSnapshot(ctx context.Context, tx Tx, prefixPath []byte, subTrieIt trie.NodeIterator,
recoveredPath []byte, seekedPath *[]byte, endPath []byte, headerID string, height *big.Int, seekingPaths [][]byte) (Tx, error) {
prom.IncActiveIterCount()
defer prom.DecActiveIterCount()
// descend in the first loop iteration to reach first child node
var err error
descend := true
for {
select {
case <-ctx.Done():
return tx, errors.New("ctx cancelled")
default:
if ok := subTrieIt.Next(descend); !ok {
return tx, subTrieIt.Error()
}
// to avoid descending further
descend = false
// move on to next node if current path is empty
// occurs when reaching root node or just before reaching the first child of a subtrie in case of some concurrent iterators
if bytes.Equal(subTrieIt.Path(), []byte{}) {
// if node path is empty and prefix is nil, it's the root node
if prefixPath == nil {
// create snapshot of node, if it is a leaf this will also create snapshot of entire storage trie
tx, err = s.createNodeSnapshot(tx, subTrieIt, headerID, height, seekingPaths, prefixPath)
if err != nil {
return tx, err
}
updateSeekedPath(seekedPath, subTrieIt.Path())
}
if ok := subTrieIt.Next(true); !ok {
// return if no further nodes available
return tx, subTrieIt.Error()
}
}
// create the full node path as it.Path() doesn't include the path before subtrie root
nodePath := append(prefixPath, subTrieIt.Path()...)
// check iterator upper bound before processing the node
// required to avoid processing duplicate nodes:
// if a node is considered more than once,
// it's whole subtrie is re-processed giving large number of duplicate nodoes
if !checkUpperPathBound(nodePath, endPath) {
// fmt.Println("failed checkUpperPathBound", nodePath, endPath)
// explicitly stop the iterator in tracker if upper bound check fails
// required since it won't be marked as stopped if further nodes are still available
if trackedSubtrieIt, ok := subTrieIt.(*trackedIter); ok {
s.tracker.stopIter(trackedSubtrieIt)
}
return tx, subTrieIt.Error()
}
// skip the current node if it's before recovered path and not along the recovered path
// nodes at the same level that are before recovered path are ignored to avoid duplicate nodes
// however, nodes along the recovered path are re-considered for redundancy
if bytes.Compare(recoveredPath, nodePath) > 0 &&
// a node is along the recovered path if it's path is shorter or equal in length
// and is part of the recovered path
!(len(nodePath) <= len(recoveredPath) && bytes.Equal(recoveredPath[:len(nodePath)], nodePath)) {
continue
}
// ignore node if it is not along paths of interest
if s.watchingAddresses && !validPath(nodePath, seekingPaths) {
// consider this node as processed since it is getting ignored
// and update the seeked path
updateSeekedPath(seekedPath, nodePath)
// move on to the next node
continue
}
// if the node is along paths of interest
// create snapshot of node, if it is a leaf this will also create snapshot of entire storage trie
tx, err = s.createNodeSnapshot(tx, subTrieIt, headerID, height, seekingPaths, prefixPath)
if err != nil {
return tx, err
}
// update seeked path after node has been processed
updateSeekedPath(seekedPath, nodePath)
// create an iterator to traverse and process the next level of this subTrie
nextSubTrieIt, err := s.createSubTrieIt(nodePath, subTrieIt.Hash(), recoveredPath)
if err != nil {
return tx, err
}
// pass on the seekedPath of the tracked concurrent iterator to be updated
tx, err = s.createSubTrieSnapshot(ctx, tx, nodePath, nextSubTrieIt, recoveredPath, seekedPath, endPath, headerID, height, seekingPaths)
if err != nil {
return tx, err
}
}
}
}
// createSubTrieIt creates an iterator to traverse the subtrie of node with the given hash
// the subtrie iterator is initialized at a node from the recovered path at corresponding level (if avaiable)
func (s *Service) createSubTrieIt(prefixPath []byte, hash common.Hash, recoveredPath []byte) (trie.NodeIterator, error) {
// skip directly to the node from the recovered path at corresponding level
// applicable if:
// node path is behind recovered path
// and recovered path includes the prefix path
var startPath []byte
if bytes.Compare(recoveredPath, prefixPath) > 0 &&
len(recoveredPath) > len(prefixPath) &&
bytes.Equal(recoveredPath[:len(prefixPath)], prefixPath) {
startPath = append(startPath, recoveredPath[len(prefixPath):len(prefixPath)+1]...)
// force the lower bound path to an even length
// (required by HexToKeyBytes())
if len(startPath)&0b1 == 1 {
// decrement first to avoid skipped nodes
decrementPath(startPath)
startPath = append(startPath, 0)
}
}
// create subTrie iterator with the given hash
subTrie, err := s.stateDB.OpenTrie(hash)
if err != nil {
return nil, err
}
return subTrie.NodeIterator(iter.HexToKeyBytes(startPath)), nil
}
// createNodeSnapshot indexes the current node
// entire storage trie is also indexed (if available)
func (s *Service) createNodeSnapshot(tx Tx, it trie.NodeIterator, headerID string, height *big.Int,
watchedAddressesLeafPaths [][]byte, prefixPath []byte) (Tx, error) {
tx, err := s.ipfsPublisher.PrepareTxForBatch(tx, s.maxBatchSize)
if err != nil {
return tx, err
}
// index values by leaf key
if it.Leaf() {
// if it is a "value" node, we will index the value by leaf key
// publish codehash => code mappings
// take storage snapshot
if err := s.processStateValueNode(it, headerID, height, prefixPath, watchedAddressesLeafPaths, tx); err != nil {
return tx, err
}
} else { // trie nodes will be written to blockstore only
// reminder that this includes leaf nodes, since the geth iterator.Leaf() actually signifies a "value" node
// so this is also where we publish the IPLD block corresponding to the "value" nodes indexed above
if IsNullHash(it.Hash()) {
// skip null node
return tx, nil
}
nodeVal := make([]byte, len(it.NodeBlob()))
copy(nodeVal, it.NodeBlob())
if len(watchedAddressesLeafPaths) > 0 {
var elements []interface{}
if err := rlp.DecodeBytes(nodeVal, &elements); err != nil {
return tx, err
}
ok, err := isLeaf(elements)
if err != nil {
return tx, err
}
if ok {
// create the full node path as it.Path() doesn't include the path before subtrie root
nodePath := append(prefixPath, it.Path()...)
partialPath := trie.CompactToHex(elements[0].([]byte))
valueNodePath := append(nodePath, partialPath...)
if !isWatchedAddress(watchedAddressesLeafPaths, valueNodePath) {
// skip this node
return tx, nil
}
}
}
nodeHash := make([]byte, len(it.Hash().Bytes()))
copy(nodeHash, it.Hash().Bytes())
if err := s.ipfsPublisher.PublishIPLD(ipld.Keccak256ToCid(ipld.MEthStateTrie, nodeHash), nodeVal, height, tx); err != nil {
return tx, err
}
}
return tx, it.Error()
}
// reminder: it.Leaf() == true when the iterator is positioned at a "value node" which is not something that actually exists in an MMPT
func (s *Service) processStateValueNode(it trie.NodeIterator, headerID string, height *big.Int, prefixPath []byte,
watchedAddressesLeafPaths [][]byte, tx Tx) error {
// create the full node path as it.Path() doesn't include the path before subtrie root
nodePath := append(prefixPath, it.Path()...)
// skip if it is not a watched address
// If we aren't watching any specific addresses, we are watching everything
if len(watchedAddressesLeafPaths) > 0 && !isWatchedAddress(watchedAddressesLeafPaths, nodePath) {
return nil
}
// since this is a "value node", we need to move up to the "parent" node which is the actual leaf node
// it should be in the fastcache since it necessarily was recently accessed to reach the current "node"
parentNodeRLP, err := s.stateDB.TrieDB().Node(it.Parent())
if err != nil {
return err
}
var nodeElements []interface{}
if err = rlp.DecodeBytes(parentNodeRLP, &nodeElements); err != nil {
return err
}
parentSubPath := make([]byte, len(it.ParentPath()))
copy(parentSubPath, it.ParentPath())
parentPath := append(prefixPath, parentSubPath...)
partialPath := trie.CompactToHex(nodeElements[0].([]byte))
valueNodePath := append(parentPath, partialPath...)
encodedPath := trie.HexToCompact(valueNodePath)
leafKey := encodedPath[1:]
// 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 types.StateAccount
accountRLP := make([]byte, len(it.LeafBlob()))
copy(accountRLP, it.LeafBlob())
if err := rlp.DecodeBytes(accountRLP, &account); err != nil {
return fmt.Errorf("error decoding account for leaf value at leaf key %x\nerror: %v", leafKey, err)
}
// write codehash => code mappings if we have a contract
if !bytes.Equal(account.CodeHash, emptyCodeHash) {
codeHash := common.BytesToHash(account.CodeHash)
code, err := s.stateDB.ContractCode(common.Hash{}, codeHash)
if err != nil {
return fmt.Errorf("failed to retrieve code for codehash %s\r\n error: %v", codeHash.String(), err)
}
if err := s.ipfsPublisher.PublishIPLD(ipld.Keccak256ToCid(ipld.RawBinary, codeHash.Bytes()), code, height, tx); err != nil {
return err
}
}
// publish the state leaf model
stateKeyStr := common.BytesToHash(leafKey).String()
stateLeafNodeModel := &models.StateNodeModel{
BlockNumber: height.String(),
HeaderID: headerID,
StateKey: stateKeyStr,
Removed: false,
CID: ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(parentNodeRLP)).String(),
Diff: false,
Balance: account.Balance.String(),
Nonce: account.Nonce,
CodeHash: common.BytesToHash(account.CodeHash).String(),
StorageRoot: account.Root.String(),
}
if err := s.ipfsPublisher.PublishStateLeafNode(stateLeafNodeModel, tx); err != nil {
return fmt.Errorf("failed publishing state leaf node for leaf key %s\r\nerror: %w", stateKeyStr, err)
}
// create storage snapshot
// this short circuits if storage is empty
if _, err := s.storageSnapshot(account.Root, stateKeyStr, headerID, height, tx); err != nil {
return fmt.Errorf("failed building storage snapshot for account %+v\r\nerror: %w", account, err)
}
return nil
}
func (s *Service) storageSnapshot(sr common.Hash, stateKey, headerID string, height *big.Int, tx Tx) (Tx, error) {
if bytes.Equal(sr.Bytes(), emptyContractRoot.Bytes()) {
return tx, nil
}
@@ -306,42 +502,96 @@ func (s *Service) storageSnapshot(sr common.Hash, headerID string, height *big.I
it := sTrie.NodeIterator(make([]byte, 0))
for it.Next(true) {
res, err := resolveNode(it, s.stateDB.TrieDB())
if err != nil {
return nil, err
}
if res == nil {
continue
}
tx, err = s.ipfsPublisher.PrepareTxForBatch(tx, s.maxBatchSize)
if err != nil {
return nil, err
}
var nodeData []byte
nodeData, err = s.stateDB.TrieDB().Node(it.Hash())
if err != nil {
return nil, err
}
res.node.Value = nodeData
switch res.node.NodeType {
case Leaf:
partialPath := trie.CompactToHex(res.elements[0].([]byte))
valueNodePath := append(res.node.Path, partialPath...)
encodedPath := trie.HexToCompact(valueNodePath)
leafKey := encodedPath[1:]
res.node.Key = common.BytesToHash(leafKey)
case Extension, Branch:
res.node.Key = common.BytesToHash([]byte{})
default:
return nil, errors.New("unexpected node type")
}
if err = s.ipfsPublisher.PublishStorageNode(&res.node, headerID, height, statePath, tx); err != nil {
return nil, err
if it.Leaf() {
if err := s.processStorageValueNode(it, stateKey, headerID, height, tx); err != nil {
return nil, err
}
} else {
nodeVal := make([]byte, len(it.NodeBlob()))
copy(nodeVal, it.NodeBlob())
nodeHash := make([]byte, len(it.Hash().Bytes()))
copy(nodeHash, it.Hash().Bytes())
if err := s.ipfsPublisher.PublishIPLD(ipld.Keccak256ToCid(ipld.MEthStorageTrie, nodeHash), nodeVal, height, tx); err != nil {
return nil, err
}
}
}
return tx, it.Error()
}
// reminder: it.Leaf() == true when the iterator is positioned at a "value node" which is not something that actually exists in an MMPT
func (s *Service) processStorageValueNode(it trie.NodeIterator, stateKey, headerID string, height *big.Int, tx Tx) error {
// skip if it is not a watched address
leafKey := make([]byte, len(it.LeafKey()))
copy(leafKey, it.LeafKey())
value := make([]byte, len(it.LeafBlob()))
copy(value, it.LeafBlob())
// since this is a "value node", we need to move up to the "parent" node which is the actual leaf node
// it should be in the fastcache since it necessarily was recently accessed to reach the current node
parentNodeRLP, err := s.stateDB.TrieDB().Node(it.Parent())
if err != nil {
return err
}
// publish storage leaf node model
storageLeafKeyStr := common.BytesToHash(leafKey).String()
storageLeafNodeModel := &models.StorageNodeModel{
BlockNumber: height.String(),
HeaderID: headerID,
StateKey: stateKey,
StorageKey: storageLeafKeyStr,
Removed: false,
CID: ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(parentNodeRLP)).String(),
Diff: false,
Value: value,
}
if err := s.ipfsPublisher.PublishStorageLeafNode(storageLeafNodeModel, tx); err != nil {
return fmt.Errorf("failed to publish storage leaf node for state leaf key %s and storage leaf key %s\r\nerr: %w", stateKey, storageLeafKeyStr, err)
}
return nil
}
// validPath checks if a path is prefix to any one of the paths in the given list
func validPath(currentPath []byte, seekingPaths [][]byte) bool {
for _, seekingPath := range seekingPaths {
if bytes.HasPrefix(seekingPath, currentPath) {
return true
}
}
return false
}
// isWatchedAddress is used to check if a state account corresponds to one of the addresses the builder is configured to watch
func isWatchedAddress(watchedAddressesLeafPaths [][]byte, valueNodePath []byte) bool {
for _, watchedAddressPath := range watchedAddressesLeafPaths {
if bytes.Equal(watchedAddressPath, valueNodePath) {
return true
}
}
return false
}
// isLeaf checks if the node we are at is a leaf
func isLeaf(elements []interface{}) (bool, error) {
if len(elements) > 2 {
return false, nil
}
if len(elements) < 2 {
return false, fmt.Errorf("node cannot be less than two elements in length")
}
switch elements[0].([]byte)[0] / 16 {
case '\x00':
return false, nil
case '\x01':
return false, nil
case '\x02':
return true, nil
case '\x03':
return true, nil
default:
return false, fmt.Errorf("unknown hex prefix")
}
}
+559 -28
View File
@@ -2,17 +2,38 @@ package snapshot
import (
"errors"
"fmt"
"math/big"
"math/rand"
"os"
"path/filepath"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/ipfs/go-cid"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/statediff/indexer/models"
"github.com/golang/mock/gomock"
fixt "github.com/vulcanize/ipld-eth-state-snapshot/fixture"
mock "github.com/vulcanize/ipld-eth-state-snapshot/mocks/snapshot"
snapt "github.com/vulcanize/ipld-eth-state-snapshot/pkg/types"
"github.com/vulcanize/ipld-eth-state-snapshot/test"
fixt "github.com/cerc-io/ipld-eth-state-snapshot/fixture"
mock "github.com/cerc-io/ipld-eth-state-snapshot/mocks/snapshot"
snapt "github.com/cerc-io/ipld-eth-state-snapshot/pkg/types"
"github.com/cerc-io/ipld-eth-state-snapshot/test"
)
var (
stateNodeDuplicateErr = "state node indexed multiple times (%d) for state key %v"
storageNodeDuplicateErr = "storage node indexed multiple times (%d) for state key %v and storage key %v"
stateNodeNotIndexedErr = "state node not indexed for state key %v"
storageNodeNotIndexedErr = "storage node not indexed for state key %v, storage key %v"
unexpectedStateNodeErr = "got unexpected state node for state key %v"
unexpectedStorageNodeErr = "got unexpected storage node for state key %v, storage key %v"
extraNodesIndexedErr = "number of nodes indexed (%v) is more than expected (max %v)"
)
func testConfig(leveldbpath, ancientdbpath string) *Config {
@@ -38,22 +59,43 @@ func makeMocks(t *testing.T) (*mock.MockPublisher, *mock.MockTx) {
func TestCreateSnapshot(t *testing.T) {
runCase := func(t *testing.T, workers int) {
expectedStateLeafKeys := sync.Map{}
for _, key := range fixt.Block1_StateNodeLeafKeys {
expectedStateLeafKeys.Store(key, struct{}{})
}
pub, tx := makeMocks(t)
pub.EXPECT().PublishHeader(gomock.Eq(&fixt.Block1_Header))
pub.EXPECT().BeginTx().Return(tx, nil).
Times(workers)
pub.EXPECT().PrepareTxForBatch(gomock.Any(), gomock.Any()).Return(tx, nil).
AnyTimes()
pub.EXPECT().PublishStateNode(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
Times(len(fixt.Block1_StateNodePaths))
// TODO: fixtures for storage node
// pub.EXPECT().PublishStorageNode(gomock.Eq(fixt.StorageNode), gomock.Eq(int64(0)), gomock.Any())
tx.EXPECT().Commit().
Times(workers)
pub.EXPECT().PublishStateLeafNode(
gomock.Any(),
gomock.Eq(tx)).
Do(func(stateNode *models.StateNodeModel, _ snapt.Tx) error {
if stateNode.BlockNumber != fixt.Block1_Header.Number.String() {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
if stateNode.HeaderID != fixt.Block1_Header.Hash().String() {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
if _, ok := expectedStateLeafKeys.Load(stateNode.StateKey); ok {
expectedStateLeafKeys.Delete(stateNode.StateKey)
} else {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
return nil
}).
AnyTimes()
pub.EXPECT().PublishIPLD(gomock.Any(), gomock.Any(), gomock.Eq(fixt.Block1_Header.Number), gomock.Eq(tx)).
AnyTimes()
// Note: block 1 doesn't have storage nodes. TODO: add fixtures with storage nodes
config := testConfig(fixt.ChaindataPath, fixt.AncientdataPath)
chainDataPath, ancientDataPath := fixt.GetChainDataPath("chaindata")
config := testConfig(chainDataPath, ancientDataPath)
edb, err := NewLevelDB(config.Eth)
if err != nil {
t.Fatal(err)
@@ -71,30 +113,247 @@ func TestCreateSnapshot(t *testing.T) {
if err != nil {
t.Fatal(err)
}
// Check if all expected state nodes are indexed
expectedStateLeafKeys.Range(func(key, value any) bool {
t.Fatalf(stateNodeNotIndexedErr, key.(string))
return true
})
}
testCases := []int{1, 4, 16, 32}
testCases := []int{1, 4, 8, 16, 32}
for _, tc := range testCases {
t.Run("case", func(t *testing.T) { runCase(t, tc) })
}
}
func failingPublishStateNode(_ *snapt.Node, _ string, _ *big.Int, _ snapt.Tx) error {
return errors.New("failingPublishStateNode")
type indexedStateLeafNode struct {
value models.StateNodeModel
isIndexed bool
}
type indexedStorageLeafNode struct {
value models.StorageNodeModel
isIndexed bool
}
type storageNodeKey struct {
stateKey string
storageKey string
}
func TestAccountSelectiveSnapshot(t *testing.T) {
snapShotHeight := uint64(32)
watchedAddresses := map[common.Address]struct{}{
common.HexToAddress("0x825a6eec09e44Cb0fa19b84353ad0f7858d7F61a"): {},
common.HexToAddress("0x0616F59D291a898e796a1FAD044C5926ed2103eC"): {},
}
expectedStateNodeIndexes := []int{0, 4}
stateKey33 := common.HexToHash("0x33153abc667e873b6036c8a46bdd847e2ade3f89b9331c78ef2553fea194c50d").String()
expectedStorageNodeIndexes33 := []int{0, 1, 2, 4, 6}
stateKey12 := common.HexToHash("0xcabc5edb305583e33f66322ceee43088aa99277da772feb5053512d03a0a702b").String()
expectedStorageNodeIndexes12 := []int{9, 11}
runCase := func(t *testing.T, workers int) {
expectedStateNodes := sync.Map{}
for _, expectedStateNodeIndex := range expectedStateNodeIndexes {
key := fixt.Chain2_Block32_StateNodes[expectedStateNodeIndex].StateKey
expectedStateNodes.Store(key, indexedStateLeafNode{
value: fixt.Chain2_Block32_StateNodes[expectedStateNodeIndex],
isIndexed: false,
})
}
expectedStorageNodes := sync.Map{}
for _, expectedStorageNodeIndex := range expectedStorageNodeIndexes33 {
key := fixt.Chain2_Block32_StorageNodes[expectedStorageNodeIndex].StorageKey
keys := storageNodeKey{
stateKey: stateKey33,
storageKey: key,
}
value := indexedStorageLeafNode{
value: fixt.Chain2_Block32_StorageNodes[expectedStorageNodeIndex],
isIndexed: false,
}
expectedStorageNodes.Store(keys, value)
}
for _, expectedStorageNodeIndex := range expectedStorageNodeIndexes12 {
key := fixt.Chain2_Block32_StorageNodes[expectedStorageNodeIndex].StorageKey
keys := storageNodeKey{
stateKey: stateKey12,
storageKey: key,
}
value := indexedStorageLeafNode{
value: fixt.Chain2_Block32_StorageNodes[expectedStorageNodeIndex],
isIndexed: false,
}
expectedStorageNodes.Store(keys, value)
}
pub, tx := makeMocks(t)
pub.EXPECT().PublishHeader(gomock.Eq(&fixt.Chain2_Block32_Header))
pub.EXPECT().BeginTx().Return(tx, nil).
Times(workers)
pub.EXPECT().PrepareTxForBatch(gomock.Any(), gomock.Any()).Return(tx, nil).
AnyTimes()
tx.EXPECT().Commit().
Times(workers)
pub.EXPECT().PublishStateLeafNode(
gomock.Any(),
gomock.Eq(tx)).
Do(func(stateNode *models.StateNodeModel, _ snapt.Tx) error {
if stateNode.BlockNumber != fixt.Chain2_Block32_Header.Number.String() {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
if stateNode.HeaderID != fixt.Chain2_Block32_Header.Hash().String() {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
key := stateNode.StateKey
// Check published nodes
if expectedStateNode, ok := expectedStateNodes.Load(key); ok {
expectedVal := expectedStateNode.(indexedStateLeafNode).value
test.ExpectEqual(t, expectedVal, *stateNode)
// Mark expected node as indexed
expectedStateNodes.Store(key, indexedStateLeafNode{
value: expectedVal,
isIndexed: true,
})
} else {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
return nil
}).
AnyTimes()
pub.EXPECT().PublishStorageLeafNode(
gomock.Any(),
gomock.Eq(tx)).
Do(func(storageNode *models.StorageNodeModel, _ snapt.Tx) error {
if storageNode.BlockNumber != fixt.Chain2_Block32_Header.Number.String() {
t.Fatalf(unexpectedStorageNodeErr, storageNode.StateKey, storageNode.StorageKey)
}
if storageNode.HeaderID != fixt.Chain2_Block32_Header.Hash().String() {
t.Fatalf(unexpectedStorageNodeErr, storageNode.StateKey, storageNode.StorageKey)
}
key := storageNodeKey{
stateKey: storageNode.StateKey,
storageKey: storageNode.StorageKey,
}
// Check published nodes
if expectedStorageNode, ok := expectedStorageNodes.Load(key); ok {
expectedVal := expectedStorageNode.(indexedStorageLeafNode).value
test.ExpectEqual(t, expectedVal, *storageNode)
// Mark expected node as indexed
expectedStorageNodes.Store(key, indexedStorageLeafNode{
value: expectedVal,
isIndexed: true,
})
} else {
t.Fatalf(unexpectedStorageNodeErr, storageNode.StateKey, storageNode.StorageKey)
}
return nil
}).
AnyTimes()
pub.EXPECT().PublishIPLD(gomock.Any(), gomock.Any(), gomock.Eq(fixt.Chain2_Block32_Header.Number), gomock.Eq(tx)).
Do(func(_ cid.Cid, _ []byte, height *big.Int, _ snapt.Tx) {
if height.String() != fixt.Chain2_Block32_Header.Number.String() {
t.Fatalf("wrong blockheight for ipld publish: %s", height.String())
}
}).
MaxTimes(len(fixt.Chain2_Block32_StateIPLDs) + len(fixt.Chain2_Block32_StorageIPLDs) + 1 + 2)
chainDataPath, ancientDataPath := fixt.GetChainDataPath("chain2data")
config := testConfig(chainDataPath, ancientDataPath)
edb, err := NewLevelDB(config.Eth)
if err != nil {
t.Fatal(err)
}
defer edb.Close()
recovery := filepath.Join(t.TempDir(), "recover.csv")
service, err := NewSnapshotService(edb, pub, recovery)
if err != nil {
t.Fatal(err)
}
params := SnapshotParams{Height: snapShotHeight, Workers: uint(workers), WatchedAddresses: watchedAddresses}
err = service.CreateSnapshot(params)
if err != nil {
t.Fatal(err)
}
expectedStateNodes.Range(func(key, value any) bool {
if !value.(indexedStateLeafNode).isIndexed {
t.Fatalf(stateNodeNotIndexedErr, key)
return false
}
return true
})
expectedStorageNodes.Range(func(key, value any) bool {
if !value.(indexedStorageLeafNode).isIndexed {
t.Fatalf(storageNodeNotIndexedErr, key.(storageNodeKey).stateKey, key.(storageNodeKey).storageKey)
return false
}
return true
})
}
testCases := []int{1, 4, 8, 16, 32}
for _, tc := range testCases {
t.Run("case", func(t *testing.T) { runCase(t, tc) })
}
}
func TestRecovery(t *testing.T) {
runCase := func(t *testing.T, workers int) {
pub, tx := makeMocks(t)
pub.EXPECT().PublishHeader(gomock.Any()).AnyTimes()
pub.EXPECT().BeginTx().Return(tx, nil).AnyTimes()
pub.EXPECT().PrepareTxForBatch(gomock.Any(), gomock.Any()).Return(tx, nil).AnyTimes()
pub.EXPECT().PublishStateNode(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
Times(workers).
DoAndReturn(failingPublishStateNode)
tx.EXPECT().Commit().AnyTimes()
runCase := func(t *testing.T, workers int, interruptAt int32) {
// map: expected state path -> number of times it got published
expectedStateNodeKeys := sync.Map{}
for _, key := range fixt.Block1_StateNodeLeafKeys {
expectedStateNodeKeys.Store(key, 0)
}
var indexedStateNodesCount int32
config := testConfig(fixt.ChaindataPath, fixt.AncientdataPath)
pub, tx := makeMocks(t)
pub.EXPECT().PublishHeader(gomock.Eq(&fixt.Block1_Header))
pub.EXPECT().BeginTx().Return(tx, nil).MaxTimes(workers)
pub.EXPECT().PrepareTxForBatch(gomock.Any(), gomock.Any()).Return(tx, nil).AnyTimes()
tx.EXPECT().Rollback().MaxTimes(workers)
tx.EXPECT().Commit().MaxTimes(workers)
pub.EXPECT().PublishStateLeafNode(
gomock.Any(),
gomock.Eq(tx)).
DoAndReturn(func(stateNode *models.StateNodeModel, _ snapt.Tx) error {
if stateNode.BlockNumber != fixt.Block1_Header.Number.String() {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
if stateNode.HeaderID != fixt.Block1_Header.Hash().String() {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
// Start throwing an error after a certain number of state nodes have been indexed
if indexedStateNodesCount >= interruptAt {
return errors.New("failingPublishStateLeafNode")
} else {
if prevCount, ok := expectedStateNodeKeys.Load(stateNode.StateKey); ok {
expectedStateNodeKeys.Store(stateNode.StateKey, prevCount.(int)+1)
atomic.AddInt32(&indexedStateNodesCount, 1)
} else {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
}
return nil
}).
MaxTimes(int(interruptAt) + workers)
pub.EXPECT().PublishIPLD(gomock.Any(), gomock.Any(), gomock.Eq(fixt.Block1_Header.Number), gomock.Eq(tx)).
AnyTimes()
chainDataPath, ancientDataPath := fixt.GetChainDataPath("chaindata")
config := testConfig(chainDataPath, ancientDataPath)
edb, err := NewLevelDB(config.Eth)
if err != nil {
t.Fatal(err)
@@ -117,12 +376,45 @@ func TestRecovery(t *testing.T) {
t.Fatal("cannot stat recovery file:", err)
}
pub.EXPECT().PublishStateNode(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
err = service.CreateSnapshot(params)
// Create new mocks for recovery
recoveryPub, tx := makeMocks(t)
recoveryPub.EXPECT().PublishHeader(gomock.Eq(&fixt.Block1_Header))
recoveryPub.EXPECT().BeginTx().Return(tx, nil).AnyTimes()
recoveryPub.EXPECT().PrepareTxForBatch(gomock.Any(), gomock.Any()).Return(tx, nil).AnyTimes()
tx.EXPECT().Commit().AnyTimes()
recoveryPub.EXPECT().PublishStateLeafNode(
gomock.Any(),
gomock.Eq(tx)).
DoAndReturn(func(stateNode *models.StateNodeModel, _ snapt.Tx) error {
if stateNode.BlockNumber != fixt.Block1_Header.Number.String() {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
if stateNode.HeaderID != fixt.Block1_Header.Hash().String() {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
if prevCount, ok := expectedStateNodeKeys.Load(stateNode.StateKey); ok {
expectedStateNodeKeys.Store(stateNode.StateKey, prevCount.(int)+1)
atomic.AddInt32(&indexedStateNodesCount, 1)
} else {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
return nil
}).
AnyTimes()
recoveryPub.EXPECT().PublishIPLD(gomock.Any(), gomock.Any(), gomock.Eq(fixt.Block1_Header.Number), gomock.Eq(tx)).
AnyTimes()
// Create a new snapshot service for recovery
recoveryService, err := NewSnapshotService(edb, recoveryPub, recovery)
if err != nil {
t.Fatal(err)
}
err = recoveryService.CreateSnapshot(params)
if err != nil {
t.Fatal(err)
}
// Check if recovery file has been deleted
_, err = os.Stat(recovery)
if err == nil {
t.Fatal("recovery file still present")
@@ -131,11 +423,250 @@ func TestRecovery(t *testing.T) {
t.Fatal(err)
}
}
// Check if all state nodes are indexed after recovery
expectedStateNodeKeys.Range(func(key, value any) bool {
if value.(int) == 0 {
t.Fatalf(stateNodeNotIndexedErr, key.(string))
}
return true
})
// nodes along the recovery path get reindexed
maxStateNodesCount := len(fixt.Block1_StateNodeLeafKeys)
if indexedStateNodesCount > int32(maxStateNodesCount) {
t.Fatalf(extraNodesIndexedErr, indexedStateNodesCount, maxStateNodesCount)
}
}
testCases := []int{1, 2, 4, 8, 16, 32}
numInterrupts := 3
interrupts := make([]int32, numInterrupts)
for i := 0; i < numInterrupts; i++ {
rand.Seed(time.Now().UnixNano())
interrupts[i] = rand.Int31n(int32(len(fixt.Block1_StateNodeLeafKeys) / 2))
}
testCases := []int{1, 4, 32}
for _, tc := range testCases {
t.Run("case", func(t *testing.T) { runCase(t, tc) })
for _, interrupt := range interrupts {
t.Run(fmt.Sprint("case", tc, interrupt), func(t *testing.T) { runCase(t, tc, interrupt) })
}
}
}
func TestAccountSelectiveRecovery(t *testing.T) {
snapShotHeight := uint64(32)
watchedAddresses := map[common.Address]struct{}{
common.HexToAddress("0x825a6eec09e44Cb0fa19b84353ad0f7858d7F61a"): {},
common.HexToAddress("0x0616F59D291a898e796a1FAD044C5926ed2103eC"): {},
}
expectedStateNodeIndexes := []int{0, 4}
expectedStorageNodeIndexes := []int{0, 1, 2, 4, 6, 9, 11}
runCase := func(t *testing.T, workers int, interruptAt int32) {
// map: expected state path -> number of times it got published
expectedStateNodeKeys := sync.Map{}
for _, expectedStateNodeIndex := range expectedStateNodeIndexes {
key := fixt.Chain2_Block32_StateNodes[expectedStateNodeIndex].StateKey
expectedStateNodeKeys.Store(key, 0)
}
expectedStorageNodeKeys := sync.Map{}
for _, expectedStorageNodeIndex := range expectedStorageNodeIndexes {
stateKey := fixt.Chain2_Block32_StorageNodes[expectedStorageNodeIndex].StateKey
storageKey := fixt.Chain2_Block32_StorageNodes[expectedStorageNodeIndex].StorageKey
keys := storageNodeKey{
stateKey: stateKey,
storageKey: storageKey,
}
expectedStorageNodeKeys.Store(keys, 0)
}
var indexedStateNodesCount, indexedStorageNodesCount int32
pub, tx := makeMocks(t)
pub.EXPECT().PublishHeader(gomock.Eq(&fixt.Chain2_Block32_Header))
pub.EXPECT().BeginTx().Return(tx, nil).Times(workers)
pub.EXPECT().PrepareTxForBatch(gomock.Any(), gomock.Any()).Return(tx, nil).AnyTimes()
tx.EXPECT().Commit().MaxTimes(workers)
tx.EXPECT().Rollback().MaxTimes(workers)
pub.EXPECT().PublishStateLeafNode(
gomock.Any(),
gomock.Eq(tx)).
DoAndReturn(func(stateNode *models.StateNodeModel, _ snapt.Tx) error {
if stateNode.BlockNumber != fixt.Chain2_Block32_Header.Number.String() {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
if stateNode.HeaderID != fixt.Chain2_Block32_Header.Hash().String() {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
// Start throwing an error after a certain number of state nodes have been indexed
if indexedStateNodesCount >= interruptAt {
return errors.New("failingPublishStateLeafNode")
} else {
if prevCount, ok := expectedStateNodeKeys.Load(stateNode.StateKey); ok {
expectedStateNodeKeys.Store(stateNode.StateKey, prevCount.(int)+1)
atomic.AddInt32(&indexedStateNodesCount, 1)
} else {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
}
return nil
}).
MaxTimes(int(interruptAt) + workers)
pub.EXPECT().PublishStorageLeafNode(
gomock.Any(),
gomock.Eq(tx)).
Do(func(storageNode *models.StorageNodeModel, _ snapt.Tx) error {
if storageNode.BlockNumber != fixt.Chain2_Block32_Header.Number.String() {
t.Fatalf(unexpectedStorageNodeErr, storageNode.StateKey, storageNode.StorageKey)
}
if storageNode.HeaderID != fixt.Chain2_Block32_Header.Hash().String() {
t.Fatalf(unexpectedStorageNodeErr, storageNode.StateKey, storageNode.StorageKey)
}
keys := storageNodeKey{
stateKey: storageNode.StateKey,
storageKey: storageNode.StorageKey,
}
if prevCount, ok := expectedStorageNodeKeys.Load(keys); ok {
expectedStorageNodeKeys.Store(keys, prevCount.(int)+1)
atomic.AddInt32(&indexedStorageNodesCount, 1)
} else {
t.Fatalf(unexpectedStorageNodeErr, storageNode.StateKey, storageNode.StorageKey)
}
return nil
}).
AnyTimes()
pub.EXPECT().PublishIPLD(gomock.Any(), gomock.Any(), gomock.Eq(fixt.Chain2_Block32_Header.Number), gomock.Eq(tx)).
AnyTimes()
chainDataPath, ancientDataPath := fixt.GetChainDataPath("chain2data")
config := testConfig(chainDataPath, ancientDataPath)
edb, err := NewLevelDB(config.Eth)
if err != nil {
t.Fatal(err)
}
defer edb.Close()
recovery := filepath.Join(t.TempDir(), "recover.csv")
service, err := NewSnapshotService(edb, pub, recovery)
if err != nil {
t.Fatal(err)
}
params := SnapshotParams{Height: snapShotHeight, Workers: uint(workers), WatchedAddresses: watchedAddresses}
err = service.CreateSnapshot(params)
if err == nil {
t.Fatal("expected an error")
}
if _, err = os.Stat(recovery); err != nil {
t.Fatal("cannot stat recovery file:", err)
}
// Create new mocks for recovery
recoveryPub, tx := makeMocks(t)
recoveryPub.EXPECT().PublishHeader(gomock.Eq(&fixt.Chain2_Block32_Header))
recoveryPub.EXPECT().BeginTx().Return(tx, nil).MaxTimes(workers)
recoveryPub.EXPECT().PrepareTxForBatch(gomock.Any(), gomock.Any()).Return(tx, nil).AnyTimes()
tx.EXPECT().Commit().MaxTimes(workers)
recoveryPub.EXPECT().PublishStateLeafNode(
gomock.Any(),
gomock.Eq(tx)).
DoAndReturn(func(stateNode *models.StateNodeModel, _ snapt.Tx) error {
if stateNode.BlockNumber != fixt.Chain2_Block32_Header.Number.String() {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
if stateNode.HeaderID != fixt.Chain2_Block32_Header.Hash().String() {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
if prevCount, ok := expectedStateNodeKeys.Load(stateNode.StateKey); ok {
expectedStateNodeKeys.Store(stateNode.StateKey, prevCount.(int)+1)
atomic.AddInt32(&indexedStateNodesCount, 1)
} else {
t.Fatalf(unexpectedStateNodeErr, stateNode.StateKey)
}
return nil
}).
AnyTimes()
recoveryPub.EXPECT().PublishStorageLeafNode(
gomock.Any(),
gomock.Eq(tx)).
Do(func(storageNode *models.StorageNodeModel, _ snapt.Tx) error {
if storageNode.BlockNumber != fixt.Chain2_Block32_Header.Number.String() {
t.Fatalf(unexpectedStorageNodeErr, storageNode.StateKey, storageNode.StorageKey)
}
if storageNode.HeaderID != fixt.Chain2_Block32_Header.Hash().String() {
t.Fatalf(unexpectedStorageNodeErr, storageNode.StateKey, storageNode.StorageKey)
}
keys := storageNodeKey{
stateKey: storageNode.StateKey,
storageKey: storageNode.StorageKey,
}
if prevCount, ok := expectedStorageNodeKeys.Load(keys); ok {
expectedStorageNodeKeys.Store(keys, prevCount.(int)+1)
atomic.AddInt32(&indexedStorageNodesCount, 1)
} else {
t.Fatalf(unexpectedStorageNodeErr, storageNode.StateKey, storageNode.StorageKey)
}
return nil
}).
AnyTimes()
recoveryPub.EXPECT().PublishIPLD(gomock.Any(), gomock.Any(), gomock.Eq(fixt.Chain2_Block32_Header.Number), gomock.Eq(tx)).
AnyTimes()
// Create a new snapshot service for recovery
recoveryService, err := NewSnapshotService(edb, recoveryPub, recovery)
if err != nil {
t.Fatal(err)
}
err = recoveryService.CreateSnapshot(params)
if err != nil {
t.Fatal(err)
}
// Check if recovery file has been deleted
_, err = os.Stat(recovery)
if err == nil {
t.Fatal("recovery file still present")
} else {
if !os.IsNotExist(err) {
t.Fatal(err)
}
}
// Check if all expected state nodes are indexed after recovery, but not in duplicate
expectedStateNodeKeys.Range(func(key, value any) bool {
if value.(int) == 0 {
t.Fatalf(stateNodeNotIndexedErr, key.(string))
}
/* TODO: fix/figure out
if value.(int) > 1 {
t.Fatalf(stateNodeDuplicateErr, value.(int), key.(string))
}
*/
return true
})
expectedStorageNodeKeys.Range(func(key, value any) bool {
if value.(int) == 0 {
t.Fatalf(storageNodeNotIndexedErr, key.(storageNodeKey).stateKey, key.(storageNodeKey).storageKey)
}
/* TODO: fix/figure out
if value.(int) > 1 {
t.Fatalf(storageNodeDuplicateErr, value.(int), key.(storageNodeKey).stateKey, key.(storageNodeKey).storageKey)
}
*/
return true
})
maxStateNodesCount := len(expectedStateNodeIndexes) + workers
if indexedStateNodesCount > int32(maxStateNodesCount) {
t.Fatalf(extraNodesIndexedErr, indexedStateNodesCount, maxStateNodesCount)
}
}
testCases := []int{1, 2, 4, 8, 16, 32}
for _, tc := range testCases {
t.Run(fmt.Sprint("case", tc, 1), func(t *testing.T) { runCase(t, tc, 1) })
}
}
+148 -44
View File
@@ -1,28 +1,65 @@
package snapshot
import (
"bytes"
"context"
"encoding/csv"
"fmt"
"os"
"os/signal"
"sync"
"sync/atomic"
"syscall"
"github.com/cerc-io/ipld-eth-state-snapshot/pkg/prom"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/trie"
iter "github.com/ethereum/go-ethereum/trie/concurrent_iterator"
log "github.com/sirupsen/logrus"
iter "github.com/vulcanize/go-eth-state-node-iterator"
)
var trackedIterCount int32
type trackedIter struct {
id int32
mu sync.Mutex
done atomic.Bool
trie.NodeIterator
tracker *iteratorTracker
seekedPath []byte // latest full node path seeked from the tracked iterator
startPath []byte // startPath for the tracked iterator
endPath []byte // endPath for the tracked iterator
lastPath []byte // latest it.Path() (not the full node path) seeked
}
func (it *trackedIter) getLastPath() []byte {
it.mu.Lock()
defer it.mu.Unlock()
return it.lastPath
}
func (it *trackedIter) setLastPath(val []byte) {
it.mu.Lock()
defer it.mu.Unlock()
it.lastPath = val
}
func (it *trackedIter) Next(descend bool) bool {
ret := it.NodeIterator.Next(descend)
if !ret {
it.tracker.stopChan <- it
if it.tracker.running {
it.tracker.stopChan <- it
} else {
log.Errorf("iterator stopped after tracker halted: path=%x", it.Path())
}
it.done.Store(true)
} else {
it.setLastPath(it.Path())
}
return ret
}
@@ -34,9 +71,7 @@ type iteratorTracker struct {
stopChan chan *trackedIter
started map[*trackedIter]struct{}
stopped []*trackedIter
haltChan chan struct{}
done chan struct{}
running bool
}
func newTracker(file string, buf int) iteratorTracker {
@@ -45,66 +80,114 @@ func newTracker(file string, buf int) iteratorTracker {
startChan: make(chan *trackedIter, buf),
stopChan: make(chan *trackedIter, buf),
started: map[*trackedIter]struct{}{},
haltChan: make(chan struct{}),
done: make(chan struct{}),
running: true,
}
}
func (tr *iteratorTracker) captureSignal() {
func (tr *iteratorTracker) captureSignal(cancelCtx context.CancelFunc) {
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
go func() {
sig := <-sigChan
log.Errorf("Signal received (%v), stopping", sig)
tr.haltAndDump()
os.Exit(1)
// cancel context on receiving a signal
// on ctx cancellation, all the iterators complete processing of their current node before stopping
cancelCtx()
}()
}
// listens for starts/stops and manages current state
func (tr *iteratorTracker) run() {
loop:
for {
select {
case start := <-tr.startChan:
tr.started[start] = struct{}{}
case stop := <-tr.stopChan:
tr.stopped = append(tr.stopped, stop)
case <-tr.haltChan:
break loop
default:
}
// Wraps an iterator in a trackedIter. This should not be called once halts are possible.
func (tr *iteratorTracker) tracked(it trie.NodeIterator, recoveredPath []byte) (ret *trackedIter) {
// create seeked path of max capacity (65)
iterSeekedPath := make([]byte, 0, 65)
// intially populate seeked path with the recovered path
// to be used in trie traversal
if recoveredPath != nil {
iterSeekedPath = append(iterSeekedPath, recoveredPath...)
}
tr.done <- struct{}{}
// if the iterator being tracked is a PrefixBoundIterator, capture it's end path
// to be used in trie traversal
var endPath []byte
var startPath []byte
if boundedIter, ok := it.(*iter.PrefixBoundIterator); ok {
startPath = boundedIter.StartPath
endPath = boundedIter.EndPath
}
ret = &trackedIter{
atomic.AddInt32(&trackedIterCount, 1),
sync.Mutex{},
atomic.Bool{},
it,
tr,
iterSeekedPath,
startPath,
endPath,
nil,
}
tr.startChan <- ret
if prom.Enabled() {
pathDepth := max(max(len(startPath), len(endPath)), 1)
totalSteps := estimateSteps(startPath, endPath, pathDepth)
prom.RegisterGaugeFunc(
fmt.Sprintf("tracked_iterator_%d", ret.id),
func() float64 {
if ret.done.Load() {
return 100.0
}
lastPath := ret.getLastPath()
if nil == lastPath {
return 0.0
}
remainingSteps := estimateSteps(lastPath, endPath, pathDepth)
return (float64(totalSteps) - float64(remainingSteps)) / float64(totalSteps) * 100.0
})
}
return
}
func (tr *iteratorTracker) tracked(it trie.NodeIterator) (ret *trackedIter) {
ret = &trackedIter{it, tr}
tr.startChan <- ret
return
// explicitly stops an iterator
func (tr *iteratorTracker) stopIter(it *trackedIter) {
tr.stopChan <- it
}
// dumps iterator path and bounds to a text file so it can be restored later
func (tr *iteratorTracker) dump() error {
log.Info("Dumping recovery state to:", tr.recoveryFile)
log.Debug("Dumping recovery state to: ", tr.recoveryFile)
var rows [][]string
for it, _ := range tr.started {
for it := range tr.started {
var startPath []byte
var endPath []byte
if impl, ok := it.NodeIterator.(*iter.PrefixBoundIterator); ok {
// if the iterator being tracked is a PrefixBoundIterator,
// initialize start and end paths with its bounds
startPath = impl.StartPath
endPath = impl.EndPath
}
// if seeked path and iterator path are non-empty, use iterator's path as startpath
if !bytes.Equal(it.seekedPath, []byte{}) && !bytes.Equal(it.Path(), []byte{}) {
startPath = it.Path()
}
rows = append(rows, []string{
fmt.Sprintf("%x", it.Path()),
fmt.Sprintf("%x", startPath),
fmt.Sprintf("%x", endPath),
fmt.Sprintf("%x", it.seekedPath),
})
}
file, err := os.Create(tr.recoveryFile)
if err != nil {
return err
}
defer file.Close()
out := csv.NewWriter(file)
return out.WriteAll(rows)
}
@@ -118,36 +201,56 @@ func (tr *iteratorTracker) restore(tree state.Trie) ([]trie.NodeIterator, error)
}
return nil, err
}
log.Debug("Restoring recovery state from: ", tr.recoveryFile)
defer file.Close()
in := csv.NewReader(file)
in.FieldsPerRecord = 2
in.FieldsPerRecord = 3
rows, err := in.ReadAll()
if err != nil {
return nil, err
}
var ret []trie.NodeIterator
for _, row := range rows {
// pick up where each interval left off
var paths [2][]byte
for i, val := range row {
if len(val) != 0 {
if _, err = fmt.Sscanf(val, "%x", &paths[i]); err != nil {
return nil, err
}
var startPath []byte
var endPath []byte
var recoveredPath []byte
if len(row[0]) != 0 {
if _, err = fmt.Sscanf(row[0], "%x", &startPath); err != nil {
return nil, err
}
}
if len(row[1]) != 0 {
if _, err = fmt.Sscanf(row[1], "%x", &endPath); err != nil {
return nil, err
}
}
if len(row[2]) != 0 {
if _, err = fmt.Sscanf(row[2], "%x", &recoveredPath); err != nil {
return nil, err
}
}
it := iter.NewPrefixBoundIterator(tree, paths[0], paths[1])
ret = append(ret, tr.tracked(it))
// force the lower bound path to an even length
// (required by HexToKeyBytes())
if len(startPath)&0b1 == 1 {
// decrement first to avoid skipped nodes
decrementPath(startPath)
startPath = append(startPath, 0)
}
it := iter.NewPrefixBoundIterator(tree.NodeIterator(iter.HexToKeyBytes(startPath)), startPath, endPath)
ret = append(ret, tr.tracked(it, recoveredPath))
}
return ret, nil
}
func (tr *iteratorTracker) haltAndDump() error {
tr.haltChan <- struct{}{}
<-tr.done
tr.running = false
// drain any pending events
// drain any pending iterators
close(tr.startChan)
for start := range tr.startChan {
tr.started[start] = struct{}{}
@@ -169,5 +272,6 @@ func (tr *iteratorTracker) haltAndDump() error {
}
return err
}
return tr.dump()
}
+145 -5
View File
@@ -1,14 +1,15 @@
package snapshot
import (
"bytes"
"context"
"fmt"
"github.com/cerc-io/ipld-eth-state-snapshot/pkg/prom"
file "github.com/cerc-io/ipld-eth-state-snapshot/pkg/snapshot/file"
"github.com/cerc-io/ipld-eth-state-snapshot/pkg/snapshot/pg"
snapt "github.com/cerc-io/ipld-eth-state-snapshot/pkg/types"
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres"
file "github.com/vulcanize/ipld-eth-state-snapshot/pkg/snapshot/file"
pg "github.com/vulcanize/ipld-eth-state-snapshot/pkg/snapshot/pg"
snapt "github.com/vulcanize/ipld-eth-state-snapshot/pkg/types"
)
func NewPublisher(mode SnapshotMode, config *Config) (snapt.Publisher, error) {
@@ -18,9 +19,148 @@ func NewPublisher(mode SnapshotMode, config *Config) (snapt.Publisher, error) {
if err != nil {
return nil, err
}
return pg.NewPublisher(postgres.NewPostgresDB(driver)), nil
prom.RegisterDBCollector(config.DB.ConnConfig.DatabaseName, driver)
return pg.NewPublisher(postgres.NewPostgresDB(driver, false)), nil
case FileSnapshot:
return file.NewPublisher(config.File.OutputDir, config.Eth.NodeInfo)
}
return nil, fmt.Errorf("invalid snapshot mode: %s", mode)
}
// Subtracts 1 from the last byte in a path slice, carrying if needed.
// Does nothing, returning false, for all-zero inputs.
func decrementPath(path []byte) bool {
// check for all zeros
allzero := true
for i := 0; i < len(path); i++ {
allzero = allzero && path[i] == 0
}
if allzero {
return false
}
for i := len(path) - 1; i >= 0; i-- {
val := path[i]
path[i]--
if val == 0 {
path[i] = 0xf
} else {
return true
}
}
return true
}
// Estimate the number of iterations necessary to step from start to end.
func estimateSteps(start []byte, end []byte, depth int) uint64 {
// We see paths in several forms (nil, 0600, 06, etc.). We need to adjust them to a comparable form.
// For nil, start and end indicate the extremes of 0x0 and 0x10. For differences in depth, we often see a
// start/end range on a bounded iterator specified like 0500:0600, while the value returned by it.Path() may
// be shorter, like 06. Since our goal is to estimate how many steps it would take to move from start to end,
// we want to perform the comparison at a stable depth, since to move from 05 to 06 is only 1 step, but
// to move from 0500:06 is 16.
normalizePathRange := func(start []byte, end []byte, depth int) ([]byte, []byte) {
if 0 == len(start) {
start = []byte{0x0}
}
if 0 == len(end) {
end = []byte{0x10}
}
normalizedStart := make([]byte, depth)
normalizedEnd := make([]byte, depth)
for i := 0; i < depth; i++ {
if i < len(start) {
normalizedStart[i] = start[i]
}
if i < len(end) {
normalizedEnd[i] = end[i]
}
}
return normalizedStart, normalizedEnd
}
// We have no need to handle negative exponents, so uints are fine.
pow := func(x uint64, y uint) uint64 {
if 0 == y {
return 1
}
ret := x
for i := uint(0); i < y; i++ {
ret *= x
}
return x
}
// Fix the paths.
start, end = normalizePathRange(start, end, depth)
// No negative distances, if the start is already >= end, the distance is 0.
if bytes.Compare(start, end) >= 0 {
return 0
}
// Subtract each component, right to left, carrying over if necessary.
difference := make([]byte, len(start))
var carry byte = 0
for i := len(start) - 1; i >= 0; i-- {
result := end[i] - start[i] - carry
if result > 0xf && i > 0 {
result &= 0xf
carry = 1
} else {
carry = 0
}
difference[i] = result
}
// Calculate the result.
var ret uint64 = 0
for i := 0; i < len(difference); i++ {
ret += uint64(difference[i]) * pow(16, uint(len(difference)-i-1))
}
return ret
}
// https://github.com/ethereum/go-ethereum/blob/master/trie/encoding.go#L97
func keybytesToHex(str []byte) []byte {
l := len(str)*2 + 1
var nibbles = make([]byte, l)
for i, b := range str {
nibbles[i*2] = b / 16
nibbles[i*2+1] = b % 16
}
nibbles[l-1] = 16
return nibbles
}
func updateSeekedPath(seekedPath *[]byte, nodePath []byte) {
// assumes len(nodePath) <= max len(*seekedPath)
*seekedPath = (*seekedPath)[:len(nodePath)]
copy(*seekedPath, nodePath)
}
// checks that the provided node path is before the end path
func checkUpperPathBound(nodePath, endPath []byte) bool {
// every path is before nil endPath
if endPath == nil {
return true
}
if len(endPath)%2 == 0 {
// in case of even length endpath
// apply open interval filter since the node at endpath will be covered by the next iterator
return bytes.Compare(nodePath, endPath) < 0
}
return bytes.Compare(nodePath, endPath) <= 0
}
func max(a int, b int) int {
if a > b {
return a
}
return b
}
+4 -14
View File
@@ -17,23 +17,13 @@ package types
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
)
// node for holding trie node information
type Node struct {
NodeType nodeType
Path []byte
Key common.Hash
Value []byte
}
// nodeType for explicitly setting type of node
type nodeType int
// NodeType for explicitly setting type of node
type NodeType int
const (
Branch nodeType = iota
Branch NodeType = iota
Extension
Leaf
Removed
@@ -41,7 +31,7 @@ const (
)
// CheckKeyType checks what type of key we have
func CheckKeyType(elements []interface{}) (nodeType, error) {
func CheckKeyType(elements []interface{}) (NodeType, error) {
if len(elements) > 2 {
return Branch, nil
}
+6 -4
View File
@@ -3,15 +3,17 @@ package types
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/statediff/indexer/models"
"github.com/ipfs/go-cid"
"github.com/ethereum/go-ethereum/core/types"
)
type Publisher interface {
PublishHeader(header *types.Header) error
PublishStateNode(node *Node, headerID string, height *big.Int, tx Tx) error
PublishStorageNode(node *Node, headerID string, height *big.Int, statePath []byte, tx Tx) error
PublishCode(height *big.Int, codeHash common.Hash, codeBytes []byte, tx Tx) error
PublishStateLeafNode(node *models.StateNodeModel, tx Tx) error
PublishStorageLeafNode(node *models.StorageNodeModel, tx Tx) error
PublishIPLD(c cid.Cid, raw []byte, height *big.Int, tx Tx) error
BeginTx() (Tx, error)
PrepareTxForBatch(tx Tx, batchSize uint) (Tx, error)
}
-76
View File
@@ -1,76 +0,0 @@
package types
var TableIPLDBlock = Table{
`public.blocks`,
[]column{
{"block_number", bigint},
{"key", text},
{"data", bytea},
},
"ON CONFLICT (key, block_number) DO NOTHING",
}
var TableNodeInfo = Table{
Name: `public.nodes`,
Columns: []column{
{"genesis_block", varchar},
{"network_id", varchar},
{"node_id", varchar},
{"client_name", varchar},
{"chain_id", integer},
},
}
var TableHeader = Table{
"eth.header_cids",
[]column{
{"block_number", bigint},
{"block_hash", varchar},
{"parent_hash", varchar},
{"cid", text},
{"td", numeric},
{"node_id", varchar},
{"reward", numeric},
{"state_root", varchar},
{"tx_root", varchar},
{"receipt_root", varchar},
{"uncle_root", varchar},
{"bloom", bytea},
{"timestamp", numeric},
{"mh_key", text},
{"times_validated", integer},
{"coinbase", varchar},
},
"ON CONFLICT (block_hash, block_number) DO UPDATE SET (parent_hash, cid, td, node_id, reward, state_root, tx_root, receipt_root, uncle_root, bloom, timestamp, mh_key, times_validated, coinbase) = (EXCLUDED.parent_hash, EXCLUDED.cid, EXCLUDED.td, EXCLUDED.node_id, EXCLUDED.reward, EXCLUDED.state_root, EXCLUDED.tx_root, EXCLUDED.receipt_root, EXCLUDED.uncle_root, EXCLUDED.bloom, EXCLUDED.timestamp, EXCLUDED.mh_key, eth.header_cids.times_validated + 1, EXCLUDED.coinbase)",
}
var TableStateNode = Table{
"eth.state_cids",
[]column{
{"block_number", bigint},
{"header_id", varchar},
{"state_leaf_key", varchar},
{"cid", text},
{"state_path", bytea},
{"node_type", integer},
{"diff", boolean},
{"mh_key", text},
},
"ON CONFLICT (header_id, state_path, block_number) DO UPDATE SET (state_leaf_key, cid, node_type, diff, mh_key) = (EXCLUDED.state_leaf_key, EXCLUDED.cid, EXCLUDED.node_type, EXCLUDED.diff, EXCLUDED.mh_key)",
}
var TableStorageNode = Table{
"eth.storage_cids",
[]column{
{"block_number", bigint},
{"header_id", varchar},
{"state_path", bytea},
{"storage_leaf_key", varchar},
{"cid", text},
{"storage_path", bytea},
{"node_type", integer},
{"diff", boolean},
{"mh_key", text},
},
"ON CONFLICT (header_id, state_path, storage_path, block_number) DO UPDATE SET (storage_leaf_key, cid, node_type, diff, mh_key) = (EXCLUDED.storage_leaf_key, EXCLUDED.cid, EXCLUDED.node_type, EXCLUDED.diff, EXCLUDED.mh_key)",
}
-79
View File
@@ -1,79 +0,0 @@
package types
import (
"fmt"
"strings"
)
type colType int
const (
integer colType = iota
boolean
bigint
numeric
bytea
varchar
text
)
type column struct {
name string
typ colType
}
type Table struct {
Name string
Columns []column
conflictClause string
}
func (tbl *Table) ToCsvRow(args ...interface{}) []string {
var row []string
for i, col := range tbl.Columns {
row = append(row, col.typ.formatter()(args[i]))
}
return row
}
func (tbl *Table) ToInsertStatement() string {
var colnames, placeholders []string
for i, col := range tbl.Columns {
colnames = append(colnames, col.name)
placeholders = append(placeholders, fmt.Sprintf("$%d", i+1))
}
return fmt.Sprintf(
"INSERT INTO %s (%s) VALUES (%s) %s",
tbl.Name, strings.Join(colnames, ", "), strings.Join(placeholders, ", "), tbl.conflictClause,
)
}
type colfmt = func(interface{}) string
func sprintf(f string) colfmt {
return func(x interface{}) string { return fmt.Sprintf(f, x) }
}
func (typ colType) formatter() colfmt {
switch typ {
case integer:
return sprintf("%d")
case boolean:
return func(x interface{}) string {
if x.(bool) {
return "t"
}
return "f"
}
case bigint:
return sprintf("%s")
case numeric:
return sprintf("%d")
case bytea:
return sprintf(`\x%x`)
case varchar:
return sprintf("%s")
case text:
return sprintf("%s")
}
panic("unreachable")
}
+73
View File
@@ -0,0 +1,73 @@
## Data Validation
* For a given table in the `ipld-eth-db` schema, we know the number of columns to be expected in each row in the data dump:
| Table | Expected columns |
| ----------------- |:----------------:|
| public.nodes | 5 |
| public.blocks | 3 |
| eth.header_cids | 16 |
| eth.state_cids | 8 |
| eth.storage_cids | 9 |
### Find Bad Data
* Run the following command to find any rows having unexpected number of columns:
```bash
./scripts/find-bad-rows.sh -i <input-file> -c <expected-columns> -o [output-file] -d [include-data]
```
* `input-file` `-i`: Input data file path
* `expected-columns` `-c`: Expected number of columns in each row of the input file
* `output-file` `-o`: Output destination file path (default: `STDOUT`)
* `include-data` `-d`: Whether to include the data row in the output (`true | false`) (default: `false`)
* The output is of format: row number, number of columns, the data row
Eg:
```bash
./scripts/find-bad-rows.sh -i eth.state_cids.csv -c 8 -o res.txt -d true
```
Output:
```
1 9 1500000,xxxxxxxx,0x83952d392f9b0059eea94b10d1a095eefb1943ea91595a16c6698757127d4e1c,,baglacgzasvqcntdahkxhufdnkm7a22s2eetj6mx6nzkarwxtkvy4x3bubdgq,\x0f,0,f,/blocks/,DMQJKYBGZRQDVLT2CRWVGPQNNJNCCJU7GL7G4VAI3LZVK4OL5Q2ARTI
```
Eg:
```bash
./scripts/find-bad-rows.sh -i public.nodes.csv -c 5 -o res.txt -d true
./scripts/find-bad-rows.sh -i public.blocks.csv -c 3 -o res.txt -d true
./scripts/find-bad-rows.sh -i eth.header_cids.csv -c 16 -o res.txt -d true
./scripts/find-bad-rows.sh -i eth.state_cids.csv -c 8 -o res.txt -d true
./scripts/find-bad-rows.sh -i eth.storage_cids.csv -c 9 -o res.txt -d true
```
## Data Cleanup
* In case of column count mismatch, data from `file` mode dumps can't be imported readily into `ipld-eth-db`.
### Filter Bad Data
* Run the following command to filter out rows having unexpected number of columns:
```bash
./scripts/filter-bad-rows.sh -i <input-file> -c <expected-columns> -o <output-file>
```
* `input-file` `-i`: Input data file path
* `expected-columns` `-c`: Expected number of columns in each row of the input file
* `output-file` `-o`: Output destination file path
Eg:
```bash
./scripts/filter-bad-rows.sh -i public.nodes.csv -c 5 -o cleaned-public.nodes.csv
./scripts/filter-bad-rows.sh -i public.blocks.csv -c 3 -o cleaned-public.blocks.csv
./scripts/filter-bad-rows.sh -i eth.header_cids.csv -c 16 -o cleaned-eth.header_cids.csv
./scripts/filter-bad-rows.sh -i eth.state_cids.csv -c 8 -o cleaned-eth.state_cids.csv
./scripts/filter-bad-rows.sh -i eth.storage_cids.csv -c 9 -o cleaned-eth.storage_cids.csv
```
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
# flags
# -i <input-file>: Input data file path
# -c <expected-columns>: Expected number of columns in each row of the input file
# -o [output-file]: Output destination file path
# eg: ./scripts/filter-bad-rows.sh -i eth.state_cids.csv -c 8 -o cleaned-eth.state_cids.csv
while getopts i:c:o: OPTION
do
case "${OPTION}" in
i) inputFile=${OPTARG};;
c) expectedColumns=${OPTARG};;
o) outputFile=${OPTARG};;
esac
done
timestamp=$(date +%s)
# select only rows having expected number of columns
if [ -z "${outputFile}" ]; then
echo "Invalid destination file arg (-o) ${outputFile}"
else
awk -F"," "NF==${expectedColumns}" ${inputFile} > ${outputFile}
fi
difference=$(($(date +%s)-timestamp))
echo Time taken: $(date -d@${difference} -u +%H:%M:%S)
+43
View File
@@ -0,0 +1,43 @@
#!/bin/bash
# flags
# -i <input-file>: Input data file path
# -c <expected-columns>: Expected number of columns in each row of the input file
# -o [output-file]: Output destination file path (default: STDOUT)
# -d [include-data]: Whether to include the data row in output (true | false) (default: false)
# eg: ./scripts/find-bad-rows.sh -i eth.state_cids.csv -c 8 -o res.txt -d true
# output: 1 9 1500000,xxxxxxxx,0x83952d392f9b0059eea94b10d1a095eefb1943ea91595a16c6698757127d4e1c,,
# baglacgzasvqcntdahkxhufdnkm7a22s2eetj6mx6nzkarwxtkvy4x3bubdgq,\x0f,0,f,/blocks/,
# DMQJKYBGZRQDVLT2CRWVGPQNNJNCCJU7GL7G4VAI3LZVK4OL5Q2ARTI
while getopts i:c:o:d: OPTION
do
case "${OPTION}" in
i) inputFile=${OPTARG};;
c) expectedColumns=${OPTARG};;
o) outputFile=${OPTARG};;
d) data=${OPTARG};;
esac
done
timestamp=$(date +%s)
# if data requested, dump row number, number of columns and the row
if [ "${data}" = true ] ; then
if [ -z "${outputFile}" ]; then
awk -F"," "NF!=${expectedColumns} {print NR, NF, \$0}" < ${inputFile}
else
awk -F"," "NF!=${expectedColumns} {print NR, NF, \$0}" < ${inputFile} > ${outputFile}
fi
# else, dump only row number, number of columns
else
if [ -z "${outputFile}" ]; then
awk -F"," "NF!=${expectedColumns} {print NR, NF}" < ${inputFile}
else
awk -F"," "NF!=${expectedColumns} {print NR, NF}" < ${inputFile} > ${outputFile}
fi
fi
difference=$(($(date +%s)-timestamp))
echo Time taken: $(date -d@${difference} -u +%H:%M:%S)
+63
View File
@@ -0,0 +1,63 @@
#!/bin/bash
# Exit if the variable tests fail
set -e
set -o pipefail
if [[ -n "$CERC_SCRIPT_DEBUG" ]]; then
env
set -x
fi
# Check the database variables are set
test "$VDB_COMMAND"
# 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
mount -t tmpfs tmpfs /tmp/overlay
mkdir -p /tmp/overlay/upper
mkdir -p /tmp/overlay/work
mkdir -p /app/geth-rw
mount -t overlay overlay -o lowerdir=/app/geth-ro,upperdir=/tmp/overlay/upper,workdir=/tmp/overlay/work /app/geth-rw
mkdir /var/run/statediff
cd /var/run/statediff
SETUID=""
if [[ -n "$TARGET_UID" ]] && [[ -n "$TARGET_GID" ]]; then
SETUID="su-exec $TARGET_UID:$TARGET_GID"
chown -R $TARGET_UID:$TARGET_GID /var/run/statediff
fi
START_TIME=`date -u +"%Y-%m-%dT%H:%M:%SZ"`
echo "Running the snapshot service" && \
if [[ ! -z "$LOGRUS_FILE" ]]; then
$SETUID /app/ipld-eth-state-snapshot "$VDB_COMMAND" $* |& $SETUID tee ${LOGRUS_FILE}.console
rc=$?
else
$SETUID /app/ipld-eth-state-snapshot "$VDB_COMMAND" $*
rc=$?
fi
STOP_TIME=`date -u +"%Y-%m-%dT%H:%M:%SZ"`
if [ $rc -eq 0 ] && [ "$VDB_COMMAND" == "stateSnapshot" ] && [ -n "$SNAPSHOT_BLOCK_HEIGHT" ]; then
cat >metadata.json <<EOF
{
"type": "snapshot",
"range": { "start": $SNAPSHOT_BLOCK_HEIGHT, "stop": $SNAPSHOT_BLOCK_HEIGHT },
"nodeId": "$ETH_NODE_ID",
"genesisBlock": "$ETH_GENESIS_BLOCK",
"networkId": "$ETH_NETWORK_ID",
"chainId": "$ETH_CHAIN_ID",
"time": { "start": "$START_TIME", "stop": "$STOP_TIME" }
}
EOF
if [[ -n "$TARGET_UID" ]] && [[ -n "$TARGET_GID" ]]; then
echo 'metadata.json' | cpio -p --owner $TARGET_UID:$TARGET_GID $FILE_OUTPUT_DIR
else
cp metadata.json $FILE_OUTPUT_DIR
fi
fi
exit $rc
+5 -5
View File
@@ -20,10 +20,10 @@ var (
}
DefaultPgConfig = postgres.Config{
Hostname: "localhost",
Port: 5432,
DatabaseName: "vulcanize_test",
Username: "vulcanize",
Password: "vulcanize_password",
Port: 8077,
DatabaseName: "cerc_testing",
Username: "vdbm",
Password: "password",
MaxIdle: 0,
MaxConnLifetime: 0,
@@ -48,7 +48,7 @@ func NoError(t *testing.T, err error) {
// ExpectEqual asserts the provided interfaces are deep equal
func ExpectEqual(t *testing.T, want, got interface{}) {
if !reflect.DeepEqual(want, got) {
t.Fatalf("Values not equal:\nExpected:\t%v\nActual:\t\t%v", want, got)
t.Fatalf("Values not equal:\nExpected:\t%+v\nActual:\t\t%+v", want, got)
}
}