Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fb86ba963 | ||
|
|
7ae4886b9a | ||
|
|
b11ace0711 | ||
|
|
d111dd85db | ||
|
|
cc935dc97b | ||
|
|
effbdc3f58 | ||
|
|
31cc17b70f | ||
|
|
e89d64aa72 | ||
|
|
a56da1782a | ||
|
|
e102a2b5ac |
+29
-28
@@ -3,8 +3,9 @@ on:
|
|||||||
workflow_call:
|
workflow_call:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
STACK_ORCHESTRATOR_REF: "main"
|
STACK_ORCHESTRATOR_REF: "f2fd766f5400fcb9eb47b50675d2e3b1f2753702"
|
||||||
GO_ETHEREUM_REF: "v1.10.18-statediff-v3"
|
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:
|
jobs:
|
||||||
integrationtest:
|
integrationtest:
|
||||||
@@ -16,43 +17,50 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Create GOPATH
|
- name: Create GOPATH
|
||||||
run: mkdir -p /tmp/go
|
run: mkdir -p /tmp/go
|
||||||
|
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: ">=1.18.0"
|
go-version: ">=1.18.0"
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
path: "./ipld-eth-db-validator"
|
path: "./ipld-eth-db-validator"
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
ref: ${{ env.STACK_ORCHESTRATOR_REF }}
|
ref: ${{ env.STACK_ORCHESTRATOR_REF }}
|
||||||
path: "./stack-orchestrator/"
|
path: "./stack-orchestrator/"
|
||||||
repository: vulcanize/stack-orchestrator
|
repository: vulcanize/stack-orchestrator
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
ref: ${{ env.GO_ETHEREUM_REF }}
|
ref: ${{ env.IPLD_ETH_DB_REF }}
|
||||||
repository: vulcanize/go-ethereum
|
repository: vulcanize/ipld-eth-db
|
||||||
path: "./go-ethereum/"
|
path: "./ipld-eth-db/"
|
||||||
|
|
||||||
- name: Create config file
|
- name: Create config file
|
||||||
run: |
|
run: |
|
||||||
echo vulcanize_go_ethereum=$GITHUB_WORKSPACE/go-ethereum/ > ./config.sh
|
|
||||||
echo vulcanize_test_contract=$GITHUB_WORKSPACE/ipld-eth-db-validator/test/contract >> ./config.sh
|
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
|
echo db_write=$DB_WRITE >> ./config.sh
|
||||||
cat ./config.sh
|
cat ./config.sh
|
||||||
- name: Build geth
|
|
||||||
|
- name: Download Geth
|
||||||
run: |
|
run: |
|
||||||
cd $GITHUB_WORKSPACE/stack-orchestrator/helper-scripts
|
cd $GITHUB_WORKSPACE/stack-orchestrator/helper-scripts
|
||||||
./compile-geth.sh \
|
wget https://github.com/vulcanize/go-ethereum/releases/download/${{env.GO_ETHEREUM_REF}}/geth-linux-amd64
|
||||||
-p "$GITHUB_WORKSPACE/config.sh" \
|
|
||||||
-e docker
|
|
||||||
- name: Run docker compose
|
- name: Run docker compose
|
||||||
run: |
|
run: |
|
||||||
docker-compose \
|
docker-compose \
|
||||||
-f "$GITHUB_WORKSPACE/stack-orchestrator/docker/latest/docker-compose-db.yml" \
|
-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-go-ethereum.yml" \
|
||||||
-f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-contract.yml" \
|
-f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-contract.yml" \
|
||||||
--env-file "$GITHUB_WORKSPACE/config.sh" \
|
--env-file "$GITHUB_WORKSPACE/config.sh" \
|
||||||
up -d --build
|
up -d --build
|
||||||
|
|
||||||
- name: Run integration test.
|
- name: Run integration test.
|
||||||
run: |
|
run: |
|
||||||
cd $GITHUB_WORKSPACE/ipld-eth-db-validator
|
cd $GITHUB_WORKSPACE/ipld-eth-db-validator
|
||||||
@@ -60,31 +68,24 @@ jobs:
|
|||||||
|
|
||||||
unittest:
|
unittest:
|
||||||
name: Run unit tests
|
name: Run unit tests
|
||||||
env:
|
|
||||||
GOPATH: /tmp/go
|
|
||||||
DB_WRITE: true
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Create GOPATH
|
- name: Create GOPATH
|
||||||
run: mkdir -p /tmp/go
|
run: mkdir -p /tmp/go
|
||||||
|
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: ">=1.18.0"
|
go-version: ">=1.18.0"
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
- name: Checkout code
|
||||||
path: "./ipld-eth-db-validator"
|
uses: actions/checkout@v2
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
- name: Spin up database
|
||||||
ref: ${{ env.STACK_ORCHESTRATOR_REF }}
|
|
||||||
path: "./stack-orchestrator/"
|
|
||||||
repository: vulcanize/stack-orchestrator
|
|
||||||
- name: Run docker compose
|
|
||||||
run: |
|
run: |
|
||||||
docker-compose \
|
docker-compose up -d
|
||||||
-f "$GITHUB_WORKSPACE/stack-orchestrator/docker/latest/docker-compose-db.yml" \
|
|
||||||
up -d --build
|
- name: Run unit tests
|
||||||
- name: Run unit test.
|
|
||||||
run: |
|
run: |
|
||||||
cd $GITHUB_WORKSPACE/ipld-eth-db-validator
|
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
|
PGPASSWORD=password DATABASE_USER=vdbm DATABASE_PORT=8077 DATABASE_PASSWORD=password DATABASE_HOSTNAME=127.0.0.1 DATABASE_NAME=vulcanize_testing make test
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ipld-eth-db-validator
|
||||||
|
.vscode
|
||||||
+1
-15
@@ -7,7 +7,6 @@ import (
|
|||||||
"os/signal"
|
"os/signal"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/statediff"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
@@ -34,20 +33,7 @@ func stateValidator() {
|
|||||||
logWithCommand.Fatal(err)
|
logWithCommand.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
height := viper.GetUint64("validate.block-height")
|
service := validator.NewService(cfg, nil)
|
||||||
if height < 1 {
|
|
||||||
logWithCommand.Fatalf("block height cannot be less the 1")
|
|
||||||
}
|
|
||||||
trail := viper.GetUint64("validate.trail")
|
|
||||||
sleepInterval := viper.GetUint("validate.sleepInterval")
|
|
||||||
|
|
||||||
chainConfigPath := viper.GetString("ethereum.chainConfig")
|
|
||||||
chainCfg, err := statediff.LoadConfig(chainConfigPath)
|
|
||||||
if err != nil {
|
|
||||||
logWithCommand.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
service := validator.NewService(cfg.DB, height, trail, sleepInterval, chainCfg)
|
|
||||||
|
|
||||||
wg := new(sync.WaitGroup)
|
wg := new(sync.WaitGroup)
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|||||||
+16
-4
@@ -1,18 +1,30 @@
|
|||||||
version: '3.2'
|
version: '3.2'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
migrations:
|
||||||
|
restart: on-failure
|
||||||
|
depends_on:
|
||||||
|
- ipld-eth-db
|
||||||
|
image: vulcanize/ipld-eth-db:v4.2.0-alpha
|
||||||
|
environment:
|
||||||
|
DATABASE_USER: "vdbm"
|
||||||
|
DATABASE_NAME: "vulcanize_testing"
|
||||||
|
DATABASE_PASSWORD: "password"
|
||||||
|
DATABASE_HOSTNAME: "ipld-eth-db"
|
||||||
|
DATABASE_PORT: 5432
|
||||||
|
|
||||||
ipld-eth-db:
|
ipld-eth-db:
|
||||||
|
image: timescale/timescaledb:latest-pg14
|
||||||
restart: always
|
restart: always
|
||||||
image: vulcanize/ipld-eth-db:v3.2.0
|
command: ["postgres", "-c", "log_statement=all"]
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: "vdbm"
|
POSTGRES_USER: "vdbm"
|
||||||
POSTGRES_DB: "vulcanize_testing"
|
POSTGRES_DB: "vulcanize_testing"
|
||||||
POSTGRES_PASSWORD: "password"
|
POSTGRES_PASSWORD: "password"
|
||||||
volumes:
|
|
||||||
- vdb_db_eth_validator:/var/lib/postgresql/data
|
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:8077:5432"
|
- "127.0.0.1:8077:5432"
|
||||||
command: ["postgres", "-c", "log_statement=all"]
|
volumes:
|
||||||
|
- vdb_db_eth_validator:/var/lib/postgresql/data
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
vdb_db_eth_validator:
|
vdb_db_eth_validator:
|
||||||
@@ -3,15 +3,15 @@ module github.com/vulcanize/ipld-eth-db-validator
|
|||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ethereum/go-ethereum v1.10.18
|
github.com/ethereum/go-ethereum v1.10.19
|
||||||
github.com/jmoiron/sqlx v1.3.5
|
github.com/jmoiron/sqlx v1.3.5
|
||||||
github.com/onsi/ginkgo v1.16.5
|
github.com/onsi/ginkgo v1.16.5
|
||||||
github.com/onsi/gomega v1.19.0
|
github.com/onsi/gomega v1.19.0
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/spf13/cobra v1.4.0
|
github.com/spf13/cobra v1.4.0
|
||||||
github.com/spf13/viper v1.11.0
|
github.com/spf13/viper v1.11.0
|
||||||
github.com/vulcanize/ipfs-ethdb/v3 v3.0.3
|
github.com/vulcanize/ipfs-ethdb/v4 v4.0.2-alpha
|
||||||
github.com/vulcanize/ipld-eth-server/v3 v3.2.0
|
github.com/vulcanize/ipld-eth-server/v4 v4.1.0-alpha
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -109,21 +109,23 @@ require (
|
|||||||
github.com/ipld/go-codec-dagpb v1.3.0 // indirect
|
github.com/ipld/go-codec-dagpb v1.3.0 // indirect
|
||||||
github.com/ipld/go-ipld-prime v0.12.2 // indirect
|
github.com/ipld/go-ipld-prime v0.12.2 // indirect
|
||||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||||
github.com/jackc/pgconn v1.10.0 // indirect
|
github.com/jackc/pgconn v1.12.1 // indirect
|
||||||
github.com/jackc/pgio v1.0.0 // indirect
|
github.com/jackc/pgio v1.0.0 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgproto3/v2 v2.1.1 // indirect
|
github.com/jackc/pgproto3/v2 v2.3.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||||
github.com/jackc/pgtype v1.8.1 // indirect
|
github.com/jackc/pgtype v1.11.0 // indirect
|
||||||
github.com/jackc/pgx/v4 v4.13.0 // indirect
|
github.com/jackc/pgx/v4 v4.16.1 // indirect
|
||||||
github.com/jackc/puddle v1.1.3 // indirect
|
github.com/jackc/puddle v1.2.1 // indirect
|
||||||
github.com/jackpal/go-nat-pmp v1.0.2 // 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/go-temp-err-catcher v0.1.0 // indirect
|
||||||
github.com/jbenet/goprocess v0.1.4 // 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.11.7 // indirect
|
github.com/klauspost/compress v1.11.7 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||||
github.com/koron/go-ssdp v0.0.2 // indirect
|
github.com/koron/go-ssdp v0.0.2 // indirect
|
||||||
github.com/lib/pq v1.10.5 // indirect
|
github.com/lib/pq v1.10.6 // indirect
|
||||||
github.com/libp2p/go-addr-util v0.1.0 // indirect
|
github.com/libp2p/go-addr-util v0.1.0 // indirect
|
||||||
github.com/libp2p/go-buffer-pool v0.0.2 // indirect
|
github.com/libp2p/go-buffer-pool v0.0.2 // indirect
|
||||||
github.com/libp2p/go-cidranger v1.1.0 // indirect
|
github.com/libp2p/go-cidranger v1.1.0 // indirect
|
||||||
@@ -234,7 +236,7 @@ require (
|
|||||||
github.com/tklauser/go-sysconf v0.3.6 // indirect
|
github.com/tklauser/go-sysconf v0.3.6 // indirect
|
||||||
github.com/tklauser/numcpus v0.2.2 // indirect
|
github.com/tklauser/numcpus v0.2.2 // indirect
|
||||||
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef // indirect
|
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef // indirect
|
||||||
github.com/vulcanize/eth-ipfs-state-validator/v3 v3.0.0 // indirect
|
github.com/vulcanize/eth-ipfs-state-validator/v4 v4.0.3-alpha // indirect
|
||||||
github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc // 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-gen v0.0.0-20210219115102-f37d292932f2 // indirect
|
||||||
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect
|
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect
|
||||||
@@ -252,7 +254,7 @@ require (
|
|||||||
go.uber.org/multierr v1.7.0 // indirect
|
go.uber.org/multierr v1.7.0 // indirect
|
||||||
go.uber.org/zap v1.19.0 // indirect
|
go.uber.org/zap v1.19.0 // indirect
|
||||||
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
|
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
|
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
|
||||||
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 // indirect
|
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 // indirect
|
||||||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect
|
golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||||
@@ -268,7 +270,9 @@ require (
|
|||||||
gopkg.in/urfave/cli.v1 v1.20.0 // indirect
|
gopkg.in/urfave/cli.v1 v1.20.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||||
|
gorm.io/driver/postgres v1.3.7 // indirect
|
||||||
|
gorm.io/gorm v1.23.5 // indirect
|
||||||
lukechampine.com/blake3 v1.1.6 // indirect
|
lukechampine.com/blake3 v1.1.6 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/ethereum/go-ethereum v1.10.18 => github.com/vulcanize/go-ethereum v1.10.18-statediff-3.2.2
|
replace github.com/ethereum/go-ethereum v1.10.19 => github.com/vulcanize/go-ethereum v1.10.19-statediff-4.1.0-alpha
|
||||||
|
|||||||
+27
-2
@@ -6,10 +6,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/statediff"
|
||||||
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres"
|
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/vulcanize/ipld-eth-server/v3/pkg/shared"
|
"github.com/vulcanize/ipld-eth-server/v4/pkg/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -59,11 +60,35 @@ var TestChainConfig = ¶ms.ChainConfig{
|
|||||||
type Config struct {
|
type Config struct {
|
||||||
dbConfig postgres.Config
|
dbConfig postgres.Config
|
||||||
DB *sqlx.DB
|
DB *sqlx.DB
|
||||||
|
|
||||||
|
ChainCfg *params.ChainConfig
|
||||||
|
|
||||||
|
BlockNum, Trail uint64
|
||||||
|
SleepInterval uint
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfig() (*Config, error) {
|
func NewConfig() (*Config, error) {
|
||||||
cfg := new(Config)
|
cfg := new(Config)
|
||||||
return cfg, cfg.setupDB()
|
err := cfg.setupDB()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.BlockNum = viper.GetUint64("validate.block-height")
|
||||||
|
if cfg.BlockNum < 1 {
|
||||||
|
return nil, fmt.Errorf("block height cannot be less the 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Trail = viper.GetUint64("validate.trail")
|
||||||
|
cfg.SleepInterval = viper.GetUint("validate.sleepInterval")
|
||||||
|
|
||||||
|
chainConfigPath := viper.GetString("ethereum.chainConfig")
|
||||||
|
cfg.ChainCfg, err = statediff.LoadConfig(chainConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) setupDB() error {
|
func (c *Config) setupDB() error {
|
||||||
|
|||||||
@@ -0,0 +1,224 @@
|
|||||||
|
package validator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidateReferentialIntegrity validates referential integrity at the given height
|
||||||
|
func ValidateReferentialIntegrity(db *sqlx.DB, blockNumber uint64) error {
|
||||||
|
|
||||||
|
err := ValidateHeaderCIDsRef(db, blockNumber)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ValidateUncleCIDsRef(db, blockNumber)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ValidateTransactionCIDsRef(db, blockNumber)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ValidateReceiptCIDsRef(db, blockNumber)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ValidateStateCIDsRef(db, blockNumber)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ValidateStorageCIDsRef(db, 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)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateUncleCIDsRef does a reference integrity check on references in eth.uncle_cids table
|
||||||
|
func ValidateUncleCIDsRef(db *sqlx.DB, blockNumber uint64) error {
|
||||||
|
var exists bool
|
||||||
|
err := db.Get(&exists, UncleCIDsRefHeaderCIDs, blockNumber)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
return fmt.Errorf(ReferentialIntegrityErr, blockNumber, "eth.header_cids")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ValidateIPFSBlocks(db, blockNumber, "eth.uncle_cids", "mh_key")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateTransactionCIDsRef does a reference integrity check on references in eth.header_cids table
|
||||||
|
func ValidateTransactionCIDsRef(db *sqlx.DB, blockNumber uint64) error {
|
||||||
|
var exists bool
|
||||||
|
err := db.Get(&exists, TransactionCIDsRefHeaderCIDs, blockNumber)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
return fmt.Errorf(ReferentialIntegrityErr, blockNumber, "eth.header_cids")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ValidateIPFSBlocks(db, blockNumber, "eth.transaction_cids", "mh_key")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateReceiptCIDsRef does a reference integrity check on references in eth.receipt_cids table
|
||||||
|
func ValidateReceiptCIDsRef(db *sqlx.DB, blockNumber uint64) error {
|
||||||
|
var exists bool
|
||||||
|
err := db.Get(&exists, ReceiptCIDsRefTransactionCIDs, blockNumber)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
return fmt.Errorf(ReferentialIntegrityErr, blockNumber, "eth.transaction_cids")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ValidateIPFSBlocks(db, blockNumber, "eth.receipt_cids", "leaf_mh_key")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateStateCIDsRef does a reference integrity check on references in eth.state_cids table
|
||||||
|
func ValidateStateCIDsRef(db *sqlx.DB, blockNumber uint64) error {
|
||||||
|
var exists bool
|
||||||
|
err := db.Get(&exists, StateCIDsRefHeaderCIDs, blockNumber)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
return fmt.Errorf(ReferentialIntegrityErr, blockNumber, "eth.header_cids")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ValidateIPFSBlocks(db, blockNumber, "eth.state_cids", "mh_key")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateStorageCIDsRef does a reference integrity check on references in eth.storage_cids table
|
||||||
|
func ValidateStorageCIDsRef(db *sqlx.DB, blockNumber uint64) error {
|
||||||
|
var exists bool
|
||||||
|
err := db.Get(&exists, StorageCIDsRefStateCIDs, blockNumber)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
return fmt.Errorf(ReferentialIntegrityErr, blockNumber, "eth.state_cids")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ValidateIPFSBlocks(db, blockNumber, "eth.storage_cids", "mh_key")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
var exists bool
|
||||||
|
err := db.Get(&exists, LogCIDsRefReceiptCIDs, blockNumber)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
return fmt.Errorf(ReferentialIntegrityErr, blockNumber, "eth.receipt_cids")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ValidateIPFSBlocks(db, blockNumber, "eth.log_cids", "leaf_mh_key")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
var exists bool
|
||||||
|
err := db.Get(&exists, fmt.Sprintf(CIDsRefIPLDBlocks, CIDTable, mhKeyField), blockNumber)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
return fmt.Errorf(ReferentialIntegrityErr, blockNumber, "public.blocks")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package validator
|
||||||
|
|
||||||
|
// Queries to validate referential integrity in the indexed data:
|
||||||
|
// At the given block number,
|
||||||
|
// In each table, for each (would be) foreign key reference, perform left join with the referenced table on the foreign key fields.
|
||||||
|
// Select rows where there are no matching rows in the referenced table.
|
||||||
|
// If any such rows exist, there are missing entries in the referenced table.
|
||||||
|
|
||||||
|
const (
|
||||||
|
CIDsRefIPLDBlocks = `SELECT EXISTS (
|
||||||
|
SELECT *
|
||||||
|
FROM %[1]s
|
||||||
|
LEFT JOIN public.blocks ON (
|
||||||
|
%[1]s.%[2]s = blocks.key
|
||||||
|
AND %[1]s.block_number = blocks.block_number
|
||||||
|
)
|
||||||
|
WHERE
|
||||||
|
%[1]s.block_number = $1
|
||||||
|
AND blocks.key IS NULL
|
||||||
|
)`
|
||||||
|
|
||||||
|
UncleCIDsRefHeaderCIDs = `SELECT EXISTS (
|
||||||
|
SELECT *
|
||||||
|
FROM eth.uncle_cids
|
||||||
|
LEFT JOIN eth.header_cids ON (
|
||||||
|
uncle_cids.header_id = header_cids.block_hash
|
||||||
|
AND uncle_cids.block_number = header_cids.block_number
|
||||||
|
)
|
||||||
|
WHERE
|
||||||
|
uncle_cids.block_number = $1
|
||||||
|
AND header_cids.block_hash IS NULL
|
||||||
|
)`
|
||||||
|
|
||||||
|
TransactionCIDsRefHeaderCIDs = `SELECT EXISTS (
|
||||||
|
SELECT *
|
||||||
|
FROM eth.transaction_cids
|
||||||
|
LEFT JOIN eth.header_cids ON (
|
||||||
|
transaction_cids.header_id = header_cids.block_hash
|
||||||
|
AND transaction_cids.block_number = header_cids.block_number
|
||||||
|
)
|
||||||
|
WHERE
|
||||||
|
transaction_cids.block_number = $1
|
||||||
|
AND header_cids.block_hash IS NULL
|
||||||
|
)`
|
||||||
|
|
||||||
|
ReceiptCIDsRefTransactionCIDs = `SELECT EXISTS (
|
||||||
|
SELECT *
|
||||||
|
FROM eth.receipt_cids
|
||||||
|
LEFT JOIN eth.transaction_cids ON (
|
||||||
|
receipt_cids.tx_id = transaction_cids.tx_hash
|
||||||
|
AND receipt_cids.header_id = transaction_cids.header_id
|
||||||
|
AND receipt_cids.block_number = transaction_cids.block_number
|
||||||
|
)
|
||||||
|
WHERE
|
||||||
|
receipt_cids.block_number = $1
|
||||||
|
AND transaction_cids.tx_hash IS NULL
|
||||||
|
)`
|
||||||
|
|
||||||
|
StateCIDsRefHeaderCIDs = `SELECT EXISTS (
|
||||||
|
SELECT *
|
||||||
|
FROM eth.state_cids
|
||||||
|
LEFT JOIN eth.header_cids ON (
|
||||||
|
state_cids.header_id = header_cids.block_hash
|
||||||
|
AND state_cids.block_number = header_cids.block_number
|
||||||
|
)
|
||||||
|
WHERE
|
||||||
|
state_cids.block_number = $1
|
||||||
|
AND header_cids.block_hash IS NULL
|
||||||
|
)`
|
||||||
|
|
||||||
|
StorageCIDsRefStateCIDs = `SELECT EXISTS (
|
||||||
|
SELECT *
|
||||||
|
FROM eth.storage_cids
|
||||||
|
LEFT JOIN eth.state_cids ON (
|
||||||
|
storage_cids.state_path = state_cids.state_path
|
||||||
|
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
|
||||||
|
)`
|
||||||
|
|
||||||
|
LogCIDsRefReceiptCIDs = `SELECT EXISTS (
|
||||||
|
SELECT *
|
||||||
|
FROM eth.log_cids
|
||||||
|
LEFT JOIN eth.receipt_cids ON (
|
||||||
|
log_cids.rct_id = receipt_cids.tx_id
|
||||||
|
AND log_cids.header_id = receipt_cids.header_id
|
||||||
|
AND log_cids.block_number = receipt_cids.block_number
|
||||||
|
)
|
||||||
|
WHERE
|
||||||
|
log_cids.block_number = $1
|
||||||
|
AND receipt_cids.tx_id IS NULL
|
||||||
|
)`
|
||||||
|
)
|
||||||
@@ -0,0 +1,334 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
+31
-10
@@ -20,14 +20,18 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
ipfsethdb "github.com/vulcanize/ipfs-ethdb/v3/postgres"
|
|
||||||
ipldEth "github.com/vulcanize/ipld-eth-server/v3/pkg/eth"
|
ipfsethdb "github.com/vulcanize/ipfs-ethdb/v4/postgres"
|
||||||
ethServerShared "github.com/vulcanize/ipld-eth-server/v3/pkg/shared"
|
ipldEth "github.com/vulcanize/ipld-eth-server/v4/pkg/eth"
|
||||||
|
ethServerShared "github.com/vulcanize/ipld-eth-server/v4/pkg/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
big8 = big.NewInt(8)
|
big8 = big.NewInt(8)
|
||||||
big32 = big.NewInt(32)
|
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 {
|
type service struct {
|
||||||
@@ -37,17 +41,19 @@ type service struct {
|
|||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
chainCfg *params.ChainConfig
|
chainCfg *params.ChainConfig
|
||||||
quitChan chan bool
|
quitChan chan bool
|
||||||
|
progressChan chan uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(db *sqlx.DB, blockNum, trailNum uint64, sleepInterval uint, chainCfg *params.ChainConfig) *service {
|
func NewService(cfg *Config, progressChan chan uint64) *service {
|
||||||
return &service{
|
return &service{
|
||||||
db: db,
|
db: cfg.DB,
|
||||||
blockNum: blockNum,
|
blockNum: cfg.BlockNum,
|
||||||
trail: trailNum,
|
trail: cfg.Trail,
|
||||||
sleepInterval: sleepInterval,
|
sleepInterval: cfg.SleepInterval,
|
||||||
logger: log.New(),
|
logger: log.New(),
|
||||||
chainCfg: chainCfg,
|
chainCfg: cfg.ChainCfg,
|
||||||
quitChan: make(chan bool),
|
quitChan: make(chan bool),
|
||||||
|
progressChan: progressChan,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +102,9 @@ func (s *service) Start(ctx context.Context, wg *sync.WaitGroup) {
|
|||||||
select {
|
select {
|
||||||
case <-s.quitChan:
|
case <-s.quitChan:
|
||||||
s.logger.Infof("last validated block %v", idxBlockNum-1)
|
s.logger.Infof("last validated block %v", idxBlockNum-1)
|
||||||
s.logger.Info("stopping ipld-eth-db-validator process")
|
if s.progressChan != nil {
|
||||||
|
close(s.progressChan)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
idxBlockNum, err = s.Validate(ctx, api, idxBlockNum)
|
idxBlockNum, err = s.Validate(ctx, api, idxBlockNum)
|
||||||
@@ -111,6 +119,7 @@ func (s *service) Start(ctx context.Context, wg *sync.WaitGroup) {
|
|||||||
|
|
||||||
// Stop is used to gracefully stop the service
|
// Stop is used to gracefully stop the service
|
||||||
func (s *service) Stop() {
|
func (s *service) Stop() {
|
||||||
|
s.logger.Info("stopping ipld-eth-db-validator process")
|
||||||
close(s.quitChan)
|
close(s.quitChan)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +138,18 @@ func (s *service) Validate(ctx context.Context, api *ipldEth.PublicEthAPI, idxBl
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Infof("state root verified for block %d", idxBlockNum)
|
s.logger.Infof("state root verified for block %d", idxBlockNum)
|
||||||
|
|
||||||
|
err = ValidateReferentialIntegrity(s.db, idxBlockNum)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Errorf("failed to verify referential integrity at block %d", idxBlockNum)
|
||||||
|
return idxBlockNum, err
|
||||||
|
}
|
||||||
|
s.logger.Infof("referential integrity verified for block %d", idxBlockNum)
|
||||||
|
|
||||||
|
if s.progressChan != nil {
|
||||||
|
s.progressChan <- idxBlockNum
|
||||||
|
}
|
||||||
|
|
||||||
idxBlockNum++
|
idxBlockNum++
|
||||||
} else {
|
} else {
|
||||||
// Sleep / wait for head to move ahead
|
// Sleep / wait for head to move ahead
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package validator_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidator(t *testing.T) {
|
||||||
|
RegisterFailHandler(Fail)
|
||||||
|
RunSpecs(t, "Validator Suite")
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
set -o xtrace
|
set -o xtrace
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
# Clear up existing docker images and volume.
|
# Clear up existing docker images and volume.
|
||||||
docker-compose down --remove-orphans --volumes
|
docker-compose down --remove-orphans --volumes
|
||||||
|
|
||||||
docker-compose -f docker-compose.yml up -d ipld-eth-db
|
# Spin up TimescaleDB
|
||||||
sleep 10
|
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
|
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
|
docker-compose down --remove-orphans --volumes
|
||||||
|
|||||||
+17
-6
@@ -1,23 +1,30 @@
|
|||||||
# Test Insructions
|
# Test Instructions
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
- For running integration tests:
|
- For running integration tests:
|
||||||
|
|
||||||
- Clone [stack-orchestrator](https://github.com/vulcanize/stack-orchestrator) and [go-ethereum](https://github.com/vulcanize/go-ethereum) repositories.
|
- 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 [v3 release](https://github.com/vulcanize/go-ethereum/releases/tag/v1.10.17-statediff-3.2.1) in go-ethereum repo.
|
- Checkout [v4 release](https://github.com/vulcanize/ipld-eth-db/releases/tag/v4.2.0-alpha) in ipld-eth-db repo.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# In ipld-eth-db repo.
|
||||||
|
git checkout v4.2.0-alpha
|
||||||
|
```
|
||||||
|
|
||||||
|
- Checkout [v4 release](https://github.com/vulcanize/go-ethereum/releases/tag/v1.10.19-statediff-4.1.0-alpha) in go-ethereum repo.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# In go-ethereum repo.
|
# In go-ethereum repo.
|
||||||
git checkout v1.10.17-statediff-3.2.1
|
git checkout v1.10.19-statediff-4.1.0-alpha
|
||||||
```
|
```
|
||||||
|
|
||||||
- Checkout working commit in stack-orchestrator repo.
|
- Checkout working commit in stack-orchestrator repo.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# In stack-orchestrator repo.
|
# In stack-orchestrator repo.
|
||||||
git checkout 3bb1796a59827fb755410c5ce69fac567a0f832b
|
git checkout f2fd766f5400fcb9eb47b50675d2e3b1f2753702
|
||||||
```
|
```
|
||||||
|
|
||||||
## Run
|
## Run
|
||||||
@@ -48,12 +55,16 @@
|
|||||||
```bash
|
```bash
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Path to ipld-eth-server repo.
|
||||||
|
vulcanize_ipld_eth_db=~/ipld-eth-db/
|
||||||
|
|
||||||
# Path to go-ethereum repo.
|
# Path to go-ethereum repo.
|
||||||
vulcanize_go_ethereum=~/go-ethereum
|
vulcanize_go_ethereum=~/go-ethereum
|
||||||
|
|
||||||
# Path to contract folder.
|
# Path to contract folder.
|
||||||
vulcanize_test_contract=~/ipld-eth-db-validator/test/contract
|
vulcanize_test_contract=~/ipld-eth-db-validator/test/contract
|
||||||
|
|
||||||
|
genesis_file_path='start-up-files/go-ethereum/genesis.json'
|
||||||
db_write=true
|
db_write=true
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -65,7 +76,7 @@
|
|||||||
|
|
||||||
./wrapper.sh \
|
./wrapper.sh \
|
||||||
-e docker \
|
-e docker \
|
||||||
-d ../docker/latest/docker-compose-db.yml \
|
-d ../docker/local/docker-compose-db-sharding.yml \
|
||||||
-d ../docker/local/docker-compose-go-ethereum.yml \
|
-d ../docker/local/docker-compose-go-ethereum.yml \
|
||||||
-d ../docker/local/docker-compose-contract.yml \
|
-d ../docker/local/docker-compose-contract.yml \
|
||||||
-v remove \
|
-v remove \
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// SPDX-License-Identifier: AGPL-3.0
|
||||||
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
contract Test {
|
||||||
|
address payable owner;
|
||||||
|
|
||||||
|
modifier onlyOwner {
|
||||||
|
require(
|
||||||
|
msg.sender == owner,
|
||||||
|
"Only owner can call this function."
|
||||||
|
);
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256[100] data;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
owner = payable(msg.sender);
|
||||||
|
data = [1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function Put(uint256 addr, uint256 value) public {
|
||||||
|
data[addr] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() public onlyOwner {
|
||||||
|
selfdestruct(owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -63,6 +63,49 @@ fastify.get('/v1/sendEth', async (req, reply) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fastify.get('/v1/deployTestContract', async (req, reply) => {
|
||||||
|
const testContract = await hre.ethers.getContractFactory("Test");
|
||||||
|
const test = await testContract.deploy();
|
||||||
|
await test.deployed();
|
||||||
|
|
||||||
|
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() {
|
async function main() {
|
||||||
try {
|
try {
|
||||||
await fastify.listen(3000, '0.0.0.0');
|
await fastify.listen(3000, '0.0.0.0');
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
+77
-23
@@ -2,53 +2,107 @@ package integration_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
"github.com/vulcanize/ipld-eth-db-validator/pkg/validator"
|
"github.com/vulcanize/ipld-eth-db-validator/pkg/validator"
|
||||||
|
integration "github.com/vulcanize/ipld-eth-db-validator/test"
|
||||||
|
|
||||||
"github.com/vulcanize/ipld-eth-server/v3/pkg/shared"
|
"github.com/vulcanize/ipld-eth-server/v4/pkg/shared"
|
||||||
integration "github.com/vulcanize/ipld-eth-server/v3/test"
|
ethServerIntegration "github.com/vulcanize/ipld-eth-server/v4/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
blockNum = 1
|
||||||
trail = 0
|
trail = 0
|
||||||
validatorSleepInterval = uint(5)
|
validatorSleepInterval = uint(5)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
testAddresses = []string{
|
||||||
|
"0x1111111111111111111111111111111111111112",
|
||||||
|
"0x1ca7c995f8eF0A2989BbcE08D5B7Efe50A584aa1",
|
||||||
|
"0x9a4b666af23a2cdb4e5538e1d222a445aeb82134",
|
||||||
|
"0xF7C7AEaECD2349b129d5d15790241c32eeE4607B",
|
||||||
|
"0x992b6E9BFCA1F7b0797Cee10b0170E536EAd3532",
|
||||||
|
"0x29ed93a7454Bc17a8D4A24D0627009eE0849B990",
|
||||||
|
"0x66E3dCA826b04B5d4988F7a37c91c9b1041e579D",
|
||||||
|
"0x96288939Ac7048c27E0E087b02bDaad3cd61b37b",
|
||||||
|
"0xD354280BCd771541c935b15bc04342c26086FE9B",
|
||||||
|
"0x7f887e25688c274E77b8DeB3286A55129B55AF14",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
var _ = Describe("Integration test", func() {
|
var _ = Describe("Integration test", func() {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
var contract *integration.ContractDeployed
|
var contract *ethServerIntegration.ContractDeployed
|
||||||
var contractErr error
|
var err error
|
||||||
sleepInterval := 5 * time.Second
|
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() {
|
Describe("Validate state", func() {
|
||||||
BeforeEach(func() {
|
It("performs validation on contract deployment", func() {
|
||||||
// Deploy a dummy contract as the first contract might get deployed at block number 0
|
contract, err = integration.DeployTestContract()
|
||||||
_, _ = integration.DeployContract()
|
Expect(err).ToNot(HaveOccurred())
|
||||||
time.Sleep(sleepInterval)
|
time.Sleep(sleepInterval)
|
||||||
|
|
||||||
contract, contractErr = integration.DeployContract()
|
Expect(validationProgressChan).ToNot(BeClosed())
|
||||||
time.Sleep(sleepInterval)
|
Eventually(validationProgressChan, timeout).Should(Receive(Equal(uint64(contract.BlockNumber))))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("Validate state root", func() {
|
It("performs validation on contract transactions", func() {
|
||||||
Expect(contractErr).ToNot(HaveOccurred())
|
for i := 0; i < 10; i++ {
|
||||||
|
res, txErr := integration.PutTestValue(contract.Address, i, i)
|
||||||
|
Expect(txErr).ToNot(HaveOccurred())
|
||||||
|
time.Sleep(sleepInterval)
|
||||||
|
|
||||||
db := shared.SetupDB()
|
Expect(validationProgressChan).ToNot(BeClosed())
|
||||||
srvc := validator.NewService(db, uint64(contract.BlockNumber), trail, validatorSleepInterval, validator.IntegrationTestChainConfig)
|
Eventually(validationProgressChan, timeout).Should(Receive(Equal(uint64(res.BlockNumber))))
|
||||||
stopCh := make(chan int, 1)
|
}
|
||||||
go func() {
|
})
|
||||||
srvc.Start(ctx, nil)
|
|
||||||
stopCh <- 1
|
It("performs validation on eth transfer transactions", func() {
|
||||||
}()
|
for _, address := range testAddresses {
|
||||||
go func() {
|
tx, txErr := ethServerIntegration.SendEth(address, "0.01")
|
||||||
<-stopCh
|
Expect(txErr).ToNot(HaveOccurred())
|
||||||
srvc.Stop()
|
time.Sleep(sleepInterval)
|
||||||
}()
|
|
||||||
|
Expect(validationProgressChan).ToNot(BeClosed())
|
||||||
|
Eventually(validationProgressChan, timeout).Should(Receive(Equal(uint64(tx.BlockNumber))))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import (
|
|||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"github.com/vulcanize/ipld-eth-server/v3/pkg/eth/test_helpers"
|
"github.com/vulcanize/ipld-eth-server/v4/pkg/eth/test_helpers"
|
||||||
"github.com/vulcanize/ipld-eth-server/v3/pkg/shared"
|
"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/pkg/validator"
|
||||||
"github.com/vulcanize/ipld-eth-db-validator/validator_test"
|
"github.com/vulcanize/ipld-eth-db-validator/validator_test"
|
||||||
@@ -120,6 +120,9 @@ var _ = Describe("eth state reading tests", func() {
|
|||||||
for i := uint64(blockHeight); i <= chainLength-trail; i++ {
|
for i := uint64(blockHeight); i <= chainLength-trail; i++ {
|
||||||
err = validator.ValidateBlock(context.Background(), api, i)
|
err = validator.ValidateBlock(context.Background(), api, i)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
err = validator.ValidateReferentialIntegrity(db, i)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user