Merge pull request #1 from vulcanize/state-validator
Implement ethereum state validator.
This commit is contained in:
commit
d60e4f6557
27
Makefile
Normal file
27
Makefile
Normal file
@ -0,0 +1,27 @@
|
||||
BIN = $(GOPATH)/bin
|
||||
BASE = $(GOPATH)/src/$(PACKAGE)
|
||||
PKGS = go list ./... | grep -v "^vendor/"
|
||||
|
||||
# Tools
|
||||
## Testing library
|
||||
GINKGO = $(BIN)/ginkgo
|
||||
$(BIN)/ginkgo:
|
||||
go install github.com/onsi/ginkgo/ginkgo
|
||||
|
||||
.PHONY: integrationtest
|
||||
integrationtest: | $(GINKGO) $(GOOSE)
|
||||
go vet ./...
|
||||
go fmt ./...
|
||||
$(GINKGO) -r test/ -v
|
||||
|
||||
.PHONY: integrationtest_blockchain
|
||||
integrationtest_blockchain: | $(GINKGO) $(GOOSE)
|
||||
go vet ./...
|
||||
go fmt ./...
|
||||
$(GINKGO) -r validator_test/ -v
|
||||
|
||||
.PHONY: integrationtest_local
|
||||
integrationtest_local: | $(GINKGO) $(GOOSE)
|
||||
go vet ./...
|
||||
go fmt ./...
|
||||
./scripts/run_integration_test.sh
|
78
README.md
Normal file
78
README.md
Normal file
@ -0,0 +1,78 @@
|
||||
- [Validator-README](#validator-readme)
|
||||
- [Overview](#overview)
|
||||
- [Intention for the Validator](#intention-for-the-validator)
|
||||
- [Edge Cases](#edge-cases)
|
||||
- [Instructions for Testing](#instructions-for-testing)
|
||||
- [Code Overview](#code-overview)
|
||||
- [Known Bugs](#known-bugs)
|
||||
- [Tests on 03/03/22](#tests-on-03-03-22)
|
||||
- [Set Up](#set-up)
|
||||
- [Testing Failures](#testing-failures)
|
||||
|
||||
<small><i><a href='http://ecotrust-canada.github.io/markdown-toc/'>Table of contents generated with markdown-toc</a></i></small>
|
||||
|
||||
# Overview
|
||||
|
||||
This repository contains the validator. The purpose of the validator is to ensure that the data in the Core Postgres database match the data on the blockchain.
|
||||
|
||||
# Intention for the Validator
|
||||
|
||||
The perfect scenario for the validator is as follows:
|
||||
|
||||
1. The validator will have the capacity to perform historical checks for the Core Postgres database. Users can contain these historical checks to specified configurations (block range).
|
||||
2. The validator will validate a certain number of trailing blocks, `t`, trailing the head, `n`. Therefore the validator will constantly perform real-time validation starting at `n` and ending at `n - t`.
|
||||
3. The validator validates the IPLD blocks in the Core Database; it will update the core database to indicate that the validator validated it.
|
||||
|
||||
## Edge Cases
|
||||
|
||||
We must consider the following edge cases for the validator.
|
||||
|
||||
- There are three different data types that the validator must account for.
|
||||
|
||||
# Instructions for Testing
|
||||
|
||||
To run the test, do the following:
|
||||
|
||||
1. Make sure `GOPATH` is set in your `~/.bashrc` or `~/.bash_profile`: `export GOPATH=$(go env GOPATH)`
|
||||
2. `./scripts/run_integration_test.sh`
|
||||
|
||||
# Code Overview
|
||||
|
||||
This section will provide some insight into specific files and their purpose.
|
||||
|
||||
- `validator_test/chain_maker.go` - This file contains the code for creating a “test” blockchain.
|
||||
- `validator_test/validator_test.go` - This file contains testing to validate the validator. It leverages `chain_maker.go` to create a blockchain to validate.
|
||||
- `pkg/validator/validator.go` - This file contains most of the core logic for the validator.
|
||||
|
||||
# Known Bugs
|
||||
|
||||
1. The validator is improperly handling missing headers from the database.
|
||||
1. Scenario
|
||||
1. The IPLD blocks from the mock blockchain are inserted into the Postgres Data.
|
||||
2. The validator runs, and all tests pass.
|
||||
3. Users manually remove the last few rows from the database.
|
||||
4. The validator runs, and all tests pass - This behavior is neither expected nor wanted.
|
||||
|
||||
# Tests on 03/03/22
|
||||
|
||||
The tests highlighted below were conducted to validate the initial behavior of the validator.
|
||||
|
||||
## Set Up
|
||||
|
||||
Below are the steps utilized to set up the test environment.
|
||||
|
||||
1. Run the `scripts/run_integration_test.sh` script.
|
||||
1. First comment outline 130 to 133 from `validator_test/validator_test.go`
|
||||
2. Once the code has completed running, comment out lines 55 to 126, 38 to 40, and 42 to 44.
|
||||
1. Make the following change `db, err = setupDB() --> db, _ = setupDB()`
|
||||
3. Run the following command: `ginkgo -r validator_test/ -v`
|
||||
1. All tests should pass
|
||||
|
||||
## Testing Failures
|
||||
|
||||
Once we had populated the database, we tested for failures.
|
||||
|
||||
1. Removing a Transaction from `transaction_cids` - If we removed a transaction from the database and ran the test, the test would fail. **This is the expected behavior.**
|
||||
2. Removing Headers from `eth.header_cids`
|
||||
1. If we removed a header block sandwiched between two header blocks, the test would fail (For example, we removed the entry for block 4, and the block range is 1-10). **This is the expected behavior.**
|
||||
2. If we removed the tail block(s) from the table, the test would pass (For example, we remove the entry for blocks 8, 9, 10, and the block range is 1-10). **This is _not_ the expected behavior.**
|
88
cmd/root.go
Normal file
88
cmd/root.go
Normal file
@ -0,0 +1,88 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var (
|
||||
cfgFile string
|
||||
subCommand string
|
||||
logWithCommand log.Entry
|
||||
)
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "ipld-eth-db-validator",
|
||||
Short: "Validates each block state stored for state-diff service.",
|
||||
Long: `Validates each block state stored for state-diff service.`,
|
||||
PersistentPreRun: initFunc,
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
log.Info("----- Starting state validator -----")
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func initFunc(cmd *cobra.Command, args []string) {
|
||||
logfile := viper.GetString("logfile")
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
viper.AutomaticEnv()
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file location")
|
||||
rootCmd.PersistentFlags().String("logfile", "", "file path for logging")
|
||||
rootCmd.PersistentFlags().String("database-name", "vulcanize_public", "database name")
|
||||
rootCmd.PersistentFlags().Int("database-port", 5432, "database port")
|
||||
rootCmd.PersistentFlags().String("database-hostname", "localhost", "database hostname")
|
||||
rootCmd.PersistentFlags().String("database-user", "", "database user")
|
||||
rootCmd.PersistentFlags().String("database-password", "", "database password")
|
||||
rootCmd.PersistentFlags().String("log-level", log.InfoLevel.String(), "Log level (trace, debug, info, warn, error, fatal, panic")
|
||||
|
||||
_ = viper.BindPFlag("logfile", rootCmd.PersistentFlags().Lookup("logfile"))
|
||||
_ = viper.BindPFlag("database.name", rootCmd.PersistentFlags().Lookup("database-name"))
|
||||
_ = viper.BindPFlag("database.port", rootCmd.PersistentFlags().Lookup("database-port"))
|
||||
_ = viper.BindPFlag("database.hostname", rootCmd.PersistentFlags().Lookup("database-hostname"))
|
||||
_ = viper.BindPFlag("database.user", rootCmd.PersistentFlags().Lookup("database-user"))
|
||||
_ = viper.BindPFlag("database.password", rootCmd.PersistentFlags().Lookup("database-password"))
|
||||
_ = viper.BindPFlag("log.level", rootCmd.PersistentFlags().Lookup("log-level"))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
71
cmd/state_validator.go
Normal file
71
cmd/state_validator.go
Normal file
@ -0,0 +1,71 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/Vulcanize/ipld-eth-db-validator/pkg/validator"
|
||||
)
|
||||
|
||||
// stateValidatorCmd represents the stateValidator command
|
||||
var stateValidatorCmd = &cobra.Command{
|
||||
Use: "stateValidator",
|
||||
Short: "Validate ethereum state",
|
||||
Long: `Usage ./ipld-eth-db-validator stateValidator --config={path to toml config file}`,
|
||||
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
subCommand = cmd.CalledAs()
|
||||
logWithCommand = *log.WithField("SubCommand", subCommand)
|
||||
stateValidator()
|
||||
},
|
||||
}
|
||||
|
||||
func stateValidator() {
|
||||
cfg, err := validator.NewConfig()
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
height := viper.GetUint64("validate.block-height")
|
||||
if height < 1 {
|
||||
logWithCommand.Fatalf("block height cannot be less the 1")
|
||||
}
|
||||
|
||||
trail := viper.GetUint64("validate.trail")
|
||||
// TODO: add chain config logic here.
|
||||
srvc := validator.NewService(cfg.DB, height, trail, nil)
|
||||
|
||||
_, err = srvc.Start(context.Background())
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
logWithCommand.Println("state validation complete")
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(stateValidatorCmd)
|
||||
|
||||
stateValidatorCmd.PersistentFlags().String("block-height", "1", "block height to initiate state validation")
|
||||
stateValidatorCmd.PersistentFlags().String("trail", "0", "trail of block height to validate")
|
||||
|
||||
_ = viper.BindPFlag("validate.block-height", stateValidatorCmd.PersistentFlags().Lookup("block-height"))
|
||||
_ = viper.BindPFlag("validate.trail", stateValidatorCmd.PersistentFlags().Lookup("trail"))
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
if cfgFile != "" {
|
||||
viper.SetConfigFile(cfgFile)
|
||||
if err := viper.ReadInConfig(); err == nil {
|
||||
log.Printf("Using config file: %s", viper.ConfigFileUsed())
|
||||
} else {
|
||||
log.Fatal(fmt.Sprintf("Couldn't read config file: %s", err.Error()))
|
||||
}
|
||||
} else {
|
||||
log.Warn("No config file passed with --config flag")
|
||||
}
|
||||
}
|
46
docker-compose.yml
Normal file
46
docker-compose.yml
Normal file
@ -0,0 +1,46 @@
|
||||
version: '3.2'
|
||||
|
||||
services:
|
||||
contract:
|
||||
depends_on:
|
||||
- dapptools
|
||||
build:
|
||||
context: ./test/contract
|
||||
args:
|
||||
ETH_ADDR: "http://dapptools:8545"
|
||||
environment:
|
||||
ETH_ADDR: "http://dapptools:8545"
|
||||
ports:
|
||||
- "127.0.0.1:3000:3000"
|
||||
|
||||
dapptools:
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- ipld-eth-db
|
||||
image: vulcanize/dapptools:v0.30.0-v1.10.14-statediff-0.0.29
|
||||
environment:
|
||||
DB_USER: vdbm
|
||||
DB_NAME: vulcanize_testing
|
||||
DB_HOST: ipld-eth-db
|
||||
DB_PORT: 5432
|
||||
DB_PASSWORD: password
|
||||
DB_WRITE: $DB_WRITE
|
||||
ports:
|
||||
- "127.0.0.1:8545:8545"
|
||||
- "127.0.0.1:8546:8546"
|
||||
|
||||
ipld-eth-db:
|
||||
restart: always
|
||||
image: vulcanize/ipld-eth-db:v0.2.0
|
||||
environment:
|
||||
POSTGRES_USER: "vdbm"
|
||||
POSTGRES_DB: "vulcanize_testing"
|
||||
POSTGRES_PASSWORD: "password"
|
||||
volumes:
|
||||
- vdb_db_eth_validator:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "127.0.0.1:8077:5432"
|
||||
command: ["postgres", "-c", "log_statement=all"]
|
||||
|
||||
volumes:
|
||||
vdb_db_eth_validator:
|
10
environments/example.toml
Normal file
10
environments/example.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[database]
|
||||
name = "vulcanize_public"
|
||||
hostname = "localhost"
|
||||
port = 5432
|
||||
password = "password"
|
||||
user = "vdbm"
|
||||
|
||||
[validate]
|
||||
block-height = 1
|
||||
trail = 10
|
258
go.mod
258
go.mod
@ -1,3 +1,261 @@
|
||||
module github.com/Vulcanize/ipld-eth-db-validator
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/ethereum/go-ethereum v1.10.14
|
||||
github.com/jmoiron/sqlx v1.2.0
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/onsi/gomega v1.13.0
|
||||
github.com/sirupsen/logrus v1.7.0
|
||||
github.com/spf13/cobra v1.3.0
|
||||
github.com/spf13/viper v1.10.0
|
||||
github.com/vulcanize/ipfs-ethdb v0.0.6
|
||||
github.com/vulcanize/ipld-eth-server v0.3.9
|
||||
)
|
||||
|
||||
require (
|
||||
bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc // indirect
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
|
||||
github.com/Stebalien/go-bitfield v0.0.1 // indirect
|
||||
github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
|
||||
github.com/benbjohnson/clock v1.1.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/btcsuite/btcd v0.22.0-beta // indirect
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cheekybits/genny v1.0.0 // indirect
|
||||
github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect
|
||||
github.com/cskr/pubsub v1.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
|
||||
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea // indirect
|
||||
github.com/deepmap/oapi-codegen v1.8.2 // indirect
|
||||
github.com/edsrzf/mmap-go v1.0.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/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect
|
||||
github.com/go-ole/go-ole v1.2.1 // indirect
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/gopacket v1.1.19 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29 // indirect
|
||||
github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e // indirect
|
||||
github.com/hashicorp/errwrap v1.0.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/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.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/influxdata/influxdb v1.8.3 // indirect
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.4.0 // indirect
|
||||
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 // indirect
|
||||
github.com/ipfs/bbloom v0.0.4 // indirect
|
||||
github.com/ipfs/go-bitswap v0.4.0 // indirect
|
||||
github.com/ipfs/go-block-format v0.0.3 // indirect
|
||||
github.com/ipfs/go-blockservice v0.1.7 // indirect
|
||||
github.com/ipfs/go-cid v0.0.7 // indirect
|
||||
github.com/ipfs/go-cidutil v0.0.2 // indirect
|
||||
github.com/ipfs/go-datastore v0.4.6 // indirect
|
||||
github.com/ipfs/go-ds-measure v0.1.0 // indirect
|
||||
github.com/ipfs/go-fetcher v1.5.0 // indirect
|
||||
github.com/ipfs/go-filestore v1.0.0 // indirect
|
||||
github.com/ipfs/go-fs-lock v0.0.7 // indirect
|
||||
github.com/ipfs/go-graphsync v0.8.0 // indirect
|
||||
github.com/ipfs/go-ipfs v0.10.0 // indirect
|
||||
github.com/ipfs/go-ipfs-blockstore v1.0.1 // indirect
|
||||
github.com/ipfs/go-ipfs-chunker v0.0.5 // indirect
|
||||
github.com/ipfs/go-ipfs-config v0.16.0 // indirect
|
||||
github.com/ipfs/go-ipfs-delay v0.0.1 // indirect
|
||||
github.com/ipfs/go-ipfs-ds-help v1.0.0 // indirect
|
||||
github.com/ipfs/go-ipfs-exchange-interface v0.0.1 // indirect
|
||||
github.com/ipfs/go-ipfs-exchange-offline v0.0.1 // indirect
|
||||
github.com/ipfs/go-ipfs-files v0.0.8 // indirect
|
||||
github.com/ipfs/go-ipfs-keystore v0.0.2 // indirect
|
||||
github.com/ipfs/go-ipfs-pinner v0.1.2 // 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.6.1 // indirect
|
||||
github.com/ipfs/go-ipfs-routing v0.1.0 // 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.2.0 // indirect
|
||||
github.com/ipfs/go-ipld-legacy v0.1.0 // indirect
|
||||
github.com/ipfs/go-ipns v0.1.2 // indirect
|
||||
github.com/ipfs/go-log v1.0.5 // indirect
|
||||
github.com/ipfs/go-log/v2 v2.3.0 // indirect
|
||||
github.com/ipfs/go-merkledag v0.4.0 // indirect
|
||||
github.com/ipfs/go-metrics-interface v0.0.1 // indirect
|
||||
github.com/ipfs/go-mfs v0.1.2 // indirect
|
||||
github.com/ipfs/go-namesys v0.3.1 // indirect
|
||||
github.com/ipfs/go-path v0.1.2 // indirect
|
||||
github.com/ipfs/go-peertaskqueue v0.4.0 // indirect
|
||||
github.com/ipfs/go-unixfs v0.2.5 // indirect
|
||||
github.com/ipfs/go-unixfsnode v1.1.3 // indirect
|
||||
github.com/ipfs/go-verifcid v0.0.1 // indirect
|
||||
github.com/ipfs/interface-go-ipfs-core v0.5.1 // indirect
|
||||
github.com/ipld/go-codec-dagpb v1.3.0 // indirect
|
||||
github.com/ipld/go-ipld-prime v0.12.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/goprocess v0.1.4 // indirect
|
||||
github.com/klauspost/compress v1.11.7 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||
github.com/koron/go-ssdp v0.0.2 // indirect
|
||||
github.com/lib/pq v1.10.4 // 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-cidranger v1.1.0 // indirect
|
||||
github.com/libp2p/go-conn-security-multistream v0.2.1 // indirect
|
||||
github.com/libp2p/go-doh-resolver v0.3.1 // 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.15.0 // indirect
|
||||
github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052 // indirect
|
||||
github.com/libp2p/go-libp2p-autonat v0.4.2 // indirect
|
||||
github.com/libp2p/go-libp2p-blankhost v0.2.0 // indirect
|
||||
github.com/libp2p/go-libp2p-circuit v0.4.0 // indirect
|
||||
github.com/libp2p/go-libp2p-connmgr v0.2.4 // indirect
|
||||
github.com/libp2p/go-libp2p-core v0.9.0 // indirect
|
||||
github.com/libp2p/go-libp2p-discovery v0.5.1 // indirect
|
||||
github.com/libp2p/go-libp2p-kad-dht v0.13.1 // 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-mplex v0.4.1 // indirect
|
||||
github.com/libp2p/go-libp2p-nat v0.0.6 // indirect
|
||||
github.com/libp2p/go-libp2p-noise v0.2.2 // indirect
|
||||
github.com/libp2p/go-libp2p-peerstore v0.2.8 // indirect
|
||||
github.com/libp2p/go-libp2p-pnet v0.2.0 // indirect
|
||||
github.com/libp2p/go-libp2p-pubsub v0.5.4 // indirect
|
||||
github.com/libp2p/go-libp2p-pubsub-router v0.4.0 // indirect
|
||||
github.com/libp2p/go-libp2p-quic-transport v0.12.0 // indirect
|
||||
github.com/libp2p/go-libp2p-record v0.1.3 // indirect
|
||||
github.com/libp2p/go-libp2p-routing-helpers v0.2.3 // indirect
|
||||
github.com/libp2p/go-libp2p-swarm v0.5.3 // indirect
|
||||
github.com/libp2p/go-libp2p-tls v0.2.0 // indirect
|
||||
github.com/libp2p/go-libp2p-transport-upgrader v0.4.6 // indirect
|
||||
github.com/libp2p/go-libp2p-xor v0.0.0-20210714161855-5c005aca55db // indirect
|
||||
github.com/libp2p/go-libp2p-yamux v0.5.4 // indirect
|
||||
github.com/libp2p/go-maddr-filter v0.1.0 // indirect
|
||||
github.com/libp2p/go-mplex v0.3.0 // indirect
|
||||
github.com/libp2p/go-msgio v0.0.6 // indirect
|
||||
github.com/libp2p/go-nat v0.0.5 // indirect
|
||||
github.com/libp2p/go-netroute v0.1.6 // indirect
|
||||
github.com/libp2p/go-openssl v0.0.7 // indirect
|
||||
github.com/libp2p/go-reuseport v0.0.2 // indirect
|
||||
github.com/libp2p/go-reuseport-transport v0.0.5 // indirect
|
||||
github.com/libp2p/go-sockaddr v0.1.1 // indirect
|
||||
github.com/libp2p/go-stream-muxer-multistream v0.3.0 // indirect
|
||||
github.com/libp2p/go-tcp-transport v0.2.8 // indirect
|
||||
github.com/libp2p/go-ws-transport v0.5.0 // indirect
|
||||
github.com/libp2p/go-yamux/v2 v2.2.0 // indirect
|
||||
github.com/libp2p/zeroconf/v2 v2.0.0 // indirect
|
||||
github.com/lucas-clemente/quic-go v0.23.0 // indirect
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
github.com/mailgun/groupcache/v2 v2.2.1 // indirect
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.4 // indirect
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0 // 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.43 // indirect
|
||||
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
|
||||
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // 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/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/multiformats/go-base32 v0.0.3 // indirect
|
||||
github.com/multiformats/go-base36 v0.1.0 // indirect
|
||||
github.com/multiformats/go-multiaddr v0.4.0 // 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.0.3 // indirect
|
||||
github.com/multiformats/go-multicodec v0.3.0 // indirect
|
||||
github.com/multiformats/go-multihash v0.0.15 // indirect
|
||||
github.com/multiformats/go-multistream v0.2.2 // indirect
|
||||
github.com/multiformats/go-varint v0.0.6 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e // indirect
|
||||
github.com/prometheus/client_golang v1.11.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.30.0 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/prometheus/tsdb v0.7.1 // indirect
|
||||
github.com/rjeczalik/notify v0.9.1 // indirect
|
||||
github.com/rs/cors v1.7.0 // indirect
|
||||
github.com/segmentio/fasthash v1.0.3 // indirect
|
||||
github.com/shirou/gopsutil v3.21.5+incompatible // indirect
|
||||
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/spf13/afero v1.6.0 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // 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/vulcanize/eth-ipfs-state-validator v0.1.0 // 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/chunker v0.0.0-20181014151217-fe64bd25879f // indirect
|
||||
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
|
||||
github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9 // indirect
|
||||
github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 // indirect
|
||||
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.opentelemetry.io/otel v0.20.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v0.20.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v0.20.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/dig v1.10.0 // indirect
|
||||
go.uber.org/fx v1.13.1 // indirect
|
||||
go.uber.org/multierr v1.7.0 // indirect
|
||||
go.uber.org/zap v1.19.0 // indirect
|
||||
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
|
||||
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e // indirect
|
||||
golang.org/x/mod v0.5.1 // indirect
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // 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.8 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/ini.v1 v1.66.2 // indirect
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/urfave/cli.v1 v1.20.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/ethereum/go-ethereum v1.10.14 => github.com/vulcanize/go-ethereum v1.10.14-statediff-0.0.29
|
||||
github.com/vulcanize/ipld-eth-server v0.3.9 => github.com/vulcanize/ipld-eth-server v0.0.0-20220117121622-5ea788912bd3
|
||||
)
|
||||
|
7
main.go
Normal file
7
main.go
Normal file
@ -0,0 +1,7 @@
|
||||
package main
|
||||
|
||||
import "github.com/Vulcanize/ipld-eth-db-validator/cmd"
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
}
|
87
pkg/validator/config.go
Normal file
87
pkg/validator/config.go
Normal file
@ -0,0 +1,87 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/node"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/postgres"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var IntegrationTestChainConfig = ¶ms.ChainConfig{
|
||||
ChainID: big.NewInt(4),
|
||||
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: ¶ms.CliqueConfig{
|
||||
Period: 0,
|
||||
Epoch: 30000,
|
||||
},
|
||||
}
|
||||
|
||||
var TestChainConfig = ¶ms.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 {
|
||||
dbParams postgres.ConnectionParams
|
||||
dbConfig postgres.ConnectionConfig
|
||||
DB *postgres.DB
|
||||
}
|
||||
|
||||
func NewConfig() (*Config, error) {
|
||||
cfg := new(Config)
|
||||
return cfg, cfg.setupDB()
|
||||
}
|
||||
|
||||
func (c *Config) setupDB() error {
|
||||
_ = viper.BindEnv("database.name", postgres.DATABASE_NAME)
|
||||
_ = viper.BindEnv("database.hostname", postgres.DATABASE_HOSTNAME)
|
||||
_ = viper.BindEnv("database.port", postgres.DATABASE_PORT)
|
||||
_ = viper.BindEnv("database.user", postgres.DATABASE_USER)
|
||||
_ = viper.BindEnv("database.password", postgres.DATABASE_PASSWORD)
|
||||
_ = viper.BindEnv("database.maxIdle", postgres.DATABASE_MAX_IDLE_CONNECTIONS)
|
||||
_ = viper.BindEnv("database.maxOpen", postgres.DATABASE_MAX_OPEN_CONNECTIONS)
|
||||
_ = viper.BindEnv("database.maxLifetime", postgres.DATABASE_MAX_CONN_LIFETIME)
|
||||
|
||||
// DB params
|
||||
c.dbParams.Name = viper.GetString("database.name")
|
||||
c.dbParams.Hostname = viper.GetString("database.hostname")
|
||||
c.dbParams.Port = viper.GetInt("database.port")
|
||||
c.dbParams.User = viper.GetString("database.user")
|
||||
c.dbParams.Password = viper.GetString("database.password")
|
||||
|
||||
// DB Config
|
||||
c.dbConfig.MaxIdle = viper.GetInt("database.maxIdle")
|
||||
c.dbConfig.MaxOpen = viper.GetInt("database.maxOpen")
|
||||
c.dbConfig.MaxLifetime = viper.GetInt("database.maxLifetime")
|
||||
|
||||
// Create DB
|
||||
db, err := NewDB(postgres.DbConnectionString(c.dbParams), postgres.ConnectionConfig{}, node.Info{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create config: %w", err)
|
||||
}
|
||||
|
||||
c.DB = db
|
||||
return nil
|
||||
}
|
109
pkg/validator/database.go
Normal file
109
pkg/validator/database.go
Normal file
@ -0,0 +1,109 @@
|
||||
// Copyright 2020 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package validator
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
|
||||
type database struct {
|
||||
ethDB ethdb.Database
|
||||
}
|
||||
|
||||
func newDatabase(db ethdb.Database) *database {
|
||||
return &database{
|
||||
ethDB: 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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// ModifyAncients is not supported.
|
||||
func (d *database) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (d *database) TruncateAncients(n uint64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *database) Sync() error {
|
||||
return d.ethDB.Sync()
|
||||
}
|
||||
|
||||
func (d *database) NewBatch() ethdb.Batch {
|
||||
return d.ethDB.NewBatch()
|
||||
}
|
||||
|
||||
func (d *database) ReadAncients(fn func(ethdb.AncientReader) error) (err error) {
|
||||
return d.ethDB.ReadAncients(fn)
|
||||
}
|
||||
|
||||
func (d *database) Close() error {
|
||||
return d.ethDB.Close()
|
||||
}
|
283
pkg/validator/validator.go
Normal file
283
pkg/validator/validator.go
Normal file
@ -0,0 +1,283 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"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/indexer/node"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/postgres"
|
||||
"github.com/jmoiron/sqlx"
|
||||
log "github.com/sirupsen/logrus"
|
||||
ipfsethdb "github.com/vulcanize/ipfs-ethdb/postgres"
|
||||
ipldEth "github.com/vulcanize/ipld-eth-server/pkg/eth"
|
||||
ethServerShared "github.com/vulcanize/ipld-eth-server/pkg/shared"
|
||||
)
|
||||
|
||||
var (
|
||||
big8 = big.NewInt(8)
|
||||
big32 = big.NewInt(32)
|
||||
)
|
||||
|
||||
type service struct {
|
||||
db *postgres.DB
|
||||
blockNum, trail uint64
|
||||
logger *log.Logger
|
||||
chainCfg *params.ChainConfig
|
||||
}
|
||||
|
||||
func NewService(db *postgres.DB, blockNum, trailNum uint64, chainCfg *params.ChainConfig) *service {
|
||||
return &service{
|
||||
db: db,
|
||||
blockNum: blockNum,
|
||||
trail: trailNum,
|
||||
logger: log.New(),
|
||||
chainCfg: chainCfg,
|
||||
}
|
||||
}
|
||||
|
||||
func NewEthBackend(db *postgres.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.DB, ipfsethdb.CacheConfig{
|
||||
Name: groupName,
|
||||
Size: gcc.StateDB.CacheSizeInMB * 1024 * 1024,
|
||||
ExpiryDuration: time.Minute * time.Duration(gcc.StateDB.CacheExpiryInMins),
|
||||
})
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func NewDB(connectString string, config postgres.ConnectionConfig, node node.Info) (*postgres.DB, error) {
|
||||
db, connectErr := sqlx.Connect("postgres", connectString)
|
||||
if connectErr != nil {
|
||||
return &postgres.DB{}, postgres.ErrDBConnectionFailed(connectErr)
|
||||
}
|
||||
if config.MaxOpen > 0 {
|
||||
db.SetMaxOpenConns(config.MaxOpen)
|
||||
}
|
||||
if config.MaxIdle > 0 {
|
||||
db.SetMaxIdleConns(config.MaxIdle)
|
||||
}
|
||||
if config.MaxLifetime > 0 {
|
||||
lifetime := time.Duration(config.MaxLifetime) * time.Second
|
||||
db.SetConnMaxLifetime(lifetime)
|
||||
}
|
||||
pg := postgres.DB{DB: db, Node: node}
|
||||
return &pg, nil
|
||||
}
|
||||
|
||||
// Start is used to begin the service
|
||||
func (s *service) Start(ctx context.Context) (uint64, error) {
|
||||
api, err := ethAPI(ctx, s.db, s.chainCfg)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
idxBlockNum := s.blockNum
|
||||
headBlock, _ := api.B.BlockByNumber(ctx, rpc.LatestBlockNumber)
|
||||
headBlockNum := headBlock.NumberU64()
|
||||
|
||||
for headBlockNum-s.trail >= idxBlockNum {
|
||||
validateBlock, err := api.B.BlockByNumber(ctx, rpc.BlockNumber(idxBlockNum))
|
||||
if err != nil {
|
||||
return idxBlockNum, err
|
||||
}
|
||||
|
||||
stateDB, err := applyTransaction(validateBlock, api.B)
|
||||
if err != nil {
|
||||
return idxBlockNum, err
|
||||
}
|
||||
|
||||
blockStateRoot := validateBlock.Header().Root.String()
|
||||
|
||||
dbStateRoot := stateDB.IntermediateRoot(true).String()
|
||||
if blockStateRoot != dbStateRoot {
|
||||
s.logger.Errorf("failed to verify state root at block %d", idxBlockNum)
|
||||
return idxBlockNum, fmt.Errorf("failed to verify state root at block")
|
||||
}
|
||||
|
||||
s.logger.Infof("state root verified for block= %d", idxBlockNum)
|
||||
|
||||
headBlock, err = api.B.BlockByNumber(ctx, rpc.LatestBlockNumber)
|
||||
if err != nil {
|
||||
return idxBlockNum, err
|
||||
}
|
||||
|
||||
headBlockNum = headBlock.NumberU64()
|
||||
idxBlockNum++
|
||||
}
|
||||
|
||||
s.logger.Infof("last validated block %v", idxBlockNum-1)
|
||||
return idxBlockNum, nil
|
||||
}
|
||||
|
||||
func ethAPI(ctx context.Context, db *postgres.DB, chainCfg *params.ChainConfig) (*ipldEth.PublicEthAPI, error) {
|
||||
// TODO: decide network for custom chainConfig.
|
||||
backend, err := NewEthBackend(db, &ipldEth.Config{
|
||||
ChainConfig: chainCfg,
|
||||
GroupCacheConfig: ðServerShared.GroupCacheConfig{
|
||||
StateDB: ethServerShared.GroupConfig{
|
||||
Name: "vulcanize_validator",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var genesisBlock *types.Block
|
||||
if backend.Config.ChainConfig == nil {
|
||||
genesisBlock, err = backend.BlockByNumber(ctx, rpc.BlockNumber(0))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
backend.Config.ChainConfig = setChainConfig(genesisBlock.Hash())
|
||||
}
|
||||
|
||||
return ipldEth.NewPublicEthAPI(backend, nil, false, false, false)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if block.NumberU64() == 0 {
|
||||
return nil, errors.New("no transaction in genesis")
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signer := types.MakeSigner(backend.Config.ChainConfig, block.Number())
|
||||
|
||||
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()))
|
||||
|
||||
// Not yet the searched for transaction, execute on top of the current state
|
||||
newEVM := vm.NewEVM(ctx, txContext, stateDB, backend.Config.ChainConfig, vm.Config{})
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
if backend.Config.ChainConfig.Ethash != nil {
|
||||
accumulateRewards(backend.Config.ChainConfig, 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) {
|
||||
// Select the correct block reward based on chain progression
|
||||
blockReward := ethash.FrontierBlockReward
|
||||
if config.IsByzantium(header.Number) {
|
||||
blockReward = ethash.ByzantiumBlockReward
|
||||
}
|
||||
|
||||
if config.IsConstantinople(header.Number) {
|
||||
blockReward = ethash.ConstantinopleBlockReward
|
||||
}
|
||||
|
||||
// Accumulate the rewards for the miner and any included uncles
|
||||
reward := new(big.Int).Set(blockReward)
|
||||
r := new(big.Int)
|
||||
for _, uncle := range uncles {
|
||||
r.Add(uncle.Number, big8)
|
||||
r.Sub(r, header.Number)
|
||||
r.Mul(r, blockReward)
|
||||
r.Div(r, big8)
|
||||
state.AddBalance(uncle.Coinbase, r)
|
||||
|
||||
r.Div(blockReward, big32)
|
||||
reward.Add(reward, r)
|
||||
}
|
||||
|
||||
state.AddBalance(header.Coinbase, reward)
|
||||
}
|
||||
|
||||
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:
|
||||
return params.AllEthashProtocolChanges
|
||||
}
|
||||
}
|
||||
|
||||
func getAuthor(b *ipldEth.Backend, header *types.Header) *common.Address {
|
||||
author, err := getEngine(b).Author(header)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &author
|
||||
}
|
||||
|
||||
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)
|
||||
return engine
|
||||
}
|
||||
|
||||
return ethash.NewFaker()
|
||||
}
|
23
scripts/run_integration_test.sh
Executable file
23
scripts/run_integration_test.sh
Executable file
@ -0,0 +1,23 @@
|
||||
set -e
|
||||
set -o xtrace
|
||||
|
||||
export ETH_FORWARD_ETH_CALLS=false
|
||||
export DB_WRITE=true
|
||||
export ETH_HTTP_PATH=""
|
||||
export ETH_PROXY_ON_ERROR=false
|
||||
|
||||
# Clear up existing docker images and volume.
|
||||
docker-compose down --remove-orphans --volumes
|
||||
|
||||
# Build and start the containers.
|
||||
docker-compose -f docker-compose.yml up -d ipld-eth-db dapptools contract
|
||||
|
||||
export PGPASSWORD=password
|
||||
export DATABASE_USER=vdbm
|
||||
export DATABASE_PORT=8077
|
||||
export DATABASE_PASSWORD=password
|
||||
export DATABASE_HOSTNAME=127.0.0.1
|
||||
|
||||
# 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_blockchain
|
3
test/contract/.dockerignore
Normal file
3
test/contract/.dockerignore
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
artifacts
|
||||
cache
|
5
test/contract/.gitignore
vendored
Normal file
5
test/contract/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
|
||||
#Hardhat files
|
||||
cache
|
||||
artifacts
|
14
test/contract/Dockerfile
Normal file
14
test/contract/Dockerfile
Normal file
@ -0,0 +1,14 @@
|
||||
FROM node:14
|
||||
|
||||
ARG ETH_ADDR
|
||||
ENV ETH_ADDR $ETH_ADDR
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
RUN npm run compile && ls -lah
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
ENTRYPOINT ["npm", "start"]
|
10
test/contract/contracts/GLDToken.sol
Normal file
10
test/contract/contracts/GLDToken.sol
Normal file
@ -0,0 +1,10 @@
|
||||
pragma solidity ^0.8.0;
|
||||
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));
|
||||
}
|
||||
}
|
32
test/contract/hardhat.config.js
Normal file
32
test/contract/hardhat.config.js
Normal file
@ -0,0 +1,32 @@
|
||||
require("@nomiclabs/hardhat-waffle");
|
||||
|
||||
// This is a sample Hardhat task. To learn how to create your own go to
|
||||
// https://hardhat.org/guides/create-task.html
|
||||
task("accounts", "Prints the list of accounts", async () => {
|
||||
const accounts = await ethers.getSigners();
|
||||
|
||||
for (const account of accounts) {
|
||||
console.log(account.address);
|
||||
}
|
||||
});
|
||||
|
||||
// You need to export an object to set up your config
|
||||
// Go to https://hardhat.org/config/ to learn more
|
||||
|
||||
/**
|
||||
* @type import('hardhat/config').HardhatUserConfig
|
||||
*/
|
||||
module.exports = {
|
||||
solidity: "0.8.0",
|
||||
networks: {
|
||||
local: {
|
||||
url: 'http://127.0.0.1:8545',
|
||||
chainId: 4
|
||||
},
|
||||
docker: {
|
||||
url: process.env.ETH_ADDR,
|
||||
chainId: 4
|
||||
}
|
||||
}
|
||||
};
|
||||
|
27700
test/contract/package-lock.json
generated
Normal file
27700
test/contract/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
test/contract/package.json
Normal file
27
test/contract/package.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "contract",
|
||||
"version": "1.0.0",
|
||||
"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"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@openzeppelin/contracts": "^4.0.0",
|
||||
"fastify": "^3.14.2",
|
||||
"hardhat": "^2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.2",
|
||||
"@nomiclabs/hardhat-waffle": "^2.0.1",
|
||||
"chai": "^4.3.4",
|
||||
"ethereum-waffle": "^3.3.0",
|
||||
"ethers": "^5.1.0"
|
||||
}
|
||||
}
|
18
test/contract/scripts/deploy.js
Normal file
18
test/contract/scripts/deploy.js
Normal file
@ -0,0 +1,18 @@
|
||||
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
test/contract/scripts/sample-script.js
Normal file
36
test/contract/scripts/sample-script.js
Normal file
@ -0,0 +1,36 @@
|
||||
// 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);
|
||||
});
|
75
test/contract/src/index.js
Normal file
75
test/contract/src/index.js
Normal file
@ -0,0 +1,75 @@
|
||||
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 })
|
||||
});
|
||||
|
||||
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 [owner] = await hre.ethers.getSigners();
|
||||
const tx = await owner.sendTransaction({
|
||||
to,
|
||||
value: hre.ethers.utils.parseEther(value)
|
||||
});
|
||||
await tx.wait(1)
|
||||
|
||||
// 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,
|
||||
}
|
||||
});
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
await fastify.listen(3000, '0.0.0.0');
|
||||
} catch (err) {
|
||||
fastify.log.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
14
test/contract/test/sample-test.js
Normal file
14
test/contract/test/sample-test.js
Normal file
@ -0,0 +1,14 @@
|
||||
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!");
|
||||
});
|
||||
});
|
19
test/integration_suite_test.go
Normal file
19
test/integration_suite_test.go
Normal file
@ -0,0 +1,19 @@
|
||||
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)
|
||||
})
|
65
test/integration_test.go
Normal file
65
test/integration_test.go
Normal file
@ -0,0 +1,65 @@
|
||||
package integration_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/node"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/postgres"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/Vulcanize/ipld-eth-db-validator/pkg/validator"
|
||||
|
||||
integration "github.com/vulcanize/ipld-eth-server/test"
|
||||
)
|
||||
|
||||
const trail = 0
|
||||
|
||||
var randomAddr = common.HexToAddress("0x1C3ab14BBaD3D99F4203bd7a11aCB94882050E6f")
|
||||
|
||||
var _ = Describe("Integration test", func() {
|
||||
directProxyEthCalls, err := strconv.ParseBool(os.Getenv("ETH_FORWARD_ETH_CALLS"))
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
ctx := context.Background()
|
||||
|
||||
var contract *integration.ContractDeployed
|
||||
var contractErr error
|
||||
sleepInterval := 5 * time.Second
|
||||
|
||||
Describe("Validate state", func() {
|
||||
BeforeEach(func() {
|
||||
if directProxyEthCalls {
|
||||
Skip("skipping no-direct-proxy-forwarding integration tests")
|
||||
}
|
||||
contract, contractErr = integration.DeployContract()
|
||||
time.Sleep(sleepInterval)
|
||||
})
|
||||
|
||||
It("Validate state root", func() {
|
||||
Expect(contractErr).ToNot(HaveOccurred())
|
||||
|
||||
db, _ := setupDB()
|
||||
srvc := validator.NewService(db, uint64(contract.BlockNumber), trail, validator.IntegrationTestChainConfig)
|
||||
_, err = srvc.Start(ctx)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
func setupDB() (*postgres.DB, error) {
|
||||
uri := postgres.DbConnectionString(postgres.ConnectionParams{
|
||||
User: "vdbm",
|
||||
Password: "password",
|
||||
Hostname: "localhost",
|
||||
Name: "vulcanize_testing",
|
||||
Port: 8077,
|
||||
})
|
||||
return validator.NewDB(uri, postgres.ConnectionConfig{}, node.Info{})
|
||||
}
|
118
validator_test/chain_maker.go
Normal file
118
validator_test/chain_maker.go
Normal file
@ -0,0 +1,118 @@
|
||||
package validator_test
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"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 = core.GenesisBlockForTesting(Testdb, TestBankAddress, TestBankFunds)
|
||||
|
||||
Account1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||
Account2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
||||
Account1Addr = crypto.PubkeyToAddress(Account1Key.PublicKey) //0x703c4b2bD70c169f5717101CaeE543299Fc946C7
|
||||
Account2Addr = crypto.PubkeyToAddress(Account2Key.PublicKey) //0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e
|
||||
DeploymentTxData = common.Hex2Bytes("608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600180819055506101e2806100676000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806343d726d61461004657806365f3c31a1461005057806373d4a13a1461007e575b600080fd5b61004e61009c565b005b61007c6004803603602081101561006657600080fd5b810190808035906020019092919050505061017b565b005b610086610185565b6040518082815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610141576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061018c6022913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b8060018190555050565b6001548156fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6e2ea265627a7a723158205ba91466129f45285f53176d805117208c231ec6343d7896790e6fc4165b802b64736f6c63430005110032")
|
||||
ContractCode = common.Hex2Bytes("608060405234801561001057600080fd5b50600436106100415760003560e01c806343d726d61461004657806365f3c31a1461005057806373d4a13a1461007e575b600080fd5b61004e61009c565b005b61007c6004803603602081101561006657600080fd5b810190808035906020019092919050505061017b565b005b610086610185565b6040518082815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610141576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061018c6022913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b8060018190555050565b6001548156fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6e2ea265627a7a723158205ba91466129f45285f53176d805117208c231ec6343d7896790e6fc4165b802b64736f6c63430005110032")
|
||||
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, params.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))
|
||||
}
|
||||
}
|
19
validator_test/validator_suite_test.go
Normal file
19
validator_test/validator_suite_test.go
Normal file
@ -0,0 +1,19 @@
|
||||
package validator_test_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)
|
||||
})
|
143
validator_test/validator_test.go
Normal file
143
validator_test/validator_test.go
Normal file
@ -0,0 +1,143 @@
|
||||
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/params"
|
||||
"github.com/ethereum/go-ethereum/statediff"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/node"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/postgres"
|
||||
sdtypes "github.com/ethereum/go-ethereum/statediff/types"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth/test_helpers"
|
||||
|
||||
"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
|
||||
)
|
||||
|
||||
// SetupDB is use to setup a db for watcher tests
|
||||
func setupDB() (*postgres.DB, error) {
|
||||
uri := postgres.DbConnectionString(postgres.ConnectionParams{
|
||||
User: "vdbm",
|
||||
Password: "password",
|
||||
Hostname: "localhost",
|
||||
Name: "vulcanize_testing",
|
||||
Port: 8077,
|
||||
})
|
||||
return postgres.NewDB(uri, postgres.ConnectionConfig{}, node.Info{})
|
||||
}
|
||||
|
||||
var _ = Describe("eth state reading tests", func() {
|
||||
var (
|
||||
blocks []*types.Block
|
||||
receipts []types.Receipts
|
||||
chain *core.BlockChain
|
||||
db *postgres.DB
|
||||
chainConfig = params.TestChainConfig
|
||||
mockTD = big.NewInt(1337)
|
||||
err error
|
||||
)
|
||||
|
||||
It("test init", func() {
|
||||
db, err = setupDB()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
transformer, err := indexer.NewStateDiffIndexer(chainConfig, db)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// make the test blockchain (and state)
|
||||
blocks, receipts, chain = validator_test.MakeChain(chainLength, test_helpers.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)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
err = tx.Close(err)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
// Insert some non-canonical data into the database so that we test our ability to discern canonicity
|
||||
indexAndPublisher, err := indexer.NewStateDiffIndexer(chainConfig, db)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
tx, err := indexAndPublisher.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = tx.Close(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.Close(err)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
defer It("test teardown", func() {
|
||||
eth.TearDownDB(db)
|
||||
chain.Stop()
|
||||
})
|
||||
|
||||
Describe("state_validation", func() {
|
||||
It("Validator", func() {
|
||||
srvc := validator.NewService(db, blockHeight, trail, validator.TestChainConfig)
|
||||
|
||||
_, err := srvc.Start(context.Background())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue
Block a user