14 Commits
v4 ... v5
Author SHA1 Message Date
roysc 72e2e4ce50 Geth 1.13 (Cancun) update (#8)
- Changes contract tests for EIP-6780 (selfdestruct change) - see cerc-io/ipld-eth-server#264.
- Beacon block roots are now injected into the blockchain before processing transactions (https://eips.ethereum.org/EIPS/eip-4788)

Reviewed-on: #8
Reviewed-by: jonathanface <jonathanface@noreply.git.vdb.to>
2024-08-06 21:06:37 +00:00
telackeyandroysc bb6dcea25e Update to 0.1.4 (#2)
Co-authored-by: Roy Crihfield <roy@manteia.ltd>
Reviewed-on: #2
Co-authored-by: Thomas E Lackey <telackey@bozemanpass.com>
Co-committed-by: Thomas E Lackey <telackey@bozemanpass.com>
2023-10-01 06:41:49 +00:00
roysc 867797225c Refactor to use plugeth-statediff (#1)
Reviewed-on: #1
2023-09-21 08:01:49 +00:00
Ian Norden 911f6f7ef8 Merge pull request #35 from cerc-io/update-schema
Update go-ethereum and ipld-eth-db
2023-08-22 23:43:38 -05:00
roysc 9ab1589d2d improve test util 2023-08-11 15:58:17 +08:00
roysc ed8c39dd41 update ipld-eth-server 2023-08-11 14:31:49 +08:00
roysc a512d3f67b add full chain ref integ. test 2023-08-11 00:27:16 +08:00
roysc b4c9f1e864 Update go-ethereum and ipld-eth-db schema
- index non-canonical data first in tests
2023-08-11 00:22:53 +08:00
roysc bc3a7934cf Upgrade to v5 schema (#32)
* refactor vulcanize => cerc
* update geth and cerc dependencies
* update packages, ginkgo
* refactor chain generation
* update integration tests, contract, makefile
* go embed contract code
* rm old readme
* move unit tests into package
* rm ginkgo where not needed
* use tx in ref integrity functions
2023-06-22 07:25:27 +08:00
Ian Norden a7f4e4d704 Merge pull request #30 from cerc-io/ian/v4_dev
[v4] update deps, refs, and go version + fixes
2023-04-01 14:29:44 -05:00
i-norden c58a4f2533 fixes after rebase 2023-04-01 14:17:32 -05:00
i-norden 71ce820b5c update deps, refs, and go version 2023-04-01 14:01:38 -05:00
Michael 517bd1823e updates for geth 1.10.26 and cerc repo migration (#24)
* updates for geth 1.10.26 and cerc repo migration

* pull from git.vdb.to

* docker login not valid here as workflow action
2022-11-09 11:46:03 -05:00
Michael e51b124130 geth 1.10.25 dependency updates and cerc-io refactoring (#22) 2022-09-27 09:24:03 -04:00
54 changed files with 5222 additions and 18122 deletions
-6
View File
@@ -1,6 +0,0 @@
name: Run all tests
on: [pull_request]
jobs:
run-tests:
uses: ./.github/workflows/tests.yml
+90
View File
@@ -0,0 +1,90 @@
name: Unit and integration tests
on:
# workflow_call:
# Job headers are hidden when not top-level - run them directly for readability until fixed:
# https://github.com/go-gitea/gitea/issues/26736
pull_request:
branches: '*'
push:
branches:
- main
- ci-test
env:
SO_VERSION: v1.1.0-36d4969-202407091537
FIXTURENET_ETH_STACKS_REF: main
jobs:
unit-tests:
name: Run unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version-file: 'go.mod'
check-latest: true
- name: Run DB container
run: |
docker compose -f test/compose-db.yml up --wait --quiet-pull
- name: Run tests
run: go test -p 1 -v ./pkg/...
integration-tests:
name: Run integration tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version-file: 'go.mod'
check-latest: true
# At present the stock setup-python action fails on Linux/aarch64
# Conditional steps below workaroud this by using deadsnakes for that case only
- name: "Install Python for ARM on Linux"
if: ${{ runner.arch == 'arm64' && runner.os == 'Linux' }}
uses: deadsnakes/action@v3.0.1
with:
python-version: 3.11
- name: "Install Python cases other than ARM on Linux"
if: ${{ ! (runner.arch == 'arm64' && runner.os == 'Linux') }}
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install stack-orchestrator
uses: actions/checkout@v3
with:
repository: cerc-io/stack-orchestrator
ref: ${{ env.SO_VERSION }}
path: ./stack-orchestrator
- run: pip install ./stack-orchestrator
- name: Clone fixturenet stack repo
uses: actions/checkout@v4
with:
repository: cerc-io/fixturenet-eth-stacks
ref: ${{ env.FIXTURENET_ETH_STACKS_REF }}
path: ./fixturenet-eth-stacks
progress: false
- name: Run testnet stack
env:
CERC_GO_AUTH_TOKEN: ${{ secrets.CICD_REPO_TOKEN }}
run: ./scripts/run-test-stack.sh ./fixturenet-eth-stacks/stack-orchestrator/stacks/fixturenet-plugeth
- name: Run contract deployer
run: |
docker compose -f test/compose-deployer.yml up --wait --quiet-pull
- name: Wait for testnet
run: |
# Start validator at current head, but not before Merge (block 1 on test chain)
while
echo "Waiting for chain head to progress..."
height=$(./scripts/get-block-number.sh $ETH_HTTP_PATH)
[[ "$height" -lt 2 ]];
do sleep 5; done
echo "Chain has reached block $height"
echo VALIDATE_FROM_BLOCK=$height >> "$GITHUB_ENV"
- name: Run tests
run: |
go test -v -p 1 ./integration/... -timeout=20m
-91
View File
@@ -1,91 +0,0 @@
name: Test the stack.
on:
workflow_call:
env:
STACK_ORCHESTRATOR_REF: "f2fd766f5400fcb9eb47b50675d2e3b1f2753702"
GO_ETHEREUM_REF: "v1.10.19-statediff-4.1.0-alpha" # Use the tag, we are going to download the bin not build it.
IPLD_ETH_DB_REF: "b59505eab252670c622b42ce60621e9747fb64f9"
jobs:
integrationtest:
name: Run integration tests
env:
GOPATH: /tmp/go
DB_WRITE: true
runs-on: ubuntu-latest
steps:
- name: Create GOPATH
run: mkdir -p /tmp/go
- uses: actions/setup-go@v3
with:
go-version: "1.18.0"
check-latest: true
- uses: actions/checkout@v2
with:
path: "./ipld-eth-db-validator"
- uses: actions/checkout@v2
with:
ref: ${{ env.STACK_ORCHESTRATOR_REF }}
path: "./stack-orchestrator/"
repository: vulcanize/stack-orchestrator
- uses: actions/checkout@v2
with:
ref: ${{ env.IPLD_ETH_DB_REF }}
repository: vulcanize/ipld-eth-db
path: "./ipld-eth-db/"
- name: Create config file
run: |
echo vulcanize_test_contract=$GITHUB_WORKSPACE/ipld-eth-db-validator/test/contract >> ./config.sh
echo vulcanize_ipld_eth_db=$GITHUB_WORKSPACE/ipld-eth-db/ >> ./config.sh
echo genesis_file_path=start-up-files/go-ethereum/genesis.json >> ./config.sh
echo db_write=$DB_WRITE >> ./config.sh
cat ./config.sh
- name: Download Geth
run: |
cd $GITHUB_WORKSPACE/stack-orchestrator/helper-scripts
wget https://github.com/vulcanize/go-ethereum/releases/download/${{env.GO_ETHEREUM_REF}}/geth-linux-amd64
- name: Run docker compose
run: |
docker-compose \
-f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-db-sharding.yml" \
-f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-go-ethereum.yml" \
-f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-contract.yml" \
--env-file "$GITHUB_WORKSPACE/config.sh" \
up -d --build
- name: Run integration test.
run: |
cd $GITHUB_WORKSPACE/ipld-eth-db-validator
./scripts/run_integration_test.sh
unittest:
name: Run unit tests
runs-on: ubuntu-latest
steps:
- name: Create GOPATH
run: mkdir -p /tmp/go
- uses: actions/setup-go@v3
with:
go-version: "1.18.0"
check-latest: true
- name: Checkout code
uses: actions/checkout@v2
- name: Spin up database
run: |
docker-compose up -d
- name: Run unit tests
run: |
sleep 30
PGPASSWORD=password DATABASE_USER=vdbm DATABASE_PORT=8077 DATABASE_PASSWORD=password DATABASE_HOSTNAME=127.0.0.1 DATABASE_NAME=vulcanize_testing make test
+9 -16
View File
@@ -1,21 +1,14 @@
BIN = $(GOPATH)/bin
BASE = $(GOPATH)/src/$(PACKAGE)
PKGS = go list ./... | grep -v "^vendor/"
CONTRACTS_DIR := ./test/contract/contracts
CONTRACTS_OUTPUT_DIR := ./internal/testdata/build
# Tools
CONTRACTS := GLDToken Test
.PHONY: integrationtest
integrationtest: | $(GOOSE)
go vet ./...
go fmt ./...
go run github.com/onsi/ginkgo/ginkgo -r test/ -v
contracts: $(foreach C,$(CONTRACTS), $(CONTRACTS_OUTPUT_DIR)/$C.bin $(CONTRACTS_OUTPUT_DIR)/$C.abi)
.PHONY: contracts
test: contracts
go test -p 1 -v ./pkg/...
.PHONY: test
test: | $(GOOSE)
go vet ./...
go fmt ./...
go run github.com/onsi/ginkgo/ginkgo -r validator_test/ -v
build:
go fmt ./...
GO111MODULE=on go build
clean:
rm $(CONTRACTS_OUTPUT_DIR)/*.bin $(CONTRACTS_OUTPUT_DIR)/*.abi
+10 -10
View File
@@ -19,19 +19,19 @@ An example config file:
```toml
[database]
# db credentials
name = "vulcanize_public" # DATABASE_NAME
hostname = "localhost" # DATABASE_HOSTNAME
port = 5432 # DATABASE_PORT
user = "vdbm" # DATABASE_USER
password = "..." # DATABASE_PASSWORD
name = "cerc_public" # DATABASE_NAME
hostname = "localhost" # DATABASE_HOSTNAME
port = 5432 # DATABASE_PORT
user = "vdbm" # DATABASE_USER
password = "..." # DATABASE_PASSWORD
[validate]
# block height to initiate database validation at
blockHeight = 1 # VALIDATE_BLOCK_HEIGHT (default: 1)
fromBlock = 1 # VALIDATE_FROM_BLOCK (default: 1)
# number of blocks to trail behind the head
trail = 16 # VALIDATE_TRAIL (default: 16)
# sleep interval after validator has caught up to (head-trail) height (in sec)
sleepInterval = 10 # VALIDATE_SLEEP_INTERVAL (default: 10)
trail = 64 # VALIDATE_TRAIL (default: 64)
# retry interval after validator has caught up to (head-trail) height (in sec)
retryInterval = 10 # VALIDATE_RETRY_INTERVAL (default: 10)
# whether to perform a statediffing call on a missing block
stateDiffMissingBlock = true # (default: false)
@@ -65,7 +65,7 @@ An example config file:
* The validation process trails behind the latest block number in the database by config parameter `validate.trail`.
* If the validator has caught up to (head-trail) height, it waits for a configured time interval (`validate.sleepInterval`) before again querying the database.
* If the validator has caught up to (head-trail) height, it waits for a configured time interval (`validate.retryInterval`) before again querying the database.
* If the validator encounters a missing block (gap) in the database, it makes a `writeStateDiffAt` call to the configured statediffing endpoint (`ethereum.httpPath`) if `validate.stateDiffMissingBlock` is set to `true`. Here it is assumed that the statediffing node pointed to is writing out to the database.
+4 -4
View File
@@ -44,9 +44,9 @@ const (
ETH_CHAIN_ID = "ETH_CHAIN_ID"
ETH_HTTP_PATH = "ETH_HTTP_PATH"
VALIDATE_BLOCK_HEIGHT = "VALIDATE_BLOCK_HEIGHT"
VALIDATE_FROM_BLOCK = "VALIDATE_FROM_BLOCK"
VALIDATE_TRAIL = "VALIDATE_TRAIL"
VALIDATE_SLEEP_INTERVAL = "VALIDATE_SLEEP_INTERVAL"
VALIDATE_RETRY_INTERVAL = "VALIDATE_RETRY_INTERVAL"
VALIDATE_STATEDIFF_MISSING_BLOCK = "VALIDATE_STATEDIFF_MISSING_BLOCK"
VALIDATE_STATEDIFF_TIMEOUT = "VALIDATE_STATEDIFF_TIMEOUT"
)
@@ -76,9 +76,9 @@ func init() {
viper.BindEnv("ethereum.chainID", ETH_CHAIN_ID)
viper.BindEnv("ethereum.httpPath", ETH_HTTP_PATH)
viper.BindEnv("validate.blockHeight", VALIDATE_BLOCK_HEIGHT)
viper.BindEnv("validate.fromBlock", VALIDATE_FROM_BLOCK)
viper.BindEnv("validate.trail", VALIDATE_TRAIL)
viper.BindEnv("validate.sleepInterval", VALIDATE_SLEEP_INTERVAL)
viper.BindEnv("validate.retryInterval", VALIDATE_RETRY_INTERVAL)
viper.BindEnv("validate.stateDiffMissingBlock", VALIDATE_STATEDIFF_MISSING_BLOCK)
viper.BindEnv("validate.stateDiffTimeout", VALIDATE_STATEDIFF_TIMEOUT)
}
+3 -33
View File
@@ -25,7 +25,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/vulcanize/ipld-eth-db-validator/pkg/prom"
"github.com/cerc-io/ipld-eth-db-validator/v5/pkg/prom"
)
var (
@@ -50,24 +50,7 @@ func Execute() {
}
func initFunc(cmd *cobra.Command, args []string) {
logfile := viper.GetString("log.file")
if logfile != "" {
file, err := os.OpenFile(logfile,
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err == nil {
log.Infof("Directing output to %s", logfile)
log.SetOutput(file)
} else {
log.SetOutput(os.Stdout)
log.Info("Failed to log to file, using default stdout")
}
} else {
log.SetOutput(os.Stdout)
}
if err := logLevel(); err != nil {
log.Fatal("Could not set log level: ", err)
}
ParseLogFlags()
if viper.GetBool("prom.metrics") {
log.Info("initializing prometheus metrics")
@@ -91,7 +74,7 @@ func init() {
viper.AutomaticEnv()
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file location")
rootCmd.PersistentFlags().String("database-name", "vulcanize_public", "database name")
rootCmd.PersistentFlags().String("database-name", "cerc_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")
@@ -119,16 +102,3 @@ func init() {
_ = viper.BindPFlag("prom.httpPort", rootCmd.PersistentFlags().Lookup("prom-httpPort"))
_ = viper.BindPFlag("prom.dbStats", rootCmd.PersistentFlags().Lookup("prom-dbStats"))
}
func logLevel() error {
lvl, err := log.ParseLevel(viper.GetString("log.level"))
if err != nil {
return err
}
log.SetLevel(lvl)
if lvl > log.InfoLevel {
log.SetReportCaller(true)
}
log.Info("Log level set to ", lvl.String())
return nil
}
+11 -8
View File
@@ -27,7 +27,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/vulcanize/ipld-eth-db-validator/pkg/validator"
"github.com/cerc-io/ipld-eth-db-validator/v5/pkg/validator"
)
// stateValidatorCmd represents the stateValidator command
@@ -49,7 +49,10 @@ func stateValidator() {
logWithCommand.Fatal(err)
}
service := validator.NewService(cfg, nil)
service, err := validator.NewService(cfg, nil)
if err != nil {
logWithCommand.Fatal(err)
}
wg := new(sync.WaitGroup)
wg.Add(1)
@@ -65,19 +68,19 @@ func stateValidator() {
func init() {
rootCmd.AddCommand(stateValidatorCmd)
stateValidatorCmd.PersistentFlags().String("block-height", "1", "block height to initiate state validation")
stateValidatorCmd.PersistentFlags().String("trail", "16", "trail of block height to validate")
stateValidatorCmd.PersistentFlags().String("sleep-interval", "10", "sleep interval in seconds after validator has caught up to (head-trail) height")
stateValidatorCmd.PersistentFlags().String("from-block", "1", "block height to initiate state validation")
stateValidatorCmd.PersistentFlags().String("trail", "64", "trail of block height to validate")
stateValidatorCmd.PersistentFlags().String("retry-interval", "10s", "retry interval in seconds after validator has caught up to (head-trail) height")
stateValidatorCmd.PersistentFlags().Bool("statediff-missing-block", false, "whether to perform a statediffing call on a missing block")
stateValidatorCmd.PersistentFlags().Uint("statediff-timeout", 240, "statediffing call timeout period (in sec)")
stateValidatorCmd.PersistentFlags().String("statediff-timeout", "240s", "statediffing call timeout period (in sec)")
stateValidatorCmd.PersistentFlags().String("eth-chain-config", "", "path to json chain config")
stateValidatorCmd.PersistentFlags().String("eth-chain-id", "1", "eth chain id")
stateValidatorCmd.PersistentFlags().String("eth-http-path", "", "http url for a statediffing node")
_ = viper.BindPFlag("validate.blockHeight", stateValidatorCmd.PersistentFlags().Lookup("block-height"))
_ = viper.BindPFlag("validate.fromBlock", stateValidatorCmd.PersistentFlags().Lookup("from-block"))
_ = viper.BindPFlag("validate.trail", stateValidatorCmd.PersistentFlags().Lookup("trail"))
_ = viper.BindPFlag("validate.sleepInterval", stateValidatorCmd.PersistentFlags().Lookup("sleep-interval"))
_ = viper.BindPFlag("validate.retryInterval", stateValidatorCmd.PersistentFlags().Lookup("retry-interval"))
_ = viper.BindPFlag("validate.stateDiffMissingBlock", stateValidatorCmd.PersistentFlags().Lookup("statediff-missing-block"))
_ = viper.BindPFlag("validate.stateDiffTimeout", stateValidatorCmd.PersistentFlags().Lookup("statediff-timeout"))
+35
View File
@@ -0,0 +1,35 @@
package cmd
import (
"os"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
)
func ParseLogFlags() {
logfile := viper.GetString("log.file")
if logfile != "" {
file, err := os.OpenFile(logfile,
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err == nil {
log.Infof("Directing output to %s", logfile)
log.SetOutput(file)
} else {
log.SetOutput(os.Stdout)
log.Info("Failed to log to file, using default stdout")
}
} else {
log.SetOutput(os.Stdout)
}
lvl, err := log.ParseLevel(viper.GetString("log.level"))
if err != nil {
log.Fatal("Could not parse log level: ", err)
}
log.SetLevel(lvl)
if lvl > log.InfoLevel {
log.SetReportCaller(true)
}
log.Info("Log level set to ", lvl)
}
+6 -5
View File
@@ -1,21 +1,22 @@
[database]
name = "vulcanize_public"
name = "cerc_public"
hostname = "localhost"
port = 5432
password = "password"
user = "vdbm"
[validate]
blockHeight = 1
trail = 16
sleepInterval = 10
fromBlock = 1
trail = 64
retryInterval = "10s"
stateDiffMissingBlock = true
stateDiffTimeout = 240
stateDiffTimeout = "240s"
[ethereum]
chainConfig = ""
chainID = "1"
httpPath = "localhost:8545"
wsPath = "localhost:8546"
[prom]
metrics = true
+204 -210
View File
@@ -1,280 +1,274 @@
module github.com/vulcanize/ipld-eth-db-validator
module github.com/cerc-io/ipld-eth-db-validator/v5
go 1.18
go 1.21
require (
github.com/ethereum/go-ethereum v1.10.23
github.com/cerc-io/ipfs-ethdb/v5 v5.1.0-alpha
github.com/cerc-io/ipld-eth-server/v5 v5.3.0-alpha
github.com/cerc-io/ipld-eth-statedb v0.1.1
github.com/cerc-io/plugeth-statediff v0.3.2
github.com/ethereum/go-ethereum v1.13.14
github.com/holiman/uint256 v1.2.4
github.com/jmoiron/sqlx v1.3.5
github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.19.0
github.com/prometheus/client_golang v1.12.1
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.4.0
github.com/spf13/viper v1.11.0
github.com/vulcanize/ipfs-ethdb/v4 v4.0.7-alpha
github.com/vulcanize/ipld-eth-server/v4 v4.1.7-alpha
github.com/onsi/ginkgo/v2 v2.15.0
github.com/onsi/gomega v1.30.0
github.com/prometheus/client_golang v1.18.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.0
github.com/spf13/viper v1.18.2
)
require (
bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc // indirect
github.com/Stebalien/go-bitfield v0.0.1 // indirect
github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a // indirect
github.com/benbjohnson/clock v1.3.0 // indirect
github.com/DataDog/zstd v1.5.5 // indirect
github.com/Jorropo/jsync v1.0.1 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/VictoriaMetrics/fastcache v1.12.2 // indirect
github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.10.0 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/btcsuite/btcd v0.22.1 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/containerd/cgroups v1.0.3 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cerc-io/eth-ipfs-state-validator/v5 v5.2.0-alpha // indirect
github.com/cerc-io/eth-iterator-utils v0.3.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cockroachdb/errors v1.10.0 // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 // indirect
github.com/cockroachdb/redact v1.1.5 // indirect
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
github.com/consensys/bavard v0.1.13 // indirect
github.com/consensys/gnark-crypto v0.12.1 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668 // indirect
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect
github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect
github.com/cskr/pubsub v1.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/edsrzf/mmap-go v1.0.0 // indirect
github.com/deckarep/golang-set/v2 v2.3.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/elastic/gosigar v0.14.2 // indirect
github.com/ethereum/c-kzg-4844 v0.4.0 // indirect
github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 // indirect
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect
github.com/flynn/noise v1.0.0 // indirect
github.com/flynn/noise v1.1.0 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect
github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect
github.com/georgysavva/scany v0.2.9 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/getsentry/sentry-go v0.22.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gofrs/flock v0.8.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.3.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e // indirect
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-bexpr v0.1.10 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
github.com/holiman/uint256 v1.2.0 // indirect
github.com/huin/goupnp v1.0.3 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/huin/goupnp v1.3.0 // indirect
github.com/inconshreveable/log15 v2.16.0+incompatible // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/ipfs/bbloom v0.0.4 // indirect
github.com/ipfs/go-bitfield v1.0.0 // indirect
github.com/ipfs/go-bitswap v0.8.0 // indirect
github.com/ipfs/go-block-format v0.0.3 // indirect
github.com/ipfs/go-blockservice v0.4.0 // indirect
github.com/ipfs/go-cid v0.2.0 // indirect
github.com/ipfs/boxo v0.19.0 // indirect
github.com/ipfs/go-bitfield v1.1.0 // indirect
github.com/ipfs/go-block-format v0.2.0 // indirect
github.com/ipfs/go-cid v0.4.1 // indirect
github.com/ipfs/go-cidutil v0.1.0 // indirect
github.com/ipfs/go-datastore v0.5.1 // indirect
github.com/ipfs/go-delegated-routing v0.3.0 // indirect
github.com/ipfs/go-datastore v0.6.0 // indirect
github.com/ipfs/go-ds-measure v0.2.0 // indirect
github.com/ipfs/go-fetcher v1.6.1 // indirect
github.com/ipfs/go-filestore v1.2.0 // indirect
github.com/ipfs/go-fs-lock v0.0.7 // indirect
github.com/ipfs/go-graphsync v0.13.1 // indirect
github.com/ipfs/go-ipfs-blockstore v1.2.0 // indirect
github.com/ipfs/go-ipfs-chunker v0.0.5 // indirect
github.com/ipfs/go-ipfs-delay v0.0.1 // indirect
github.com/ipfs/go-ipfs-ds-help v1.1.0 // indirect
github.com/ipfs/go-ipfs-exchange-interface v0.2.0 // indirect
github.com/ipfs/go-ipfs-exchange-offline v0.3.0 // indirect
github.com/ipfs/go-ipfs-files v0.1.1 // indirect
github.com/ipfs/go-ipfs-keystore v0.0.2 // indirect
github.com/ipfs/go-ipfs-pinner v0.2.1 // indirect
github.com/ipfs/go-ipfs-posinfo v0.0.1 // indirect
github.com/ipfs/go-ipfs-pq v0.0.2 // indirect
github.com/ipfs/go-ipfs-provider v0.7.1 // indirect
github.com/ipfs/go-ipfs-routing v0.2.1 // indirect
github.com/ipfs/go-ipfs-util v0.0.2 // indirect
github.com/ipfs/go-ipld-cbor v0.0.5 // indirect
github.com/ipfs/go-ipld-format v0.4.0 // indirect
github.com/ipfs/go-ipld-legacy v0.1.1 // indirect
github.com/ipfs/go-ipns v0.1.2 // indirect
github.com/ipfs/go-ipfs-pq v0.0.3 // indirect
github.com/ipfs/go-ipfs-redirects-file v0.1.1 // indirect
github.com/ipfs/go-ipfs-util v0.0.3 // indirect
github.com/ipfs/go-ipld-cbor v0.1.0 // indirect
github.com/ipfs/go-ipld-format v0.6.0 // indirect
github.com/ipfs/go-ipld-legacy v0.2.1 // indirect
github.com/ipfs/go-log v1.0.5 // indirect
github.com/ipfs/go-log/v2 v2.5.1 // indirect
github.com/ipfs/go-merkledag v0.6.0 // indirect
github.com/ipfs/go-metrics-interface v0.0.1 // indirect
github.com/ipfs/go-mfs v0.2.1 // indirect
github.com/ipfs/go-namesys v0.5.0 // indirect
github.com/ipfs/go-path v0.3.0 // indirect
github.com/ipfs/go-peertaskqueue v0.7.1 // indirect
github.com/ipfs/go-unixfs v0.4.0 // indirect
github.com/ipfs/go-unixfsnode v1.4.0 // indirect
github.com/ipfs/go-verifcid v0.0.1 // indirect
github.com/ipfs/interface-go-ipfs-core v0.7.0 // indirect
github.com/ipfs/kubo v0.14.0 // indirect
github.com/ipld/edelweiss v0.1.4 // indirect
github.com/ipld/go-codec-dagpb v1.4.0 // indirect
github.com/ipld/go-ipld-prime v0.17.0 // indirect
github.com/ipfs/go-peertaskqueue v0.8.1 // indirect
github.com/ipfs/go-unixfsnode v1.9.0 // indirect
github.com/ipfs/kubo v0.27.0 // indirect
github.com/ipld/go-car/v2 v2.13.1 // indirect
github.com/ipld/go-codec-dagpb v1.6.0 // indirect
github.com/ipld/go-ipld-prime v0.21.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.12.1 // indirect
github.com/jackc/pgconn v1.14.3 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.11.0 // indirect
github.com/jackc/pgx/v4 v4.16.1 // indirect
github.com/jackc/puddle v1.2.1 // indirect
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgtype v1.14.0 // indirect
github.com/jackc/pgx/v4 v4.18.3 // indirect
github.com/jackc/puddle v1.3.0 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
github.com/jbenet/goprocess v0.1.4 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.4 // indirect
github.com/klauspost/compress v1.15.1 // indirect
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
github.com/koron/go-ssdp v0.0.2 // indirect
github.com/lib/pq v1.10.6 // indirect
github.com/libp2p/go-buffer-pool v0.0.2 // indirect
github.com/klauspost/compress v1.17.6 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/koron/go-ssdp v0.0.4 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
github.com/libp2p/go-cidranger v1.1.0 // indirect
github.com/libp2p/go-doh-resolver v0.4.0 // indirect
github.com/libp2p/go-eventbus v0.2.1 // indirect
github.com/libp2p/go-flow-metrics v0.0.3 // indirect
github.com/libp2p/go-libp2p v0.20.3 // indirect
github.com/libp2p/go-libp2p-asn-util v0.2.0 // indirect
github.com/libp2p/go-libp2p-core v0.16.1 // indirect
github.com/libp2p/go-libp2p-discovery v0.7.0 // indirect
github.com/libp2p/go-libp2p-kad-dht v0.16.0 // indirect
github.com/libp2p/go-libp2p-kbucket v0.4.7 // indirect
github.com/libp2p/go-libp2p-loggables v0.1.0 // indirect
github.com/libp2p/go-libp2p-peerstore v0.6.0 // indirect
github.com/libp2p/go-libp2p-pubsub v0.6.1 // indirect
github.com/libp2p/go-libp2p-pubsub-router v0.5.0 // indirect
github.com/libp2p/go-libp2p-record v0.1.3 // indirect
github.com/libp2p/go-libp2p-resource-manager v0.3.0 // indirect
github.com/libp2p/go-libp2p-routing-helpers v0.2.3 // indirect
github.com/libp2p/go-libp2p-swarm v0.11.0 // indirect
github.com/libp2p/go-flow-metrics v0.1.0 // indirect
github.com/libp2p/go-libp2p v0.33.0 // indirect
github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect
github.com/libp2p/go-libp2p-kad-dht v0.24.4 // indirect
github.com/libp2p/go-libp2p-kbucket v0.6.3 // indirect
github.com/libp2p/go-libp2p-pubsub v0.10.0 // indirect
github.com/libp2p/go-libp2p-pubsub-router v0.6.0 // indirect
github.com/libp2p/go-libp2p-record v0.2.0 // indirect
github.com/libp2p/go-libp2p-routing-helpers v0.7.3 // indirect
github.com/libp2p/go-libp2p-xor v0.1.0 // indirect
github.com/libp2p/go-mplex v0.7.0 // indirect
github.com/libp2p/go-msgio v0.2.0 // indirect
github.com/libp2p/go-nat v0.1.0 // indirect
github.com/libp2p/go-netroute v0.2.0 // indirect
github.com/libp2p/go-openssl v0.0.7 // indirect
github.com/libp2p/go-reuseport v0.2.0 // indirect
github.com/libp2p/go-yamux/v3 v3.1.2 // indirect
github.com/libp2p/zeroconf/v2 v2.1.1 // indirect
github.com/lucas-clemente/quic-go v0.27.1 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/libp2p/go-msgio v0.3.0 // indirect
github.com/libp2p/go-nat v0.2.0 // indirect
github.com/libp2p/go-netroute v0.2.1 // indirect
github.com/libp2p/go-reuseport v0.4.0 // indirect
github.com/libp2p/go-yamux/v4 v4.0.1 // indirect
github.com/libp2p/zeroconf/v2 v2.2.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailgun/groupcache/v2 v2.3.0 // indirect
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/miekg/dns v1.1.48 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/miekg/dns v1.1.58 // indirect
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
github.com/minio/sha256-simd v1.0.0 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/mitchellh/pointerstructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mmcloughlin/addchain v0.4.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-multiaddr v0.5.0 // indirect
github.com/multiformats/go-base32 v0.1.0 // indirect
github.com/multiformats/go-base36 v0.2.0 // indirect
github.com/multiformats/go-multiaddr v0.12.2 // indirect
github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
github.com/multiformats/go-multibase v0.1.0 // indirect
github.com/multiformats/go-multicodec v0.5.0 // indirect
github.com/multiformats/go-multihash v0.2.0 // indirect
github.com/multiformats/go-multistream v0.3.3 // indirect
github.com/multiformats/go-varint v0.0.6 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/multiformats/go-multibase v0.2.0 // indirect
github.com/multiformats/go-multicodec v0.9.0 // indirect
github.com/multiformats/go-multihash v0.2.3 // indirect
github.com/multiformats/go-multistream v0.5.0 // indirect
github.com/multiformats/go-varint v0.0.7 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/opencontainers/runtime-spec v1.0.2 // indirect
github.com/opencontainers/runtime-spec v1.2.0 // indirect
github.com/openrelayxyz/plugeth-utils v1.5.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect
github.com/pganalyze/pg_query_go/v2 v2.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 // indirect
github.com/pganalyze/pg_query_go/v4 v4.2.1 // indirect
github.com/pion/datachannel v1.5.5 // indirect
github.com/pion/dtls/v2 v2.2.8 // indirect
github.com/pion/ice/v2 v2.3.11 // indirect
github.com/pion/interceptor v0.1.25 // indirect
github.com/pion/logging v0.2.2 // indirect
github.com/pion/mdns v0.0.9 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.13 // indirect
github.com/pion/rtp v1.8.3 // indirect
github.com/pion/sctp v1.8.9 // indirect
github.com/pion/sdp/v3 v3.0.6 // indirect
github.com/pion/srtp/v2 v2.0.18 // indirect
github.com/pion/stun v0.6.1 // indirect
github.com/pion/transport/v2 v2.2.4 // indirect
github.com/pion/turn/v2 v2.1.4 // indirect
github.com/pion/webrtc/v3 v3.2.23 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.33.0 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/prometheus/tsdb v0.10.0 // indirect
github.com/raulk/clock v1.1.0 // indirect
github.com/raulk/go-watchdog v1.2.0 // indirect
github.com/rjeczalik/notify v0.9.1 // indirect
github.com/rs/cors v1.7.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/polydawn/refmt v0.89.0 // indirect
github.com/prometheus/client_model v0.6.0 // indirect
github.com/prometheus/common v0.47.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/quic-go v0.41.0 // indirect
github.com/quic-go/webtransport-go v0.6.0 // indirect
github.com/raulk/go-watchdog v1.3.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/samber/lo v1.39.0 // indirect
github.com/segmentio/fasthash v1.0.3 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 // indirect
github.com/stretchr/objx v0.2.0 // indirect
github.com/stretchr/testify v1.7.2 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/status-im/keycard-go v0.2.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.9.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/supranational/blst v0.3.11 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect
github.com/thoas/go-funk v0.9.2 // indirect
github.com/tidwall/gjson v1.14.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tklauser/go-sysconf v0.3.6 // indirect
github.com/tklauser/numcpus v0.2.2 // indirect
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef // indirect
github.com/urfave/cli/v2 v2.10.2 // indirect
github.com/vulcanize/eth-ipfs-state-validator/v4 v4.0.7-alpha // indirect
github.com/vulcanize/go-eth-state-node-iterator v1.1.4 // indirect
github.com/wI2L/jsondiff v0.2.0 // indirect
github.com/thoas/go-funk v0.9.3 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb // indirect
github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc // indirect
github.com/whyrusleeping/cbor-gen v0.0.0-20210219115102-f37d292932f2 // indirect
github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 // indirect
github.com/whyrusleeping/cbor-gen v0.0.0-20240109153615-66e95c3e8a87 // indirect
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 // indirect
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.opencensus.io v0.23.0 // indirect
go.opentelemetry.io/otel v1.7.0 // indirect
go.opentelemetry.io/otel/trace v1.7.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/dig v1.14.0 // indirect
go.uber.org/fx v1.16.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.21.0 // indirect
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel v1.25.0 // indirect
go.opentelemetry.io/otel/metric v1.25.0 // indirect
go.opentelemetry.io/otel/trace v1.25.0 // indirect
go.uber.org/dig v1.17.1 // indirect
go.uber.org/fx v1.20.1 // indirect
go.uber.org/mock v0.4.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/term v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.20.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
gonum.org/v1/gonum v0.14.0 // indirect
google.golang.org/protobuf v1.32.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/postgres v1.3.7 // indirect
gorm.io/gorm v1.23.5 // indirect
lukechampine.com/blake3 v1.1.7 // indirect
lukechampine.com/blake3 v1.2.2 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
)
replace github.com/ethereum/go-ethereum v1.10.23 => github.com/vulcanize/go-ethereum v1.10.23-statediff-4.2.0-alpha
+588 -1406
View File
File diff suppressed because it is too large Load Diff
+64
View File
@@ -0,0 +1,64 @@
package integration
import (
"encoding/json"
"fmt"
"math/big"
"net/http"
)
type ContractDeployed struct {
Address string `json:"address"`
TransactionHash string `json:"txHash"`
BlockNumber uint64 `json:"blockNumber"`
BlockHash string `json:"blockHash"`
}
type ContractDestroyed struct {
BlockNumber uint64 `json:"blockNumber"`
}
type PutResult struct {
BlockNumber uint64 `json:"blockNumber"`
}
type Tx struct {
From string `json:"from"`
To string `json:"to"`
Value *big.Int `json:"value"`
TransactionHash string `json:"txHash"`
BlockNumber uint64 `json:"blockNumber"`
BlockHash string `json:"blockHash"`
}
const ContractServerUrl = "http://localhost:3000"
// Factory to generate endpoint functions
func MakeGetAndDecodeFunc[R any](format string) func(...interface{}) (*R, error) {
return func(params ...interface{}) (*R, error) {
params = append([]interface{}{ContractServerUrl}, params...)
url := fmt.Sprintf(format, params...)
res, err := http.Get(url)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%s: %s", url, res.Status)
}
var data R
decoder := json.NewDecoder(res.Body)
return &data, decoder.Decode(&data)
}
}
var (
SendEth = MakeGetAndDecodeFunc[Tx]("%s/v1/sendEth?to=%s&value=%s")
DeployContract = MakeGetAndDecodeFunc[ContractDeployed]("%s/v1/deployContract")
DestroyContract = MakeGetAndDecodeFunc[ContractDestroyed]("%s/v1/destroyContract?addr=%s")
DeployTestContract = MakeGetAndDecodeFunc[ContractDeployed]("%s/v1/deployTestContract")
DestroyTestContract = MakeGetAndDecodeFunc[ContractDestroyed]("%s/v1/destroyTestContract?addr=%s")
PutTestValue = MakeGetAndDecodeFunc[PutResult]("%s/v1/putTestValue?addr=%s&value=%d")
)
+140
View File
@@ -0,0 +1,140 @@
package integration_test
import (
"context"
"sync"
"testing"
"time"
"github.com/cerc-io/plugeth-statediff/indexer/database/sql/postgres"
"github.com/onsi/gomega"
. "github.com/onsi/gomega"
"github.com/cerc-io/ipld-eth-db-validator/v5/cmd" // this registers env vars with viper
"github.com/cerc-io/ipld-eth-db-validator/v5/integration"
"github.com/cerc-io/ipld-eth-db-validator/v5/pkg/validator"
)
const (
timeout = 20 * time.Minute
pollInterval = time.Second
progressBufferSize = 200
)
var (
testAddresses = []string{
"0x1111111111111111111111111111111111111112",
"0x1ca7c995f8eF0A2989BbcE08D5B7Efe50A584aa1",
"0x9a4b666af23a2cdb4e5538e1d222a445aeb82134",
"0xF7C7AEaECD2349b129d5d15790241c32eeE4607B",
"0x992b6E9BFCA1F7b0797Cee10b0170E536EAd3532",
}
// Track the blocks validated on this chain
lastValidated uint64
validated = newBlockSet()
ctx = context.Background()
wg sync.WaitGroup
)
func init() {
cmd.ParseLogFlags()
gomega.SetDefaultEventuallyTimeout(timeout)
gomega.SetDefaultEventuallyPollingInterval(pollInterval)
}
func setup(t *testing.T, progressChan chan uint64) {
cfg, err := validator.NewConfig()
if err != nil {
t.Fatal(err)
}
// set the default DB config to the testing defaults
cfg.DBConfig, _ = postgres.TestConfig.WithEnv()
// update the start block if we have already validated past it
if lastValidated > cfg.FromBlock {
cfg.FromBlock = lastValidated
}
// default trail is unnecessarily long
cfg.Trail = 8
service, err := validator.NewService(cfg, progressChan)
if err != nil {
t.Fatal(err)
}
go func() {
for block := range progressChan {
validated.add(block)
lastValidated = block
}
}()
wg.Add(1)
go service.Start(ctx, &wg)
t.Cleanup(func() {
service.Stop()
wg.Wait()
g := gomega.NewWithT(t)
g.Expect(progressChan).To(BeClosed())
})
}
func TestValidateContracts(t *testing.T) {
progressChan := make(chan uint64, progressBufferSize)
setup(t, progressChan)
contract, err := integration.DeployTestContract()
if err != nil {
t.Fatal(err)
}
t.Run("contract deployment", func(t *testing.T) {
g := gomega.NewWithT(t)
t.Logf("Deployed contract at block %d", contract.BlockNumber)
g.Expect(progressChan).ToNot(BeClosed())
g.Eventually(validated.contains, timeout).WithArguments(contract.BlockNumber).Should(BeTrue())
})
t.Run("contract method calls", func(t *testing.T) {
g := gomega.NewWithT(t)
var blocks []uint64
for i := 0; i < 3; i++ {
res, err := integration.PutTestValue(contract.Address, i)
if err != nil {
t.Fatal(err)
}
t.Logf("Put() called at block %d", res.BlockNumber)
blocks = append(blocks, res.BlockNumber)
}
g.Expect(progressChan).ToNot(BeClosed())
g.Eventually(validated.containsAll, timeout).WithArguments(blocks).Should(BeTrue())
})
}
func TestValidateTransactions(t *testing.T) {
progressChan := make(chan uint64, progressBufferSize)
setup(t, progressChan)
t.Run("ETH transfer transactions", func(t *testing.T) {
g := gomega.NewWithT(t)
var blocks []uint64
for _, address := range testAddresses {
tx, err := integration.SendEth(address, "0.01")
if err != nil {
t.Fatal(err)
}
t.Logf("Sent tx at block %d", tx.BlockNumber)
blocks = append(blocks, tx.BlockNumber)
}
g.Expect(progressChan).ToNot(BeClosed())
g.Eventually(validated.containsAll, timeout).WithArguments(blocks).Should(BeTrue())
})
}
+36
View File
@@ -0,0 +1,36 @@
package integration_test
import (
"sync"
)
type atomicBlockSet struct {
blocks map[uint64]struct{}
sync.RWMutex
}
func newBlockSet() *atomicBlockSet {
return &atomicBlockSet{blocks: make(map[uint64]struct{})}
}
func (set *atomicBlockSet) contains(block uint64) bool {
set.RLock()
defer set.RUnlock()
_, has := set.blocks[block]
return has
}
func (set *atomicBlockSet) containsAll(blocks []uint64) bool {
for _, block := range blocks {
if !set.contains(block) {
return false
}
}
return true
}
func (set *atomicBlockSet) add(block uint64) {
set.Lock()
defer set.Unlock()
set.blocks[block] = struct{}{}
}
+100
View File
@@ -0,0 +1,100 @@
package chaingen
import (
"math/big"
"github.com/cerc-io/plugeth-statediff/test_helpers"
"github.com/cerc-io/plugeth-statediff/test_helpers/chaingen"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
"github.com/cerc-io/ipld-eth-db-validator/v5/internal/testdata"
)
var (
bank, acct1, acct2 common.Address
contractAddr common.Address
contractDataRoot string
defaultContract *chaingen.ContractSpec
)
func init() {
var err error
defaultContract, err = chaingen.ParseContract(testdata.TestContractABI, testdata.TestContractCode)
if err != nil {
panic(err)
}
}
// A GenContext which exactly replicates the chain generator used in existing tests
func DefaultGenContext(chainConfig *params.ChainConfig, db ethdb.Database) *chaingen.GenContext {
gen := chaingen.NewGenContext(chainConfig, db)
bank = gen.AddOwnedAccount(test_helpers.TestBankKey)
acct1 = gen.AddOwnedAccount(test_helpers.Account1Key)
acct2 = gen.AddOwnedAccount(test_helpers.Account2Key)
gen.AddContract("Test", defaultContract)
gen.AddFunction(func(i int, block *core.BlockGen) {
if err := defaultChainGen(gen, i, block); err != nil {
panic(err)
}
})
gen.Genesis = test_helpers.GenesisBlockForTesting(
db, bank, test_helpers.TestBankFunds, big.NewInt(params.InitialBaseFee), params.MaxGasLimit,
)
return gen
}
func defaultChainGen(gen *chaingen.GenContext, i int, block *core.BlockGen) error {
switch i {
case 0:
// In block 1, the test bank sends account #1 some ether.
tx, err := gen.CreateSendTx(bank, acct1, big.NewInt(10000))
if err != nil {
panic(err)
}
block.AddTx(tx)
case 1:
// In block 2, the test bank sends some more ether to account #1.
// acct1 passes it on to account #2.
// acct1 creates a test contract.
tx1, err := gen.CreateSendTx(bank, acct1, big.NewInt(1000))
if err != nil {
panic(err)
}
block.AddTx(tx1)
tx2, err := gen.CreateSendTx(acct1, acct2, big.NewInt(1000))
if err != nil {
panic(err)
}
block.AddTx(tx2)
contractAddr, err = gen.DeployContract(acct1, "Test")
if err != nil {
panic(err)
}
case 2:
block.SetCoinbase(acct2)
tx, err := gen.CreateCallTx(bank, contractAddr, "Put", big.NewInt(3))
if err != nil {
panic(err)
}
block.AddTx(tx)
case 3:
block.SetCoinbase(acct2)
tx, err := gen.CreateCallTx(bank, contractAddr, "Put", big.NewInt(9))
if err != nil {
panic(err)
}
block.AddTx(tx)
case 4:
block.SetCoinbase(acct1)
tx, err := gen.CreateCallTx(bank, contractAddr, "Put", big.NewInt(0))
if err != nil {
panic(err)
}
block.AddTx(tx)
}
return nil
}
+1
View File
@@ -0,0 +1 @@
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"","type":"address"},{"indexed":false,"internalType":"uint256","name":"","type":"uint256"}],"name":"logPut","type":"event"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"Put","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"close","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"data","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
+1
View File
@@ -0,0 +1 @@
608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610459806100606000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806343d726d61461004657806365f3c31a14610050578063b90d3d0c1461006c575b600080fd5b61004e61009c565b005b61006a60048036038101906100659190610266565b610193565b005b610086600480360381019061008191906102f1565b610213565b604051610093919061032d565b60405180910390f35b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461012a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610121906103cb565b60405180910390fd5b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc479081150290604051600060405180830381858888f19350505050158015610190573d6000803e3d6000fd5b50565b7f370acc53a76362ca0f71a1b2e0c8b8ffbbc1ba9ff3166a1e2fa8445b4848626c33826040516101c49291906103fa565b60405180910390a180600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050565b60016020528060005260406000206000915090505481565b600080fd5b6000819050919050565b61024381610230565b811461024e57600080fd5b50565b6000813590506102608161023a565b92915050565b60006020828403121561027c5761027b61022b565b5b600061028a84828501610251565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102be82610293565b9050919050565b6102ce816102b3565b81146102d957600080fd5b50565b6000813590506102eb816102c5565b92915050565b6000602082840312156103075761030661022b565b5b6000610315848285016102dc565b91505092915050565b61032781610230565b82525050565b6000602082019050610342600083018461031e565b92915050565b600082825260208201905092915050565b7f4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f60008201527f6e2e000000000000000000000000000000000000000000000000000000000000602082015250565b60006103b5602283610348565b91506103c082610359565b604082019050919050565b600060208201905081810360008301526103e4816103a8565b9050919050565b6103f4816102b3565b82525050565b600060408201905061040f60008301856103eb565b61041c602083018461031e565b939250505056fea26469706673582212205c59738e694ff2e1f03f21fa2e7d943137128c0f43ae0e8175a589b32fa81a9a64736f6c63430008140033
+10
View File
@@ -0,0 +1,10 @@
package testdata
import _ "embed"
var (
//go:embed build/Test.abi
TestContractABI string
//go:embed build/Test.bin
TestContractCode string
)
+1 -1
View File
@@ -16,7 +16,7 @@
package main
import "github.com/vulcanize/ipld-eth-db-validator/cmd"
import "github.com/cerc-io/ipld-eth-db-validator/v5/cmd"
func main() {
cmd.Execute()
+25 -73
View File
@@ -18,64 +18,26 @@ package validator
import (
"fmt"
"math/big"
"time"
"github.com/cerc-io/plugeth-statediff/indexer/database/sql/postgres"
"github.com/cerc-io/plugeth-statediff/utils"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/statediff"
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres"
"github.com/jmoiron/sqlx"
"github.com/spf13/viper"
"github.com/vulcanize/ipld-eth-server/v4/pkg/shared"
"github.com/vulcanize/ipld-eth-db-validator/pkg/prom"
)
var IntegrationTestChainConfig = &params.ChainConfig{
ChainID: big.NewInt(99),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
Clique: &params.CliqueConfig{
Period: 0,
Epoch: 30000,
},
}
var TestChainConfig = &params.ChainConfig{
ChainID: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(6),
ArrowGlacierBlock: big.NewInt(0),
Ethash: new(params.EthashConfig),
}
type Config struct {
dbConfig postgres.Config
DB *sqlx.DB
DBConfig postgres.Config
DBStats bool
ChainCfg *params.ChainConfig
ChainConfig *params.ChainConfig
// Used to trigger writing state diffs for gaps in the index
Client *rpc.Client
FromBlock, Trail uint64
RetryInterval time.Duration
StateDiffMissingBlock bool
StateDiffTimeout uint
BlockNum, Trail uint64
SleepInterval uint
StateDiffTimeout time.Duration
}
func NewConfig() (*Config, error) {
@@ -100,27 +62,17 @@ func NewConfig() (*Config, error) {
func (c *Config) setupDB() error {
// DB Config
c.dbConfig.DatabaseName = viper.GetString("database.name")
c.dbConfig.Hostname = viper.GetString("database.hostname")
c.dbConfig.Port = viper.GetInt("database.port")
c.dbConfig.Username = viper.GetString("database.user")
c.dbConfig.Password = viper.GetString("database.password")
c.DBConfig.DatabaseName = viper.GetString("database.name")
c.DBConfig.Hostname = viper.GetString("database.hostname")
c.DBConfig.Port = viper.GetInt("database.port")
c.DBConfig.Username = viper.GetString("database.user")
c.DBConfig.Password = viper.GetString("database.password")
c.dbConfig.MaxIdle = viper.GetInt("database.maxIdle")
c.dbConfig.MaxConns = viper.GetInt("database.maxOpen")
c.dbConfig.MaxConnLifetime = time.Duration(viper.GetInt("database.maxLifetime"))
c.DBConfig.MaxIdle = viper.GetInt("database.maxIdle")
c.DBConfig.MaxConns = viper.GetInt("database.maxOpen")
c.DBConfig.MaxConnLifetime = viper.GetDuration("database.maxLifetime")
// Create DB
db, err := shared.NewDB(c.dbConfig.DbConnectionString(), c.dbConfig)
if err != nil {
return fmt.Errorf("failed to create config: %w", err)
}
c.DB = db
// Enable DB stats
if viper.GetBool("prom.dbStats") {
prom.RegisterDBCollector(c.dbConfig.DatabaseName, c.DB)
}
c.DBStats = viper.GetBool("prom.dbStats")
return nil
}
@@ -129,11 +81,11 @@ func (c *Config) setupEth() error {
var err error
chainConfigPath := viper.GetString("ethereum.chainConfig")
if chainConfigPath != "" {
c.ChainCfg, err = statediff.LoadConfig(chainConfigPath)
c.ChainConfig, err = utils.LoadConfig(chainConfigPath)
} else {
// read chainID if chain config path not provided
chainID := viper.GetUint64("ethereum.chainID")
c.ChainCfg, err = statediff.ChainConfig(chainID)
c.ChainConfig, err = utils.ChainConfig(chainID)
}
if err != nil {
return err
@@ -151,16 +103,16 @@ func (c *Config) setupEth() error {
func (c *Config) setupValidator() error {
var err error
c.BlockNum = viper.GetUint64("validate.blockHeight")
if c.BlockNum < 1 {
return fmt.Errorf("block height cannot be less the 1")
c.FromBlock = viper.GetUint64("validate.fromBlock")
if c.FromBlock < 1 {
return fmt.Errorf("starting block height cannot be less than 1")
}
c.Trail = viper.GetUint64("validate.trail")
c.SleepInterval = viper.GetUint("validate.sleepInterval")
c.RetryInterval = viper.GetDuration("validate.retryInterval")
c.StateDiffMissingBlock = viper.GetBool("validate.stateDiffMissingBlock")
if c.StateDiffMissingBlock {
c.StateDiffTimeout = viper.GetUint("validate.stateDiffTimeout")
c.StateDiffTimeout = viper.GetDuration("validate.stateDiffTimeout")
}
return err
+4 -105
View File
@@ -25,120 +25,19 @@ import (
var errNotSupported = errors.New("this operation is not supported")
type database struct {
ethDB ethdb.Database
ethdb.Database
}
func newDatabase(db ethdb.Database) *database {
return &database{
ethDB: db,
Database: db,
}
}
func (d *database) NewIterator(prefix []byte, start []byte) ethdb.Iterator {
return d.ethDB.NewIterator(prefix, start)
}
func (d *database) Has(key []byte) (bool, error) {
return d.ethDB.Has(key)
}
func (d *database) Get(key []byte) ([]byte, error) {
return d.ethDB.Get(key)
}
func (d *database) Put(key []byte, value []byte) error {
return nil
return errNotSupported
}
func (d *database) Delete(key []byte) error {
return nil
}
func (d *database) Stat(property string) (string, error) {
return d.ethDB.Stat(property)
}
func (d *database) Compact(start []byte, limit []byte) error {
return d.ethDB.Compact(start, limit)
}
// HasAncient returns an error as we don't have a backing chain freezer.
func (d *database) HasAncient(kind string, number uint64) (bool, error) {
return d.ethDB.HasAncient(kind, number)
}
// Ancient returns an error as we don't have a backing chain freezer.
func (d *database) Ancient(kind string, number uint64) ([]byte, error) {
return d.ethDB.Ancient(kind, number)
}
// AncientRange returns an error as we don't have a backing chain freezer.
func (d *database) AncientRange(kind string, start, max, maxByteSize uint64) ([][]byte, error) {
return d.ethDB.AncientRange(kind, start, max, maxByteSize)
}
// Ancients returns an error as we don't have a backing chain freezer.
func (d *database) Ancients() (uint64, error) {
return d.ethDB.Ancients()
}
// AncientSize returns an error as we don't have a backing chain freezer.
func (d *database) AncientSize(kind string) (uint64, error) {
return d.ethDB.AncientSize(kind)
}
// Tail returns the number of first stored item in the freezer.
func (d *database) Tail() (uint64, error) {
return d.Tail()
}
// ModifyAncients is not supported.
func (d *database) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (int64, error) {
return 0, nil
}
// TruncateHead discards all but the first n ancient data from the ancient store.
func (d *database) TruncateHead(n uint64) error {
return nil
}
// TruncateTail discards the first n ancient data from the ancient store.
func (d *database) TruncateTail(n uint64) error {
return nil
}
func (d *database) Sync() error {
return d.ethDB.Sync()
}
// MigrateTable processes and migrates entries of a given table to a new format.
func (d *database) MigrateTable(string, func([]byte) ([]byte, error)) error {
return nil
}
func (d *database) NewBatch() ethdb.Batch {
return d.ethDB.NewBatch()
}
// NewBatchWithSize creates a write-only database batch with pre-allocated buffer.
func (d *database) NewBatchWithSize(size int) ethdb.Batch {
return d.ethDB.NewBatchWithSize(size)
}
func (d *database) ReadAncients(fn func(ethdb.AncientReaderOp) error) (err error) {
return d.ethDB.ReadAncients(fn)
}
func (d *database) Close() error {
return d.ethDB.Close()
}
// NewSnapshot creates a database snapshot based on the current state.
func (d *database) NewSnapshot() (ethdb.Snapshot, error) {
return d.NewSnapshot()
}
// NewSnapshot creates a database snapshot based on the current state.
func (d *database) AncientDatadir() (string, error) {
return "", errNotSupported
return errNotSupported
}
+13
View File
@@ -0,0 +1,13 @@
package validator
import (
"fmt"
)
type ChainNotSyncedError struct {
Head uint64
}
func (e *ChainNotSyncedError) Error() string {
return fmt.Sprintf("chain not synced (current head: %d)", e.Head)
}
+36 -69
View File
@@ -22,49 +22,44 @@ import (
"github.com/jmoiron/sqlx"
)
var (
ReferentialIntegrityErr = "referential integrity check failed at block %d, entry for %s not found"
EntryNotFoundErr = "entry for %s not found"
)
// ValidateReferentialIntegrity validates referential integrity at the given height
func ValidateReferentialIntegrity(db *sqlx.DB, blockNumber uint64) error {
err := ValidateHeaderCIDsRef(db, blockNumber)
func ValidateReferentialIntegrity(tx *sqlx.Tx, blockNumber uint64) error {
err := ValidateHeaderCIDsRef(tx, blockNumber)
if err != nil {
return err
}
err = ValidateUncleCIDsRef(db, blockNumber)
err = ValidateUncleCIDsRef(tx, blockNumber)
if err != nil {
return err
}
err = ValidateTransactionCIDsRef(db, blockNumber)
err = ValidateTransactionCIDsRef(tx, blockNumber)
if err != nil {
return err
}
err = ValidateReceiptCIDsRef(db, blockNumber)
err = ValidateReceiptCIDsRef(tx, blockNumber)
if err != nil {
return err
}
err = ValidateStateCIDsRef(db, blockNumber)
err = ValidateStateCIDsRef(tx, blockNumber)
if err != nil {
return err
}
err = ValidateStorageCIDsRef(db, blockNumber)
err = ValidateStorageCIDsRef(tx, blockNumber)
if err != nil {
return err
}
err = ValidateStateAccountsRef(db, blockNumber)
if err != nil {
return err
}
err = ValidateAccessListElementsRef(db, blockNumber)
if err != nil {
return err
}
err = ValidateLogCIDsRef(db, blockNumber)
err = ValidateLogCIDsRef(tx, blockNumber)
if err != nil {
return err
}
@@ -73,8 +68,8 @@ func ValidateReferentialIntegrity(db *sqlx.DB, blockNumber uint64) error {
}
// ValidateHeaderCIDsRef does a reference integrity check on references in eth.header_cids table
func ValidateHeaderCIDsRef(db *sqlx.DB, blockNumber uint64) error {
err := ValidateIPFSBlocks(db, blockNumber, "eth.header_cids", "mh_key")
func ValidateHeaderCIDsRef(tx *sqlx.Tx, blockNumber uint64) error {
err := ValidateIPFSBlocks(tx, blockNumber, "eth.header_cids", "cid")
if err != nil {
return err
}
@@ -83,9 +78,9 @@ func ValidateHeaderCIDsRef(db *sqlx.DB, blockNumber uint64) error {
}
// ValidateUncleCIDsRef does a reference integrity check on references in eth.uncle_cids table
func ValidateUncleCIDsRef(db *sqlx.DB, blockNumber uint64) error {
func ValidateUncleCIDsRef(tx *sqlx.Tx, blockNumber uint64) error {
var exists bool
err := db.Get(&exists, UncleCIDsRefHeaderCIDs, blockNumber)
err := tx.Get(&exists, UncleCIDsRefHeaderCIDs, blockNumber)
if err != nil {
return err
}
@@ -93,7 +88,7 @@ func ValidateUncleCIDsRef(db *sqlx.DB, blockNumber uint64) error {
return fmt.Errorf(ReferentialIntegrityErr, blockNumber, "eth.header_cids")
}
err = ValidateIPFSBlocks(db, blockNumber, "eth.uncle_cids", "mh_key")
err = ValidateIPFSBlocks(tx, blockNumber, "eth.uncle_cids", "cid")
if err != nil {
return err
}
@@ -102,9 +97,9 @@ func ValidateUncleCIDsRef(db *sqlx.DB, blockNumber uint64) error {
}
// ValidateTransactionCIDsRef does a reference integrity check on references in eth.header_cids table
func ValidateTransactionCIDsRef(db *sqlx.DB, blockNumber uint64) error {
func ValidateTransactionCIDsRef(tx *sqlx.Tx, blockNumber uint64) error {
var exists bool
err := db.Get(&exists, TransactionCIDsRefHeaderCIDs, blockNumber)
err := tx.Get(&exists, TransactionCIDsRefHeaderCIDs, blockNumber)
if err != nil {
return err
}
@@ -112,7 +107,7 @@ func ValidateTransactionCIDsRef(db *sqlx.DB, blockNumber uint64) error {
return fmt.Errorf(ReferentialIntegrityErr, blockNumber, "eth.header_cids")
}
err = ValidateIPFSBlocks(db, blockNumber, "eth.transaction_cids", "mh_key")
err = ValidateIPFSBlocks(tx, blockNumber, "eth.transaction_cids", "cid")
if err != nil {
return err
}
@@ -121,9 +116,9 @@ func ValidateTransactionCIDsRef(db *sqlx.DB, blockNumber uint64) error {
}
// ValidateReceiptCIDsRef does a reference integrity check on references in eth.receipt_cids table
func ValidateReceiptCIDsRef(db *sqlx.DB, blockNumber uint64) error {
func ValidateReceiptCIDsRef(tx *sqlx.Tx, blockNumber uint64) error {
var exists bool
err := db.Get(&exists, ReceiptCIDsRefTransactionCIDs, blockNumber)
err := tx.Get(&exists, ReceiptCIDsRefTransactionCIDs, blockNumber)
if err != nil {
return err
}
@@ -131,7 +126,7 @@ func ValidateReceiptCIDsRef(db *sqlx.DB, blockNumber uint64) error {
return fmt.Errorf(ReferentialIntegrityErr, blockNumber, "eth.transaction_cids")
}
err = ValidateIPFSBlocks(db, blockNumber, "eth.receipt_cids", "leaf_mh_key")
err = ValidateIPFSBlocks(tx, blockNumber, "eth.receipt_cids", "cid")
if err != nil {
return err
}
@@ -140,9 +135,9 @@ func ValidateReceiptCIDsRef(db *sqlx.DB, blockNumber uint64) error {
}
// ValidateStateCIDsRef does a reference integrity check on references in eth.state_cids table
func ValidateStateCIDsRef(db *sqlx.DB, blockNumber uint64) error {
func ValidateStateCIDsRef(tx *sqlx.Tx, blockNumber uint64) error {
var exists bool
err := db.Get(&exists, StateCIDsRefHeaderCIDs, blockNumber)
err := tx.Get(&exists, StateCIDsRefHeaderCIDs, blockNumber)
if err != nil {
return err
}
@@ -150,7 +145,7 @@ func ValidateStateCIDsRef(db *sqlx.DB, blockNumber uint64) error {
return fmt.Errorf(ReferentialIntegrityErr, blockNumber, "eth.header_cids")
}
err = ValidateIPFSBlocks(db, blockNumber, "eth.state_cids", "mh_key")
err = ValidateIPFSBlocks(tx, blockNumber, "eth.state_cids", "cid")
if err != nil {
return err
}
@@ -159,9 +154,9 @@ func ValidateStateCIDsRef(db *sqlx.DB, blockNumber uint64) error {
}
// ValidateStorageCIDsRef does a reference integrity check on references in eth.storage_cids table
func ValidateStorageCIDsRef(db *sqlx.DB, blockNumber uint64) error {
func ValidateStorageCIDsRef(tx *sqlx.Tx, blockNumber uint64) error {
var exists bool
err := db.Get(&exists, StorageCIDsRefStateCIDs, blockNumber)
err := tx.Get(&exists, StorageCIDsRefStateCIDs, blockNumber)
if err != nil {
return err
}
@@ -169,7 +164,7 @@ func ValidateStorageCIDsRef(db *sqlx.DB, blockNumber uint64) error {
return fmt.Errorf(ReferentialIntegrityErr, blockNumber, "eth.state_cids")
}
err = ValidateIPFSBlocks(db, blockNumber, "eth.storage_cids", "mh_key")
err = ValidateIPFSBlocks(tx, blockNumber, "eth.storage_cids", "cid")
if err != nil {
return err
}
@@ -177,38 +172,10 @@ func ValidateStorageCIDsRef(db *sqlx.DB, blockNumber uint64) error {
return nil
}
// ValidateStateAccountsRef does a reference integrity check on references in eth.state_accounts table
func ValidateStateAccountsRef(db *sqlx.DB, blockNumber uint64) error {
var exists bool
err := db.Get(&exists, StateAccountsRefStateCIDs, blockNumber)
if err != nil {
return err
}
if exists {
return fmt.Errorf(ReferentialIntegrityErr, blockNumber, "eth.state_cids")
}
return nil
}
// ValidateAccessListElementsRef does a reference integrity check on references in eth.access_list_elements table
func ValidateAccessListElementsRef(db *sqlx.DB, blockNumber uint64) error {
var exists bool
err := db.Get(&exists, AccessListElementsRefTransactionCIDs, blockNumber)
if err != nil {
return err
}
if exists {
return fmt.Errorf(ReferentialIntegrityErr, blockNumber, "eth.transaction_cids")
}
return nil
}
// ValidateLogCIDsRef does a reference integrity check on references in eth.log_cids table
func ValidateLogCIDsRef(db *sqlx.DB, blockNumber uint64) error {
func ValidateLogCIDsRef(tx *sqlx.Tx, blockNumber uint64) error {
var exists bool
err := db.Get(&exists, LogCIDsRefReceiptCIDs, blockNumber)
err := tx.Get(&exists, LogCIDsRefReceiptCIDs, blockNumber)
if err != nil {
return err
}
@@ -216,7 +183,7 @@ func ValidateLogCIDsRef(db *sqlx.DB, blockNumber uint64) error {
return fmt.Errorf(ReferentialIntegrityErr, blockNumber, "eth.receipt_cids")
}
err = ValidateIPFSBlocks(db, blockNumber, "eth.log_cids", "leaf_mh_key")
err = ValidateIPFSBlocks(tx, blockNumber, "eth.log_cids", "cid")
if err != nil {
return err
}
@@ -225,14 +192,14 @@ func ValidateLogCIDsRef(db *sqlx.DB, blockNumber uint64) error {
}
// ValidateIPFSBlocks does a reference integrity check between the given CID table and IPFS blocks table on MHKey and block number
func ValidateIPFSBlocks(db *sqlx.DB, blockNumber uint64, CIDTable string, mhKeyField string) error {
func ValidateIPFSBlocks(tx *sqlx.Tx, blockNumber uint64, CIDTable string, CIDField string) error {
var exists bool
err := db.Get(&exists, fmt.Sprintf(CIDsRefIPLDBlocks, CIDTable, mhKeyField), blockNumber)
err := tx.Get(&exists, fmt.Sprintf(CIDsRefIPLDBlocks, CIDTable, CIDField), blockNumber)
if err != nil {
return err
}
if exists {
return fmt.Errorf(ReferentialIntegrityErr, blockNumber, "public.blocks")
return fmt.Errorf(ReferentialIntegrityErr, blockNumber, "ipld.blocks")
}
return nil
+3 -28
View File
@@ -26,7 +26,7 @@ const (
CIDsRefIPLDBlocks = `SELECT EXISTS (
SELECT *
FROM %[1]s
LEFT JOIN public.blocks ON (
LEFT JOIN ipld.blocks ON (
%[1]s.%[2]s = blocks.key
AND %[1]s.block_number = blocks.block_number
)
@@ -88,38 +88,13 @@ const (
SELECT *
FROM eth.storage_cids
LEFT JOIN eth.state_cids ON (
storage_cids.state_path = state_cids.state_path
storage_cids.state_leaf_key = state_cids.state_leaf_key
AND storage_cids.header_id = state_cids.header_id
AND storage_cids.block_number = state_cids.block_number
)
WHERE
storage_cids.block_number = $1
AND state_cids.state_path IS NULL
)`
StateAccountsRefStateCIDs = `SELECT EXISTS (
SELECT *
FROM eth.state_accounts
LEFT JOIN eth.state_cids ON (
state_accounts.state_path = state_cids.state_path
AND state_accounts.header_id = state_cids.header_id
AND state_accounts.block_number = state_cids.block_number
)
WHERE
state_accounts.block_number = $1
AND state_cids.state_path IS NULL
)`
AccessListElementsRefTransactionCIDs = `SELECT EXISTS (
SELECT *
FROM eth.access_list_elements
LEFT JOIN eth.transaction_cids ON (
access_list_elements.tx_id = transaction_cids.tx_hash
AND access_list_elements.block_number = transaction_cids.block_number
)
WHERE
access_list_elements.block_number = $1
AND transaction_cids.tx_hash IS NULL
AND state_cids.state_leaf_key IS NULL
)`
LogCIDsRefReceiptCIDs = `SELECT EXISTS (
+258
View File
@@ -0,0 +1,258 @@
package validator_test
import (
"context"
"fmt"
"math/big"
"testing"
indexer_helpers "github.com/cerc-io/plugeth-statediff/indexer/test_helpers"
helpers "github.com/cerc-io/plugeth-statediff/test_helpers"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/jmoiron/sqlx"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/cerc-io/ipld-eth-db-validator/v5/internal/chaingen"
"github.com/cerc-io/ipld-eth-db-validator/v5/pkg/validator"
)
func TestRefIntegrity(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "ETH IPLD validator ref integrity suite test")
}
var _ = Describe("referential integrity", Ordered, func() {
var (
db *sqlx.DB
tx *sqlx.Tx
checkedBlock *types.Block // Generated block of interest
)
BeforeAll(func() {
var (
blocks []*types.Block
receipts []types.Receipts
chain *core.BlockChain
chainConfig = TestChainConfig
mockTD = big.NewInt(1337)
testdb = rawdb.NewMemoryDatabase()
)
gen := chaingen.DefaultGenContext(chainConfig, testdb)
gen.AddFunction(func(i int, block *core.BlockGen) {
if i >= 2 {
uncle := &types.Header{
Number: big.NewInt(int64(i - 1)),
Root: common.HexToHash("0x1"),
TxHash: common.HexToHash("0x1"),
ReceiptHash: common.HexToHash("0x1"),
ParentHash: block.PrevBlock(i - 1).Hash(),
}
block.AddUncle(uncle)
}
})
blocks, receipts, chain = gen.MakeChain(5)
indexer, err := helpers.NewIndexer(context.Background(), chainConfig, gen.Genesis.Hash(), TestDBConfig)
Expect(err).ToNot(HaveOccurred())
helpers.IndexChain(indexer, helpers.IndexChainParams{
StateCache: chain.StateCache(),
Blocks: blocks,
Receipts: receipts,
TotalDifficulty: mockTD,
})
checkedBlock = blocks[5]
db = SetupDB()
})
AfterAll(func() { Expect(indexer_helpers.ClearSqlxDB(db)).ToNot(HaveOccurred()) })
BeforeEach(func() { tx = db.MustBegin() })
AfterEach(func() {
tx.Rollback()
})
Describe("ValidateHeaderCIDsRef", func() {
It("Validates referential integrity of header_cids table", func() {
err := validator.ValidateHeaderCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).ToNot(HaveOccurred())
})
It("Throws an error if corresponding header IPFS block entry not found", func() {
err := deleteEntriesFrom(tx, "ipld.blocks")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateHeaderCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "ipld.blocks"))
})
})
Describe("ValidateUncleCIDsRef", func() {
It("Validates referential integrity of uncle_cids table", func() {
err := validator.ValidateUncleCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).ToNot(HaveOccurred())
})
It("Throws an error if corresponding header_cid entry not found", func() {
err := deleteEntriesFrom(tx, "eth.header_cids")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateUncleCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "eth.header_cids"))
})
It("Throws an error if corresponding uncle IPFS block entry not found", func() {
err := deleteEntriesFrom(tx, "ipld.blocks")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateUncleCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "ipld.blocks"))
})
})
Describe("ValidateTransactionCIDsRef", func() {
It("Validates referential integrity of transaction_cids table", func() {
err := validator.ValidateTransactionCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).ToNot(HaveOccurred())
})
It("Throws an error if corresponding header_cid entry not found", func() {
err := deleteEntriesFrom(tx, "eth.header_cids")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateTransactionCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "eth.header_cids"))
})
It("Throws an error if corresponding transaction IPFS block entry not found", func() {
err := deleteEntriesFrom(tx, "ipld.blocks")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateTransactionCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "ipld.blocks"))
})
})
Describe("ValidateReceiptCIDsRef", func() {
It("Validates referential integrity of receipt_cids table", func() {
err := validator.ValidateReceiptCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).ToNot(HaveOccurred())
})
It("Throws an error if corresponding transaction_cids entry not found", func() {
err := deleteEntriesFrom(tx, "eth.transaction_cids")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateReceiptCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "eth.transaction_cids"))
})
It("Throws an error if corresponding receipt IPFS block entry not found", func() {
err := deleteEntriesFrom(tx, "ipld.blocks")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateReceiptCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "ipld.blocks"))
})
})
Describe("ValidateStateCIDsRef", func() {
It("Validates referential integrity of state_cids table", func() {
err := validator.ValidateStateCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).ToNot(HaveOccurred())
})
It("Throws an error if corresponding header_cids entry not found", func() {
err := deleteEntriesFrom(tx, "eth.header_cids")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateStateCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "eth.header_cids"))
})
It("Throws an error if corresponding state IPFS block entry not found", func() {
err := deleteEntriesFrom(tx, "ipld.blocks")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateStateCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "ipld.blocks"))
})
})
Describe("ValidateStorageCIDsRef", func() {
It("Validates referential integrity of storage_cids table", func() {
err := validator.ValidateStorageCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).ToNot(HaveOccurred())
})
It("Throws an error if corresponding state_cids entry not found", func() {
err := deleteEntriesFrom(tx, "eth.state_cids")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateStorageCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "eth.state_cids"))
})
It("Throws an error if corresponding storage IPFS block entry not found", func() {
err := deleteEntriesFrom(tx, "ipld.blocks")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateStorageCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "ipld.blocks"))
})
})
Describe("ValidateLogCIDsRef", func() {
It("Validates referential integrity of log_cids table", func() {
err := validator.ValidateLogCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).ToNot(HaveOccurred())
})
It("Throws an error if corresponding receipt_cids entry not found", func() {
err := deleteEntriesFrom(tx, "eth.receipt_cids")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateLogCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "eth.receipt_cids"))
})
It("Throws an error if corresponding log IPFS block entry not found", func() {
err := deleteEntriesFrom(tx, "ipld.blocks")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateLogCIDsRef(tx, checkedBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "ipld.blocks"))
})
})
Describe("ValidateReferentialIntegrity", func() {
It("Validates referential integrity of full chain", func() {
for i := uint64(startBlock); i <= chainLength; i++ {
err := validator.ValidateReferentialIntegrity(tx, i)
Expect(err).ToNot(HaveOccurred())
}
})
})
})
func deleteEntriesFrom(tx *sqlx.Tx, tableName string) error {
pgStr := "TRUNCATE %s"
_, err := tx.Exec(fmt.Sprintf(pgStr, tableName))
return err
}
+46
View File
@@ -0,0 +1,46 @@
package validator_test
import (
"context"
"math/big"
"github.com/cerc-io/plugeth-statediff/indexer/database/sql/postgres"
"github.com/ethereum/go-ethereum/params"
"github.com/jmoiron/sqlx"
)
var TestChainConfig = &params.ChainConfig{
ChainID: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(6),
ArrowGlacierBlock: big.NewInt(0),
GrayGlacierBlock: big.NewInt(0),
Ethash: new(params.EthashConfig),
}
var TestDBConfig postgres.Config
func init() {
var err error
TestDBConfig, err = postgres.TestConfig.WithEnv()
if err != nil {
panic(err)
}
}
func SetupDB() *sqlx.DB {
db, err := postgres.ConnectSQLX(context.Background(), TestDBConfig)
if err != nil {
panic(err)
}
return db
}
+233 -187
View File
@@ -25,232 +25,187 @@ import (
"sync"
"time"
statediff "github.com/cerc-io/plugeth-statediff"
"github.com/cerc-io/plugeth-statediff/indexer/database/sql/postgres"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/statediff"
"github.com/holiman/uint256"
"github.com/jmoiron/sqlx"
log "github.com/sirupsen/logrus"
ipfsethdb "github.com/vulcanize/ipfs-ethdb/v4/postgres"
ipldEth "github.com/vulcanize/ipld-eth-server/v4/pkg/eth"
ethServerShared "github.com/vulcanize/ipld-eth-server/v4/pkg/shared"
ipfsethdb "github.com/cerc-io/ipfs-ethdb/v5/postgres/v0"
ipldeth "github.com/cerc-io/ipld-eth-server/v5/pkg/eth"
"github.com/cerc-io/ipld-eth-server/v5/pkg/shared"
ipldstate "github.com/cerc-io/ipld-eth-statedb/trie_by_cid/state"
"github.com/vulcanize/ipld-eth-db-validator/pkg/prom"
"github.com/cerc-io/ipld-eth-db-validator/v5/pkg/prom"
)
var (
big8 = big.NewInt(8)
big32 = big.NewInt(32)
ReferentialIntegrityErr = "referential integrity check failed at block %d, entry for %s not found"
EntryNotFoundErr = "entry for %s not found"
)
type service struct {
db *sqlx.DB
blockNum, trail uint64
sleepInterval uint
chainCfg *params.ChainConfig
type Service struct {
db *sqlx.DB
stateDiffMissingBlock bool
stateDiffTimeout uint
chainConfig *params.ChainConfig
ethClient *rpc.Client
blockNum, trail uint64
retryInterval time.Duration
stateDiffMissingBlock bool
stateDiffTimeout time.Duration
quitChan chan bool
progressChan chan uint64
progressChan chan<- uint64
}
func NewService(cfg *Config, progressChan chan uint64) *service {
return &service{
db: cfg.DB,
blockNum: cfg.BlockNum,
func NewService(cfg *Config, progressChan chan<- uint64) (*Service, error) {
db, err := postgres.ConnectSQLX(context.Background(), cfg.DBConfig)
if err != nil {
return nil, err
}
// Enable DB stats
if cfg.DBStats {
prom.RegisterDBCollector(cfg.DBConfig.DatabaseName, db)
}
return &Service{
db: db,
chainConfig: cfg.ChainConfig,
ethClient: cfg.Client,
blockNum: cfg.FromBlock,
trail: cfg.Trail,
sleepInterval: cfg.SleepInterval,
chainCfg: cfg.ChainCfg,
retryInterval: cfg.RetryInterval,
stateDiffMissingBlock: cfg.StateDiffMissingBlock,
stateDiffTimeout: cfg.StateDiffTimeout,
ethClient: cfg.Client,
quitChan: make(chan bool),
progressChan: progressChan,
}
}
func NewEthBackend(db *sqlx.DB, c *ipldEth.Config) (*ipldEth.Backend, error) {
gcc := c.GroupCacheConfig
groupName := gcc.StateDB.Name
if groupName == "" {
groupName = ipldEth.StateDBGroupCacheName
}
r := ipldEth.NewCIDRetriever(db)
ethDB := ipfsethdb.NewDatabase(db, ipfsethdb.CacheConfig{
Name: groupName,
Size: gcc.StateDB.CacheSizeInMB * 1024 * 1024,
ExpiryDuration: time.Minute * time.Duration(gcc.StateDB.CacheExpiryInMins),
})
// Read only wrapper around ipfs-ethdb eth.Database implementation
customEthDB := newDatabase(ethDB)
return &ipldEth.Backend{
DB: db,
Retriever: r,
Fetcher: ipldEth.NewIPLDFetcher(db),
IPLDRetriever: ipldEth.NewIPLDRetriever(db),
EthDB: customEthDB,
StateDatabase: state.NewDatabase(customEthDB),
Config: c,
}, nil
}
// Start is used to begin the service
func (s *service) Start(ctx context.Context, wg *sync.WaitGroup) {
func (s *Service) Start(ctx context.Context, wg *sync.WaitGroup) {
defer wg.Done()
api, err := EthAPI(ctx, s.db, s.chainCfg)
api, err := EthAPI(ctx, s.db, s.chainConfig)
if err != nil {
log.Fatal(err)
return
}
idxBlockNum := s.blockNum
nextBlockNum := s.blockNum
var delay time.Duration
for {
select {
case <-s.quitChan:
log.Infof("last validated block %v", idxBlockNum-1)
log.Info("stopping ipld-eth-db-validator process")
if s.progressChan != nil {
close(s.progressChan)
}
if err := api.B.Close(); err != nil {
log.Errorf("error closing backend: %s", err)
}
return
default:
idxBlockNum, err = s.Validate(ctx, api, idxBlockNum)
case <-time.After(delay):
err := s.Validate(ctx, api, nextBlockNum)
// If chain is not synced, wait for trail to catch up before trying again
if notsynced, ok := err.(*ChainNotSyncedError); ok {
delay = s.retryInterval
log.Infof("waiting %v for chain to advance to block %d (head is at %d)",
delay, nextBlockNum+s.trail, notsynced.Head)
continue
}
if err != nil {
log.Infof("last validated block %v", idxBlockNum-1)
log.Fatal(err)
return
}
prom.SetLastValidatedBlock(float64(idxBlockNum))
prom.SetLastValidatedBlock(float64(nextBlockNum))
nextBlockNum++
delay = 0
}
}
}
// Stop is used to gracefully stop the service
func (s *service) Stop() {
log.Info("stopping ipld-eth-db-validator process")
func (s *Service) Stop() {
close(s.quitChan)
}
func (s *service) Validate(ctx context.Context, api *ipldEth.PublicEthAPI, idxBlockNum uint64) (uint64, error) {
func (s *Service) Validate(ctx context.Context, api *ipldeth.PublicEthAPI, idxBlockNum uint64) error {
log.Debugf("validating block %d", idxBlockNum)
headBlockNum, err := fetchHeadBlockNumber(ctx, api)
if err != nil {
return idxBlockNum, err
}
// Check if it block at height idxBlockNum can be validated
if idxBlockNum <= headBlockNum-s.trail {
blockToBeValidated, err := api.B.BlockByNumber(ctx, rpc.BlockNumber(idxBlockNum))
if err != nil {
log.Errorf("failed to fetch block at height %d", idxBlockNum)
return idxBlockNum, err
}
// Make a writeStateDiffAt call if block not found in the db
if blockToBeValidated == nil {
err = s.writeStateDiffAt(idxBlockNum)
return idxBlockNum, err
}
err = ValidateBlock(blockToBeValidated, api.B, idxBlockNum)
if err != nil {
log.Errorf("failed to verify state root at block %d", idxBlockNum)
return idxBlockNum, err
}
log.Infof("state root verified for block %d", idxBlockNum)
err = ValidateReferentialIntegrity(s.db, idxBlockNum)
if err != nil {
log.Errorf("failed to verify referential integrity at block %d", idxBlockNum)
return idxBlockNum, err
}
log.Infof("referential integrity verified for block %d", idxBlockNum)
if s.progressChan != nil {
s.progressChan <- idxBlockNum
}
idxBlockNum++
} else {
// Sleep / wait for head to move ahead
time.Sleep(time.Second * time.Duration(s.sleepInterval))
}
return idxBlockNum, nil
}
// writeStateDiffAt calls out to a statediffing geth client to fill in a gap in the index
func (s *service) writeStateDiffAt(height uint64) error {
if !s.stateDiffMissingBlock {
return nil
}
var data json.RawMessage
params := statediff.Params{
IntermediateStateNodes: true,
IntermediateStorageNodes: true,
IncludeBlock: true,
IncludeReceipts: true,
IncludeTD: true,
IncludeCode: true,
}
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(s.stateDiffTimeout*uint(time.Second)))
defer cancel()
log.Warnf("making writeStateDiffAt call at height %d", height)
if err := s.ethClient.CallContext(ctx, &data, "statediff_writeStateDiffAt", height, params); err != nil {
log.Errorf("writeStateDiffAt %d faild with err %s", height, err.Error())
return err
}
// Check if block at requested height can be validated
if idxBlockNum+s.trail > headBlockNum {
return &ChainNotSyncedError{headBlockNum}
}
blockToBeValidated, err := api.B.BlockByNumber(ctx, rpc.BlockNumber(idxBlockNum))
if err != nil {
log.Errorf("failed to fetch block at height %d", idxBlockNum)
return err
}
// Make a writeStateDiffAt call if block not found in the db
if blockToBeValidated == nil {
return s.writeStateDiffAt(idxBlockNum)
}
err = ValidateBlock(blockToBeValidated, api.B, idxBlockNum)
if err != nil {
log.Errorf("failed to verify state root at block %d", idxBlockNum)
return err
}
log.Infof("state root verified for block %d", idxBlockNum)
tx := s.db.MustBegin()
defer tx.Rollback()
err = ValidateReferentialIntegrity(tx, idxBlockNum)
if err != nil {
log.Errorf("failed to verify referential integrity at block %d", idxBlockNum)
return err
}
log.Infof("referential integrity verified for block %d", idxBlockNum)
if s.progressChan != nil {
s.progressChan <- idxBlockNum
}
return nil
}
// ValidateBlock validates block at the given height
func ValidateBlock(blockToBeValidated *types.Block, b *ipldEth.Backend, blockNumber uint64) error {
stateDB, err := applyTransaction(blockToBeValidated, b)
func ValidateBlock(blockToBeValidated *types.Block, b *ipldeth.Backend, blockNumber uint64) error {
state, err := applyTransactions(blockToBeValidated, b)
if err != nil {
return err
}
blockStateRoot := blockToBeValidated.Header().Root.String()
dbStateRoot := stateDB.IntermediateRoot(true).String()
blockStateRoot := blockToBeValidated.Header().Root
dbStateRoot := state.IntermediateRoot(true)
if blockStateRoot != dbStateRoot {
return fmt.Errorf("state roots do not match at block %d", blockNumber)
}
return nil
}
func EthAPI(ctx context.Context, db *sqlx.DB, chainCfg *params.ChainConfig) (*ipldEth.PublicEthAPI, error) {
func EthAPI(ctx context.Context, db *sqlx.DB, chainCfg *params.ChainConfig) (*ipldeth.PublicEthAPI, error) {
// TODO: decide network for custom chainConfig.
backend, err := NewEthBackend(db, &ipldEth.Config{
backend, err := ethBackend(db, &ipldeth.Config{
ChainConfig: chainCfg,
GroupCacheConfig: &ethServerShared.GroupCacheConfig{
StateDB: ethServerShared.GroupConfig{
Name: "vulcanize_validator",
GroupCacheConfig: &shared.GroupCacheConfig{
StateDB: shared.GroupConfig{
Name: "cerc_validator",
},
},
})
@@ -267,12 +222,44 @@ func EthAPI(ctx context.Context, db *sqlx.DB, chainCfg *params.ChainConfig) (*ip
}
backend.Config.ChainConfig = setChainConfig(genesisBlock.Hash())
}
config := ipldeth.APIConfig{
SupportsStateDiff: false,
ForwardEthCalls: false,
ForwardGetStorageAt: false,
ProxyOnError: false,
StateDiffTimeout: 0,
}
return ipldeth.NewPublicEthAPI(backend, nil, config)
}
return ipldEth.NewPublicEthAPI(backend, nil, false, false, false)
func ethBackend(db *sqlx.DB, c *ipldeth.Config) (*ipldeth.Backend, error) {
gcc := c.GroupCacheConfig
groupName := gcc.StateDB.Name
if groupName == "" {
groupName = ipldeth.StateDBGroupCacheName
}
r := ipldeth.NewRetriever(db)
ethDB := ipfsethdb.NewDatabase(db, ipfsethdb.CacheConfig{
Name: groupName,
Size: gcc.StateDB.CacheSizeInMB * 1024 * 1024,
ExpiryDuration: time.Minute * time.Duration(gcc.StateDB.CacheExpiryInMins),
})
// Read only wrapper around ipfs-ethdb eth.Database implementation
ethDB = newDatabase(ethDB)
return &ipldeth.Backend{
DB: db,
Retriever: r,
EthDB: ethDB,
IpldTrieStateDatabase: ipldstate.NewDatabase(ethDB),
Config: c,
}, nil
}
// fetchHeadBlockNumber gets the latest block number from the db
func fetchHeadBlockNumber(ctx context.Context, api *ipldEth.PublicEthAPI) (uint64, error) {
func fetchHeadBlockNumber(ctx context.Context, api *ipldeth.PublicEthAPI) (uint64, error) {
headBlock, err := api.B.BlockByNumber(ctx, rpc.LatestBlockNumber)
if err != nil {
return 0, err
@@ -281,58 +268,101 @@ func fetchHeadBlockNumber(ctx context.Context, api *ipldEth.PublicEthAPI) (uint6
return headBlock.NumberU64(), nil
}
// applyTransaction attempts to apply a transaction to the given state database
// and uses the input parameters for its environment. It returns the stateDB of parent with applied transa
func applyTransaction(block *types.Block, backend *ipldEth.Backend) (*state.StateDB, error) {
// writeStateDiffAt calls out to a statediffing geth client to fill in a gap in the index
func (s *Service) writeStateDiffAt(height uint64) error {
if !s.stateDiffMissingBlock {
return nil
}
var data json.RawMessage
params := statediff.Params{
IncludeBlock: true,
IncludeReceipts: true,
IncludeTD: true,
IncludeCode: true,
}
ctx, cancel := context.WithTimeout(context.Background(), s.stateDiffTimeout)
defer cancel()
log.Warnf("calling writeStateDiffAt at block %d", height)
if err := s.ethClient.CallContext(ctx, &data, "statediff_writeStateDiffAt", height, params); err != nil {
log.Errorf("writeStateDiffAt %d failed with err %s", height, err)
return err
}
return nil
}
// applyTransaction attempts to apply block transactions to the given state database
// and uses the input parameters for its environment. It returns the stateDB of parent with applied txs.
//
// Note: this skips the DAO hard fork refund
func applyTransactions(block *types.Block, backend *ipldeth.Backend) (*ipldstate.StateDB, error) {
if block.NumberU64() == 0 {
return nil, errors.New("no transaction in genesis")
}
config := backend.Config.ChainConfig
// Create the parent state database
parentHash := block.ParentHash()
parentRPCBlockHash := rpc.BlockNumberOrHash{
BlockHash: &parentHash,
RequireCanonical: false,
}
parent, _ := backend.BlockByNumberOrHash(context.Background(), parentRPCBlockHash)
if parent == nil {
return nil, fmt.Errorf("parent %#x not found", block.ParentHash())
}
stateDB, _, err := backend.StateAndHeaderByNumberOrHash(context.Background(), parentRPCBlockHash)
nrOrHash := rpc.BlockNumberOrHash{BlockHash: &parentHash}
statedb, _, err := backend.IPLDTrieStateDBAndHeaderByNumberOrHash(context.Background(), nrOrHash)
if err != nil {
return nil, err
return nil, fmt.Errorf("error accessing state DB: %w", err)
}
signer := types.MakeSigner(backend.Config.ChainConfig, block.Number())
var gp core.GasPool
gp.AddGas(block.GasLimit())
for idx, tx := range block.Transactions() {
// Assemble the transaction call message and return if the requested offset
msg, _ := tx.AsMessage(signer, block.BaseFee())
txContext := core.NewEVMTxContext(msg)
ctx := core.NewEVMBlockContext(block.Header(), backend, getAuthor(backend, block.Header()))
blockContext := core.NewEVMBlockContext(block.Header(), backend, getAuthor(backend, block.Header()))
evm := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, vm.Config{})
signer := types.MakeSigner(config, block.Number(), block.Time())
// Not yet the searched for transaction, execute on top of the current state
newEVM := vm.NewEVM(ctx, txContext, stateDB, backend.Config.ChainConfig, vm.Config{})
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
ProcessBeaconBlockRoot(*beaconRoot, evm, statedb)
}
stateDB.Prepare(tx.Hash(), idx)
if _, err := core.ApplyMessage(newEVM, msg, new(core.GasPool).AddGas(block.GasLimit())); err != nil {
return nil, fmt.Errorf("transaction %#x failed: %w", tx.Hash(), err)
// Iterate over and process the individual transactions
for i, tx := range block.Transactions() {
msg, err := core.TransactionToMessage(tx, signer, block.BaseFee())
if err != nil {
return nil, fmt.Errorf("error converting transaction to message: %w", err)
}
statedb.SetTxContext(tx.Hash(), i)
// Create a new context to be used in the EVM environment.
evm.Reset(core.NewEVMTxContext(msg), statedb)
// Apply the transaction to the current state (included in the env).
if _, err := core.ApplyMessage(evm, msg, &gp); err != nil {
return nil, fmt.Errorf("error applying tx %#x: %w", tx.Hash(), err)
}
if config.IsByzantium(block.Number()) {
statedb.Finalise(true)
} else {
statedb.IntermediateRoot(config.IsEIP158(block.Number())).Bytes()
}
}
if backend.Config.ChainConfig.Ethash != nil {
accumulateRewards(backend.Config.ChainConfig, stateDB, block.Header(), block.Uncles())
// Withdrawals processing.
for _, w := range block.Withdrawals() {
// Convert amount from gwei to wei.
amount := new(uint256.Int).SetUint64(w.Amount)
amount = amount.Mul(amount, uint256.NewInt(params.GWei))
statedb.AddBalance(w.Address, amount)
}
return stateDB, nil
if config.Ethash != nil {
accumulateRewards(config, statedb, block.Header(), block.Uncles())
}
return statedb, nil
}
// accumulateRewards credits the coinbase of the given block with the mining
// reward. The total reward consists of the static block reward and rewards for
// included uncles. The coinbase of each uncle block is also rewarded.
func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) {
func accumulateRewards(config *params.ChainConfig, state *ipldstate.StateDB, header *types.Header, uncles []*types.Header) {
// Select the correct block reward based on chain progression
blockReward := ethash.FrontierBlockReward
if config.IsByzantium(header.Number) {
@@ -344,32 +374,48 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header
}
// Accumulate the rewards for the miner and any included uncles
reward := new(big.Int).Set(blockReward)
reward := new(big.Int).Set(blockReward.ToBig())
r := new(big.Int)
for _, uncle := range uncles {
r.Add(uncle.Number, big8)
r.Sub(r, header.Number)
r.Mul(r, blockReward)
r.Mul(r, blockReward.ToBig())
r.Div(r, big8)
state.AddBalance(uncle.Coinbase, r)
state.AddBalance(uncle.Coinbase, uint256.MustFromBig(r))
r.Div(blockReward, big32)
r.Div(blockReward.ToBig(), big32)
reward.Add(reward, r)
}
state.AddBalance(header.Coinbase, reward)
state.AddBalance(header.Coinbase, uint256.MustFromBig(reward))
}
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
// contract. This method is exported to be used in tests.
func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *ipldstate.StateDB) {
// If EIP-4788 is enabled, we need to invoke the beaconroot storage contract with
// the new root
msg := &core.Message{
From: params.SystemAddress,
GasLimit: 30_000_000,
GasPrice: common.Big0,
GasFeeCap: common.Big0,
GasTipCap: common.Big0,
To: &params.BeaconRootsStorageAddress,
Data: beaconRoot[:],
}
vmenv.Reset(core.NewEVMTxContext(msg), statedb)
statedb.AddAddressToAccessList(params.BeaconRootsStorageAddress)
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
statedb.Finalise(true)
}
func setChainConfig(ghash common.Hash) *params.ChainConfig {
switch {
case ghash == params.MainnetGenesisHash:
return params.MainnetChainConfig
case ghash == params.RopstenGenesisHash:
return params.RopstenChainConfig
case ghash == params.SepoliaGenesisHash:
return params.SepoliaChainConfig
case ghash == params.RinkebyGenesisHash:
return params.RinkebyChainConfig
case ghash == params.GoerliGenesisHash:
return params.GoerliChainConfig
default:
@@ -377,7 +423,7 @@ func setChainConfig(ghash common.Hash) *params.ChainConfig {
}
}
func getAuthor(b *ipldEth.Backend, header *types.Header) *common.Address {
func getAuthor(b *ipldeth.Backend, header *types.Header) *common.Address {
author, err := getEngine(b).Author(header)
if err != nil {
return nil
@@ -386,7 +432,7 @@ func getAuthor(b *ipldEth.Backend, header *types.Header) *common.Address {
return &author
}
func getEngine(b *ipldEth.Backend) consensus.Engine {
func getEngine(b *ipldeth.Backend) consensus.Engine {
// TODO: add logic for other engines
if b.Config.ChainConfig.Clique != nil {
engine := clique.New(b.Config.ChainConfig.Clique, nil)
+134
View File
@@ -0,0 +1,134 @@
package validator_test
import (
"context"
"math/big"
"testing"
"github.com/cerc-io/plugeth-statediff/indexer/ipld"
indexer_helpers "github.com/cerc-io/plugeth-statediff/indexer/test_helpers"
helpers "github.com/cerc-io/plugeth-statediff/test_helpers"
sdtypes "github.com/cerc-io/plugeth-statediff/types"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
"github.com/jmoiron/sqlx"
// import server helpers for non-canonical chain data
server_mocks "github.com/cerc-io/ipld-eth-server/v5/pkg/eth/test_helpers"
"github.com/cerc-io/ipld-eth-db-validator/v5/internal/chaingen"
"github.com/cerc-io/ipld-eth-db-validator/v5/pkg/validator"
)
const (
chainLength = 10
startBlock = 1
)
var (
chainConfig = TestChainConfig
mockTD = big.NewInt(1337)
testDB = rawdb.NewMemoryDatabase()
)
func init() {
// The geth sync logs are noisy, silence them
log.SetDefault(log.NewLogger(log.DiscardHandler()))
}
func setupStateValidator(t *testing.T) *sqlx.DB {
// Make the test blockchain and state
gen := chaingen.DefaultGenContext(chainConfig, testDB)
blocks, receipts, chain := gen.MakeChain(chainLength)
t.Cleanup(func() {
chain.Stop()
})
indexer, err := helpers.NewIndexer(context.Background(), chainConfig, gen.Genesis.Hash(), TestDBConfig)
if err != nil {
t.Fatal(err)
}
// Insert some non-canonical data into the database so that we test our ability to discern canonicity
// Use a func here for defer
func() {
tx, err := indexer.PushBlock(server_mocks.MockBlock, server_mocks.MockReceipts,
server_mocks.MockBlock.Difficulty())
if err != nil {
t.Fatal(err)
}
defer tx.RollbackOnFailure(err)
err = tx.Submit()
if err != nil {
t.Fatal(err)
}
// The non-canonical header has a child
tx, err = indexer.PushBlock(server_mocks.MockChild, server_mocks.MockReceipts,
server_mocks.MockChild.Difficulty())
if err != nil {
t.Fatal(err)
}
defer tx.RollbackOnFailure(err)
ipld := sdtypes.IPLD{
CID: ipld.Keccak256ToCid(ipld.RawBinary, server_mocks.CodeHash.Bytes()).String(),
Content: server_mocks.ContractCode,
}
err = indexer.PushIPLD(tx, ipld)
if err != nil {
t.Fatal(err)
}
err = tx.Submit()
if err != nil {
t.Fatal(err)
}
}()
if err := helpers.IndexChain(indexer, helpers.IndexChainParams{
StateCache: chain.StateCache(),
Blocks: blocks,
Receipts: receipts,
TotalDifficulty: mockTD,
}); err != nil {
t.Fatal(err)
}
db := SetupDB()
t.Cleanup(func() {
if err := indexer_helpers.ClearSqlxDB(db); err != nil {
t.Fatal(err)
}
})
return db
}
func TestStateValidation(t *testing.T) {
db := setupStateValidator(t)
t.Run("Validator", func(t *testing.T) {
api, err := validator.EthAPI(context.Background(), db, chainConfig)
if err != nil {
t.Fatal(err)
}
for i := uint64(startBlock); i <= chainLength; i++ {
blockToBeValidated, err := api.B.BlockByNumber(context.Background(), rpc.BlockNumber(i))
if err != nil {
t.Fatal(err)
}
if blockToBeValidated == nil {
t.Fatal("block was not found")
}
err = validator.ValidateBlock(blockToBeValidated, api.B, i)
if err != nil {
t.Fatal(err)
}
}
})
}
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
set -eu
geth_endpoint="${1:-$ETH_HTTP_PATH}"
latest_block_hex=$(curl -s $geth_endpoint -X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":42}' | \
python3 -c 'import json, sys; print(int(json.load(sys.stdin)["result"], 16))' \
)
printf "%d" $latest_block_hex
+55
View File
@@ -0,0 +1,55 @@
#!/bin/bash
set -ex -o pipefail
stack_dir=$(readlink -f "$1")
[[ -d "$stack_dir" ]]
CONFIG_DIR=$(readlink -f "${CONFIG_DIR:-$(mktemp -d)}")
# By default assume we are running in the project root.
export CERC_REPO_BASE_DIR="${CERC_REPO_BASE_DIR:-$(git rev-parse --show-toplevel)/..}"
laconic_so="laconic-so --verbose --stack $stack_dir"
# Don't run geth/plugeth in the debugger, it will swallow error backtraces
echo CERC_REMOTE_DEBUG=false >> $CONFIG_DIR/stack.env
# Passing this lets us run eth_call forwarding tests without running ipld-eth-db
echo CERC_RUN_STATEDIFF=${CERC_RUN_STATEDIFF:-true} >> $CONFIG_DIR/stack.env
if [[ -z $SKIP_BUILD ]]; then
# Prevent conflicting tty output
export BUILDKIT_PROGRESS=plain
$laconic_so setup-repositories \
--exclude git.vdb.to/cerc-io/ipld-eth-server
$laconic_so build-containers \
--exclude cerc/ipld-eth-server
fi
if ! $laconic_so deploy \
--exclude ipld-eth-server \
--env-file $CONFIG_DIR/stack.env \
--cluster test up
then
$laconic_so deploy --cluster test logs
exit 1
fi
set +x
# Get IPv4 endpoint of geth and bootnode file server
bootnode_endpoint=localhost:$(docker port test-fixturenet-eth-bootnode-geth-1 9898 | head -1 | cut -d':' -f2)
geth_endpoint=localhost:$(docker port test-fixturenet-eth-geth-1-1 8545 | head -1 | cut -d':' -f2)
# Extract the chain config and ID from genesis file
curl -s $bootnode_endpoint/geth.json | jq '.config' > "$CONFIG_DIR/chain.json"
# Output vars if we are running on Github
if [[ -n "$GITHUB_ENV" ]]; then
echo ETH_CHAIN_ID="$(jq '.chainId' $CONFIG_DIR/chain.json)" >> "$GITHUB_ENV"
echo ETH_CHAIN_CONFIG="$CONFIG_DIR/chain.json" >> "$GITHUB_ENV"
echo ETH_HTTP_PATH=$geth_endpoint >> "$GITHUB_ENV"
# Read a private key so we can send from a funded account
echo DEPLOYER_PRIVATE_KEY="$(curl -s $bootnode_endpoint/accounts.csv | head -1 | cut -d',' -f3)" >> "$GITHUB_ENV"
fi
-15
View File
@@ -1,15 +0,0 @@
#!/bin/bash
set -e
set -o xtrace
export PGPASSWORD=password
export DATABASE_USER=vdbm
export DATABASE_PORT=8077
export DATABASE_PASSWORD=password
export DATABASE_HOSTNAME=127.0.0.1
export DATABASE_NAME=vulcanize_testing
# Wait for containers to be up and execute the integration test.
while [ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:8545)" != "200" ]; do echo "waiting for geth-statediff..." && sleep 5; done && \
make integrationtest
-17
View File
@@ -1,17 +0,0 @@
#!/bin/bash
set -e
# Clear up existing docker images and volume.
docker-compose down --remove-orphans --volumes
# Spin up TimescaleDB
docker-compose -f docker-compose.yml up -d migrations ipld-eth-db
sleep 45
# Run unit tests
go clean -testcache
PGPASSWORD=password DATABASE_USER=vdbm DATABASE_PORT=8077 DATABASE_PASSWORD=password DATABASE_HOSTNAME=127.0.0.1 DATABASE_NAME=vulcanize_testing make test
# Clean up
docker-compose down --remove-orphans --volumes
-91
View File
@@ -1,91 +0,0 @@
# Test Instructions
## Setup
- For running integration tests:
- Clone [stack-orchestrator](https://github.com/vulcanize/stack-orchestrator), [go-ethereum](https://github.com/vulcanize/go-ethereum) and [ipld-eth-db](https://github.com/vulcanize/ipld-eth-db) repositories.
- Checkout [v4 release](https://github.com/vulcanize/ipld-eth-db/releases/tag/v4.2.1-alpha) in ipld-eth-db repo.
```bash
# In ipld-eth-db repo.
git checkout v4.2.1-alpha
```
- Checkout [v4 release](https://github.com/vulcanize/go-ethereum/releases/tag/v1.10.21-statediff-4.1.2-alpha) in go-ethereum repo.
```bash
# In go-ethereum repo.
git checkout v1.10.21-statediff-4.1.2-alpha
```
- Checkout working commit in stack-orchestrator repo.
```bash
# In stack-orchestrator repo.
git checkout f2fd766f5400fcb9eb47b50675d2e3b1f2753702
```
## Run
- Run unit tests:
```bash
# In ipld-eth-db-validator root directory.
./scripts/run_unit_test.sh
```
- Run integration tests:
- In stack-orchestrator repo:
- Create config file:
```bash
cd helper-scripts
./create-config.sh
```
A `config.sh` will be created in the root directory.
- Update/Edit the config file `config.sh`:
```bash
#!/bin/bash
# Path to ipld-eth-server repo.
vulcanize_ipld_eth_db=~/ipld-eth-db/
# Path to go-ethereum repo.
vulcanize_go_ethereum=~/go-ethereum
# Path to contract folder.
vulcanize_test_contract=~/ipld-eth-db-validator/test/contract
genesis_file_path='start-up-files/go-ethereum/genesis.json'
db_write=true
```
- Run stack-orchestrator:
```bash
# In stack-orchestrator root directory.
cd helper-scripts
./wrapper.sh \
-e docker \
-d ../docker/local/docker-compose-db-sharding.yml \
-d ../docker/local/docker-compose-go-ethereum.yml \
-d ../docker/local/docker-compose-contract.yml \
-v remove \
-p ../config.sh
```
- Run tests:
```bash
# In ipld-eth-db-validator root directory.
./scripts/run_integration_test.sh
```
+5 -9
View File
@@ -1,30 +1,26 @@
version: '3.2'
# Containers to run backing DB for unit testing
services:
migrations:
restart: on-failure
depends_on:
- ipld-eth-db
image: vulcanize/ipld-eth-db:v4.2.0-alpha
image: git.vdb.to/cerc-io/ipld-eth-db/ipld-eth-db:v5.3.0-alpha
environment:
DATABASE_USER: "vdbm"
DATABASE_NAME: "vulcanize_testing"
DATABASE_NAME: "cerc_testing"
DATABASE_PASSWORD: "password"
DATABASE_HOSTNAME: "ipld-eth-db"
DATABASE_PORT: 5432
ipld-eth-db:
container_name: test-ipld-eth-db
image: timescale/timescaledb:latest-pg14
restart: always
command: ["postgres", "-c", "log_statement=all"]
environment:
POSTGRES_USER: "vdbm"
POSTGRES_DB: "vulcanize_testing"
POSTGRES_DB: "cerc_testing"
POSTGRES_PASSWORD: "password"
ports:
- "127.0.0.1:8077:5432"
volumes:
- vdb_db_eth_validator:/var/lib/postgresql/data
volumes:
vdb_db_eth_validator:
+19
View File
@@ -0,0 +1,19 @@
# Runs the test contract deployment server
services:
contract-deployer:
restart: on-failure
image: cerc/ipld-eth-db-validator/test-contract-deployer:local
build: ./contract
networks:
- test_default
environment:
ETH_ADDR: "http://fixturenet-eth-geth-1:8545"
ETH_CHAIN_ID: $ETH_CHAIN_ID
DEPLOYER_PRIVATE_KEY: $DEPLOYER_PRIVATE_KEY
ports:
- 127.0.0.1:3000:3000
networks:
test_default:
external: true
+3 -5
View File
@@ -1,7 +1,5 @@
FROM node:14
ARG ETH_ADDR
ENV ETH_ADDR $ETH_ADDR
# Downgrade from 18.16, see https://github.com/NomicFoundation/hardhat/issues/3877
FROM node:20-slim
WORKDIR /usr/src/app
COPY package*.json ./
@@ -11,4 +9,4 @@ RUN npm run compile && ls -lah
EXPOSE 3000
ENTRYPOINT ["npm", "start"]
ENTRYPOINT ["node", "src/index.js"]
+8 -2
View File
@@ -1,10 +1,16 @@
pragma solidity ^0.8.0;
pragma solidity ^0.8.25;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract GLDToken is ERC20 {
constructor() ERC20("Gold", "GLD") {
_mint(msg.sender, 1000000000000000000000);
}
function destroy() public {
selfdestruct(payable(msg.sender));
(bool ok, ) = payable(msg.sender).call{value: address(this).balance}("");
require(ok, "ETH transfer failed");
_burn(msg.sender, balanceOf(msg.sender));
}
}
+19 -19
View File
@@ -2,28 +2,28 @@
pragma solidity ^0.8.0;
contract Test {
address payable owner;
event logPut (address, uint256);
modifier onlyOwner {
require(
msg.sender == owner,
"Only owner can call this function."
);
_;
}
address payable owner;
mapping(address => uint256) public data;
uint256[100] data;
modifier onlyOwner {
require(msg.sender == owner, "Only owner can call this function.");
_;
}
constructor() {
owner = payable(msg.sender);
data = [1];
}
constructor() {
owner = payable(msg.sender);
}
function Put(uint256 addr, uint256 value) public {
data[addr] = value;
}
function Put(uint256 value) public {
emit logPut(msg.sender, value);
function close() public onlyOwner {
selfdestruct(owner);
}
data[msg.sender] = value;
}
function close() public onlyOwner {
(bool ok, ) = owner.call{value: address(this).balance}("");
require(ok, "ETH transfer failed");
}
}
+26 -12
View File
@@ -16,17 +16,31 @@ task("accounts", "Prints the list of accounts", async () => {
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: "0.8.0",
networks: {
local: {
url: 'http://127.0.0.1:8545',
chainId: 99
},
docker: {
url: process.env.ETH_ADDR,
chainId: 99
}
}
const localNetwork = {
url: process.env.ETH_ADDR || "http://127.0.0.1:8545",
chainId: Number(process.env.ETH_CHAIN_ID) || 99,
};
if (process.env.DEPLOYER_PRIVATE_KEY) {
localNetwork["accounts"] = [process.env.DEPLOYER_PRIVATE_KEY];
}
module.exports = {
solidity: {
version: "0.8.25",
settings: {
outputSelection: {
'*': {
'*': [
'abi', 'storageLayout',
]
}
}
}
},
networks: {
local: localNetwork
},
defaultNetwork: "local"
};
+2920 -14737
View File
File diff suppressed because it is too large Load Diff
+8 -9
View File
@@ -4,22 +4,21 @@
"main": "index.js",
"scripts": {
"compile": "npx hardhat compile",
"start": "HARDHAT_NETWORK=docker node src/index.js",
"start:local": "ETH_ADDR=http://127.0.0.1:8545 npm run start",
"test": "echo \"Error: no test specified\" && exit 1"
"start": "node src/index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"description": "Solidity contract deployment server for integration testing",
"dependencies": {
"@openzeppelin/contracts": "^4.0.0",
"fastify": "^3.14.2",
"hardhat": "^2.2.0"
"@openzeppelin/contracts": "^5.0.2",
"fastify": "^4.26.2",
"hardhat": "^2.22.3",
"solidity-create2-deployer": "^0.4.0"
},
"devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@nomiclabs/hardhat-ethers": "^2.2.3",
"@nomiclabs/hardhat-waffle": "^2.0.4",
"chai": "^4.3.4",
"ethereum-waffle": "^3.3.0",
"ethers": "^5.1.0"
-18
View File
@@ -1,18 +0,0 @@
const hre = require("hardhat");
async function main() {
// await hre.run('compile');
// We get the contract to deploy
const GLDToken = await hre.ethers.getContractFactory("GLDToken");
const token = await GLDToken.deploy();
await token.deployed();
console.log("GLDToken deployed to:", token.address, token.deployTransaction.hash);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
-36
View File
@@ -1,36 +0,0 @@
// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// When running the script with `hardhat run <script>` you'll find the Hardhat
// Runtime Environment's members available in the global scope.
const hre = require("hardhat");
async function main() {
// Hardhat always runs the compile task when running scripts with its command
// line interface.
//
// If this script is run directly using `node` you may want to call compile
// manually to make sure everything is compiled
// await hre.run('compile');
// We get the contract to deploy
const Greeter = await hre.ethers.getContractFactory("Greeter");
const greeter = await Greeter.deploy("Hello, Hardhat!");
await greeter.deployed();
console.log("Greeter deployed to:", greeter.address, "; tx hash: ", greeter.deployTransaction.hash);
const result = await greeter.setGreeting("Hello 123!");
console.log("Greeter updated", "; tx hash: ", result.hash, "; block hash: ", result.blockHash);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
+69 -100
View File
@@ -1,118 +1,87 @@
const fastify = require('fastify')({ logger: true });
const hre = require("hardhat");
// readiness check
fastify.get('/v1/healthz', async (req, reply) => {
reply
.code(200)
.header('Content-Type', 'application/json; charset=utf-8')
.send({ success: true })
reply
.code(200)
.header('Content-Type', 'application/json; charset=utf-8')
.send({ success: true })
});
fastify.get('/v1/deployContract', async (req, reply) => {
const GLDToken = await hre.ethers.getContractFactory("GLDToken");
const token = await GLDToken.deploy();
await token.deployed();
return {
address: token.address,
txHash: token.deployTransaction.hash,
blockNumber: token.deployTransaction.blockNumber,
blockHash: token.deployTransaction.blockHash,
}
});
fastify.get('/v1/destroyContract', async (req, reply) => {
const addr = req.query.addr;
const Token = await hre.ethers.getContractFactory("GLDToken");
const token = await Token.attach(addr);
await token.destroy();
const blockNum = await hre.ethers.provider.getBlockNumber()
return {
blockNumber: blockNum,
}
})
fastify.get('/v1/sendEth', async (req, reply) => {
const to = req.query.to;
const value = req.query.value;
const to = req.query.to;
const value = hre.ethers.utils.parseEther(req.query.value);
const [owner] = await hre.ethers.getSigners();
const tx = await owner.sendTransaction({
to,
value: hre.ethers.utils.parseEther(value)
});
await tx.wait(1)
const owner = await hre.ethers.getSigner();
const tx = await owner.sendTransaction({to, value}).then(tx => tx.wait());
// console.log(tx);
// const coinbaseBalance = await hre.ethers.provider.getBalance(owner.address);
// const receiverBalance = await hre.ethers.provider.getBalance(to);
// console.log(coinbaseBalance.toString(), receiverBalance.toString());
return {
from: tx.from,
to: tx.to,
//value: tx.value.toString(),
txHash: tx.hash,
blockNumber: tx.blockNumber,
blockHash: tx.blockHash,
}
return {
from: tx.from,
to: tx.to,
txHash: tx.hash,
blockNumber: tx.blockNumber,
blockHash: tx.blockHash,
}
});
fastify.get('/v1/deployTestContract', async (req, reply) => {
const testContract = await hre.ethers.getContractFactory("Test");
const test = await testContract.deploy();
await test.deployed();
function contractCreator(name) {
return async (req, reply) => {
const contract = await hre.ethers.getContractFactory(name);
const instance = await contract.deploy();
const rct = await instance.deployTransaction.wait();
return {
address: test.address,
txHash: test.deployTransaction.hash,
blockNumber: test.deployTransaction.blockNumber,
blockHash: test.deployTransaction.blockHash,
}
});
fastify.get('/v1/putTestValue', async (req, reply) => {
const addr = req.query.addr;
const index = req.query.index;
const value = req.query.value;
const testContract = await hre.ethers.getContractFactory("Test");
const test = await testContract.attach(addr);
const tx = await test.Put(index, value);
const receipt = await tx.wait();
return {
blockNumber: receipt.blockNumber,
}
});
fastify.get('/v1/destroyTestContract', async (req, reply) => {
const addr = req.query.addr;
const testContract = await hre.ethers.getContractFactory("Test");
const test = await testContract.attach(addr);
await test.destroy();
const blockNum = await hre.ethers.provider.getBlockNumber()
return {
blockNumber: blockNum,
}
})
async function main() {
try {
await fastify.listen(3000, '0.0.0.0');
} catch (err) {
fastify.log.error(err);
process.exit(1);
address: instance.address,
txHash: rct.transactionHash,
blockNumber: rct.blockNumber,
blockHash: rct.blockHash,
}
}
}
function contractDestroyer(name) {
return async (req, reply) => {
const addr = req.query.addr;
const contract = await hre.ethers.getContractFactory(name);
const instance = contract.attach(addr);
const rct = await instance.destroy().then(tx => tx.wait());
return {
blockNumber: rct.blockNumber,
}
}
}
fastify.get('/v1/deployContract', contractCreator("GLDToken"));
fastify.get('/v1/destroyContract', contractDestroyer("GLDToken"));
fastify.get('/v1/deployTestContract', contractCreator("Test"));
fastify.get('/v1/destroyTestContract', contractDestroyer("Test"));
fastify.get('/v1/putTestValue', async (req, reply) => {
const addr = req.query.addr;
const value = req.query.value;
const testContract = await hre.ethers.getContractFactory("Test");
const test = await testContract.attach(addr);
const rct = await test.Put(value).then(tx => tx.wait());
return {
blockNumber: rct.blockNumber,
}
});
async function main() {
try {
await fastify.listen({ port: 3000, host: '0.0.0.0' });
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
}
process.on('SIGINT', () => fastify.close().then(() => process.exit(1)));
main();
+13
View File
@@ -0,0 +1,13 @@
import { expect } from 'chai';
describe("GLDToken", function() {
it("Should return the owner's balance", async function() {
const Token = await ethers.getContractFactory("GLDToken");
const token = await Token.deploy();
await token.deployed();
const [owner] = await ethers.getSigners();
const balance = await token.balanceOf(owner.address);
expect(balance).to.equal('1000000000000000000000');
expect(await token.totalSupply()).to.equal(balance); });
});
-14
View File
@@ -1,14 +0,0 @@
const { expect } = require("chai");
describe("Greeter", function() {
it("Should return the new greeting once it's changed", async function() {
const Greeter = await ethers.getContractFactory("Greeter");
const greeter = await Greeter.deploy("Hello, world!");
await greeter.deployed();
expect(await greeter.greet()).to.equal("Hello, world!");
await greeter.setGreeting("Hola, mundo!");
expect(await greeter.greet()).to.equal("Hola, mundo!");
});
});
-54
View File
@@ -1,54 +0,0 @@
package integration
import (
"encoding/json"
"fmt"
"net/http"
ethServerIntegration "github.com/vulcanize/ipld-eth-server/v4/test"
)
type PutResult struct {
BlockNumber int64 `json:"blockNumber"`
}
const srvUrl = "http://localhost:3000"
func DeployTestContract() (*ethServerIntegration.ContractDeployed, error) {
ethServerIntegration.DeployContract()
res, err := http.Get(fmt.Sprintf("%s/v1/deployTestContract", srvUrl))
if err != nil {
return nil, err
}
defer res.Body.Close()
var contract ethServerIntegration.ContractDeployed
decoder := json.NewDecoder(res.Body)
return &contract, decoder.Decode(&contract)
}
func PutTestValue(addr string, index, value int) (*PutResult, error) {
res, err := http.Get(fmt.Sprintf("%s/v1/putTestValue?addr=%s&index=%d&value=%d", srvUrl, addr, index, value))
if err != nil {
return nil, err
}
var blockNumber PutResult
decoder := json.NewDecoder(res.Body)
return &blockNumber, decoder.Decode(&blockNumber)
}
func DestroyTestContract(addr string) (*ethServerIntegration.ContractDestroyed, error) {
res, err := http.Get(fmt.Sprintf("%s/v1/destroyTestContract?addr=%s", srvUrl, addr))
if err != nil {
return nil, err
}
defer res.Body.Close()
var data ethServerIntegration.ContractDestroyed
decoder := json.NewDecoder(res.Body)
return &data, decoder.Decode(&data)
}
-19
View File
@@ -1,19 +0,0 @@
package integration_test
import (
"io/ioutil"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/sirupsen/logrus"
)
func TestIntegration(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "integration test suite")
}
var _ = BeforeSuite(func() {
logrus.SetOutput(ioutil.Discard)
})
-108
View File
@@ -1,108 +0,0 @@
package integration_test
import (
"context"
"sync"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/ipld-eth-db-validator/pkg/validator"
integration "github.com/vulcanize/ipld-eth-db-validator/test"
"github.com/vulcanize/ipld-eth-server/v4/pkg/shared"
ethServerIntegration "github.com/vulcanize/ipld-eth-server/v4/test"
)
const (
blockNum = 1
trail = 0
validatorSleepInterval = uint(5)
)
var (
testAddresses = []string{
"0x1111111111111111111111111111111111111112",
"0x1ca7c995f8eF0A2989BbcE08D5B7Efe50A584aa1",
"0x9a4b666af23a2cdb4e5538e1d222a445aeb82134",
"0xF7C7AEaECD2349b129d5d15790241c32eeE4607B",
"0x992b6E9BFCA1F7b0797Cee10b0170E536EAd3532",
"0x29ed93a7454Bc17a8D4A24D0627009eE0849B990",
"0x66E3dCA826b04B5d4988F7a37c91c9b1041e579D",
"0x96288939Ac7048c27E0E087b02bDaad3cd61b37b",
"0xD354280BCd771541c935b15bc04342c26086FE9B",
"0x7f887e25688c274E77b8DeB3286A55129B55AF14",
}
)
var _ = Describe("Integration test", func() {
ctx := context.Background()
var contract *ethServerIntegration.ContractDeployed
var err error
sleepInterval := 2 * time.Second
timeout := 4 * time.Second
db := shared.SetupDB()
cfg := validator.Config{
DB: db,
BlockNum: blockNum,
Trail: trail,
SleepInterval: validatorSleepInterval,
ChainCfg: validator.IntegrationTestChainConfig,
}
validationProgressChan := make(chan uint64)
service := validator.NewService(&cfg, validationProgressChan)
wg := new(sync.WaitGroup)
It("test init", func() {
wg.Add(1)
go service.Start(ctx, wg)
// Deploy a dummy contract as the first contract might get deployed at block number 0
_, _ = ethServerIntegration.DeployContract()
time.Sleep(sleepInterval)
})
defer It("test teardown", func() {
service.Stop()
wg.Wait()
Expect(validationProgressChan).To(BeClosed())
})
Describe("Validate state", func() {
It("performs validation on contract deployment", func() {
contract, err = integration.DeployTestContract()
Expect(err).ToNot(HaveOccurred())
time.Sleep(sleepInterval)
Expect(validationProgressChan).ToNot(BeClosed())
Eventually(validationProgressChan, timeout).Should(Receive(Equal(uint64(contract.BlockNumber))))
})
It("performs validation on contract transactions", func() {
for i := 0; i < 10; i++ {
res, txErr := integration.PutTestValue(contract.Address, i, i)
Expect(txErr).ToNot(HaveOccurred())
time.Sleep(sleepInterval)
Expect(validationProgressChan).ToNot(BeClosed())
Eventually(validationProgressChan, timeout).Should(Receive(Equal(uint64(res.BlockNumber))))
}
})
It("performs validation on eth transfer transactions", func() {
for _, address := range testAddresses {
tx, txErr := ethServerIntegration.SendEth(address, "0.01")
Expect(txErr).ToNot(HaveOccurred())
time.Sleep(sleepInterval)
Expect(validationProgressChan).ToNot(BeClosed())
Eventually(validationProgressChan, timeout).Should(Receive(Equal(uint64(tx.BlockNumber))))
}
})
})
})
-118
View File
@@ -1,118 +0,0 @@
package validator_test
import (
"crypto/ecdsa"
"github.com/ethereum/go-ethereum/statediff/test_helpers"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
log "github.com/sirupsen/logrus"
"github.com/vulcanize/ipld-eth-db-validator/pkg/validator"
)
// Test variables
var (
Testdb = rawdb.NewMemoryDatabase()
TestBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
TestBankAddress = crypto.PubkeyToAddress(TestBankKey.PublicKey) //0x71562b71999873DB5b286dF957af199Ec94617F7
TestBankFunds = big.NewInt(100000000)
Genesis = test_helpers.GenesisBlockForTesting(Testdb, TestBankAddress, TestBankFunds)
Account1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
Account2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
Account1Addr = crypto.PubkeyToAddress(Account1Key.PublicKey) //0x703c4b2bD70c169f5717101CaeE543299Fc946C7
Account2Addr = crypto.PubkeyToAddress(Account2Key.PublicKey) //0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e
DeploymentTxData = common.Hex2Bytes("0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600180819055506101e2806100676000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806343d726d61461004657806365f3c31a1461005057806373d4a13a1461007e575b600080fd5b61004e61009c565b005b61007c6004803603602081101561006657600080fd5b810190808035906020019092919050505061017b565b005b610086610185565b6040518082815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610141576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061018c6022913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b8060018190555050565b6001548156fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6e2ea265627a7a723158205ba91466129f45285f53176d805117208c231ec6343d7896790e6fc4165b802b64736f6c63430005110032")
ContractCode = common.Hex2Bytes("0x608060405234801561001057600080fd5b50600436106100415760003560e01c806343d726d61461004657806365f3c31a1461005057806373d4a13a1461007e575b600080fd5b61004e61009c565b005b61007c6004803603602081101561006657600080fd5b810190808035906020019092919050505061017b565b005b610086610185565b6040518082815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610141576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061018c6022913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b8060018190555050565b6001548156fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6e2ea265627a7a723158205ba91466129f45285f53176d805117208c231ec6343d7896790e6fc4165b802b64736f6c63430005110032")
CodeHash = crypto.Keccak256Hash(ContractCode)
ContractAddr common.Address
IndexZero = "0000000000000000000000000000000000000000000000000000000000000000"
IndexOne = "0000000000000000000000000000000000000000000000000000000000000001"
ContractSlotPosition = common.FromHex(IndexOne)
ContractSlotKeyHash = crypto.Keccak256Hash(ContractSlotPosition)
MiningReward = big.NewInt(2000000000000000000)
)
/* test function signatures
put function sig: 65f3c31a
close function sig: 43d726d6
data function sig: 73d4a13a
*/
func createLondonTransaction(block *core.BlockGen, addr *common.Address, key *ecdsa.PrivateKey) *types.Transaction {
londonTrx := types.NewTx(&types.DynamicFeeTx{
ChainID: validator.TestChainConfig.ChainID,
Nonce: block.TxNonce(*addr),
GasTipCap: big.NewInt(50),
GasFeeCap: big.NewInt(1000000000),
Gas: 21000,
To: addr,
Value: big.NewInt(1000),
Data: []byte{},
})
transactionSigner := types.MakeSigner(validator.TestChainConfig, block.Number())
signedTrx1, err := types.SignTx(londonTrx, transactionSigner, key)
if err != nil {
log.Fatal(err.Error())
}
return signedTrx1
}
// MakeChain creates a chain of n blocks starting at and including parent.
// the returned hash chain is ordered head->parent.
func MakeChain(n int, parent *types.Block, chainGen func(int, *core.BlockGen)) ([]*types.Block, []types.Receipts, *core.BlockChain) {
config := validator.TestChainConfig
blocks, receipts := core.GenerateChain(config, parent, ethash.NewFaker(), Testdb, n, chainGen)
chain, _ := core.NewBlockChain(Testdb, nil, validator.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
return append([]*types.Block{parent}, blocks...), receipts, chain
}
func TestChainGen(i int, block *core.BlockGen) {
signer := types.HomesteadSigner{}
switch i {
case 0:
// In block 1, the test bank sends account #1 some ether.
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(TestBankAddress), Account1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, TestBankKey)
block.AddTx(tx)
case 1:
// In block 2, the test bank sends some more ether to account #1.
// Account1Addr passes it on to account #2.
// Account1Addr creates a test contract.
tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(TestBankAddress), Account1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, TestBankKey)
nonce := block.TxNonce(Account1Addr)
tx2, _ := types.SignTx(types.NewTransaction(nonce, Account2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, Account1Key)
nonce++
tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, big.NewInt(0), DeploymentTxData), signer, Account1Key)
ContractAddr = crypto.CreateAddress(Account1Addr, nonce)
block.AddTx(tx1)
block.AddTx(tx2)
block.AddTx(tx3)
case 2:
block.SetCoinbase(Account2Addr)
data := common.Hex2Bytes("65F3C31A0000000000000000000000000000000000000000000000000000000000000003")
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(TestBankAddress), ContractAddr, big.NewInt(0), 100000, nil, data), signer, TestBankKey)
block.AddTx(tx)
case 3:
block.SetCoinbase(Account2Addr)
data := common.Hex2Bytes("65F3C31A0000000000000000000000000000000000000000000000000000000000000009")
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(TestBankAddress), ContractAddr, big.NewInt(0), 100000, nil, data), signer, TestBankKey)
block.AddTx(tx)
case 4:
block.SetCoinbase(Account1Addr)
data := common.Hex2Bytes("65F3C31A0000000000000000000000000000000000000000000000000000000000000000")
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(TestBankAddress), ContractAddr, big.NewInt(0), 100000, nil, data), signer, TestBankKey)
block.AddTx(tx)
case 5:
block.AddTx(createLondonTransaction(block, &Account1Addr, Account1Key))
case 6:
block.AddTx(createLondonTransaction(block, &Account2Addr, Account2Key))
}
}
-334
View File
@@ -1,334 +0,0 @@
package validator_test
import (
"context"
"fmt"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/statediff/indexer/interfaces"
"github.com/ethereum/go-ethereum/statediff/indexer/mocks"
"github.com/jmoiron/sqlx"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/ipld-eth-db-validator/pkg/validator"
"github.com/vulcanize/ipld-eth-server/v4/pkg/eth/test_helpers"
"github.com/vulcanize/ipld-eth-server/v4/pkg/shared"
)
var _ = Describe("RefIntegrity", func() {
var (
ctx = context.Background()
db *sqlx.DB
diffIndexer interfaces.StateDiffIndexer
)
BeforeEach(func() {
db = shared.SetupDB()
diffIndexer = shared.SetupTestStateDiffIndexer(ctx, params.TestChainConfig, test_helpers.Genesis.Hash())
})
AfterEach(func() {
shared.TearDownDB(db)
})
Describe("ValidateHeaderCIDsRef", func() {
BeforeEach(func() {
tx, err := diffIndexer.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
Expect(err).ToNot(HaveOccurred())
err = tx.Submit(err)
Expect(err).ToNot(HaveOccurred())
})
It("Validates referential integrity of header_cids table", func() {
err := validator.ValidateHeaderCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).ToNot(HaveOccurred())
})
It("Throws an error if corresponding header IPFS block entry not found", func() {
err := deleteEntriesFrom(db, "public.blocks")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateHeaderCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "public.blocks"))
})
})
Describe("ValidateUncleCIDsRef", func() {
BeforeEach(func() {
tx, err := diffIndexer.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
Expect(err).ToNot(HaveOccurred())
err = tx.Submit(err)
Expect(err).ToNot(HaveOccurred())
})
It("Validates referential integrity of uncle_cids table", func() {
err := validator.ValidateUncleCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).ToNot(HaveOccurred())
})
It("Throws an error if corresponding header_cid entry not found", func() {
err := deleteEntriesFrom(db, "eth.header_cids")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateUncleCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "eth.header_cids"))
})
It("Throws an error if corresponding uncle IPFS block entry not found", func() {
err := deleteEntriesFrom(db, "public.blocks")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateUncleCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "public.blocks"))
})
})
Describe("ValidateTransactionCIDsRef", func() {
BeforeEach(func() {
tx, err := diffIndexer.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
Expect(err).ToNot(HaveOccurred())
err = tx.Submit(err)
Expect(err).ToNot(HaveOccurred())
})
It("Validates referential integrity of transaction_cids table", func() {
err := validator.ValidateTransactionCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).ToNot(HaveOccurred())
})
It("Throws an error if corresponding header_cid entry not found", func() {
err := deleteEntriesFrom(db, "eth.header_cids")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateTransactionCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "eth.header_cids"))
})
It("Throws an error if corresponding transaction IPFS block entry not found", func() {
err := deleteEntriesFrom(db, "public.blocks")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateTransactionCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "public.blocks"))
})
})
Describe("ValidateReceiptCIDsRef", func() {
BeforeEach(func() {
tx, err := diffIndexer.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
Expect(err).ToNot(HaveOccurred())
err = tx.Submit(err)
Expect(err).ToNot(HaveOccurred())
})
It("Validates referential integrity of receipt_cids table", func() {
err := validator.ValidateReceiptCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).ToNot(HaveOccurred())
})
It("Throws an error if corresponding transaction_cids entry not found", func() {
err := deleteEntriesFrom(db, "eth.transaction_cids")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateReceiptCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "eth.transaction_cids"))
})
It("Throws an error if corresponding receipt IPFS block entry not found", func() {
err := deleteEntriesFrom(db, "public.blocks")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateReceiptCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "public.blocks"))
})
})
Describe("ValidateStateCIDsRef", func() {
BeforeEach(func() {
tx, err := diffIndexer.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
Expect(err).ToNot(HaveOccurred())
for _, node := range test_helpers.MockStateNodes {
err = diffIndexer.PushStateNode(tx, node, test_helpers.MockBlock.Hash().String())
Expect(err).ToNot(HaveOccurred())
}
err = tx.Submit(err)
Expect(err).ToNot(HaveOccurred())
})
It("Validates referential integrity of state_cids table", func() {
err := validator.ValidateStateCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).ToNot(HaveOccurred())
})
It("Throws an error if corresponding header_cids entry not found", func() {
err := deleteEntriesFrom(db, "eth.header_cids")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateStateCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "eth.header_cids"))
})
It("Throws an error if corresponding state IPFS block entry not found", func() {
err := deleteEntriesFrom(db, "public.blocks")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateStateCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "public.blocks"))
})
})
Describe("ValidateStorageCIDsRef", func() {
BeforeEach(func() {
tx, err := diffIndexer.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
Expect(err).ToNot(HaveOccurred())
for _, node := range test_helpers.MockStateNodes {
err = diffIndexer.PushStateNode(tx, node, test_helpers.MockBlock.Hash().String())
Expect(err).ToNot(HaveOccurred())
}
err = tx.Submit(err)
Expect(err).ToNot(HaveOccurred())
})
It("Validates referential integrity of storage_cids table", func() {
err := validator.ValidateStorageCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).ToNot(HaveOccurred())
})
It("Throws an error if corresponding state_cids entry not found", func() {
err := deleteEntriesFrom(db, "eth.state_cids")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateStorageCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "eth.state_cids"))
})
It("Throws an error if corresponding storage IPFS block entry not found", func() {
err := deleteEntriesFrom(db, "public.blocks")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateStorageCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "public.blocks"))
})
})
Describe("ValidateStateAccountsRef", func() {
BeforeEach(func() {
tx, err := diffIndexer.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
Expect(err).ToNot(HaveOccurred())
for _, node := range test_helpers.MockStateNodes {
err = diffIndexer.PushStateNode(tx, node, test_helpers.MockBlock.Hash().String())
Expect(err).ToNot(HaveOccurred())
}
err = tx.Submit(err)
Expect(err).ToNot(HaveOccurred())
})
It("Validates referential integrity of state_accounts table", func() {
err := validator.ValidateStateAccountsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).ToNot(HaveOccurred())
})
It("Throws an error if corresponding state_cids entry not found", func() {
err := deleteEntriesFrom(db, "eth.state_cids")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateStateAccountsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "eth.state_cids"))
})
})
Describe("ValidateAccessListElementsRef", func() {
BeforeEach(func() {
indexAndPublisher := shared.SetupTestStateDiffIndexer(ctx, mocks.TestConfig, test_helpers.Genesis.Hash())
tx, err := indexAndPublisher.PushBlock(mocks.MockBlock, mocks.MockReceipts, mocks.MockBlock.Difficulty())
Expect(err).ToNot(HaveOccurred())
err = tx.Submit(err)
Expect(err).ToNot(HaveOccurred())
})
It("Validates referential integrity of access_list_elements table", func() {
err := validator.ValidateAccessListElementsRef(db, mocks.MockBlock.NumberU64())
Expect(err).ToNot(HaveOccurred())
})
It("Throws an error if corresponding transaction_cids entry not found", func() {
err := deleteEntriesFrom(db, "eth.transaction_cids")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateAccessListElementsRef(db, mocks.MockBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "eth.transaction_cids"))
})
})
Describe("ValidateLogCIDsRef", func() {
BeforeEach(func() {
tx, err := diffIndexer.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
Expect(err).ToNot(HaveOccurred())
for _, node := range test_helpers.MockStateNodes {
err = diffIndexer.PushStateNode(tx, node, test_helpers.MockBlock.Hash().String())
Expect(err).ToNot(HaveOccurred())
}
err = tx.Submit(err)
Expect(err).ToNot(HaveOccurred())
})
It("Validates referential integrity of log_cids table", func() {
err := validator.ValidateLogCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).ToNot(HaveOccurred())
})
It("Throws an error if corresponding receipt_cids entry not found", func() {
err := deleteEntriesFrom(db, "eth.receipt_cids")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateLogCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "eth.receipt_cids"))
})
It("Throws an error if corresponding log IPFS block entry not found", func() {
err := deleteEntriesFrom(db, "public.blocks")
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateLogCIDsRef(db, test_helpers.MockBlock.NumberU64())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(validator.EntryNotFoundErr, "public.blocks"))
})
})
})
func deleteEntriesFrom(db *sqlx.DB, tableName string) error {
pgStr := "DELETE FROM %s"
_, err := db.Exec(fmt.Sprintf(pgStr, tableName))
return err
}
-19
View File
@@ -1,19 +0,0 @@
package validator_test
import (
"io/ioutil"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/sirupsen/logrus"
)
func TestETHSuite(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "eth ipld validator eth suite test")
}
var _ = BeforeSuite(func() {
logrus.SetOutput(ioutil.Discard)
})
-134
View File
@@ -1,134 +0,0 @@
package validator_test_test
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/statediff"
sdtypes "github.com/ethereum/go-ethereum/statediff/types"
"github.com/jmoiron/sqlx"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/ipld-eth-server/v4/pkg/eth/test_helpers"
"github.com/vulcanize/ipld-eth-server/v4/pkg/shared"
"github.com/vulcanize/ipld-eth-db-validator/pkg/validator"
"github.com/vulcanize/ipld-eth-db-validator/validator_test"
)
const (
chainLength = 20
blockHeight = 1
trail = 2
)
var _ = Describe("eth state reading tests", func() {
var (
blocks []*types.Block
receipts []types.Receipts
chain *core.BlockChain
db *sqlx.DB
chainConfig = validator.TestChainConfig
mockTD = big.NewInt(1337)
)
It("test init", func() {
db = shared.SetupDB()
transformer := shared.SetupTestStateDiffIndexer(context.Background(), chainConfig, validator_test.Genesis.Hash())
// make the test blockchain (and state)
blocks, receipts, chain = validator_test.MakeChain(chainLength, validator_test.Genesis, validator_test.TestChainGen)
params := statediff.Params{
IntermediateStateNodes: true,
IntermediateStorageNodes: true,
}
// iterate over the blocks, generating statediff payloads, and transforming the data into Postgres
builder := statediff.NewBuilder(chain.StateCache())
for i, block := range blocks {
var args statediff.Args
var rcts types.Receipts
if i == 0 {
args = statediff.Args{
OldStateRoot: common.Hash{},
NewStateRoot: block.Root(),
BlockNumber: block.Number(),
BlockHash: block.Hash(),
}
} else {
args = statediff.Args{
OldStateRoot: blocks[i-1].Root(),
NewStateRoot: block.Root(),
BlockNumber: block.Number(),
BlockHash: block.Hash(),
}
rcts = receipts[i-1]
}
diff, err := builder.BuildStateDiffObject(args, params)
Expect(err).ToNot(HaveOccurred())
tx, err := transformer.PushBlock(block, rcts, mockTD)
Expect(err).ToNot(HaveOccurred())
for _, node := range diff.Nodes {
err := transformer.PushStateNode(tx, node, block.Hash().String())
Expect(err).ToNot(HaveOccurred())
}
err = tx.Submit(err)
Expect(err).ToNot(HaveOccurred())
}
// Insert some non-canonical data into the database so that we test our ability to discern canonicity
indexAndPublisher := shared.SetupTestStateDiffIndexer(context.Background(), chainConfig, validator_test.Genesis.Hash())
tx, err := indexAndPublisher.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
Expect(err).ToNot(HaveOccurred())
err = tx.Submit(err)
Expect(err).ToNot(HaveOccurred())
// The non-canonical header has a child
tx, err = indexAndPublisher.PushBlock(test_helpers.MockChild, test_helpers.MockReceipts, test_helpers.MockChild.Difficulty())
Expect(err).ToNot(HaveOccurred())
hash := sdtypes.CodeAndCodeHash{
Hash: test_helpers.CodeHash,
Code: test_helpers.ContractCode,
}
err = indexAndPublisher.PushCodeAndCodeHash(tx, hash)
Expect(err).ToNot(HaveOccurred())
err = tx.Submit(err)
Expect(err).ToNot(HaveOccurred())
})
defer It("test teardown", func() {
shared.TearDownDB(db)
chain.Stop()
})
Describe("state_validation", func() {
It("Validator", func() {
api, err := validator.EthAPI(context.Background(), db, validator.TestChainConfig)
Expect(err).ToNot(HaveOccurred())
for i := uint64(blockHeight); i <= chainLength-trail; i++ {
blockToBeValidated, err := api.B.BlockByNumber(context.Background(), rpc.BlockNumber(i))
Expect(err).ToNot(HaveOccurred())
Expect(blockToBeValidated).ToNot(BeNil())
err = validator.ValidateBlock(blockToBeValidated, api.B, i)
Expect(err).ToNot(HaveOccurred())
err = validator.ValidateReferentialIntegrity(db, i)
Expect(err).ToNot(HaveOccurred())
}
})
})
})