forked from cerc-io/ipld-eth-server
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0a7a7d999 | ||
|
|
7d6629e5b4 | ||
|
|
d761f88b30 | ||
|
|
948246cf83 | ||
|
|
e11f2c8c59 | ||
|
|
95fa6280c5 | ||
|
|
fd126c7bea | ||
|
|
f292888195 | ||
|
|
adf91df23a | ||
|
|
ede6ad1c8f | ||
|
|
ee13d5c1df | ||
|
|
eb2b3a0263 | ||
|
|
5088574437 | ||
|
|
d61f4f3cb4 | ||
|
|
1a455741ce | ||
|
|
180c2d963e | ||
|
|
2598704690 | ||
|
|
e2909797fc | ||
|
|
79765c7998 | ||
|
|
9c6549258e | ||
|
|
b4cdf76905 | ||
|
|
622ea44a6f | ||
|
|
d947c8f30a | ||
|
|
481988cb08 | ||
|
|
4580f0fc2e | ||
|
|
eba52442ec | ||
|
|
7ddf728db2 | ||
|
|
829a581727 | ||
|
|
fa03716cb2 | ||
|
|
bd3e841e21 | ||
|
|
8dc9ddf65e | ||
|
|
7d1b334bda | ||
|
|
ade1429ce3 | ||
|
|
5d1ba5903c | ||
|
|
a49f5d7582 | ||
|
|
436d9b9cb1 | ||
|
|
f7d520c933 | ||
|
|
e1a0d894a2 | ||
|
|
ba81766f6c | ||
|
|
b96f6c4d31 | ||
|
|
c5cfa85f4c | ||
|
|
702e839f5b | ||
|
|
ca354de229 | ||
|
|
3b531950f2 | ||
|
|
36c4da372b | ||
|
|
202fb6e6a6 | ||
|
|
77b7ee9e4c | ||
|
|
754c8cb18d | ||
|
|
782e3fd095 | ||
|
|
6716c3b92a | ||
|
|
b036053937 | ||
|
|
76ab914bdc | ||
|
|
d77f3fe012 | ||
|
|
6a86de87b4 | ||
|
|
bf4b1687a0 | ||
|
|
2d684c5aec | ||
|
|
c93e6adb27 | ||
|
|
a344432156 | ||
|
|
eb1a9cb381 | ||
|
|
bc3f0d5eaf | ||
|
|
caba9e91d3 | ||
|
|
01ffc80ef2 | ||
|
|
9c06e9387c | ||
|
|
3fd6269b78 | ||
|
|
13cd7126cc | ||
|
|
7d2542aa19 | ||
|
|
355f245bfe | ||
|
|
ec796d9813 | ||
|
|
a1adf0e7fc | ||
|
|
dbd2208d31 | ||
|
|
f99e397699 | ||
|
|
0f425ac76b | ||
|
|
964fc8bd22 | ||
|
|
4e8485810e | ||
|
|
aac2d852ca | ||
|
|
2a9b7b1414 | ||
|
|
bd12149594 | ||
|
|
8b704e9f1e | ||
|
|
fcaa28d923 | ||
|
|
8903b6fb2d |
@@ -14,3 +14,5 @@ postgraphile/package-lock.json
|
|||||||
vulcanizedb.log
|
vulcanizedb.log
|
||||||
db/migrations/20*.sql
|
db/migrations/20*.sql
|
||||||
plugins/*.so
|
plugins/*.so
|
||||||
|
postgraphile/*.toml
|
||||||
|
postgraphile/schema.graphql
|
||||||
|
|||||||
+2
-5
@@ -5,21 +5,18 @@ go:
|
|||||||
services:
|
services:
|
||||||
- postgresql
|
- postgresql
|
||||||
addons:
|
addons:
|
||||||
postgresql: '9.6'
|
postgresql: '11.2'
|
||||||
go_import_path: github.com/vulcanize/vulcanizedb
|
go_import_path: github.com/vulcanize/vulcanizedb
|
||||||
before_install:
|
before_install:
|
||||||
- make installtools
|
- make installtools
|
||||||
- bash ./scripts/install-postgres-10.sh
|
- bash ./scripts/install-postgres-11.sh
|
||||||
- curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
|
- curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
|
||||||
- echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
|
- echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
|
||||||
- sudo apt-get update && sudo apt-get install yarn
|
- sudo apt-get update && sudo apt-get install yarn
|
||||||
before_script:
|
before_script:
|
||||||
- sudo -u postgres createdb vulcanize_private
|
- sudo -u postgres createdb vulcanize_private
|
||||||
- make migrate NAME=vulcanize_private
|
- make migrate NAME=vulcanize_private
|
||||||
- cd postgraphile && yarn
|
|
||||||
script:
|
script:
|
||||||
- yarn test
|
|
||||||
- cd ../
|
|
||||||
- make test
|
- make test
|
||||||
- make integrationtest
|
- make integrationtest
|
||||||
notifications:
|
notifications:
|
||||||
|
|||||||
Generated
-1
@@ -542,7 +542,6 @@
|
|||||||
"github.com/ethereum/go-ethereum/ethdb",
|
"github.com/ethereum/go-ethereum/ethdb",
|
||||||
"github.com/ethereum/go-ethereum/p2p",
|
"github.com/ethereum/go-ethereum/p2p",
|
||||||
"github.com/ethereum/go-ethereum/p2p/discv5",
|
"github.com/ethereum/go-ethereum/p2p/discv5",
|
||||||
"github.com/ethereum/go-ethereum/params",
|
|
||||||
"github.com/ethereum/go-ethereum/rlp",
|
"github.com/ethereum/go-ethereum/rlp",
|
||||||
"github.com/ethereum/go-ethereum/rpc",
|
"github.com/ethereum/go-ethereum/rpc",
|
||||||
"github.com/hashicorp/golang-lru",
|
"github.com/hashicorp/golang-lru",
|
||||||
|
|||||||
@@ -70,7 +70,8 @@ build: dep
|
|||||||
HOST_NAME = localhost
|
HOST_NAME = localhost
|
||||||
PORT = 5432
|
PORT = 5432
|
||||||
NAME =
|
NAME =
|
||||||
CONNECT_STRING=postgresql://$(HOST_NAME):$(PORT)/$(NAME)?sslmode=disable
|
USER = postgres
|
||||||
|
CONNECT_STRING=postgresql://$(USER)@$(HOST_NAME):$(PORT)/$(NAME)?sslmode=disable
|
||||||
|
|
||||||
# Parameter checks
|
# Parameter checks
|
||||||
## Check that DB variables are provided
|
## Check that DB variables are provided
|
||||||
@@ -79,6 +80,7 @@ checkdbvars:
|
|||||||
test -n "$(HOST_NAME)" # $$HOST_NAME
|
test -n "$(HOST_NAME)" # $$HOST_NAME
|
||||||
test -n "$(PORT)" # $$PORT
|
test -n "$(PORT)" # $$PORT
|
||||||
test -n "$(NAME)" # $$NAME
|
test -n "$(NAME)" # $$NAME
|
||||||
|
test -n "$(USER)" # $$USER
|
||||||
@echo $(CONNECT_STRING)
|
@echo $(CONNECT_STRING)
|
||||||
|
|
||||||
## Check that the migration variable (id/timestamp) is provided
|
## Check that the migration variable (id/timestamp) is provided
|
||||||
|
|||||||
@@ -7,34 +7,40 @@
|
|||||||
|
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
1. [Background](../staging/README.md#background)
|
1. [Background](#background)
|
||||||
1. [Dependencies](../staging/README.md#dependencies)
|
1. [Install](#install)
|
||||||
1. [Install](../staging/README.md#install)
|
1. [Usage](#usage)
|
||||||
1. [Usage](../staging/README.md#usage)
|
1. [Contributing](#contributing)
|
||||||
1. [Tests](../staging/README.md#tests)
|
1. [License](#license)
|
||||||
1. [API](../staging/README.md#API)
|
|
||||||
1. [Contributing](../staging/README.md#contributing)
|
|
||||||
1. [License](../staging/README.md#license)
|
|
||||||
|
|
||||||
|
|
||||||
## Background
|
## Background
|
||||||
The same data structures and encodings that make Ethereum an effective and trust-less distributed virtual machine
|
The same data structures and encodings that make Ethereum an effective and trust-less distributed virtual machine
|
||||||
complicate data accessibility and usability for dApp developers.
|
complicate data accessibility and usability for dApp developers. VulcanizeDB improves Ethereum data accessibility by
|
||||||
|
providing a suite of tools to ease the extraction and transformation of data into a more useful state, including
|
||||||
|
allowing for exposing aggregate data from a suite of smart contracts.
|
||||||
|
|
||||||
|
VulanizeDB includes processes that sync, transform and expose data. Syncing involves
|
||||||
|
querying an Ethereum node and then persisting core data into a Postgres database. Transforming focuses on using previously synced data to
|
||||||
|
query for and transform log event and storage data for specifically configured smart contract addresses. Exposing data is a matter of getting
|
||||||
|
data from VulcanizeDB's underlying Postgres database and making it accessible.
|
||||||
|
|
||||||
## Dependencies
|

|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
1. [Dependencies](#dependencies)
|
||||||
|
1. [Building the project](#building-the-project)
|
||||||
|
1. [Setting up the database](#setting-up-the-database)
|
||||||
|
1. [Configuring a synced Ethereum node](#configuring-a-synced-ethereum-node)
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
- Go 1.11+
|
- Go 1.11+
|
||||||
- Postgres 10.6
|
- Postgres 11.2
|
||||||
- Ethereum Node
|
- Ethereum Node
|
||||||
- [Go Ethereum](https://ethereum.github.io/go-ethereum/downloads/) (1.8.23+)
|
- [Go Ethereum](https://ethereum.github.io/go-ethereum/downloads/) (1.8.23+)
|
||||||
- [Parity 1.8.11+](https://github.com/paritytech/parity/releases)
|
- [Parity 1.8.11+](https://github.com/paritytech/parity/releases)
|
||||||
|
|
||||||
|
|
||||||
## Install
|
|
||||||
1. [Building the project](../staging/README.md#building-the-project)
|
|
||||||
1. [Setting up the database](../staging/README.md#setting-up-the-database)
|
|
||||||
1. [Configuring a synced Ethereum node](../staging/README.md#configuring-a-synced-ethereum-node)
|
|
||||||
|
|
||||||
### Building the project
|
### Building the project
|
||||||
Download the codebase to your local `GOPATH` via:
|
Download the codebase to your local `GOPATH` via:
|
||||||
|
|
||||||
@@ -60,13 +66,15 @@ It can be additionally helpful to add `$GOPATH/bin` to your shell's `$PATH`.
|
|||||||
1. `createdb vulcanize_public`
|
1. `createdb vulcanize_public`
|
||||||
1. `cd $GOPATH/src/github.com/vulcanize/vulcanizedb`
|
1. `cd $GOPATH/src/github.com/vulcanize/vulcanizedb`
|
||||||
1. Run the migrations: `make migrate HOST_NAME=localhost NAME=vulcanize_public PORT=5432`
|
1. Run the migrations: `make migrate HOST_NAME=localhost NAME=vulcanize_public PORT=5432`
|
||||||
|
- There is an optional var `USER=username` if the database user is not the default user `postgres`
|
||||||
- To rollback a single step: `make rollback NAME=vulcanize_public`
|
- To rollback a single step: `make rollback NAME=vulcanize_public`
|
||||||
- To rollback to a certain migration: `make rollback_to MIGRATION=n NAME=vulcanize_public`
|
- To rollback to a certain migration: `make rollback_to MIGRATION=n NAME=vulcanize_public`
|
||||||
- To see status of migrations: `make migration_status NAME=vulcanize_public`
|
- To see status of migrations: `make migration_status NAME=vulcanize_public`
|
||||||
|
|
||||||
* See below for configuring additional environments
|
* See below for configuring additional environments
|
||||||
|
|
||||||
In some cases (such as recent Ubuntu systems), it may be necessary to overcome failures of password authentication from localhost. To allow access on Ubuntu, set localhost connections via hostname, ipv4, and ipv6 from peer/md5 to trust in: /etc/postgresql/<version>/pg_hba.conf
|
In some cases (such as recent Ubuntu systems), it may be necessary to overcome failures of password authentication from
|
||||||
|
localhost. To allow access on Ubuntu, set localhost connections via hostname, ipv4, and ipv6 from peer/md5 to trust in: /etc/postgresql/<version>/pg_hba.conf
|
||||||
|
|
||||||
(It should be noted that trusted auth should only be enabled on systems without sensitive data in them: development and local test databases)
|
(It should be noted that trusted auth should only be enabled on systems without sensitive data in them: development and local test databases)
|
||||||
|
|
||||||
@@ -78,8 +86,9 @@ In some cases (such as recent Ubuntu systems), it may be necessary to overcome f
|
|||||||
- The IPC file is called `geth.ipc`.
|
- The IPC file is called `geth.ipc`.
|
||||||
- The geth IPC file path is printed to the console when you start geth.
|
- The geth IPC file path is printed to the console when you start geth.
|
||||||
- The default location is:
|
- The default location is:
|
||||||
- Mac: `<full home path>/Library/Ethereum`
|
- Mac: `<full home path>/Library/Ethereum/geth.ipc`
|
||||||
- Linux: `<full home path>/ethereum/geth.ipc`
|
- Linux: `<full home path>/ethereum/geth.ipc`
|
||||||
|
- Note: the geth.ipc file may not exist until you've started the geth process
|
||||||
|
|
||||||
- For Parity:
|
- For Parity:
|
||||||
- The IPC file is called `jsonrpc.ipc`.
|
- The IPC file is called `jsonrpc.ipc`.
|
||||||
@@ -96,23 +105,35 @@ In some cases (such as recent Ubuntu systems), it may be necessary to overcome f
|
|||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
Usage is broken up into two processes:
|
As mentioned above, VulcanizeDB's processes can be split into three categories: syncing, transforming and exposing data.
|
||||||
|
|
||||||
### Data syncing
|
### Data syncing
|
||||||
To provide data for transformations, raw Ethereum data must first be synced into vDB.
|
To provide data for transformations, raw Ethereum data must first be synced into VulcanizeDB.
|
||||||
This is accomplished through the use of the `lightSync`, `sync`, or `coldImport` commands.
|
This is accomplished through the use of the `headerSync`, `fullSync`, or `coldImport` commands.
|
||||||
These commands are described in detail [here](../staging/documentation/sync.md).
|
These commands are described in detail [here](documentation/data-syncing.md).
|
||||||
|
|
||||||
### Data transformation
|
### Data transformation
|
||||||
Contract watchers use the raw data that has been synced into Postgres to filter out and apply transformations to specific data of interest.
|
Data transformation uses the raw data that has been synced into Postgres to filter out and apply transformations to
|
||||||
|
specific data of interest. Since there are different types of data that may be useful for observing smart contracts, it
|
||||||
|
follows that there are different ways to transform this data. We've started by categorizing this into Generic and
|
||||||
|
Custom transformers:
|
||||||
|
|
||||||
There is a built-in `contractWatcher` command which provides generic transformation of most contract data. This command is described in detail [here](../staging/documentation/contractWatcher.md).
|
- Generic Contract Transformer: Generic contract transformation can be done using a built-in command,
|
||||||
|
`contractWatcher`, which transforms contract events provided the contract's ABI is available. It also
|
||||||
|
provides some state variable coverage by automating polling of public methods, with some restrictions.
|
||||||
|
`contractWatcher` is described further [here](documentation/generic-transformer.md).
|
||||||
|
|
||||||
In many cases a custom transformer or set of transformers will need to be written to provide complete or more comprehensive coverage or to optimize other aspects of the output for a specific end-use.
|
- Custom Transformers: In many cases custom transformers will need to be written to provide
|
||||||
In this case we have provided the `compose`, `execute`, and `composeAndExecute` commands for running custom transformers from external repositories. This is described in detail [here](../staging/documentation/composeAndExecute.md).
|
more comprehensive coverage of contract data. In this case we have provided the `compose`, `execute`, and
|
||||||
|
`composeAndExecute` commands for running custom transformers from external repositories. Documentation on how to write,
|
||||||
|
build and run custom transformers as Go plugins can be found
|
||||||
|
[here](documentation/custom-transformers.md).
|
||||||
|
|
||||||
|
### Exposing the data
|
||||||
|
[Postgraphile](https://www.graphile.org/postgraphile/) is used to expose GraphQL endpoints for our database schemas, this is described in detail [here](documentation/postgraphile.md).
|
||||||
|
|
||||||
|
|
||||||
## Tests
|
### Tests
|
||||||
- Replace the empty `ipcPath` in the `environments/infura.toml` with a path to a full node's eth_jsonrpc endpoint (e.g. local geth node ipc path or infura url)
|
- Replace the empty `ipcPath` in the `environments/infura.toml` with a path to a full node's eth_jsonrpc endpoint (e.g. local geth node ipc path or infura url)
|
||||||
- Note: integration tests require configuration with an archival node
|
- Note: integration tests require configuration with an archival node
|
||||||
- `createdb vulcanize_private` will create the test db
|
- `createdb vulcanize_private` will create the test db
|
||||||
@@ -120,15 +141,13 @@ In this case we have provided the `compose`, `execute`, and `composeAndExecute`
|
|||||||
- `make test` will run the unit tests and skip the integration tests
|
- `make test` will run the unit tests and skip the integration tests
|
||||||
- `make integrationtest` will run just the integration tests
|
- `make integrationtest` will run just the integration tests
|
||||||
|
|
||||||
## API
|
|
||||||
[Postgraphile](https://www.graphile.org/postgraphile/) is used to expose GraphQL endpoints for our database schemas, this is described in detail [here](../staging/postgraphile/README.md).
|
|
||||||
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
Contributions are welcome! For more on this, please see [here](../staging/documentation/contributing.md).
|
Contributions are welcome!
|
||||||
|
|
||||||
Small note: If editing the Readme, please conform to the [standard-readme specification](https://github.com/RichardLitt/standard-readme).
|
VulcanizeDB follows the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/1/4/code-of-conduct).
|
||||||
|
|
||||||
|
For more information on contributing, please see [here](documentation/contributing.md).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
[AGPL-3.0](../staging/LICENSE) © Vulcanize Inc
|
[AGPL-3.0](LICENSE) © Vulcanize Inc
|
||||||
@@ -16,16 +16,20 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"plugin"
|
||||||
|
syn "sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/vulcanize/vulcanizedb/libraries/shared/fetcher"
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/watcher"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/watcher"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/fs"
|
"github.com/vulcanize/vulcanizedb/pkg/fs"
|
||||||
p2 "github.com/vulcanize/vulcanizedb/pkg/plugin"
|
p2 "github.com/vulcanize/vulcanizedb/pkg/plugin"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/plugin/helpers"
|
"github.com/vulcanize/vulcanizedb/pkg/plugin/helpers"
|
||||||
"github.com/vulcanize/vulcanizedb/utils"
|
"github.com/vulcanize/vulcanizedb/utils"
|
||||||
"os"
|
|
||||||
"plugin"
|
|
||||||
syn "sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// composeAndExecuteCmd represents the composeAndExecute command
|
// composeAndExecuteCmd represents the composeAndExecute command
|
||||||
@@ -170,7 +174,8 @@ func composeAndExecute() {
|
|||||||
|
|
||||||
if len(ethStorageInitializers) > 0 {
|
if len(ethStorageInitializers) > 0 {
|
||||||
tailer := fs.FileTailer{Path: storageDiffsPath}
|
tailer := fs.FileTailer{Path: storageDiffsPath}
|
||||||
sw := watcher.NewStorageWatcher(tailer, &db)
|
storageFetcher := fetcher.NewCsvTailStorageFetcher(tailer)
|
||||||
|
sw := watcher.NewStorageWatcher(storageFetcher, &db)
|
||||||
sw.AddTransformers(ethStorageInitializers)
|
sw.AddTransformers(ethStorageInitializers)
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go watchEthStorage(&sw, &wg)
|
go watchEthStorage(&sw, &wg)
|
||||||
@@ -187,5 +192,6 @@ func composeAndExecute() {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(composeAndExecuteCmd)
|
rootCmd.AddCommand(composeAndExecuteCmd)
|
||||||
composeAndExecuteCmd.Flags().BoolVar(&recheckHeadersArg, "recheckHeaders", false, "checks headers that are already checked for each transformer.")
|
composeAndExecuteCmd.Flags().BoolVarP(&recheckHeadersArg, "recheck-headers", "r", false, "whether to re-check headers for watched events")
|
||||||
|
composeAndExecuteCmd.Flags().DurationVarP(&queueRecheckInterval, "queue-recheck-interval", "q", 5*time.Minute, "interval duration for rechecking queued storage diffs (ex: 5m30s)")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import (
|
|||||||
|
|
||||||
st "github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
st "github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
||||||
ft "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/full/transformer"
|
ft "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/full/transformer"
|
||||||
lt "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/light/transformer"
|
ht "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/transformer"
|
||||||
"github.com/vulcanize/vulcanizedb/utils"
|
"github.com/vulcanize/vulcanizedb/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -98,8 +98,8 @@ func contractWatcher() {
|
|||||||
con := config.ContractConfig{}
|
con := config.ContractConfig{}
|
||||||
con.PrepConfig()
|
con.PrepConfig()
|
||||||
switch mode {
|
switch mode {
|
||||||
case "light":
|
case "header":
|
||||||
t = lt.NewTransformer(con, blockChain, &db)
|
t = ht.NewTransformer(con, blockChain, &db)
|
||||||
case "full":
|
case "full":
|
||||||
t = ft.NewTransformer(con, blockChain, &db)
|
t = ft.NewTransformer(con, blockChain, &db)
|
||||||
default:
|
default:
|
||||||
@@ -121,5 +121,5 @@ func contractWatcher() {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(contractWatcherCmd)
|
rootCmd.AddCommand(contractWatcherCmd)
|
||||||
contractWatcherCmd.Flags().StringVarP(&mode, "mode", "o", "light", "'light' or 'full' mode to work with either light synced or fully synced vDB (default is light)")
|
contractWatcherCmd.Flags().StringVarP(&mode, "mode", "o", "header", "'header' or 'full' mode to work with either header synced or fully synced vDB (default is header)")
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-3
@@ -26,6 +26,8 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/constants"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/constants"
|
||||||
|
"github.com/vulcanize/vulcanizedb/libraries/shared/fetcher"
|
||||||
|
storageUtils "github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/watcher"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/watcher"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/fs"
|
"github.com/vulcanize/vulcanizedb/pkg/fs"
|
||||||
@@ -118,7 +120,8 @@ func execute() {
|
|||||||
|
|
||||||
if len(ethStorageInitializers) > 0 {
|
if len(ethStorageInitializers) > 0 {
|
||||||
tailer := fs.FileTailer{Path: storageDiffsPath}
|
tailer := fs.FileTailer{Path: storageDiffsPath}
|
||||||
sw := watcher.NewStorageWatcher(tailer, &db)
|
storageFetcher := fetcher.NewCsvTailStorageFetcher(tailer)
|
||||||
|
sw := watcher.NewStorageWatcher(storageFetcher, &db)
|
||||||
sw.AddTransformers(ethStorageInitializers)
|
sw.AddTransformers(ethStorageInitializers)
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go watchEthStorage(&sw, &wg)
|
go watchEthStorage(&sw, &wg)
|
||||||
@@ -135,7 +138,8 @@ func execute() {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(executeCmd)
|
rootCmd.AddCommand(executeCmd)
|
||||||
executeCmd.Flags().BoolVar(&recheckHeadersArg, "recheckHeaders", false, "checks headers that are already checked for each transformer.")
|
executeCmd.Flags().BoolVarP(&recheckHeadersArg, "recheck-headers", "r", false, "whether to re-check headers for watched events")
|
||||||
|
executeCmd.Flags().DurationVarP(&queueRecheckInterval, "queue-recheck-interval", "q", 5*time.Minute, "interval duration for rechecking queued storage diffs (ex: 5m30s)")
|
||||||
}
|
}
|
||||||
|
|
||||||
type Exporter interface {
|
type Exporter interface {
|
||||||
@@ -166,7 +170,9 @@ func watchEthStorage(w *watcher.StorageWatcher, wg *syn.WaitGroup) {
|
|||||||
ticker := time.NewTicker(pollingInterval)
|
ticker := time.NewTicker(pollingInterval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
w.Execute()
|
errs := make(chan error)
|
||||||
|
rows := make(chan storageUtils.StorageDiffRow)
|
||||||
|
w.Execute(rows, errs, queueRecheckInterval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,14 +29,14 @@ import (
|
|||||||
"github.com/vulcanize/vulcanizedb/utils"
|
"github.com/vulcanize/vulcanizedb/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// syncCmd represents the sync command
|
// fullSyncCmd represents the fullSync command
|
||||||
var syncCmd = &cobra.Command{
|
var fullSyncCmd = &cobra.Command{
|
||||||
Use: "sync",
|
Use: "fullSync",
|
||||||
Short: "Syncs VulcanizeDB with local ethereum node",
|
Short: "Syncs VulcanizeDB with local ethereum node",
|
||||||
Long: `Syncs VulcanizeDB with local ethereum node. Populates
|
Long: `Syncs VulcanizeDB with local ethereum node. Populates
|
||||||
Postgres with blocks, transactions, receipts, and logs.
|
Postgres with blocks, transactions, receipts, and logs.
|
||||||
|
|
||||||
./vulcanizedb sync --starting-block-number 0 --config public.toml
|
./vulcanizedb fullSync --starting-block-number 0 --config public.toml
|
||||||
|
|
||||||
Expects ethereum node to be running and requires a .toml config:
|
Expects ethereum node to be running and requires a .toml config:
|
||||||
|
|
||||||
@@ -49,14 +49,14 @@ Expects ethereum node to be running and requires a .toml config:
|
|||||||
ipcPath = "/Users/user/Library/Ethereum/geth.ipc"
|
ipcPath = "/Users/user/Library/Ethereum/geth.ipc"
|
||||||
`,
|
`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
sync()
|
fullSync()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(syncCmd)
|
rootCmd.AddCommand(fullSyncCmd)
|
||||||
|
|
||||||
syncCmd.Flags().Int64VarP(&startingBlockNumber, "starting-block-number", "s", 0, "Block number to start syncing from")
|
fullSyncCmd.Flags().Int64VarP(&startingBlockNumber, "starting-block-number", "s", 0, "Block number to start syncing from")
|
||||||
}
|
}
|
||||||
|
|
||||||
func backFillAllBlocks(blockchain core.BlockChain, blockRepository datastore.BlockRepository, missingBlocksPopulated chan int, startingBlockNumber int64) {
|
func backFillAllBlocks(blockchain core.BlockChain, blockRepository datastore.BlockRepository, missingBlocksPopulated chan int, startingBlockNumber int64) {
|
||||||
@@ -67,20 +67,20 @@ func backFillAllBlocks(blockchain core.BlockChain, blockRepository datastore.Blo
|
|||||||
missingBlocksPopulated <- populated
|
missingBlocksPopulated <- populated
|
||||||
}
|
}
|
||||||
|
|
||||||
func sync() {
|
func fullSync() {
|
||||||
ticker := time.NewTicker(pollingInterval)
|
ticker := time.NewTicker(pollingInterval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
blockChain := getBlockChain()
|
blockChain := getBlockChain()
|
||||||
lastBlock, err := blockChain.LastBlock()
|
lastBlock, err := blockChain.LastBlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("sync: Error getting last block: ", err)
|
log.Error("fullSync: Error getting last block: ", err)
|
||||||
}
|
}
|
||||||
if lastBlock.Int64() == 0 {
|
if lastBlock.Int64() == 0 {
|
||||||
log.Fatal("geth initial: state sync not finished")
|
log.Fatal("geth initial: state sync not finished")
|
||||||
}
|
}
|
||||||
if startingBlockNumber > lastBlock.Int64() {
|
if startingBlockNumber > lastBlock.Int64() {
|
||||||
log.Fatal("sync: starting block number > current block number")
|
log.Fatal("fullSync: starting block number > current block number")
|
||||||
}
|
}
|
||||||
|
|
||||||
db := utils.LoadPostgres(databaseConfig, blockChain.Node())
|
db := utils.LoadPostgres(databaseConfig, blockChain.Node())
|
||||||
@@ -94,7 +94,7 @@ func sync() {
|
|||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
window, err := validator.ValidateBlocks()
|
window, err := validator.ValidateBlocks()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("sync: error in validateBlocks: ", err)
|
log.Error("fullSync: error in validateBlocks: ", err)
|
||||||
}
|
}
|
||||||
log.Info(window.GetString())
|
log.Info(window.GetString())
|
||||||
case <-missingBlocksPopulated:
|
case <-missingBlocksPopulated:
|
||||||
@@ -30,14 +30,14 @@ import (
|
|||||||
"github.com/vulcanize/vulcanizedb/utils"
|
"github.com/vulcanize/vulcanizedb/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// lightSyncCmd represents the lightSync command
|
// headerSyncCmd represents the headerSync command
|
||||||
var lightSyncCmd = &cobra.Command{
|
var headerSyncCmd = &cobra.Command{
|
||||||
Use: "lightSync",
|
Use: "headerSync",
|
||||||
Short: "Syncs VulcanizeDB with local ethereum node's block headers",
|
Short: "Syncs VulcanizeDB with local ethereum node's block headers",
|
||||||
Long: `Syncs VulcanizeDB with local ethereum node. Populates
|
Long: `Syncs VulcanizeDB with local ethereum node. Populates
|
||||||
Postgres with block headers.
|
Postgres with block headers.
|
||||||
|
|
||||||
./vulcanizedb lightSync --starting-block-number 0 --config public.toml
|
./vulcanizedb headerSync --starting-block-number 0 --config public.toml
|
||||||
|
|
||||||
Expects ethereum node to be running and requires a .toml config:
|
Expects ethereum node to be running and requires a .toml config:
|
||||||
|
|
||||||
@@ -50,13 +50,13 @@ Expects ethereum node to be running and requires a .toml config:
|
|||||||
ipcPath = "/Users/user/Library/Ethereum/geth.ipc"
|
ipcPath = "/Users/user/Library/Ethereum/geth.ipc"
|
||||||
`,
|
`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
lightSync()
|
headerSync()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(lightSyncCmd)
|
rootCmd.AddCommand(headerSyncCmd)
|
||||||
lightSyncCmd.Flags().Int64VarP(&startingBlockNumber, "starting-block-number", "s", 0, "Block number to start syncing from")
|
headerSyncCmd.Flags().Int64VarP(&startingBlockNumber, "starting-block-number", "s", 0, "Block number to start syncing from")
|
||||||
}
|
}
|
||||||
|
|
||||||
func backFillAllHeaders(blockchain core.BlockChain, headerRepository datastore.HeaderRepository, missingBlocksPopulated chan int, startingBlockNumber int64) {
|
func backFillAllHeaders(blockchain core.BlockChain, headerRepository datastore.HeaderRepository, missingBlocksPopulated chan int, startingBlockNumber int64) {
|
||||||
@@ -69,7 +69,7 @@ func backFillAllHeaders(blockchain core.BlockChain, headerRepository datastore.H
|
|||||||
missingBlocksPopulated <- populated
|
missingBlocksPopulated <- populated
|
||||||
}
|
}
|
||||||
|
|
||||||
func lightSync() {
|
func headerSync() {
|
||||||
ticker := time.NewTicker(pollingInterval)
|
ticker := time.NewTicker(pollingInterval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
blockChain := getBlockChain()
|
blockChain := getBlockChain()
|
||||||
@@ -86,7 +86,7 @@ func lightSync() {
|
|||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
window, err := validator.ValidateHeaders()
|
window, err := validator.ValidateHeaders()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("lightSync: ValidateHeaders failed: ", err)
|
log.Error("headerSync: ValidateHeaders failed: ", err)
|
||||||
}
|
}
|
||||||
log.Info(window.GetString())
|
log.Info(window.GetString())
|
||||||
case n := <-missingBlocksPopulated:
|
case n := <-missingBlocksPopulated:
|
||||||
+11
-10
@@ -36,16 +36,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cfgFile string
|
cfgFile string
|
||||||
databaseConfig config.Database
|
databaseConfig config.Database
|
||||||
genConfig config.Plugin
|
genConfig config.Plugin
|
||||||
ipc string
|
ipc string
|
||||||
levelDbPath string
|
levelDbPath string
|
||||||
startingBlockNumber int64
|
queueRecheckInterval time.Duration
|
||||||
storageDiffsPath string
|
startingBlockNumber int64
|
||||||
syncAll bool
|
storageDiffsPath string
|
||||||
endingBlockNumber int64
|
syncAll bool
|
||||||
recheckHeadersArg bool
|
endingBlockNumber int64
|
||||||
|
recheckHeadersArg bool
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ CREATE TABLE public.queued_storage (
|
|||||||
block_hash BYTEA,
|
block_hash BYTEA,
|
||||||
contract BYTEA,
|
contract BYTEA,
|
||||||
storage_key BYTEA,
|
storage_key BYTEA,
|
||||||
storage_value BYTEA
|
storage_value BYTEA,
|
||||||
|
UNIQUE (block_height, block_hash, contract, storage_key, storage_value)
|
||||||
);
|
);
|
||||||
|
|
||||||
-- +goose Down
|
-- +goose Down
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
-- +goose Up
|
-- +goose Up
|
||||||
CREATE TABLE light_sync_transactions (
|
CREATE TABLE header_sync_transactions (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
header_id INTEGER NOT NULL REFERENCES headers(id) ON DELETE CASCADE,
|
header_id INTEGER NOT NULL REFERENCES headers(id) ON DELETE CASCADE,
|
||||||
hash VARCHAR(66),
|
hash VARCHAR(66),
|
||||||
@@ -16,4 +16,4 @@ CREATE TABLE light_sync_transactions (
|
|||||||
);
|
);
|
||||||
|
|
||||||
-- +goose Down
|
-- +goose Down
|
||||||
DROP TABLE light_sync_transactions;
|
DROP TABLE header_sync_transactions;
|
||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
-- +goose Up
|
-- +goose Up
|
||||||
CREATE TABLE light_sync_receipts(
|
CREATE TABLE header_sync_receipts(
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
transaction_id INTEGER NOT NULL REFERENCES light_sync_transactions(id) ON DELETE CASCADE,
|
transaction_id INTEGER NOT NULL REFERENCES header_sync_transactions(id) ON DELETE CASCADE,
|
||||||
header_id INTEGER NOT NULL REFERENCES headers(id) ON DELETE CASCADE,
|
header_id INTEGER NOT NULL REFERENCES headers(id) ON DELETE CASCADE,
|
||||||
contract_address VARCHAR(42),
|
contract_address VARCHAR(42),
|
||||||
cumulative_gas_used NUMERIC,
|
cumulative_gas_used NUMERIC,
|
||||||
@@ -15,4 +15,4 @@ CREATE TABLE light_sync_receipts(
|
|||||||
|
|
||||||
|
|
||||||
-- +goose Down
|
-- +goose Down
|
||||||
DROP TABLE light_sync_receipts;
|
DROP TABLE header_sync_receipts;
|
||||||
+159
-164
@@ -2,8 +2,8 @@
|
|||||||
-- PostgreSQL database dump
|
-- PostgreSQL database dump
|
||||||
--
|
--
|
||||||
|
|
||||||
-- Dumped from database version 10.6
|
-- Dumped from database version 11.3
|
||||||
-- Dumped by pg_dump version 10.6
|
-- Dumped by pg_dump version 11.3
|
||||||
|
|
||||||
SET statement_timeout = 0;
|
SET statement_timeout = 0;
|
||||||
SET lock_timeout = 0;
|
SET lock_timeout = 0;
|
||||||
@@ -12,23 +12,10 @@ SET client_encoding = 'UTF8';
|
|||||||
SET standard_conforming_strings = on;
|
SET standard_conforming_strings = on;
|
||||||
SELECT pg_catalog.set_config('search_path', '', false);
|
SELECT pg_catalog.set_config('search_path', '', false);
|
||||||
SET check_function_bodies = false;
|
SET check_function_bodies = false;
|
||||||
|
SET xmloption = content;
|
||||||
SET client_min_messages = warning;
|
SET client_min_messages = warning;
|
||||||
SET row_security = off;
|
SET row_security = off;
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
|
|
||||||
|
|
||||||
|
|
||||||
SET default_tablespace = '';
|
SET default_tablespace = '';
|
||||||
|
|
||||||
SET default_with_oids = false;
|
SET default_with_oids = false;
|
||||||
@@ -259,6 +246,84 @@ CREATE SEQUENCE public.goose_db_version_id_seq
|
|||||||
ALTER SEQUENCE public.goose_db_version_id_seq OWNED BY public.goose_db_version.id;
|
ALTER SEQUENCE public.goose_db_version_id_seq OWNED BY public.goose_db_version.id;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: header_sync_receipts; Type: TABLE; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE public.header_sync_receipts (
|
||||||
|
id integer NOT NULL,
|
||||||
|
transaction_id integer NOT NULL,
|
||||||
|
header_id integer NOT NULL,
|
||||||
|
contract_address character varying(42),
|
||||||
|
cumulative_gas_used numeric,
|
||||||
|
gas_used numeric,
|
||||||
|
state_root character varying(66),
|
||||||
|
status integer,
|
||||||
|
tx_hash character varying(66),
|
||||||
|
rlp bytea
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: header_sync_receipts_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE SEQUENCE public.header_sync_receipts_id_seq
|
||||||
|
AS integer
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: header_sync_receipts_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER SEQUENCE public.header_sync_receipts_id_seq OWNED BY public.header_sync_receipts.id;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: header_sync_transactions; Type: TABLE; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE public.header_sync_transactions (
|
||||||
|
id integer NOT NULL,
|
||||||
|
header_id integer NOT NULL,
|
||||||
|
hash character varying(66),
|
||||||
|
gas_limit numeric,
|
||||||
|
gas_price numeric,
|
||||||
|
input_data bytea,
|
||||||
|
nonce numeric,
|
||||||
|
raw bytea,
|
||||||
|
tx_from character varying(44),
|
||||||
|
tx_index integer,
|
||||||
|
tx_to character varying(44),
|
||||||
|
value numeric
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: header_sync_transactions_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE SEQUENCE public.header_sync_transactions_id_seq
|
||||||
|
AS integer
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: header_sync_transactions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER SEQUENCE public.header_sync_transactions_id_seq OWNED BY public.header_sync_transactions.id;
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: headers; Type: TABLE; Schema: public; Owner: -
|
-- Name: headers; Type: TABLE; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@@ -294,84 +359,6 @@ CREATE SEQUENCE public.headers_id_seq
|
|||||||
ALTER SEQUENCE public.headers_id_seq OWNED BY public.headers.id;
|
ALTER SEQUENCE public.headers_id_seq OWNED BY public.headers.id;
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: light_sync_receipts; Type: TABLE; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE public.light_sync_receipts (
|
|
||||||
id integer NOT NULL,
|
|
||||||
transaction_id integer NOT NULL,
|
|
||||||
header_id integer NOT NULL,
|
|
||||||
contract_address character varying(42),
|
|
||||||
cumulative_gas_used numeric,
|
|
||||||
gas_used numeric,
|
|
||||||
state_root character varying(66),
|
|
||||||
status integer,
|
|
||||||
tx_hash character varying(66),
|
|
||||||
rlp bytea
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: light_sync_receipts_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE SEQUENCE public.light_sync_receipts_id_seq
|
|
||||||
AS integer
|
|
||||||
START WITH 1
|
|
||||||
INCREMENT BY 1
|
|
||||||
NO MINVALUE
|
|
||||||
NO MAXVALUE
|
|
||||||
CACHE 1;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: light_sync_receipts_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER SEQUENCE public.light_sync_receipts_id_seq OWNED BY public.light_sync_receipts.id;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: light_sync_transactions; Type: TABLE; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE public.light_sync_transactions (
|
|
||||||
id integer NOT NULL,
|
|
||||||
header_id integer NOT NULL,
|
|
||||||
hash character varying(66),
|
|
||||||
gas_limit numeric,
|
|
||||||
gas_price numeric,
|
|
||||||
input_data bytea,
|
|
||||||
nonce numeric,
|
|
||||||
raw bytea,
|
|
||||||
tx_from character varying(44),
|
|
||||||
tx_index integer,
|
|
||||||
tx_to character varying(44),
|
|
||||||
value numeric
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: light_sync_transactions_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE SEQUENCE public.light_sync_transactions_id_seq
|
|
||||||
AS integer
|
|
||||||
START WITH 1
|
|
||||||
INCREMENT BY 1
|
|
||||||
NO MINVALUE
|
|
||||||
NO MAXVALUE
|
|
||||||
CACHE 1;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: light_sync_transactions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER SEQUENCE public.light_sync_transactions_id_seq OWNED BY public.light_sync_transactions.id;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: log_filters; Type: TABLE; Schema: public; Owner: -
|
-- Name: log_filters; Type: TABLE; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@@ -619,6 +606,20 @@ ALTER TABLE ONLY public.full_sync_transactions ALTER COLUMN id SET DEFAULT nextv
|
|||||||
ALTER TABLE ONLY public.goose_db_version ALTER COLUMN id SET DEFAULT nextval('public.goose_db_version_id_seq'::regclass);
|
ALTER TABLE ONLY public.goose_db_version ALTER COLUMN id SET DEFAULT nextval('public.goose_db_version_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: header_sync_receipts id; Type: DEFAULT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.header_sync_receipts ALTER COLUMN id SET DEFAULT nextval('public.header_sync_receipts_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: header_sync_transactions id; Type: DEFAULT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.header_sync_transactions ALTER COLUMN id SET DEFAULT nextval('public.header_sync_transactions_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: headers id; Type: DEFAULT; Schema: public; Owner: -
|
-- Name: headers id; Type: DEFAULT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@@ -626,20 +627,6 @@ ALTER TABLE ONLY public.goose_db_version ALTER COLUMN id SET DEFAULT nextval('pu
|
|||||||
ALTER TABLE ONLY public.headers ALTER COLUMN id SET DEFAULT nextval('public.headers_id_seq'::regclass);
|
ALTER TABLE ONLY public.headers ALTER COLUMN id SET DEFAULT nextval('public.headers_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: light_sync_receipts id; Type: DEFAULT; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.light_sync_receipts ALTER COLUMN id SET DEFAULT nextval('public.light_sync_receipts_id_seq'::regclass);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: light_sync_transactions id; Type: DEFAULT; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.light_sync_transactions ALTER COLUMN id SET DEFAULT nextval('public.light_sync_transactions_id_seq'::regclass);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: log_filters id; Type: DEFAULT; Schema: public; Owner: -
|
-- Name: log_filters id; Type: DEFAULT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@@ -739,6 +726,38 @@ ALTER TABLE ONLY public.goose_db_version
|
|||||||
ADD CONSTRAINT goose_db_version_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT goose_db_version_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: header_sync_receipts header_sync_receipts_header_id_transaction_id_key; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.header_sync_receipts
|
||||||
|
ADD CONSTRAINT header_sync_receipts_header_id_transaction_id_key UNIQUE (header_id, transaction_id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: header_sync_receipts header_sync_receipts_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.header_sync_receipts
|
||||||
|
ADD CONSTRAINT header_sync_receipts_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: header_sync_transactions header_sync_transactions_header_id_hash_key; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.header_sync_transactions
|
||||||
|
ADD CONSTRAINT header_sync_transactions_header_id_hash_key UNIQUE (header_id, hash);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: header_sync_transactions header_sync_transactions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.header_sync_transactions
|
||||||
|
ADD CONSTRAINT header_sync_transactions_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: headers headers_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
-- Name: headers headers_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@@ -747,38 +766,6 @@ ALTER TABLE ONLY public.headers
|
|||||||
ADD CONSTRAINT headers_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT headers_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: light_sync_receipts light_sync_receipts_header_id_transaction_id_key; Type: CONSTRAINT; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.light_sync_receipts
|
|
||||||
ADD CONSTRAINT light_sync_receipts_header_id_transaction_id_key UNIQUE (header_id, transaction_id);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: light_sync_receipts light_sync_receipts_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.light_sync_receipts
|
|
||||||
ADD CONSTRAINT light_sync_receipts_pkey PRIMARY KEY (id);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: light_sync_transactions light_sync_transactions_header_id_hash_key; Type: CONSTRAINT; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.light_sync_transactions
|
|
||||||
ADD CONSTRAINT light_sync_transactions_header_id_hash_key UNIQUE (header_id, hash);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: light_sync_transactions light_sync_transactions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.light_sync_transactions
|
|
||||||
ADD CONSTRAINT light_sync_transactions_pkey PRIMARY KEY (id);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: logs logs_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
-- Name: logs logs_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@@ -803,6 +790,14 @@ ALTER TABLE ONLY public.eth_nodes
|
|||||||
ADD CONSTRAINT nodes_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT nodes_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: queued_storage queued_storage_block_height_block_hash_contract_storage_key_key; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.queued_storage
|
||||||
|
ADD CONSTRAINT queued_storage_block_height_block_hash_contract_storage_key_key UNIQUE (block_height, block_hash, contract, storage_key, storage_value);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: queued_storage queued_storage_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
-- Name: queued_storage queued_storage_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@@ -909,6 +904,30 @@ ALTER TABLE ONLY public.full_sync_transactions
|
|||||||
ADD CONSTRAINT full_sync_transactions_block_id_fkey FOREIGN KEY (block_id) REFERENCES public.blocks(id) ON DELETE CASCADE;
|
ADD CONSTRAINT full_sync_transactions_block_id_fkey FOREIGN KEY (block_id) REFERENCES public.blocks(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: header_sync_receipts header_sync_receipts_header_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.header_sync_receipts
|
||||||
|
ADD CONSTRAINT header_sync_receipts_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: header_sync_receipts header_sync_receipts_transaction_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.header_sync_receipts
|
||||||
|
ADD CONSTRAINT header_sync_receipts_transaction_id_fkey FOREIGN KEY (transaction_id) REFERENCES public.header_sync_transactions(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: header_sync_transactions header_sync_transactions_header_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.header_sync_transactions
|
||||||
|
ADD CONSTRAINT header_sync_transactions_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: headers headers_eth_node_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
-- Name: headers headers_eth_node_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@@ -917,30 +936,6 @@ ALTER TABLE ONLY public.headers
|
|||||||
ADD CONSTRAINT headers_eth_node_id_fkey FOREIGN KEY (eth_node_id) REFERENCES public.eth_nodes(id) ON DELETE CASCADE;
|
ADD CONSTRAINT headers_eth_node_id_fkey FOREIGN KEY (eth_node_id) REFERENCES public.eth_nodes(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: light_sync_receipts light_sync_receipts_header_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.light_sync_receipts
|
|
||||||
ADD CONSTRAINT light_sync_receipts_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: light_sync_receipts light_sync_receipts_transaction_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.light_sync_receipts
|
|
||||||
ADD CONSTRAINT light_sync_receipts_transaction_id_fkey FOREIGN KEY (transaction_id) REFERENCES public.light_sync_transactions(id) ON DELETE CASCADE;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: light_sync_transactions light_sync_transactions_header_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.light_sync_transactions
|
|
||||||
ADD CONSTRAINT light_sync_transactions_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: blocks node_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
|
-- Name: blocks node_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ S
|
|||||||
`Dockerfile` will build an alpine image containing:
|
`Dockerfile` will build an alpine image containing:
|
||||||
- vDB as a binary with runtime deps statically linked: `/app/vulcanizedb`
|
- vDB as a binary with runtime deps statically linked: `/app/vulcanizedb`
|
||||||
- The migration tool goose: `/app/goose`
|
- The migration tool goose: `/app/goose`
|
||||||
- Two services for running `lightSync` and `continuousLogSync`, started with the default configuration `environments/staging.toml`.
|
- Two services for running `headerSync` and `continuousLogSync`, started with the default configuration `environments/staging.toml`.
|
||||||
|
|
||||||
By default, vDB is configured towards the Kovan deploy. The configuration values can be overridden using environment variables, using the same hierarchical naming pattern but in CAPS and using underscores. For example, the contract address for the `Pit` can be set with the variable `CONTRACT_ADDRESS_PIT="0x123..."`.
|
By default, vDB is configured towards the Kovan deploy. The configuration values can be overridden using environment variables, using the same hierarchical naming pattern but in CAPS and using underscores. For example, the contract address for the `Pit` can be set with the variable `CONTRACT_ADDRESS_PIT="0x123..."`.
|
||||||
|
|
||||||
@@ -18,8 +18,8 @@ e`
|
|||||||
* `CONTRACT_ADDRESS_[CONTRACT NAME]=0x123...`
|
* `CONTRACT_ADDRESS_[CONTRACT NAME]=0x123...`
|
||||||
* `CONTRACT_ABI_[CONTRACT NAME]="ABI STRING"`
|
* `CONTRACT_ABI_[CONTRACT NAME]="ABI STRING"`
|
||||||
* `CONTRACT_DEPLOYMENT-BLOCK_[CONTRACT NAME]=0` (doesn't really matter on a short chain, just avoids long unnecessary searching)
|
* `CONTRACT_DEPLOYMENT-BLOCK_[CONTRACT NAME]=0` (doesn't really matter on a short chain, just avoids long unnecessary searching)
|
||||||
6. Start the `lightSync` and `continuousLogSync` services:
|
6. Start the `headerSync` and `continuousLogSync` services:
|
||||||
* `./vulcanizedb lightSync --config environments/staging.toml`
|
* `./vulcanizedb headerSync --config environments/staging.toml`
|
||||||
* `./vulcanizedb continuousLogSync --config environments/staging.toml`
|
* `./vulcanizedb continuousLogSync --config environments/staging.toml`
|
||||||
|
|
||||||
### Automated
|
### Automated
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Runs the migrations and starts the lightSync and continuousLogSync services
|
# Runs the migrations and starts the headerSync and continuousLogSync services
|
||||||
|
|
||||||
# Exit if the variable tests fail
|
# Exit if the variable tests fail
|
||||||
set -e
|
set -e
|
||||||
@@ -21,7 +21,7 @@ set +e
|
|||||||
./goose postgres "$CONNECT_STRING" up
|
./goose postgres "$CONNECT_STRING" up
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
# Fire up the services
|
# Fire up the services
|
||||||
./vulcanizedb lightSync --config environments/staging.toml &
|
./vulcanizedb headerSync --config environments/staging.toml &
|
||||||
./vulcanizedb continuousLogSync --config environments/staging.toml &
|
./vulcanizedb continuousLogSync --config environments/staging.toml &
|
||||||
else
|
else
|
||||||
echo "Could not run migrations. Are the database details correct?"
|
echo "Could not run migrations. Are the database details correct?"
|
||||||
|
|||||||
@@ -1,11 +1,50 @@
|
|||||||
# Contribution guidelines
|
# Contribution guidelines
|
||||||
|
|
||||||
Contributions are welcome! In addition to core contributions, developers are encouraged to build their own custom transformers which
|
Contributions are welcome! Please open an Issues or Pull Request for any changes.
|
||||||
can be run together with other custom transformers using the [composeAndExeucte](../../staging/documentation/composeAndExecute.md) command.
|
|
||||||
|
In addition to core contributions, developers are encouraged to build their own custom transformers which
|
||||||
|
can be run together with other custom transformers using the [composeAndExecute](../../staging/documentation/custom-transformers.md) command.
|
||||||
|
|
||||||
|
## Pull Requests
|
||||||
|
- `go fmt` is run as part of `make test` and `make integrationtest`, please make sure to check in the format changes.
|
||||||
|
- Ensure that new code is well tested, including integration testing if applicable.
|
||||||
|
- Make sure the build is passing.
|
||||||
|
- Update the README or any [documentation files](./) as necessary. If editing the Readme, please
|
||||||
|
conform to the
|
||||||
|
[standard-readme specification](https://github.com/RichardLitt/standard-readme).
|
||||||
|
- Once a Pull Request has received two approvals it can be merged in by a core developer.
|
||||||
|
|
||||||
## Creating a new migration file
|
## Creating a new migration file
|
||||||
1. `make new_migration NAME=add_columnA_to_table1`
|
1. `make new_migration NAME=add_columnA_to_table1`
|
||||||
- This will create a new timestamped migration file in `db/migrations`
|
- This will create a new timestamped migration file in `db/migrations`
|
||||||
1. Write the migration code in the created file, under the respective `goose` pragma
|
1. Write the migration code in the created file, under the respective `goose` pragma
|
||||||
- Goose automatically runs each migration in a transaction; don't add `BEGIN` and `COMMIT` statements.
|
- Goose automatically runs each migration in a transaction; don't add `BEGIN` and `COMMIT` statements.
|
||||||
1. Core migrations should be committed in their `goose fix`ed form.
|
1. Core migrations should be committed in their `goose fix`ed form. To do this, run `make version_migrations` which
|
||||||
|
converts timestamped migrations to migrations versioned by an incremented integer.
|
||||||
|
|
||||||
|
## Diagrams
|
||||||
|
- Diagrams were created with [draw.io](https://www.draw.io).
|
||||||
|
- To update a diagram:
|
||||||
|
1. Go to [draw.io](https://www.draw.io).
|
||||||
|
1. Click on *File > Open from* and choose the location of the diagram you want to update.
|
||||||
|
1. Once open in draw.io, you may update it.
|
||||||
|
1. Export the diagram to this repository's directory and commit it.
|
||||||
|
|
||||||
|
|
||||||
|
## Generating the Changelog
|
||||||
|
We use [github-changelog-generator](https://github.com/github-changelog-generator/github-changelog-generator) to generate release Changelogs. To be consistent with previous Changelogs, the following flags should be passed to the command:
|
||||||
|
|
||||||
|
```
|
||||||
|
--user vulcanize
|
||||||
|
--project vulcanizedb
|
||||||
|
--token {YOUR_GITHUB_TOKEN}
|
||||||
|
--no-issues
|
||||||
|
--usernames-as-github-logins
|
||||||
|
--since-tag {PREVIOUS_RELEASE_TAG}
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information on why your github token is needed, and how to generate it see [https://github
|
||||||
|
.com/github-changelog-generator/github-changelog-generator#github-token](https://github.com/github-changelog-generator/github-changelog-generator#github-token).
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
VulcanizeDB follows the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/1/4/code-of-conduct).
|
||||||
|
|||||||
@@ -1,26 +1,30 @@
|
|||||||
# composeAndExecute
|
# Custom Transformers
|
||||||
The `composeAndExecute` command is used to compose and execute over an arbitrary set of custom transformers.
|
When the capabilities of the generic `contractWatcher` are not sufficient, custom transformers tailored to a specific
|
||||||
This is accomplished by generating a Go pluggin which allows the `vulcanizedb` binary to link to external transformers, so
|
purpose can be leveraged.
|
||||||
long as they abide by one of the standard [interfaces](../staging/libraries/shared/transformer).
|
|
||||||
|
|
||||||
This command requires Go 1.11+ and [Go plugins](https://golang.org/pkg/plugin/) only work on Unix-based systems.
|
Individual custom transformers can be composed together from any number of external repositories and executed as a
|
||||||
|
single process using the `compose` and `execute` commands or the `composeAndExecute` command. This is accomplished by
|
||||||
|
generating a Go plugin which allows the `vulcanizedb` binary to link to the external transformers, so long as they
|
||||||
|
abide by one of the standard [interfaces](../staging/libraries/shared/transformer).
|
||||||
|
|
||||||
## Writing custom transformers
|
## Writing custom transformers
|
||||||
Storage Transformers
|
For help with writing different types of custom transformers please see below:
|
||||||
|
|
||||||
|
Storage Transformers: transform data derived from contract storage tries
|
||||||
* [Guide](../../staging/libraries/shared/factories/storage/README.md)
|
* [Guide](../../staging/libraries/shared/factories/storage/README.md)
|
||||||
* [Example](../../staging/libraries/shared/factories/storage/EXAMPLE.md)
|
* [Example](../../staging/libraries/shared/factories/storage/EXAMPLE.md)
|
||||||
|
|
||||||
Event Transformers
|
Event Transformers: transform data derived from Ethereum log events
|
||||||
* [Guide](../../staging/libraries/shared/factories/event/README.md)
|
* [Guide](../../staging/libraries/shared/factories/event/README.md)
|
||||||
* [Example 1](https://github.com/vulcanize/ens_transformers/tree/master/transformers/registar)
|
* [Example 1](https://github.com/vulcanize/ens_transformers/tree/master/transformers/registar)
|
||||||
* [Example 2](https://github.com/vulcanize/ens_transformers/tree/master/transformers/registry)
|
* [Example 2](https://github.com/vulcanize/ens_transformers/tree/master/transformers/registry)
|
||||||
* [Example 3](https://github.com/vulcanize/ens_transformers/tree/master/transformers/resolver)
|
* [Example 3](https://github.com/vulcanize/ens_transformers/tree/master/transformers/resolver)
|
||||||
|
|
||||||
Contract Transformers
|
Contract Transformers: transform data derived from Ethereum log events and use it to poll public contract methods
|
||||||
* [Example 1](https://github.com/vulcanize/account_transformers)
|
* [Example 1](https://github.com/vulcanize/account_transformers)
|
||||||
* [Example 2](https://github.com/vulcanize/ens_transformers/tree/master/transformers/domain_records)
|
* [Example 2](https://github.com/vulcanize/ens_transformers/tree/master/transformers/domain_records)
|
||||||
|
|
||||||
## Preparing transformers to work as a plugin for composeAndExecute
|
## Preparing custom transformers to work as part of a plugin
|
||||||
To plug in an external transformer we need to:
|
To plug in an external transformer we need to:
|
||||||
|
|
||||||
1. Create a package that exports a variable `TransformerInitializer`, `StorageTransformerInitializer`, or `ContractTransformerInitializer` that are of type [TransformerInitializer](../staging/libraries/shared/transformer/event_transformer.go#L33)
|
1. Create a package that exports a variable `TransformerInitializer`, `StorageTransformerInitializer`, or `ContractTransformerInitializer` that are of type [TransformerInitializer](../staging/libraries/shared/transformer/event_transformer.go#L33)
|
||||||
@@ -35,16 +39,56 @@ or [contract](../../staging/libraries/shared/watcher/contract_watcher.go#L68) wa
|
|||||||
* If the base vDB migrations occupy this path as well, they need to be in their `goose fix`ed form
|
* If the base vDB migrations occupy this path as well, they need to be in their `goose fix`ed form
|
||||||
as they are [here](../../staging/db/migrations)
|
as they are [here](../../staging/db/migrations)
|
||||||
|
|
||||||
To update a plugin repository with changes to the core vulcanizedb repository, replace the vulcanizedb vendored in the plugin repo (`plugin_repo/vendor/github.com/vulcanize/vulcanizedb`)
|
To update a plugin repository with changes to the core vulcanizedb repository, run `dep ensure` to update its dependencies.
|
||||||
with the newly updated version
|
|
||||||
* The entire vendor lib within the vendored vulcanizedb needs to be deleted (`plugin_repo/vendor/github.com/vulcanize/vulcanizedb/vendor`)
|
|
||||||
* These complications arise due to this [conflict](https://github.com/golang/go/issues/20481) between `dep` and Go plugins
|
|
||||||
|
|
||||||
## Configuration
|
## Building and Running Custom Transformers
|
||||||
A .toml config file is specified when executing the command:
|
### Commands
|
||||||
`./vulcanizedb composeAndExecute --config=./environments/config_name.toml`
|
* The `compose`, `execute`, `composeAndExecute` commands require Go 1.11+ and use [Go plugins](https://golang
|
||||||
|
.org/pkg/plugin/) which only work on Unix-based systems.
|
||||||
|
|
||||||
The config provides information for composing a set of transformers:
|
* There is an ongoing [conflict](https://github.com/golang/go/issues/20481) between Go plugins and the use of vendored
|
||||||
|
dependencies which imposes certain limitations on how the plugins are built.
|
||||||
|
|
||||||
|
* Separate `compose` and `execute` commands allow pre-building and linking to the pre-built .so file. So, if
|
||||||
|
these are run independently, instead of using `composeAndExecute`, a couple of things need to be considered:
|
||||||
|
* It is necessary that the .so file was built with the same exact dependencies that are present in the execution
|
||||||
|
environment, i.e. we need to `compose` and `execute` the plugin .so file with the same exact version of vulcanizeDB.
|
||||||
|
* The plugin migrations are run during the plugin's composition. As such, if `execute` is used to run a prebuilt .so
|
||||||
|
in a different environment than the one it was composed in, then the database structure will need to be loaded
|
||||||
|
into the environment's Postgres database. This can either be done by manually loading the plugin's schema into
|
||||||
|
Postgres, or by manually running the plugin's migrations.
|
||||||
|
|
||||||
|
* The `compose` and `composeAndExecute` commands assume you are in the vulcanizdb directory located at your system's
|
||||||
|
`$GOPATH`, and that the plugin dependencies are present at their `$GOPATH` directories.
|
||||||
|
|
||||||
|
* The `execute` command does not require the plugin transformer dependencies be located in their `$GOPATH` directories,
|
||||||
|
instead it expects a .so file (of the name specified in the config file) to be in
|
||||||
|
`$GOPATH/src/github.com/vulcanize/vulcanizedb/plugins/` and, as noted above, also expects the plugin db migrations to
|
||||||
|
have already been ran against the database.
|
||||||
|
|
||||||
|
* Usage:
|
||||||
|
* compose: `./vulcanizedb compose --config=environments/config_name.toml`
|
||||||
|
|
||||||
|
* execute: `./vulcanizedb execute --config=environments/config_name.toml`
|
||||||
|
|
||||||
|
* composeAndExecute: `./vulcanizedb composeAndExecute --config=environments/config_name.toml`
|
||||||
|
|
||||||
|
### Flags
|
||||||
|
The `execute` and `composeAndExecute` commands can be passed optional flags to specify the operation of the watchers:
|
||||||
|
|
||||||
|
- `--recheck-headers`/`-r` - specifies whether to re-check headers for events after the header has already been queried for watched logs.
|
||||||
|
Can be useful for redundancy if you suspect that your node is not always returning all desired logs on every query.
|
||||||
|
Argument is expected to be a boolean: e.g. `-r=true`.
|
||||||
|
Defaults to `false`.
|
||||||
|
|
||||||
|
- `query-recheck-interval`/`-q` - specifies interval for re-checking storage diffs that haven been queued for later processing
|
||||||
|
(by default, the storage watched queues storage diffs if transformer execution fails, on the assumption that subsequent data derived from the event transformers may enable us to decode storage keys that we don't recognize right now).
|
||||||
|
Argument is expected to be a duration (integer measured in nanoseconds): e.g. `-q=10m30s` (for 10 minute, 30 second intervals).
|
||||||
|
Defaults to `5m` (5 minutes).
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
A .toml config file is specified when executing the commands.
|
||||||
|
The config provides information for composing a set of transformers from external repositories:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[database]
|
[database]
|
||||||
@@ -107,7 +151,7 @@ The config provides information for composing a set of transformers:
|
|||||||
that fetches event logs from an ETH node
|
that fetches event logs from an ETH node
|
||||||
- `eth_contract` indicates the transformer works with the [contract watcher](../staging/libraries/shared/watcher/contract_watcher.go)
|
- `eth_contract` indicates the transformer works with the [contract watcher](../staging/libraries/shared/watcher/contract_watcher.go)
|
||||||
that is made to work with [contract_watcher pkg](../../staging/pkg/contract_watcher)
|
that is made to work with [contract_watcher pkg](../../staging/pkg/contract_watcher)
|
||||||
based transformers which work with either a light or full sync vDB to watch events and poll public methods ([example1](https://github.com/vulcanize/account_transformers/tree/master/transformers/account/light), [example2](https://github.com/vulcanize/ens_transformers/tree/working/transformers/domain_records))
|
based transformers which work with either a header or full sync vDB to watch events and poll public methods ([example1](https://github.com/vulcanize/account_transformers/tree/master/transformers/account/light), [example2](https://github.com/vulcanize/ens_transformers/tree/working/transformers/domain_records))
|
||||||
- `migrations` is the relative path from `repository` to the db migrations directory for the transformer
|
- `migrations` is the relative path from `repository` to the db migrations directory for the transformer
|
||||||
- `rank` determines the order that migrations are ran, with lower ranked migrations running first
|
- `rank` determines the order that migrations are ran, with lower ranked migrations running first
|
||||||
- this is to help isolate any potential conflicts between transformer migrations
|
- this is to help isolate any potential conflicts between transformer migrations
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
# Syncing commands
|
||||||
|
These commands are used to sync raw Ethereum data into Postgres, with varying levels of data granularity.
|
||||||
|
|
||||||
|
## headerSync
|
||||||
|
Syncs block headers from a running Ethereum node into the VulcanizeDB table `headers`.
|
||||||
|
- Queries the Ethereum node using RPC calls.
|
||||||
|
- Validates headers from the last 15 blocks to ensure that data is up to date.
|
||||||
|
- Useful when you want a minimal baseline from which to track targeted data on the blockchain (e.g. individual smart
|
||||||
|
contract storage values or event logs).
|
||||||
|
- Handles chain reorgs by [validating the most recent blocks' hashes](../pkg/history/header_validator.go). If the hash is
|
||||||
|
different from what we have already stored in the database, the header record will be updated.
|
||||||
|
|
||||||
|
#### Usage
|
||||||
|
- Run: `./vulcanizedb headerSync --config <config.toml> --starting-block-number <block-number>`
|
||||||
|
- The config file must be formatted as follows, and should contain an ipc path to a running Ethereum node:
|
||||||
|
```toml
|
||||||
|
[database]
|
||||||
|
name = "vulcanize_public"
|
||||||
|
hostname = "localhost"
|
||||||
|
user = "vulcanize"
|
||||||
|
password = "vulcanize"
|
||||||
|
port = 5432
|
||||||
|
|
||||||
|
[client]
|
||||||
|
ipcPath = <path to a running Ethereum node>
|
||||||
|
```
|
||||||
|
- Alternatively, the ipc path can be passed as a flag instead `--client-ipcPath`.
|
||||||
|
|
||||||
|
## fullSync
|
||||||
|
Syncs blocks, transactions, receipts and logs from a running Ethereum node into VulcanizeDB tables named
|
||||||
|
`blocks`, `uncles`, `full_sync_transactions`, `full_sync_receipts` and `logs`.
|
||||||
|
- Queries the Ethereum node using RPC calls.
|
||||||
|
- Validates headers from the last 15 blocks to ensure that data is up to date.
|
||||||
|
- Useful when you want to maintain a broad cache of what's happening on the blockchain.
|
||||||
|
- Handles chain reorgs by [validating the most recent blocks' hashes](../pkg/history/header_validator.go). If the hash is
|
||||||
|
different from what we have already stored in the database, the header record will be updated.
|
||||||
|
|
||||||
|
#### Usage
|
||||||
|
- Run `./vulcanizedb fullSync --config <config.toml> --starting-block-number <block-number>`
|
||||||
|
- The config file must be formatted as follows, and should contain an ipc path to a running Ethereum node:
|
||||||
|
```toml
|
||||||
|
[database]
|
||||||
|
name = "vulcanize_public"
|
||||||
|
hostname = "localhost"
|
||||||
|
user = "vulcanize"
|
||||||
|
password = "vulcanize"
|
||||||
|
port = 5432
|
||||||
|
|
||||||
|
[client]
|
||||||
|
ipcPath = <path to a running Ethereum node>
|
||||||
|
```
|
||||||
|
- Alternatively, the ipc path can be passed as a flag instead `--client-ipcPath`.
|
||||||
|
|
||||||
|
*Please note, that if you are fast syncing your Ethereum node, wait for the initial sync to finish.*
|
||||||
|
|
||||||
|
## coldImport
|
||||||
|
Syncs VulcanizeDB from Geth's underlying LevelDB datastore and persists Ethereum blocks,
|
||||||
|
transactions, receipts and logs into VulcanizeDB tables named `blocks`, `uncles`,
|
||||||
|
`full_sync_transactions`, `full_sync_receipts` and `logs` respectively.
|
||||||
|
|
||||||
|
#### Usage
|
||||||
|
1. Ensure the Ethereum node you're point at is not running, and that it has synced to the desired block height.
|
||||||
|
1. Run `./vulcanizedb coldImport --config <config.toml>`
|
||||||
|
1. Optional flags:
|
||||||
|
- `--starting-block-number <block number>`/`-s <block number>`: block number to start syncing from
|
||||||
|
- `--ending-block-number <block number>`/`-e <block number>`: block number to sync to
|
||||||
|
- `--all`/`-a`: sync all missing blocks
|
||||||
|
|
||||||
|
The config file can be formatted as follows, and must contain the LevelDB path.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[database]
|
||||||
|
name = "vulcanize_public"
|
||||||
|
hostname = "localhost"
|
||||||
|
user = "vulcanize"
|
||||||
|
password = "vulcanize"
|
||||||
|
port = 5432
|
||||||
|
|
||||||
|
[client]
|
||||||
|
leveldbpath = "/Users/user/Library/Ethereum/geth/chaindata"
|
||||||
|
```
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
@@ -1,15 +1,15 @@
|
|||||||
# contractWatcher
|
# Generic Transformer
|
||||||
The `contractWatcher` command is a built-in generic contract watcher. It can watch any and all events for a given contract provided the contract's ABI is available.
|
The `contractWatcher` command is a built-in generic contract watcher. It can watch any and all events for a given contract provided the contract's ABI is available.
|
||||||
It also provides some state variable coverage by automating polling of public methods, with some restrictions:
|
It also provides some state variable coverage by automating polling of public methods, with some restrictions:
|
||||||
1. The method must have 2 or less arguments
|
1. The method must have 2 or less arguments
|
||||||
1. The method's arguments must all be of type address or bytes32 (hash)
|
1. The method's arguments must all be of type address or bytes32 (hash)
|
||||||
1. The method must return a single value
|
1. The method must return a single value
|
||||||
|
|
||||||
This command operates in two modes- `light` and `full`- which require a light or full-synced vulcanizeDB, respectively.
|
This command operates in two modes- `header` and `full`- which require a header or full-synced vulcanizeDB, respectively.
|
||||||
|
|
||||||
This command requires the contract ABI be available on Etherscan if it is not provided in the config file by the user.
|
This command requires the contract ABI be available on Etherscan if it is not provided in the config file by the user.
|
||||||
|
|
||||||
If method polling is turned on we require an archival node at the ETH ipc endpoint in our config, whether or not we are operating in `light` or `full` mode.
|
If method polling is turned on we require an archival node at the ETH ipc endpoint in our config, whether or not we are operating in `header` or `full` mode.
|
||||||
Otherwise we only need to connect to a full node.
|
Otherwise we only need to connect to a full node.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
@@ -97,16 +97,16 @@ Modify `./environments/example.toml` to replace the empty `ipcPath` with a path
|
|||||||
This endpoint should be for an archival eth node if we want to perform method polling as this configuration is currently set up to do. To work with a non-archival full node,
|
This endpoint should be for an archival eth node if we want to perform method polling as this configuration is currently set up to do. To work with a non-archival full node,
|
||||||
remove the `balanceOf` method from the `0x8dd5fbce2f6a956c3022ba3663759011dd51e73e` (TrueUSD) contract.
|
remove the `balanceOf` method from the `0x8dd5fbce2f6a956c3022ba3663759011dd51e73e` (TrueUSD) contract.
|
||||||
|
|
||||||
If you are operating a light sync vDB, run:
|
If you are operating a header sync vDB, run:
|
||||||
|
|
||||||
`./vulcanizedb contractWatcher --config=./environments/example.toml --mode=light`
|
`./vulcanizedb contractWatcher --config=./environments/example.toml --mode=header`
|
||||||
|
|
||||||
If instead you are operating a full sync vDB and provided an archival node IPC path, run in full mode:
|
If instead you are operating a full sync vDB and provided an archival node IPC path, run in full mode:
|
||||||
|
|
||||||
`./vulcanizedb contractWatcher --config=./environments/example.toml --mode=full`
|
`./vulcanizedb contractWatcher --config=./environments/example.toml --mode=full`
|
||||||
|
|
||||||
This will run the contractWatcher and configures it to watch the contracts specified in the config file. Note that
|
This will run the contractWatcher and configures it to watch the contracts specified in the config file. Note that
|
||||||
by default we operate in `light` mode but the flag is included here to demonstrate its use.
|
by default we operate in `header` mode but the flag is included here to demonstrate its use.
|
||||||
|
|
||||||
The example config we link to in this example watches two contracts, the ENS Registry (0x314159265dD8dbb310642f98f50C066173C1259b) and TrueUSD (0x8dd5fbCe2F6a956C3022bA3663759011Dd51e73E).
|
The example config we link to in this example watches two contracts, the ENS Registry (0x314159265dD8dbb310642f98f50C066173C1259b) and TrueUSD (0x8dd5fbCe2F6a956C3022bA3663759011Dd51e73E).
|
||||||
|
|
||||||
@@ -117,43 +117,43 @@ The TrueUSD contract is configured with two events (`Transfer` and `Mint`) and a
|
|||||||
to poll the `balanceOf` method with those addresses at every block. Note that we do not provide an ABI for TrueUSD as its ABI can be fetched from Etherscan.
|
to poll the `balanceOf` method with those addresses at every block. Note that we do not provide an ABI for TrueUSD as its ABI can be fetched from Etherscan.
|
||||||
|
|
||||||
For the ENS contract, it produces and populates a schema with four tables"
|
For the ENS contract, it produces and populates a schema with four tables"
|
||||||
`light_0x314159265dd8dbb310642f98f50c066173c1259b.newowner_event`
|
`header_0x314159265dd8dbb310642f98f50c066173c1259b.newowner_event`
|
||||||
`light_0x314159265dd8dbb310642f98f50c066173c1259b.newresolver_event`
|
`header_0x314159265dd8dbb310642f98f50c066173c1259b.newresolver_event`
|
||||||
`light_0x314159265dd8dbb310642f98f50c066173c1259b.newttl_event`
|
`header_0x314159265dd8dbb310642f98f50c066173c1259b.newttl_event`
|
||||||
`light_0x314159265dd8dbb310642f98f50c066173c1259b.transfer_event`
|
`header_0x314159265dd8dbb310642f98f50c066173c1259b.transfer_event`
|
||||||
|
|
||||||
For the TrusUSD contract, it produces and populates a schema with three tables:
|
For the TrusUSD contract, it produces and populates a schema with three tables:
|
||||||
|
|
||||||
`light_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.transfer_event`
|
`header_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.transfer_event`
|
||||||
`light_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.mint_event`
|
`header_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.mint_event`
|
||||||
`light_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.balanceof_method`
|
`header_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.balanceof_method`
|
||||||
|
|
||||||
Column ids and types for these tables are generated based on the event and method argument names and types and method return types, resulting in tables such as:
|
Column ids and types for these tables are generated based on the event and method argument names and types and method return types, resulting in tables such as:
|
||||||
|
|
||||||
Table "light_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.transfer_event"
|
Table "header_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.transfer_event"
|
||||||
|
|
||||||
| Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
| Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
||||||
|:----------:|:---------------------:|:---------:|:--------:|:-------------------------------------------------------------------------------------------:|:--------:|:------------:|:-----------:|
|
|:----------:|:---------------------:|:---------:|:--------:|:--------------------------------------------------------------------------------------------:|:--------:|:------------:|:-----------:|
|
||||||
| id | integer | | not null | nextval('light_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.transfer_event_id_seq'::regclass) | plain | | |
|
| id | integer | | not null | nextval('header_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.transfer_event_id_seq'::regclass) | plain | | |
|
||||||
| header_id | integer | | not null | | plain | | |
|
| header_id | integer | | not null | | plain | | |
|
||||||
| token_name | character varying(66) | | not null | | extended | | |
|
| token_name | character varying(66) | | not null | | extended | | |
|
||||||
| raw_log | jsonb | | | | extended | | |
|
| raw_log | jsonb | | | | extended | | |
|
||||||
| log_idx | integer | | not null | | plain | | |
|
| log_idx | integer | | not null | | plain | | |
|
||||||
| tx_idx | integer | | not null | | plain | | |
|
| tx_idx | integer | | not null | | plain | | |
|
||||||
| from_ | character varying(66) | | not null | | extended | | |
|
| from_ | character varying(66) | | not null | | extended | | |
|
||||||
| to_ | character varying(66) | | not null | | extended | | |
|
| to_ | character varying(66) | | not null | | extended | | |
|
||||||
| value_ | numeric | | not null | | main | | |
|
| value_ | numeric | | not null | | main | | |
|
||||||
|
|
||||||
|
|
||||||
Table "light_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.balanceof_method"
|
Table "header_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.balanceof_method"
|
||||||
|
|
||||||
| Column | Type | Collation | Nullable | Default | Storage | Stats target | Description |
|
| Column | Type | Collation | Nullable | Default | Storage | Stats target | Description |
|
||||||
|:----------:|:---------------------:|:---------:|:--------:|:-------------------------------------------------------------------------------------------:|:--------:|:------------:|:-----------:|
|
|:----------:|:---------------------:|:---------:|:--------:|:----------------------------------------------------------------------------------------------:|:--------:|:------------:|:-----------:|
|
||||||
| id | integer | | not null | nextval('light_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.balanceof_method_id_seq'::regclass) | plain | | |
|
| id | integer | | not null | nextval('header_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.balanceof_method_id_seq'::regclass) | plain | | |
|
||||||
| token_name | character varying(66) | | not null | | extended | | |
|
| token_name | character varying(66) | | not null | | extended | | |
|
||||||
| block | integer | | not null | | plain | | |
|
| block | integer | | not null | | plain | | |
|
||||||
| who_ | character varying(66) | | not null | | extended | | |
|
| who_ | character varying(66) | | not null | | extended | | |
|
||||||
| returned | numeric | | not null | | main | | |
|
| returned | numeric | | not null | | main | | |
|
||||||
|
|
||||||
The addition of '_' after table names is to prevent collisions with reserved Postgres words.
|
The addition of '_' after table names is to prevent collisions with reserved Postgres words.
|
||||||
|
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Postgraphile
|
||||||
|
|
||||||
|
You can expose VulcanizeDB data via [Postgraphile](https://github.com/graphile/postgraphile).
|
||||||
|
Check out [their documentation](https://www.graphile.org/postgraphile/) for the most up-to-date instructions on installing, running, and customizing Postgraphile.
|
||||||
|
|
||||||
|
## Simple Setup
|
||||||
|
|
||||||
|
As of April 30, 2019, you can run Postgraphile pointed at the default `vulcanize_public` database with the following commands:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install -g postgraphile
|
||||||
|
postgraphile --connection postgres://localhost/vulcanize_public --schema=public,custom --disable-default-mutations --no-ignore-rbac
|
||||||
|
```
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
- `--connection` specifies the database. The above command connects to the default `vulcanize_public` database
|
||||||
|
defined in [the example config](../environments/public.toml.example).
|
||||||
|
- `--schema` defines what schema(s) to expose. The above exposes the `public` schema (for core VulcanizeDB data) as well as a `custom` schema (where `custom` is the name of a schema defined in executed transformers).
|
||||||
|
- `--disable-default-mutations` prevents Postgraphile from exposing create, update, and delete operations on your data, which are otherwise enabled by default.
|
||||||
|
- `--no-ignore-rbac` ensures that Postgraphile will only expose the tables, columns, fields, and query functions that the user has explicit access to.
|
||||||
|
|
||||||
|
## Customizing Postgraphile
|
||||||
|
|
||||||
|
By default, Postgraphile will expose queries for all tables defined in your chosen database/schema(s), including [filtering](https://www.graphile.org/postgraphile/filtering/) and [auto-discovered relations](https://www.graphile.org/postgraphile/relations/).
|
||||||
|
|
||||||
|
If you'd like to expose more customized windows into your data, there are some techniques you can apply when writing migrations:
|
||||||
|
|
||||||
|
- [Computed columns](https://www.graphile.org/postgraphile/computed-columns/) enable you to derive additional fields from types defined in your database.
|
||||||
|
For example, you could write a function to expose a block header's state root over Postgraphile with a computed column - without modifying the `public.headers` table.
|
||||||
|
- [Custom queries](https://www.graphile.org/postgraphile/custom-queries/) enable you to provide on-demand access to more complex data (e.g. the product of joining and filtering several tables' data based on a passed argument).
|
||||||
|
For example, you could write a custom query to get the block timestamp for every transaction originating from a given address.
|
||||||
|
- [Subscriptions](https://www.graphile.org/postgraphile/subscriptions/) enable you to publish data as it is coming into your database.
|
||||||
|
|
||||||
|
The above list is not exhaustive - please see the Postgraphile documentation for a more comprehensive and up-to-date description of available features.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Repository Maintenance
|
||||||
|
|
||||||
|
## Diagrams
|
||||||
|
- Diagrams were created with [draw.io](draw.io).
|
||||||
|
- To update a diagram:
|
||||||
|
1. Go to [draw.io](draw.io).
|
||||||
|
1. Click on *File > Open from* and choose the location of the diagram you want to update.
|
||||||
|
1. Once open in draw.io, you may update it.
|
||||||
|
1. Export the diagram to this repository's directory and add commit it.
|
||||||
|
|
||||||
|
|
||||||
|
## Generating the Changelog
|
||||||
|
We use [github-changelog-generator](https://github.com/github-changelog-generator/github-changelog-generator) to
|
||||||
|
generate release Changelogs. To be consistent with previous Changelogs, the following flags should be passed to the
|
||||||
|
command:
|
||||||
|
|
||||||
|
```
|
||||||
|
--user vulcanize
|
||||||
|
--project vulcanizedb
|
||||||
|
--token {YOUR_GITHUB_TOKEN}
|
||||||
|
--no-issues
|
||||||
|
--usernames-as-github-logins
|
||||||
|
--since-tag {PREVIOUS_RELEASE_TAG}
|
||||||
|
```
|
||||||
|
|
||||||
|
More information on why your github token is needed, and how to generate it here:
|
||||||
|
https://github.com/github-changelog-generator/github-changelog-generator#github-token
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# Syncing commands
|
|
||||||
These commands are used to sync raw Ethereum data into Postgres.
|
|
||||||
|
|
||||||
## lightSync
|
|
||||||
Syncs VulcanizeDB with the configured Ethereum node, populating only block headers.
|
|
||||||
This command is useful when you want a minimal baseline from which to track targeted data on the blockchain (e.g. individual smart contract storage values or event logs).
|
|
||||||
1. Start Ethereum node
|
|
||||||
1. In a separate terminal start VulcanizeDB:
|
|
||||||
- `./vulcanizedb lightSync --config <config.toml> --starting-block-number <block-number>`
|
|
||||||
|
|
||||||
## sync
|
|
||||||
Syncs VulcanizeDB with the configured Ethereum node, populating blocks, transactions, receipts, and logs.
|
|
||||||
This command is useful when you want to maintain a broad cache of what's happening on the blockchain.
|
|
||||||
1. Start Ethereum node (**if fast syncing your Ethereum node, wait for initial sync to finish**)
|
|
||||||
1. In a separate terminal start VulcanizeDB:
|
|
||||||
- `./vulcanizedb sync --config <config.toml> --starting-block-number <block-number>`
|
|
||||||
|
|
||||||
## coldImport
|
|
||||||
Sync VulcanizeDB from the LevelDB underlying a Geth node.
|
|
||||||
1. Assure node is not running, and that it has synced to the desired block height.
|
|
||||||
1. Start vulcanize_db
|
|
||||||
- `./vulcanizedb coldImport --config <config.toml>`
|
|
||||||
1. Optional flags:
|
|
||||||
- `--starting-block-number <block number>`/`-s <block number>`: block number to start syncing from
|
|
||||||
- `--ending-block-number <block number>`/`-e <block number>`: block number to sync to
|
|
||||||
- `--all`/`-a`: sync all missing blocks
|
|
||||||
@@ -286,7 +286,7 @@ var _ = Describe("contractWatcher full transformer", func() {
|
|||||||
err = t.Execute()
|
err = t.Execute()
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
log := test_helpers.LightNewOwnerLog{}
|
log := test_helpers.HeaderSyncNewOwnerLog{}
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.newowner_event", ensAddr)).StructScan(&log)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.newowner_event", ensAddr)).StructScan(&log)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("does not exist"))
|
Expect(err.Error()).To(ContainSubstring("does not exist"))
|
||||||
|
|||||||
+31
-24
@@ -9,7 +9,7 @@ import (
|
|||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/config"
|
"github.com/vulcanize/vulcanizedb/pkg/config"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/light/transformer"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/transformer"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/constants"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/constants"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers/mocks"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers/mocks"
|
||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("contractWatcher light transformer", func() {
|
var _ = Describe("contractWatcher headerSync transformer", func() {
|
||||||
var db *postgres.DB
|
var db *postgres.DB
|
||||||
var err error
|
var err error
|
||||||
var blockChain core.BlockChain
|
var blockChain core.BlockChain
|
||||||
@@ -49,6 +49,13 @@ var _ = Describe("contractWatcher light transformer", func() {
|
|||||||
c, ok := t.Contracts[tusdAddr]
|
c, ok := t.Contracts[tusdAddr]
|
||||||
Expect(ok).To(Equal(true))
|
Expect(ok).To(Equal(true))
|
||||||
|
|
||||||
|
// TODO: Fix this
|
||||||
|
// This test sometimes randomly fails because
|
||||||
|
// for some reason the starting block number is not updated from
|
||||||
|
// its original value (5197514) to the block number (6194632)
|
||||||
|
// of the earliest header (mocks.MockHeader1) in the repository
|
||||||
|
// It is not clear how this happens without one of the above insertErrs
|
||||||
|
// having been thrown and without any errors thrown during the Init() call
|
||||||
Expect(c.StartingBlock).To(Equal(int64(6194632)))
|
Expect(c.StartingBlock).To(Equal(int64(6194632)))
|
||||||
Expect(c.Abi).To(Equal(constants.TusdAbiString))
|
Expect(c.Abi).To(Equal(constants.TusdAbiString))
|
||||||
Expect(c.Name).To(Equal("TrueUSD"))
|
Expect(c.Name).To(Equal("TrueUSD"))
|
||||||
@@ -100,8 +107,8 @@ var _ = Describe("contractWatcher light transformer", func() {
|
|||||||
err = t.Execute()
|
err = t.Execute()
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
log := test_helpers.LightTransferLog{}
|
log := test_helpers.HeaderSyncTransferLog{}
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.transfer_event", tusdAddr)).StructScan(&log)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.transfer_event", tusdAddr)).StructScan(&log)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
// We don't know vulcID, so compare individual fields instead of complete structures
|
// We don't know vulcID, so compare individual fields instead of complete structures
|
||||||
Expect(log.HeaderID).To(Equal(headerID))
|
Expect(log.HeaderID).To(Equal(headerID))
|
||||||
@@ -168,12 +175,12 @@ var _ = Describe("contractWatcher light transformer", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
res := test_helpers.BalanceOf{}
|
res := test_helpers.BalanceOf{}
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.balanceof_method WHERE who_ = '0x1062a747393198f70F71ec65A582423Dba7E5Ab3' AND block = '6791669'", tusdAddr)).StructScan(&res)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0x1062a747393198f70F71ec65A582423Dba7E5Ab3' AND block = '6791669'", tusdAddr)).StructScan(&res)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(res.Balance).To(Equal("55849938025000000000000"))
|
Expect(res.Balance).To(Equal("55849938025000000000000"))
|
||||||
Expect(res.TokenName).To(Equal("TrueUSD"))
|
Expect(res.TokenName).To(Equal("TrueUSD"))
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.balanceof_method WHERE who_ = '0x09BbBBE21a5975cAc061D82f7b843b1234567890' AND block = '6791669'", tusdAddr)).StructScan(&res)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0x09BbBBE21a5975cAc061D82f7b843b1234567890' AND block = '6791669'", tusdAddr)).StructScan(&res)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
||||||
})
|
})
|
||||||
@@ -208,8 +215,8 @@ var _ = Describe("contractWatcher light transformer", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(t.Start).To(Equal(int64(6885698)))
|
Expect(t.Start).To(Equal(int64(6885698)))
|
||||||
|
|
||||||
log := test_helpers.LightNewOwnerLog{}
|
log := test_helpers.HeaderSyncNewOwnerLog{}
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.newowner_event", ensAddr)).StructScan(&log)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.newowner_event", ensAddr)).StructScan(&log)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
// We don't know vulcID, so compare individual fields instead of complete structures
|
// We don't know vulcID, so compare individual fields instead of complete structures
|
||||||
Expect(log.HeaderID).To(Equal(headerID))
|
Expect(log.HeaderID).To(Equal(headerID))
|
||||||
@@ -260,17 +267,17 @@ var _ = Describe("contractWatcher light transformer", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
res := test_helpers.Owner{}
|
res := test_helpers.Owner{}
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae' AND block = '6885696'", ensAddr)).StructScan(&res)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae' AND block = '6885696'", ensAddr)).StructScan(&res)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(res.Address).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
|
Expect(res.Address).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
|
||||||
Expect(res.TokenName).To(Equal(""))
|
Expect(res.TokenName).To(Equal(""))
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047' AND block = '6885696'", ensAddr)).StructScan(&res)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047' AND block = '6885696'", ensAddr)).StructScan(&res)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(res.Address).To(Equal("0x0000000000000000000000000000000000000000"))
|
Expect(res.Address).To(Equal("0x0000000000000000000000000000000000000000"))
|
||||||
Expect(res.TokenName).To(Equal(""))
|
Expect(res.TokenName).To(Equal(""))
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x9THIS110dcc444fIS242510c09bbAbe21aFAKEcacNODE82f7b843HASH61ba391' AND block = '6885696'", ensAddr)).StructScan(&res)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x9THIS110dcc444fIS242510c09bbAbe21aFAKEcacNODE82f7b843HASH61ba391' AND block = '6885696'", ensAddr)).StructScan(&res)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
||||||
})
|
})
|
||||||
@@ -287,8 +294,8 @@ var _ = Describe("contractWatcher light transformer", func() {
|
|||||||
err = t.Execute()
|
err = t.Execute()
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
log := test_helpers.LightNewOwnerLog{}
|
log := test_helpers.HeaderSyncNewOwnerLog{}
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.newowner_event", ensAddr)).StructScan(&log)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.newowner_event", ensAddr)).StructScan(&log)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("does not exist"))
|
Expect(err.Error()).To(ContainSubstring("does not exist"))
|
||||||
})
|
})
|
||||||
@@ -309,12 +316,12 @@ var _ = Describe("contractWatcher light transformer", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
res := test_helpers.Owner{}
|
res := test_helpers.Owner{}
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae' AND block = '6885696'", ensAddr)).StructScan(&res)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae' AND block = '6885696'", ensAddr)).StructScan(&res)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(res.Address).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
|
Expect(res.Address).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
|
||||||
Expect(res.TokenName).To(Equal(""))
|
Expect(res.TokenName).To(Equal(""))
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047' AND block = '6885696'", ensAddr)).StructScan(&res)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047' AND block = '6885696'", ensAddr)).StructScan(&res)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
||||||
})
|
})
|
||||||
@@ -338,16 +345,16 @@ var _ = Describe("contractWatcher light transformer", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(t.Start).To(Equal(int64(6885702)))
|
Expect(t.Start).To(Equal(int64(6885702)))
|
||||||
|
|
||||||
newOwnerLog := test_helpers.LightNewOwnerLog{}
|
newOwnerLog := test_helpers.HeaderSyncNewOwnerLog{}
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.newowner_event", ensAddr)).StructScan(&newOwnerLog)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.newowner_event", ensAddr)).StructScan(&newOwnerLog)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
// We don't know vulcID, so compare individual fields instead of complete structures
|
// We don't know vulcID, so compare individual fields instead of complete structures
|
||||||
Expect(newOwnerLog.Node).To(Equal("0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"))
|
Expect(newOwnerLog.Node).To(Equal("0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"))
|
||||||
Expect(newOwnerLog.Label).To(Equal("0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047"))
|
Expect(newOwnerLog.Label).To(Equal("0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047"))
|
||||||
Expect(newOwnerLog.Owner).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
|
Expect(newOwnerLog.Owner).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
|
||||||
|
|
||||||
transferLog := test_helpers.LightTransferLog{}
|
transferLog := test_helpers.HeaderSyncTransferLog{}
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.transfer_event", tusdAddr)).StructScan(&transferLog)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.transfer_event", tusdAddr)).StructScan(&transferLog)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
// We don't know vulcID, so compare individual fields instead of complete structures
|
// We don't know vulcID, so compare individual fields instead of complete structures
|
||||||
Expect(transferLog.From).To(Equal("0x8cA465764873E71CEa525F5EB6AE973d650c22C2"))
|
Expect(transferLog.From).To(Equal("0x8cA465764873E71CEa525F5EB6AE973d650c22C2"))
|
||||||
@@ -410,27 +417,27 @@ var _ = Describe("contractWatcher light transformer", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
owner := test_helpers.Owner{}
|
owner := test_helpers.Owner{}
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae' AND block = '6885696'", ensAddr)).StructScan(&owner)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae' AND block = '6885696'", ensAddr)).StructScan(&owner)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(owner.Address).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
|
Expect(owner.Address).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
|
||||||
Expect(owner.TokenName).To(Equal(""))
|
Expect(owner.TokenName).To(Equal(""))
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047' AND block = '6885696'", ensAddr)).StructScan(&owner)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047' AND block = '6885696'", ensAddr)).StructScan(&owner)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(owner.Address).To(Equal("0x0000000000000000000000000000000000000000"))
|
Expect(owner.Address).To(Equal("0x0000000000000000000000000000000000000000"))
|
||||||
Expect(owner.TokenName).To(Equal(""))
|
Expect(owner.TokenName).To(Equal(""))
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x95832c7a47ff8a7840e28b78ceMADEUPaaf4HASHc186badTHItransformers.8IS625bFAKE' AND block = '6885696'", ensAddr)).StructScan(&owner)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x95832c7a47ff8a7840e28b78ceMADEUPaaf4HASHc186badTHItransformers.8IS625bFAKE' AND block = '6885696'", ensAddr)).StructScan(&owner)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
||||||
|
|
||||||
bal := test_helpers.BalanceOf{}
|
bal := test_helpers.BalanceOf{}
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.balanceof_method WHERE who_ = '0x8cA465764873E71CEa525F5EB6AE973d650c22C2' AND block = '6885701'", tusdAddr)).StructScan(&bal)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0x8cA465764873E71CEa525F5EB6AE973d650c22C2' AND block = '6885701'", tusdAddr)).StructScan(&bal)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(bal.Balance).To(Equal("1954436000000000000000"))
|
Expect(bal.Balance).To(Equal("1954436000000000000000"))
|
||||||
Expect(bal.TokenName).To(Equal("TrueUSD"))
|
Expect(bal.TokenName).To(Equal("TrueUSD"))
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.balanceof_method WHERE who_ = '0x09BbBBE21a5975cAc061D82f7b843b1234567890' AND block = '6885701'", tusdAddr)).StructScan(&bal)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0x09BbBBE21a5975cAc061D82f7b843b1234567890' AND block = '6885701'", tusdAddr)).StructScan(&bal)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
||||||
})
|
})
|
||||||
@@ -130,10 +130,10 @@ var _ = Describe("Poller", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("Light sync mode", func() {
|
Describe("Header sync mode", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
db, bc = test_helpers.SetupDBandBC()
|
db, bc = test_helpers.SetupDBandBC()
|
||||||
contractPoller = poller.NewPoller(bc, db, types.LightSync)
|
contractPoller = poller.NewPoller(bc, db, types.HeaderSync)
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("PollContract", func() {
|
Describe("PollContract", func() {
|
||||||
@@ -148,22 +148,22 @@ var _ = Describe("Poller", func() {
|
|||||||
|
|
||||||
scanStruct := test_helpers.BalanceOf{}
|
scanStruct := test_helpers.BalanceOf{}
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6707322'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6707322'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(scanStruct.Balance).To(Equal("66386309548896882859581786"))
|
Expect(scanStruct.Balance).To(Equal("66386309548896882859581786"))
|
||||||
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6707323'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6707323'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(scanStruct.Balance).To(Equal("66386309548896882859581786"))
|
Expect(scanStruct.Balance).To(Equal("66386309548896882859581786"))
|
||||||
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.balanceof_method WHERE who_ = '0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE' AND block = '6707322'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE' AND block = '6707322'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(scanStruct.Balance).To(Equal("17982350181394112023885864"))
|
Expect(scanStruct.Balance).To(Equal("17982350181394112023885864"))
|
||||||
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.balanceof_method WHERE who_ = '0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE' AND block = '6707323'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE' AND block = '6707323'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(scanStruct.Balance).To(Equal("17982350181394112023885864"))
|
Expect(scanStruct.Balance).To(Equal("17982350181394112023885864"))
|
||||||
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
||||||
@@ -180,12 +180,12 @@ var _ = Describe("Poller", func() {
|
|||||||
|
|
||||||
scanStruct := test_helpers.Owner{}
|
scanStruct := test_helpers.Owner{}
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x7e74a86b6e146964fb965db04dc2590516da77f720bb6759337bf5632415fd86' AND block = '6885877'", constants.EnsContractAddress)).StructScan(&scanStruct)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x7e74a86b6e146964fb965db04dc2590516da77f720bb6759337bf5632415fd86' AND block = '6885877'", constants.EnsContractAddress)).StructScan(&scanStruct)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(scanStruct.Address).To(Equal("0x546aA2EaE2514494EeaDb7bbb35243348983C59d"))
|
Expect(scanStruct.Address).To(Equal("0x546aA2EaE2514494EeaDb7bbb35243348983C59d"))
|
||||||
Expect(scanStruct.TokenName).To(Equal("ENS-Registry"))
|
Expect(scanStruct.TokenName).To(Equal("ENS-Registry"))
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.owner_method WHERE node_ = '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae' AND block = '6885877'", constants.EnsContractAddress)).StructScan(&scanStruct)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae' AND block = '6885877'", constants.EnsContractAddress)).StructScan(&scanStruct)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(scanStruct.Address).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
|
Expect(scanStruct.Address).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
|
||||||
Expect(scanStruct.TokenName).To(Equal("ENS-Registry"))
|
Expect(scanStruct.TokenName).To(Equal("ENS-Registry"))
|
||||||
@@ -202,7 +202,7 @@ var _ = Describe("Poller", func() {
|
|||||||
|
|
||||||
scanStruct := test_helpers.BalanceOf{}
|
scanStruct := test_helpers.BalanceOf{}
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6707322'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6707322'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ var _ = Describe("Poller", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
scanStruct := test_helpers.Resolver{}
|
scanStruct := test_helpers.Resolver{}
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.resolver_method WHERE node_ = '0x495b6e6efdedb750aa519919b5cf282bdaa86067b82a2293a3ff5723527141e8' AND block = '6921967'", constants.EnsContractAddress)).StructScan(&scanStruct)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.resolver_method WHERE node_ = '0x495b6e6efdedb750aa519919b5cf282bdaa86067b82a2293a3ff5723527141e8' AND block = '6921967'", constants.EnsContractAddress)).StructScan(&scanStruct)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(scanStruct.Address).To(Equal("0x5FfC014343cd971B7eb70732021E26C35B744cc4"))
|
Expect(scanStruct.Address).To(Equal("0x5FfC014343cd971B7eb70732021E26C35B744cc4"))
|
||||||
Expect(scanStruct.TokenName).To(Equal("ENS-Registry"))
|
Expect(scanStruct.TokenName).To(Equal("ENS-Registry"))
|
||||||
@@ -225,13 +225,13 @@ var _ = Describe("Poller", func() {
|
|||||||
|
|
||||||
test_helpers.TearDown(db)
|
test_helpers.TearDown(db)
|
||||||
db, bc = test_helpers.SetupDBandBC()
|
db, bc = test_helpers.SetupDBandBC()
|
||||||
contractPoller = poller.NewPoller(bc, db, types.LightSync)
|
contractPoller = poller.NewPoller(bc, db, types.HeaderSync)
|
||||||
|
|
||||||
con.Piping = true
|
con.Piping = true
|
||||||
err = contractPoller.PollContract(*con, 6921968)
|
err = contractPoller.PollContract(*con, 6921968)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.resolver_method WHERE node_ = '0x495b6e6efdedb750aa519919b5cf282bdaa86067b82a2293a3ff5723527141e8' AND block = '6921967'", constants.EnsContractAddress)).StructScan(&scanStruct)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.resolver_method WHERE node_ = '0x495b6e6efdedb750aa519919b5cf282bdaa86067b82a2293a3ff5723527141e8' AND block = '6921967'", constants.EnsContractAddress)).StructScan(&scanStruct)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(scanStruct.Address).To(Equal("0x5FfC014343cd971B7eb70732021E26C35B744cc4"))
|
Expect(scanStruct.Address).To(Equal("0x5FfC014343cd971B7eb70732021E26C35B744cc4"))
|
||||||
Expect(scanStruct.TokenName).To(Equal("ENS-Registry"))
|
Expect(scanStruct.TokenName).To(Equal("ENS-Registry"))
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
One approach VulcanizeDB takes to caching and indexing smart contracts is to watch contract events emitted in receipt logs.
|
One approach VulcanizeDB takes to caching and indexing smart contracts is to watch contract events emitted in receipt logs.
|
||||||
|
|
||||||
With a light synced vDB we can watch events by iterating over headers retrieved from the synced `headers` table and using these headers to
|
With a header synced vDB we can watch events by iterating over headers retrieved from the synced `headers` table and using these headers to
|
||||||
fetch and verify relevant event logs from a full Ethereum node, keeping track of which headers we have checked for which events
|
fetch and verify relevant event logs from a full Ethereum node, keeping track of which headers we have checked for which events
|
||||||
with our `checked_headers` table.
|
with our `checked_headers` table.
|
||||||
|
|
||||||
## Assumptions
|
## Assumptions
|
||||||
|
|
||||||
This approach assumes you are running a vDB light sync which is itself run against a light Ethereum node,
|
This approach assumes you are running a vDB header sync which is run against a light Ethereum node;
|
||||||
this approach also assumes there is a full node available.
|
this approach also assumes there is a full node available.
|
||||||
|
|
||||||
Looking forward, we will be building fetchers that enable sourcing data from IPFS instead of an ETH node.
|
Looking forward, we will be building fetchers that enable sourcing data from IPFS instead of an ETH node.
|
||||||
@@ -17,7 +17,7 @@ Looking forward, we will be building fetchers that enable sourcing data from IPF
|
|||||||
|
|
||||||
VulcanizeDB has shared code built out for building and plugging in event transformers
|
VulcanizeDB has shared code built out for building and plugging in event transformers
|
||||||
|
|
||||||
### [Event Watcher (light sync)](../staging/libraries/shared/watcher/event_watcher.go)
|
### [Event Watcher (header sync)](../staging/libraries/shared/watcher/event_watcher.go)
|
||||||
|
|
||||||
The event watcher is responsible for continuously fetching and delegating chunks of logs and their associated header to the appropriate transformers.
|
The event watcher is responsible for continuously fetching and delegating chunks of logs and their associated header to the appropriate transformers.
|
||||||
|
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
// VulcanizeDB
|
|
||||||
// Copyright © 2019 Vulcanize
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package event
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/constants"
|
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LogNoteTransformer struct {
|
|
||||||
Config transformer.EventTransformerConfig
|
|
||||||
Converter LogNoteConverter
|
|
||||||
Repository Repository
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr LogNoteTransformer) NewLogNoteTransformer(db *postgres.DB) transformer.EventTransformer {
|
|
||||||
tr.Repository.SetDB(db)
|
|
||||||
return tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr LogNoteTransformer) Execute(logs []types.Log, header core.Header, recheckedHeader constants.TransformerExecution) error {
|
|
||||||
transformerName := tr.Config.TransformerName
|
|
||||||
|
|
||||||
// No matching logs, mark the header as checked for this type of logs
|
|
||||||
if len(logs) < 1 {
|
|
||||||
err := tr.Repository.MarkHeaderChecked(header.Id)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error marking header as checked in %v: %v", transformerName, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
models, err := tr.Converter.ToModels(logs)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error converting logs in %v: %v", transformerName, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tr.Repository.Create(header.Id, models)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error persisting %v record: %v", transformerName, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr LogNoteTransformer) GetName() string {
|
|
||||||
return tr.Config.TransformerName
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr LogNoteTransformer) GetConfig() transformer.EventTransformerConfig {
|
|
||||||
return tr.Config
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
// VulcanizeDB
|
|
||||||
// Copyright © 2019 Vulcanize
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package event_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/rand"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/constants"
|
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/factories/event"
|
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/mocks"
|
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/test_data"
|
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ = Describe("LogNoteTransformer", func() {
|
|
||||||
var (
|
|
||||||
repository mocks.MockRepository
|
|
||||||
converter mocks.MockLogNoteConverter
|
|
||||||
headerOne core.Header
|
|
||||||
t transformer.EventTransformer
|
|
||||||
model test_data.GenericModel
|
|
||||||
config = test_data.GenericTestConfig
|
|
||||||
logs = test_data.GenericTestLogs
|
|
||||||
)
|
|
||||||
|
|
||||||
BeforeEach(func() {
|
|
||||||
repository = mocks.MockRepository{}
|
|
||||||
converter = mocks.MockLogNoteConverter{}
|
|
||||||
t = event.LogNoteTransformer{
|
|
||||||
Config: config,
|
|
||||||
Converter: &converter,
|
|
||||||
Repository: &repository,
|
|
||||||
}.NewLogNoteTransformer(nil)
|
|
||||||
|
|
||||||
headerOne = core.Header{Id: rand.Int63(), BlockNumber: rand.Int63()}
|
|
||||||
})
|
|
||||||
|
|
||||||
It("sets the database", func() {
|
|
||||||
Expect(repository.SetDbCalled).To(BeTrue())
|
|
||||||
})
|
|
||||||
|
|
||||||
It("marks header checked if no logs are provided", func() {
|
|
||||||
err := t.Execute([]types.Log{}, headerOne, constants.HeaderMissing)
|
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
repository.AssertMarkHeaderCheckedCalledWith(headerOne.Id)
|
|
||||||
})
|
|
||||||
|
|
||||||
It("doesn't attempt to convert or persist an empty collection when there are no logs", func() {
|
|
||||||
err := t.Execute([]types.Log{}, headerOne, constants.HeaderMissing)
|
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
Expect(converter.ToModelsCalledCounter).To(Equal(0))
|
|
||||||
Expect(repository.CreateCalledCounter).To(Equal(0))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("does not call repository.MarkCheckedHeader when there are logs", func() {
|
|
||||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
repository.AssertMarkHeaderCheckedNotCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
It("returns error if marking header checked returns err", func() {
|
|
||||||
repository.SetMarkHeaderCheckedError(fakes.FakeError)
|
|
||||||
|
|
||||||
err := t.Execute([]types.Log{}, headerOne, constants.HeaderMissing)
|
|
||||||
|
|
||||||
Expect(err).To(HaveOccurred())
|
|
||||||
Expect(err).To(MatchError(fakes.FakeError))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("converts matching logs to models", func() {
|
|
||||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
Expect(converter.PassedLogs).To(Equal(logs))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("returns error if converter returns error", func() {
|
|
||||||
converter.SetConverterError(fakes.FakeError)
|
|
||||||
|
|
||||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
|
||||||
|
|
||||||
Expect(err).To(HaveOccurred())
|
|
||||||
Expect(err).To(MatchError(fakes.FakeError))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("persists the model", func() {
|
|
||||||
converter.SetReturnModels([]interface{}{model})
|
|
||||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
Expect(repository.PassedHeaderID).To(Equal(headerOne.Id))
|
|
||||||
Expect(repository.PassedModels).To(Equal([]interface{}{model}))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("returns error if repository returns error for create", func() {
|
|
||||||
repository.SetCreateError(fakes.FakeError)
|
|
||||||
|
|
||||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
|
||||||
|
|
||||||
Expect(err).To(HaveOccurred())
|
|
||||||
Expect(err).To(MatchError(fakes.FakeError))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -17,14 +17,11 @@
|
|||||||
package event
|
package event
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Repository interface {
|
type Repository interface {
|
||||||
Create(headerID int64, models []interface{}) error
|
Create(headerID int64, models []interface{}) error
|
||||||
MarkHeaderChecked(headerID int64) error
|
MarkHeaderChecked(headerID int64) error
|
||||||
MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error)
|
|
||||||
RecheckHeaders(startingBlockNumber, endingBlockNUmber int64) ([]core.Header, error)
|
|
||||||
SetDB(db *postgres.DB)
|
SetDB(db *postgres.DB)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/constants"
|
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
@@ -37,7 +36,7 @@ func (transformer Transformer) NewTransformer(db *postgres.DB) transformer.Event
|
|||||||
return transformer
|
return transformer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (transformer Transformer) Execute(logs []types.Log, header core.Header, recheckHeaders constants.TransformerExecution) error {
|
func (transformer Transformer) Execute(logs []types.Log, header core.Header) error {
|
||||||
transformerName := transformer.Config.TransformerName
|
transformerName := transformer.Config.TransformerName
|
||||||
config := transformer.Config
|
config := transformer.Config
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import (
|
|||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/constants"
|
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/factories/event"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/factories/event"
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/mocks"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/mocks"
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/test_data"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/test_data"
|
||||||
@@ -60,14 +59,14 @@ var _ = Describe("Transformer", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("marks header checked if no logs returned", func() {
|
It("marks header checked if no logs returned", func() {
|
||||||
err := t.Execute([]types.Log{}, headerOne, constants.HeaderMissing)
|
err := t.Execute([]types.Log{}, headerOne)
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
repository.AssertMarkHeaderCheckedCalledWith(headerOne.Id)
|
repository.AssertMarkHeaderCheckedCalledWith(headerOne.Id)
|
||||||
})
|
})
|
||||||
|
|
||||||
It("doesn't attempt to convert or persist an empty collection when there are no logs", func() {
|
It("doesn't attempt to convert or persist an empty collection when there are no logs", func() {
|
||||||
err := t.Execute([]types.Log{}, headerOne, constants.HeaderMissing)
|
err := t.Execute([]types.Log{}, headerOne)
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(converter.ToEntitiesCalledCounter).To(Equal(0))
|
Expect(converter.ToEntitiesCalledCounter).To(Equal(0))
|
||||||
@@ -76,7 +75,7 @@ var _ = Describe("Transformer", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("does not call repository.MarkCheckedHeader when there are logs", func() {
|
It("does not call repository.MarkCheckedHeader when there are logs", func() {
|
||||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
err := t.Execute(logs, headerOne)
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
repository.AssertMarkHeaderCheckedNotCalled()
|
repository.AssertMarkHeaderCheckedNotCalled()
|
||||||
@@ -85,14 +84,14 @@ var _ = Describe("Transformer", func() {
|
|||||||
It("returns error if marking header checked returns err", func() {
|
It("returns error if marking header checked returns err", func() {
|
||||||
repository.SetMarkHeaderCheckedError(fakes.FakeError)
|
repository.SetMarkHeaderCheckedError(fakes.FakeError)
|
||||||
|
|
||||||
err := t.Execute([]types.Log{}, headerOne, constants.HeaderMissing)
|
err := t.Execute([]types.Log{}, headerOne)
|
||||||
|
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(err).To(MatchError(fakes.FakeError))
|
Expect(err).To(MatchError(fakes.FakeError))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("converts an eth log to an entity", func() {
|
It("converts an eth log to an entity", func() {
|
||||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
err := t.Execute(logs, headerOne)
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(converter.ContractAbi).To(Equal(config.ContractAbi))
|
Expect(converter.ContractAbi).To(Equal(config.ContractAbi))
|
||||||
@@ -102,7 +101,7 @@ var _ = Describe("Transformer", func() {
|
|||||||
It("returns an error if converter fails", func() {
|
It("returns an error if converter fails", func() {
|
||||||
converter.ToEntitiesError = fakes.FakeError
|
converter.ToEntitiesError = fakes.FakeError
|
||||||
|
|
||||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
err := t.Execute(logs, headerOne)
|
||||||
|
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(err).To(MatchError(fakes.FakeError))
|
Expect(err).To(MatchError(fakes.FakeError))
|
||||||
@@ -111,7 +110,7 @@ var _ = Describe("Transformer", func() {
|
|||||||
It("converts an entity to a model", func() {
|
It("converts an entity to a model", func() {
|
||||||
converter.EntitiesToReturn = []interface{}{test_data.GenericEntity{}}
|
converter.EntitiesToReturn = []interface{}{test_data.GenericEntity{}}
|
||||||
|
|
||||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
err := t.Execute(logs, headerOne)
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(converter.EntitiesToConvert[0]).To(Equal(test_data.GenericEntity{}))
|
Expect(converter.EntitiesToConvert[0]).To(Equal(test_data.GenericEntity{}))
|
||||||
@@ -121,7 +120,7 @@ var _ = Describe("Transformer", func() {
|
|||||||
converter.EntitiesToReturn = []interface{}{test_data.GenericEntity{}}
|
converter.EntitiesToReturn = []interface{}{test_data.GenericEntity{}}
|
||||||
converter.ToModelsError = fakes.FakeError
|
converter.ToModelsError = fakes.FakeError
|
||||||
|
|
||||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
err := t.Execute(logs, headerOne)
|
||||||
|
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(err).To(MatchError(fakes.FakeError))
|
Expect(err).To(MatchError(fakes.FakeError))
|
||||||
@@ -130,7 +129,7 @@ var _ = Describe("Transformer", func() {
|
|||||||
It("persists the record", func() {
|
It("persists the record", func() {
|
||||||
converter.ModelsToReturn = []interface{}{test_data.GenericModel{}}
|
converter.ModelsToReturn = []interface{}{test_data.GenericModel{}}
|
||||||
|
|
||||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
err := t.Execute(logs, headerOne)
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(repository.PassedHeaderID).To(Equal(headerOne.Id))
|
Expect(repository.PassedHeaderID).To(Equal(headerOne.Id))
|
||||||
@@ -139,7 +138,7 @@ var _ = Describe("Transformer", func() {
|
|||||||
|
|
||||||
It("returns error if persisting the record fails", func() {
|
It("returns error if persisting the record fails", func() {
|
||||||
repository.SetCreateError(fakes.FakeError)
|
repository.SetCreateError(fakes.FakeError)
|
||||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
err := t.Execute(logs, headerOne)
|
||||||
|
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(err).To(MatchError(fakes.FakeError))
|
Expect(err).To(MatchError(fakes.FakeError))
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ type Repository interface {
|
|||||||
A contract-specific implementation of the repository interface enables the transformer to write the decoded storage value to the appropriate table in postgres.
|
A contract-specific implementation of the repository interface enables the transformer to write the decoded storage value to the appropriate table in postgres.
|
||||||
|
|
||||||
The `Create` function is expected to recognize and persist a given storage value by the variable's name, as indicated on the row's metadata.
|
The `Create` function is expected to recognize and persist a given storage value by the variable's name, as indicated on the row's metadata.
|
||||||
|
Note: we advise silently discarding duplicates in `Create` - as it's possible that you may read the same diff several times, and an error will trigger the storage watcher to queue that diff for later processing.
|
||||||
|
|
||||||
The `SetDB` function is required for the repository to connect to the database.
|
The `SetDB` function is required for the repository to connect to the database.
|
||||||
|
|
||||||
|
|||||||
@@ -24,22 +24,22 @@ import (
|
|||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LogFetcher interface {
|
type ILogFetcher interface {
|
||||||
FetchLogs(contractAddresses []common.Address, topics []common.Hash, missingHeader core.Header) ([]types.Log, error)
|
FetchLogs(contractAddresses []common.Address, topics []common.Hash, missingHeader core.Header) ([]types.Log, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Fetcher struct {
|
type LogFetcher struct {
|
||||||
blockChain core.BlockChain
|
blockChain core.BlockChain
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFetcher(blockchain core.BlockChain) *Fetcher {
|
func NewLogFetcher(blockchain core.BlockChain) *LogFetcher {
|
||||||
return &Fetcher{
|
return &LogFetcher{
|
||||||
blockChain: blockchain,
|
blockChain: blockchain,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks all topic0s, on all addresses, fetching matching logs for the given header
|
// Checks all topic0s, on all addresses, fetching matching logs for the given header
|
||||||
func (fetcher Fetcher) FetchLogs(addresses []common.Address, topic0s []common.Hash, header core.Header) ([]types.Log, error) {
|
func (logFetcher LogFetcher) FetchLogs(addresses []common.Address, topic0s []common.Hash, header core.Header) ([]types.Log, error) {
|
||||||
blockHash := common.HexToHash(header.Hash)
|
blockHash := common.HexToHash(header.Hash)
|
||||||
query := ethereum.FilterQuery{
|
query := ethereum.FilterQuery{
|
||||||
BlockHash: &blockHash,
|
BlockHash: &blockHash,
|
||||||
@@ -48,7 +48,7 @@ func (fetcher Fetcher) FetchLogs(addresses []common.Address, topic0s []common.Ha
|
|||||||
Topics: [][]common.Hash{topic0s},
|
Topics: [][]common.Hash{topic0s},
|
||||||
}
|
}
|
||||||
|
|
||||||
logs, err := fetcher.blockChain.GetEthLogsWithCustomQuery(query)
|
logs, err := logFetcher.blockChain.GetEthLogsWithCustomQuery(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO review aggregate fetching error handling
|
// TODO review aggregate fetching error handling
|
||||||
return []types.Log{}, err
|
return []types.Log{}, err
|
||||||
|
|||||||
@@ -22,16 +22,16 @@ import (
|
|||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
fetch "github.com/vulcanize/vulcanizedb/libraries/shared/fetcher"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/fetcher"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("Fetcher", func() {
|
var _ = Describe("LogFetcher", func() {
|
||||||
Describe("FetchLogs", func() {
|
Describe("FetchLogs", func() {
|
||||||
It("fetches logs based on the given query", func() {
|
It("fetches logs based on the given query", func() {
|
||||||
blockChain := fakes.NewMockBlockChain()
|
blockChain := fakes.NewMockBlockChain()
|
||||||
fetcher := fetch.NewFetcher(blockChain)
|
logFetcher := fetcher.NewLogFetcher(blockChain)
|
||||||
header := fakes.FakeHeader
|
header := fakes.FakeHeader
|
||||||
|
|
||||||
addresses := []common.Address{
|
addresses := []common.Address{
|
||||||
@@ -41,7 +41,7 @@ var _ = Describe("Fetcher", func() {
|
|||||||
|
|
||||||
topicZeros := []common.Hash{common.BytesToHash([]byte{1, 2, 3, 4, 5})}
|
topicZeros := []common.Hash{common.BytesToHash([]byte{1, 2, 3, 4, 5})}
|
||||||
|
|
||||||
_, err := fetcher.FetchLogs(addresses, topicZeros, header)
|
_, err := logFetcher.FetchLogs(addresses, topicZeros, header)
|
||||||
|
|
||||||
address1 := common.HexToAddress("0xfakeAddress")
|
address1 := common.HexToAddress("0xfakeAddress")
|
||||||
address2 := common.HexToAddress("0xanotherFakeAddress")
|
address2 := common.HexToAddress("0xanotherFakeAddress")
|
||||||
@@ -59,9 +59,9 @@ var _ = Describe("Fetcher", func() {
|
|||||||
It("returns an error if fetching the logs fails", func() {
|
It("returns an error if fetching the logs fails", func() {
|
||||||
blockChain := fakes.NewMockBlockChain()
|
blockChain := fakes.NewMockBlockChain()
|
||||||
blockChain.SetGetEthLogsWithCustomQueryErr(fakes.FakeError)
|
blockChain.SetGetEthLogsWithCustomQueryErr(fakes.FakeError)
|
||||||
fetcher := fetch.NewFetcher(blockChain)
|
logFetcher := fetcher.NewLogFetcher(blockChain)
|
||||||
|
|
||||||
_, err := fetcher.FetchLogs([]common.Address{}, []common.Hash{}, core.Header{})
|
_, err := logFetcher.FetchLogs([]common.Address{}, []common.Hash{}, core.Header{})
|
||||||
|
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(err).To(MatchError(fakes.FakeError))
|
Expect(err).To(MatchError(fakes.FakeError))
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// VulcanizeDB
|
||||||
|
// Copyright © 2019 Vulcanize
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package fetcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/fs"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IStorageFetcher interface {
|
||||||
|
FetchStorageDiffs(chan<- utils.StorageDiffRow, chan<- error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CsvTailStorageFetcher struct {
|
||||||
|
tailer fs.Tailer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCsvTailStorageFetcher(tailer fs.Tailer) CsvTailStorageFetcher {
|
||||||
|
return CsvTailStorageFetcher{tailer: tailer}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (storageFetcher CsvTailStorageFetcher) FetchStorageDiffs(out chan<- utils.StorageDiffRow, errs chan<- error) {
|
||||||
|
t, tailErr := storageFetcher.tailer.Tail()
|
||||||
|
if tailErr != nil {
|
||||||
|
errs <- tailErr
|
||||||
|
}
|
||||||
|
for line := range t.Lines {
|
||||||
|
row, parseErr := utils.FromStrings(strings.Split(line.Text, ","))
|
||||||
|
if parseErr != nil {
|
||||||
|
errs <- parseErr
|
||||||
|
} else {
|
||||||
|
out <- row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
// VulcanizeDB
|
||||||
|
// Copyright © 2019 Vulcanize
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package fetcher_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/hpcloud/tail"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
|
"github.com/vulcanize/vulcanizedb/libraries/shared/fetcher"
|
||||||
|
"github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("Csv Tail Storage Fetcher", func() {
|
||||||
|
var (
|
||||||
|
errorsChannel chan error
|
||||||
|
mockTailer *fakes.MockTailer
|
||||||
|
rowsChannel chan utils.StorageDiffRow
|
||||||
|
storageFetcher fetcher.CsvTailStorageFetcher
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
errorsChannel = make(chan error)
|
||||||
|
rowsChannel = make(chan utils.StorageDiffRow)
|
||||||
|
mockTailer = fakes.NewMockTailer()
|
||||||
|
storageFetcher = fetcher.NewCsvTailStorageFetcher(mockTailer)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("adds error to errors channel if tailing file fails", func(done Done) {
|
||||||
|
mockTailer.TailErr = fakes.FakeError
|
||||||
|
|
||||||
|
go storageFetcher.FetchStorageDiffs(rowsChannel, errorsChannel)
|
||||||
|
|
||||||
|
Expect(<-errorsChannel).To(MatchError(fakes.FakeError))
|
||||||
|
close(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("adds parsed csv row to rows channel for storage diff", func(done Done) {
|
||||||
|
line := getFakeLine()
|
||||||
|
|
||||||
|
go storageFetcher.FetchStorageDiffs(rowsChannel, errorsChannel)
|
||||||
|
mockTailer.Lines <- line
|
||||||
|
|
||||||
|
expectedRow, err := utils.FromStrings(strings.Split(line.Text, ","))
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(<-rowsChannel).To(Equal(expectedRow))
|
||||||
|
close(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("adds error to errors channel if parsing csv fails", func(done Done) {
|
||||||
|
line := &tail.Line{Text: "invalid"}
|
||||||
|
|
||||||
|
go storageFetcher.FetchStorageDiffs(rowsChannel, errorsChannel)
|
||||||
|
mockTailer.Lines <- line
|
||||||
|
|
||||||
|
Expect(<-errorsChannel).To(HaveOccurred())
|
||||||
|
select {
|
||||||
|
case <-rowsChannel:
|
||||||
|
Fail("value passed to rows channel on error")
|
||||||
|
default:
|
||||||
|
Succeed()
|
||||||
|
}
|
||||||
|
close(done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
func getFakeLine() *tail.Line {
|
||||||
|
address := common.HexToAddress("0x1234567890abcdef")
|
||||||
|
blockHash := []byte{4, 5, 6}
|
||||||
|
blockHeight := int64(789)
|
||||||
|
storageKey := []byte{9, 8, 7}
|
||||||
|
storageValue := []byte{6, 5, 4}
|
||||||
|
return &tail.Line{
|
||||||
|
Text: fmt.Sprintf("%s,%s,%d,%s,%s", common.Bytes2Hex(address.Bytes()), common.Bytes2Hex(blockHash),
|
||||||
|
blockHeight, common.Bytes2Hex(storageKey), common.Bytes2Hex(storageValue)),
|
||||||
|
Time: time.Time{},
|
||||||
|
Err: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,6 +53,6 @@ func (converter *MockConverter) SetToEntityConverterError(err error) {
|
|||||||
converter.entityConverterError = err
|
converter.entityConverterError = err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MockConverter) SetToModelConverterError(err error) {
|
func (converter *MockConverter) SetToModelConverterError(err error) {
|
||||||
c.modelConverterError = err
|
converter.modelConverterError = err
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-4
@@ -14,10 +14,26 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package event
|
package mocks
|
||||||
|
|
||||||
import "github.com/ethereum/go-ethereum/core/types"
|
import "github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
|
||||||
|
|
||||||
type LogNoteConverter interface {
|
type MockStorageFetcher struct {
|
||||||
ToModels(ethLog []types.Log) ([]interface{}, error)
|
RowsToReturn []utils.StorageDiffRow
|
||||||
|
ErrsToReturn []error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMockStorageFetcher() *MockStorageFetcher {
|
||||||
|
return &MockStorageFetcher{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fetcher *MockStorageFetcher) FetchStorageDiffs(out chan<- utils.StorageDiffRow, errs chan<- error) {
|
||||||
|
defer close(out)
|
||||||
|
defer close(errs)
|
||||||
|
for _, err := range fetcher.ErrsToReturn {
|
||||||
|
errs <- err
|
||||||
|
}
|
||||||
|
for _, row := range fetcher.RowsToReturn {
|
||||||
|
out <- row
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -21,11 +21,26 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type MockStorageQueue struct {
|
type MockStorageQueue struct {
|
||||||
AddCalled bool
|
AddCalled bool
|
||||||
AddError error
|
AddError error
|
||||||
|
AddPassedRow utils.StorageDiffRow
|
||||||
|
DeleteErr error
|
||||||
|
DeletePassedId int
|
||||||
|
GetAllErr error
|
||||||
|
RowsToReturn []utils.StorageDiffRow
|
||||||
}
|
}
|
||||||
|
|
||||||
func (queue *MockStorageQueue) Add(row utils.StorageDiffRow) error {
|
func (queue *MockStorageQueue) Add(row utils.StorageDiffRow) error {
|
||||||
queue.AddCalled = true
|
queue.AddCalled = true
|
||||||
|
queue.AddPassedRow = row
|
||||||
return queue.AddError
|
return queue.AddError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (queue *MockStorageQueue) Delete(id int) error {
|
||||||
|
queue.DeletePassedId = id
|
||||||
|
return queue.DeleteErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue *MockStorageQueue) GetAll() ([]utils.StorageDiffRow, error) {
|
||||||
|
return queue.RowsToReturn, queue.GetAllErr
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package mocks
|
|||||||
import (
|
import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/constants"
|
|
||||||
shared_t "github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
shared_t "github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
@@ -33,7 +32,7 @@ type MockTransformer struct {
|
|||||||
config shared_t.EventTransformerConfig
|
config shared_t.EventTransformerConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mh *MockTransformer) Execute(logs []types.Log, header core.Header, recheckHeaders constants.TransformerExecution) error {
|
func (mh *MockTransformer) Execute(logs []types.Log, header core.Header) error {
|
||||||
if mh.ExecuteError != nil {
|
if mh.ExecuteError != nil {
|
||||||
return mh.ExecuteError
|
return mh.ExecuteError
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,31 +69,6 @@ func MissingHeaders(startingBlockNumber, endingBlockNumber int64, db *postgres.D
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func RecheckHeaders(startingBlockNumber, endingBlockNumber int64, db *postgres.DB, checkedHeadersColumn string) ([]core.Header, error) {
|
|
||||||
var result []core.Header
|
|
||||||
var query string
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if endingBlockNumber == -1 {
|
|
||||||
query = `SELECT headers.id, headers.block_number, headers.hash FROM headers
|
|
||||||
LEFT JOIN checked_headers on headers.id = header_id
|
|
||||||
WHERE ` + checkedHeadersColumn + ` between 1 and ` + constants.RecheckHeaderCap + `
|
|
||||||
AND headers.block_number >= $1
|
|
||||||
AND headers.eth_node_fingerprint = $2`
|
|
||||||
err = db.Select(&result, query, startingBlockNumber, db.Node.ID)
|
|
||||||
} else {
|
|
||||||
query = `SELECT headers.id, headers.block_number, headers.hash FROM headers
|
|
||||||
LEFT JOIN checked_headers on headers.id = header_id
|
|
||||||
WHERE ` + checkedHeadersColumn + ` between 1 and ` + constants.RecheckHeaderCap + `
|
|
||||||
AND headers.block_number >= $1
|
|
||||||
AND headers.block_number <= $2
|
|
||||||
AND headers.eth_node_fingerprint = $3`
|
|
||||||
err = db.Select(&result, query, startingBlockNumber, endingBlockNumber, db.Node.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetCheckedColumnNames(db *postgres.DB) ([]string, error) {
|
func GetCheckedColumnNames(db *postgres.DB) ([]string, error) {
|
||||||
// Query returns `[]driver.Value`, nullable polymorphic interface
|
// Query returns `[]driver.Value`, nullable polymorphic interface
|
||||||
var queryResult []driver.Value
|
var queryResult []driver.Value
|
||||||
@@ -121,37 +96,47 @@ func GetCheckedColumnNames(db *postgres.DB) ([]string, error) {
|
|||||||
return columnNames, nil
|
return columnNames, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Builds a SQL string that checks if any column value is 0, given the column names.
|
// Builds a SQL string that checks if any column should be checked/rechecked.
|
||||||
// Defaults to FALSE when no columns are provided.
|
// Defaults to FALSE when no columns are provided.
|
||||||
// Ex: ["columnA", "columnB"] => "NOT (columnA!=0 AND columnB!=0)"
|
// Ex: ["columnA", "columnB"] => "NOT (columnA!=0 AND columnB!=0)"
|
||||||
// [] => "FALSE"
|
// [] => "FALSE"
|
||||||
func CreateNotCheckedSQL(boolColumns []string, recheckHeaders constants.TransformerExecution) string {
|
func CreateHeaderCheckedPredicateSQL(boolColumns []string, recheckHeaders constants.TransformerExecution) string {
|
||||||
|
|
||||||
var result bytes.Buffer
|
|
||||||
|
|
||||||
if len(boolColumns) == 0 {
|
if len(boolColumns) == 0 {
|
||||||
return "FALSE"
|
return "FALSE"
|
||||||
}
|
}
|
||||||
|
|
||||||
result.WriteString("NOT (")
|
if recheckHeaders {
|
||||||
|
return createHeaderCheckedPredicateSQLForRecheckedHeaders(boolColumns)
|
||||||
|
} else {
|
||||||
|
return createHeaderCheckedPredicateSQLForMissingHeaders(boolColumns)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createHeaderCheckedPredicateSQLForMissingHeaders(boolColumns []string) string {
|
||||||
|
var result bytes.Buffer
|
||||||
|
result.WriteString(" (")
|
||||||
|
|
||||||
// Loop excluding last column name
|
// Loop excluding last column name
|
||||||
for _, column := range boolColumns[:len(boolColumns)-1] {
|
for _, column := range boolColumns[:len(boolColumns)-1] {
|
||||||
|
result.WriteString(fmt.Sprintf("%v=0 OR ", column))
|
||||||
if recheckHeaders {
|
|
||||||
result.WriteString(fmt.Sprintf("%v>=%s AND ", column, constants.RecheckHeaderCap))
|
|
||||||
} else {
|
|
||||||
result.WriteString(fmt.Sprintf("%v!=0 AND ", column))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No trailing "OR" for the last column name
|
result.WriteString(fmt.Sprintf("%v=0)", boolColumns[len(boolColumns)-1]))
|
||||||
if recheckHeaders {
|
|
||||||
result.WriteString(fmt.Sprintf("%v>=%s)", boolColumns[len(boolColumns)-1], constants.RecheckHeaderCap))
|
return result.String()
|
||||||
} else {
|
}
|
||||||
result.WriteString(fmt.Sprintf("%v!=0)", boolColumns[len(boolColumns)-1]))
|
|
||||||
|
func createHeaderCheckedPredicateSQLForRecheckedHeaders(boolColumns []string) string {
|
||||||
|
var result bytes.Buffer
|
||||||
|
result.WriteString(" (")
|
||||||
|
|
||||||
|
// Loop excluding last column name
|
||||||
|
for _, column := range boolColumns[:len(boolColumns)-1] {
|
||||||
|
result.WriteString(fmt.Sprintf("%v<%s OR ", column, constants.RecheckHeaderCap))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No trailing "OR" for the last column name
|
||||||
|
result.WriteString(fmt.Sprintf("%v<%s)", boolColumns[len(boolColumns)-1], constants.RecheckHeaderCap))
|
||||||
|
|
||||||
return result.String()
|
return result.String()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import (
|
|||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/constants"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/constants"
|
||||||
shared "github.com/vulcanize/vulcanizedb/libraries/shared/repository"
|
shared "github.com/vulcanize/vulcanizedb/libraries/shared/repository"
|
||||||
r2 "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/light/repository"
|
r2 "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/repository"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore"
|
"github.com/vulcanize/vulcanizedb/pkg/datastore"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
@@ -36,12 +36,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("Repository", func() {
|
var _ = Describe("Repository", func() {
|
||||||
Describe("MarkHeaderChecked", func() {
|
var (
|
||||||
var (
|
checkedHeadersColumn string
|
||||||
checkedHeadersColumn string
|
db *postgres.DB
|
||||||
db *postgres.DB
|
)
|
||||||
)
|
|
||||||
|
|
||||||
|
Describe("MarkHeaderChecked", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
db = test_config.NewTestDB(test_config.NewTestNode())
|
db = test_config.NewTestDB(test_config.NewTestNode())
|
||||||
test_config.CleanTestDB(db)
|
test_config.CleanTestDB(db)
|
||||||
@@ -73,11 +73,6 @@ var _ = Describe("Repository", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Describe("MarkHeaderCheckedInTransaction", func() {
|
Describe("MarkHeaderCheckedInTransaction", func() {
|
||||||
var (
|
|
||||||
checkedHeadersColumn string
|
|
||||||
db *postgres.DB
|
|
||||||
)
|
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
db = test_config.NewTestDB(test_config.NewTestNode())
|
db = test_config.NewTestDB(test_config.NewTestNode())
|
||||||
test_config.CleanTestDB(db)
|
test_config.CleanTestDB(db)
|
||||||
@@ -114,16 +109,17 @@ var _ = Describe("Repository", func() {
|
|||||||
|
|
||||||
Describe("MissingHeaders", func() {
|
Describe("MissingHeaders", func() {
|
||||||
var (
|
var (
|
||||||
db *postgres.DB
|
|
||||||
headerRepository datastore.HeaderRepository
|
headerRepository datastore.HeaderRepository
|
||||||
startingBlockNumber int64
|
startingBlockNumber int64
|
||||||
endingBlockNumber int64
|
endingBlockNumber int64
|
||||||
eventSpecificBlockNumber int64
|
eventSpecificBlockNumber int64
|
||||||
|
outOfRangeBlockNumber int64
|
||||||
blockNumbers []int64
|
blockNumbers []int64
|
||||||
headerIDs []int64
|
headerIDs []int64
|
||||||
notCheckedSQL string
|
notCheckedSQL string
|
||||||
err error
|
err error
|
||||||
hr r2.HeaderRepository
|
hr r2.HeaderRepository
|
||||||
|
columnNames []string
|
||||||
)
|
)
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
@@ -133,14 +129,14 @@ var _ = Describe("Repository", func() {
|
|||||||
hr = r2.NewHeaderRepository(db)
|
hr = r2.NewHeaderRepository(db)
|
||||||
hr.AddCheckColumns(getExpectedColumnNames())
|
hr.AddCheckColumns(getExpectedColumnNames())
|
||||||
|
|
||||||
columnNames, err := shared.GetCheckedColumnNames(db)
|
columnNames, err = shared.GetCheckedColumnNames(db)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
notCheckedSQL = shared.CreateNotCheckedSQL(columnNames, constants.HeaderMissing)
|
notCheckedSQL = shared.CreateHeaderCheckedPredicateSQL(columnNames, constants.HeaderMissing)
|
||||||
|
|
||||||
startingBlockNumber = rand.Int63()
|
startingBlockNumber = rand.Int63()
|
||||||
eventSpecificBlockNumber = startingBlockNumber + 1
|
eventSpecificBlockNumber = startingBlockNumber + 1
|
||||||
endingBlockNumber = startingBlockNumber + 2
|
endingBlockNumber = startingBlockNumber + 2
|
||||||
outOfRangeBlockNumber := endingBlockNumber + 1
|
outOfRangeBlockNumber = endingBlockNumber + 1
|
||||||
|
|
||||||
blockNumbers = []int64{startingBlockNumber, eventSpecificBlockNumber, endingBlockNumber, outOfRangeBlockNumber}
|
blockNumbers = []int64{startingBlockNumber, eventSpecificBlockNumber, endingBlockNumber, outOfRangeBlockNumber}
|
||||||
|
|
||||||
@@ -157,6 +153,7 @@ var _ = Describe("Repository", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("only treats headers as checked if the event specific logs have been checked", func() {
|
It("only treats headers as checked if the event specific logs have been checked", func() {
|
||||||
|
//add a checked_header record, but don't mark it check for any of the columns
|
||||||
_, err = db.Exec(`INSERT INTO public.checked_headers (header_id) VALUES ($1)`, headerIDs[1])
|
_, err = db.Exec(`INSERT INTO public.checked_headers (header_id) VALUES ($1)`, headerIDs[1])
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
@@ -191,83 +188,47 @@ var _ = Describe("Repository", func() {
|
|||||||
Expect(nodeTwoMissingHeaders[0].BlockNumber).To(Or(Equal(startingBlockNumber+10), Equal(eventSpecificBlockNumber+10), Equal(endingBlockNumber+10)))
|
Expect(nodeTwoMissingHeaders[0].BlockNumber).To(Or(Equal(startingBlockNumber+10), Equal(eventSpecificBlockNumber+10), Equal(endingBlockNumber+10)))
|
||||||
Expect(nodeTwoMissingHeaders[1].BlockNumber).To(Or(Equal(startingBlockNumber+10), Equal(eventSpecificBlockNumber+10), Equal(endingBlockNumber+10)))
|
Expect(nodeTwoMissingHeaders[1].BlockNumber).To(Or(Equal(startingBlockNumber+10), Equal(eventSpecificBlockNumber+10), Equal(endingBlockNumber+10)))
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
Describe("RecheckHeaders", func() {
|
It("handles an ending block of -1 ", func() {
|
||||||
var (
|
endingBlock := int64(-1)
|
||||||
checkedHeadersColumn string
|
headers, err := shared.MissingHeaders(startingBlockNumber, endingBlock, db, notCheckedSQL)
|
||||||
db *postgres.DB
|
|
||||||
headerOneID, headerTwoID, headerThreeID, headerFourID int64
|
|
||||||
headerOneErr, headerTwoErr, headerThreeErr, headerFourErr error
|
|
||||||
)
|
|
||||||
|
|
||||||
BeforeEach(func() {
|
Expect(err).NotTo(HaveOccurred())
|
||||||
db = test_config.NewTestDB(test_config.NewTestNode())
|
Expect(len(headers)).To(Equal(4))
|
||||||
test_config.CleanTestDB(db)
|
Expect(headers[0].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(eventSpecificBlockNumber), Equal(outOfRangeBlockNumber)))
|
||||||
|
Expect(headers[1].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(eventSpecificBlockNumber), Equal(outOfRangeBlockNumber)))
|
||||||
|
Expect(headers[2].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(eventSpecificBlockNumber), Equal(outOfRangeBlockNumber)))
|
||||||
|
Expect(headers[3].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(eventSpecificBlockNumber), Equal(outOfRangeBlockNumber)))
|
||||||
|
|
||||||
// create header checked column
|
})
|
||||||
checkedHeadersColumn = "test_column_checked"
|
|
||||||
_, migrateErr := db.Exec(`ALTER TABLE public.checked_headers ADD COLUMN ` + checkedHeadersColumn + ` integer`)
|
|
||||||
Expect(migrateErr).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
// create headers
|
It("when a the `notCheckedSQL` argument allows for rechecks it returns headers where the checked count is less than the maximum", func() {
|
||||||
headerRepository := repositories.NewHeaderRepository(db)
|
columnName := columnNames[0]
|
||||||
headerOneID, headerOneErr = headerRepository.CreateOrUpdateHeader(fakes.GetFakeHeader(1))
|
recheckedSQL := shared.CreateHeaderCheckedPredicateSQL([]string{columnName}, constants.HeaderRecheck)
|
||||||
Expect(headerOneErr).NotTo(HaveOccurred())
|
// mark every header checked at least once
|
||||||
headerTwoID, headerTwoErr = headerRepository.CreateOrUpdateHeader(fakes.GetFakeHeader(2))
|
// header 4 is marked the maximum number of times, it it is not longer checked
|
||||||
Expect(headerTwoErr).NotTo(HaveOccurred())
|
|
||||||
headerThreeID, headerThreeErr = headerRepository.CreateOrUpdateHeader(fakes.GetFakeHeader(3))
|
|
||||||
Expect(headerThreeErr).NotTo(HaveOccurred())
|
|
||||||
headerFourID, headerFourErr = headerRepository.CreateOrUpdateHeader(fakes.GetFakeHeader(4))
|
|
||||||
Expect(headerFourErr).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
// mark every header checked at least once, with one fully rechecked (headerThree)
|
|
||||||
maxCheckCount, intConversionErr := strconv.Atoi(constants.RecheckHeaderCap)
|
maxCheckCount, intConversionErr := strconv.Atoi(constants.RecheckHeaderCap)
|
||||||
Expect(intConversionErr).NotTo(HaveOccurred())
|
Expect(intConversionErr).NotTo(HaveOccurred())
|
||||||
_, markHeaderOneCheckedErr := db.Exec(
|
|
||||||
`INSERT INTO public.checked_headers (header_id, `+checkedHeadersColumn+`) VALUES ($1, $2)`,
|
|
||||||
headerOneID, maxCheckCount)
|
|
||||||
Expect(markHeaderOneCheckedErr).NotTo(HaveOccurred())
|
|
||||||
_, markHeaderTwoCheckedErr := db.Exec(
|
|
||||||
`INSERT INTO public.checked_headers (header_id, `+checkedHeadersColumn+`) VALUES ($1, $2)`,
|
|
||||||
headerTwoID, maxCheckCount)
|
|
||||||
Expect(markHeaderTwoCheckedErr).NotTo(HaveOccurred())
|
|
||||||
_, markHeaderThreeCheckedErr := db.Exec(
|
|
||||||
`INSERT INTO public.checked_headers (header_id, `+checkedHeadersColumn+`) VALUES ($1, $2)`,
|
|
||||||
headerThreeID, maxCheckCount+1)
|
|
||||||
Expect(markHeaderThreeCheckedErr).NotTo(HaveOccurred())
|
|
||||||
_, markHeaderFourCheckedErr := db.Exec(
|
|
||||||
`INSERT INTO public.checked_headers (header_id, `+checkedHeadersColumn+`) VALUES ($1, $2)`,
|
|
||||||
headerFourID, maxCheckCount)
|
|
||||||
Expect(markHeaderFourCheckedErr).NotTo(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
AfterEach(func() {
|
markHeaderOneErr := shared.MarkHeaderChecked(headerIDs[0], db, columnName)
|
||||||
_, cleanupMigrateErr := db.Exec(`ALTER TABLE public.checked_headers DROP COLUMN ` + checkedHeadersColumn)
|
Expect(markHeaderOneErr).NotTo(HaveOccurred())
|
||||||
Expect(cleanupMigrateErr).NotTo(HaveOccurred())
|
markHeaderTwoErr := shared.MarkHeaderChecked(headerIDs[1], db, columnName)
|
||||||
})
|
Expect(markHeaderTwoErr).NotTo(HaveOccurred())
|
||||||
|
markHeaderThreeErr := shared.MarkHeaderChecked(headerIDs[2], db, columnName)
|
||||||
|
Expect(markHeaderThreeErr).NotTo(HaveOccurred())
|
||||||
|
for i := 0; i <= maxCheckCount; i++ {
|
||||||
|
markHeaderFourErr := shared.MarkHeaderChecked(headerIDs[3], db, columnName)
|
||||||
|
Expect(markHeaderFourErr).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
Describe("when no ending block number (ending block number == -1)", func() {
|
headers, err := shared.MissingHeaders(1, -1, db, recheckedSQL)
|
||||||
It("returns all headers since starting block where checked count is less than cap", func() {
|
|
||||||
headers, err := shared.RecheckHeaders(1, -1, db, checkedHeadersColumn)
|
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(len(headers)).To(Equal(3))
|
Expect(len(headers)).To(Equal(3))
|
||||||
Expect(headers[0].Id).To(Or(Equal(headerOneID), Equal(headerTwoID), Equal(headerFourID)))
|
Expect(headers[0].Id).To(Or(Equal(headerIDs[0]), Equal(headerIDs[1]), Equal(headerIDs[2])))
|
||||||
Expect(headers[1].Id).To(Or(Equal(headerOneID), Equal(headerTwoID), Equal(headerFourID)))
|
Expect(headers[1].Id).To(Or(Equal(headerIDs[0]), Equal(headerIDs[1]), Equal(headerIDs[2])))
|
||||||
Expect(headers[2].Id).To(Or(Equal(headerOneID), Equal(headerTwoID), Equal(headerFourID)))
|
Expect(headers[2].Id).To(Or(Equal(headerIDs[0]), Equal(headerIDs[1]), Equal(headerIDs[2])))
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
Describe("when ending block number specified", func() {
|
|
||||||
It("returns headers between starting and ending block where checked count is less than cap", func() {
|
|
||||||
headers, err := shared.RecheckHeaders(1, 3, db, checkedHeadersColumn)
|
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
Expect(len(headers)).To(Equal(2))
|
|
||||||
Expect(headers[0].Id).To(Or(Equal(headerOneID), Equal(headerTwoID)))
|
|
||||||
Expect(headers[1].Id).To(Or(Equal(headerOneID), Equal(headerTwoID)))
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -285,32 +246,49 @@ var _ = Describe("Repository", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("CreateNotCheckedSQL", func() {
|
Describe("CreateHeaderCheckedPredicateSQL", func() {
|
||||||
It("generates a correct SQL string for one column", func() {
|
Describe("for headers that haven't been checked for logs", func() {
|
||||||
columns := []string{"columnA"}
|
It("generates a correct SQL string for one column", func() {
|
||||||
expected := "NOT (columnA!=0)"
|
columns := []string{"columnA"}
|
||||||
actual := shared.CreateNotCheckedSQL(columns, constants.HeaderMissing)
|
expected := " (columnA=0)"
|
||||||
Expect(actual).To(Equal(expected))
|
actual := shared.CreateHeaderCheckedPredicateSQL(columns, constants.HeaderMissing)
|
||||||
|
Expect(actual).To(Equal(expected))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("generates a correct SQL string for several columns", func() {
|
||||||
|
columns := []string{"columnA", "columnB"}
|
||||||
|
expected := " (columnA=0 OR columnB=0)"
|
||||||
|
actual := shared.CreateHeaderCheckedPredicateSQL(columns, constants.HeaderMissing)
|
||||||
|
Expect(actual).To(Equal(expected))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("defaults to FALSE when there are no columns", func() {
|
||||||
|
expected := "FALSE"
|
||||||
|
actual := shared.CreateHeaderCheckedPredicateSQL([]string{}, constants.HeaderMissing)
|
||||||
|
Expect(actual).To(Equal(expected))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
It("generates a correct SQL string for several columns", func() {
|
Describe("for headers that are being rechecked for logs", func() {
|
||||||
columns := []string{"columnA", "columnB"}
|
It("generates a correct SQL string for rechecking headers for one column", func() {
|
||||||
expected := "NOT (columnA!=0 AND columnB!=0)"
|
columns := []string{"columnA"}
|
||||||
actual := shared.CreateNotCheckedSQL(columns, constants.HeaderMissing)
|
expected := fmt.Sprintf(" (columnA<%s)", constants.RecheckHeaderCap)
|
||||||
Expect(actual).To(Equal(expected))
|
actual := shared.CreateHeaderCheckedPredicateSQL(columns, constants.HeaderRecheck)
|
||||||
})
|
Expect(actual).To(Equal(expected))
|
||||||
|
})
|
||||||
|
|
||||||
It("defaults to FALSE when there are no columns", func() {
|
It("generates a correct SQL string for rechecking headers for several columns", func() {
|
||||||
expected := "FALSE"
|
columns := []string{"columnA", "columnB"}
|
||||||
actual := shared.CreateNotCheckedSQL([]string{}, constants.HeaderMissing)
|
expected := fmt.Sprintf(" (columnA<%s OR columnB<%s)", constants.RecheckHeaderCap, constants.RecheckHeaderCap)
|
||||||
Expect(actual).To(Equal(expected))
|
actual := shared.CreateHeaderCheckedPredicateSQL(columns, constants.HeaderRecheck)
|
||||||
})
|
Expect(actual).To(Equal(expected))
|
||||||
|
})
|
||||||
|
|
||||||
It("generates a correct SQL string for rechecking headers", func() {
|
It("defaults to FALSE when there are no columns", func() {
|
||||||
columns := []string{"columnA", "columnB"}
|
expected := "FALSE"
|
||||||
expected := fmt.Sprintf("NOT (columnA>=%s AND columnB>=%s)", constants.RecheckHeaderCap, constants.RecheckHeaderCap)
|
actual := shared.CreateHeaderCheckedPredicateSQL([]string{}, constants.HeaderRecheck)
|
||||||
actual := shared.CreateNotCheckedSQL(columns, constants.HeaderRecheck)
|
Expect(actual).To(Equal(expected))
|
||||||
Expect(actual).To(Equal(expected))
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func GetMapping(indexOnContract, key string) common.Hash {
|
func GetMapping(indexOnContract, key string) common.Hash {
|
||||||
keyBytes := common.FromHex("0x" + key + indexOnContract)
|
keyBytes := common.FromHex(key + indexOnContract)
|
||||||
encoded := crypto.Keccak256(keyBytes)
|
encoded := crypto.Keccak256(keyBytes)
|
||||||
return common.BytesToHash(encoded)
|
return common.BytesToHash(encoded)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,16 @@ var _ = Describe("Mappings", func() {
|
|||||||
expectedStorageKey := common.HexToHash("0xee0c1b59a3856bafbfb8730e7694c4badc271eb5f01ce4a8d7a53d8a6499676f")
|
expectedStorageKey := common.HexToHash("0xee0c1b59a3856bafbfb8730e7694c4badc271eb5f01ce4a8d7a53d8a6499676f")
|
||||||
Expect(storageKey).To(Equal(expectedStorageKey))
|
Expect(storageKey).To(Equal(expectedStorageKey))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("returns same result if value includes hex prefix", func() {
|
||||||
|
indexOfMappingOnContract := storage.IndexZero
|
||||||
|
keyForDesiredValueInMapping := "0x1234567890abcdef"
|
||||||
|
|
||||||
|
storageKey := storage.GetMapping(indexOfMappingOnContract, keyForDesiredValueInMapping)
|
||||||
|
|
||||||
|
expectedStorageKey := common.HexToHash("0xee0c1b59a3856bafbfb8730e7694c4badc271eb5f01ce4a8d7a53d8a6499676f")
|
||||||
|
Expect(storageKey).To(Equal(expectedStorageKey))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("GetNestedMapping", func() {
|
Describe("GetNestedMapping", func() {
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import (
|
|||||||
|
|
||||||
type IStorageQueue interface {
|
type IStorageQueue interface {
|
||||||
Add(row utils.StorageDiffRow) error
|
Add(row utils.StorageDiffRow) error
|
||||||
|
Delete(id int) error
|
||||||
|
GetAll() ([]utils.StorageDiffRow, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type StorageQueue struct {
|
type StorageQueue struct {
|
||||||
@@ -36,7 +38,18 @@ func NewStorageQueue(db *postgres.DB) StorageQueue {
|
|||||||
func (queue StorageQueue) Add(row utils.StorageDiffRow) error {
|
func (queue StorageQueue) Add(row utils.StorageDiffRow) error {
|
||||||
_, err := queue.db.Exec(`INSERT INTO public.queued_storage (contract,
|
_, err := queue.db.Exec(`INSERT INTO public.queued_storage (contract,
|
||||||
block_hash, block_height, storage_key, storage_value) VALUES
|
block_hash, block_height, storage_key, storage_value) VALUES
|
||||||
($1, $2, $3, $4, $5)`, row.Contract.Bytes(), row.BlockHash.Bytes(),
|
($1, $2, $3, $4, $5) ON CONFLICT DO NOTHING`, row.Contract.Bytes(), row.BlockHash.Bytes(),
|
||||||
row.BlockHeight, row.StorageKey.Bytes(), row.StorageValue.Bytes())
|
row.BlockHeight, row.StorageKey.Bytes(), row.StorageValue.Bytes())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (queue StorageQueue) Delete(id int) error {
|
||||||
|
_, err := queue.db.Exec(`DELETE FROM public.queued_storage WHERE id = $1`, id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue StorageQueue) GetAll() ([]utils.StorageDiffRow, error) {
|
||||||
|
var result []utils.StorageDiffRow
|
||||||
|
err := queue.db.Select(&result, `SELECT * FROM public.queued_storage`)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
// VulcanizeDB
|
||||||
|
// Copyright © 2019 Vulcanize
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package storage_test
|
package storage_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -7,27 +23,90 @@ import (
|
|||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/storage"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/storage"
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
|
||||||
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
"github.com/vulcanize/vulcanizedb/test_config"
|
"github.com/vulcanize/vulcanizedb/test_config"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("Storage queue", func() {
|
var _ = Describe("Storage queue", func() {
|
||||||
It("adds a storage row to the db", func() {
|
var (
|
||||||
row := utils.StorageDiffRow{
|
db *postgres.DB
|
||||||
|
row utils.StorageDiffRow
|
||||||
|
queue storage.IStorageQueue
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
row = utils.StorageDiffRow{
|
||||||
Contract: common.HexToAddress("0x123456"),
|
Contract: common.HexToAddress("0x123456"),
|
||||||
BlockHash: common.HexToHash("0x678901"),
|
BlockHash: common.HexToHash("0x678901"),
|
||||||
BlockHeight: 987,
|
BlockHeight: 987,
|
||||||
StorageKey: common.HexToHash("0x654321"),
|
StorageKey: common.HexToHash("0x654321"),
|
||||||
StorageValue: common.HexToHash("0x198765"),
|
StorageValue: common.HexToHash("0x198765"),
|
||||||
}
|
}
|
||||||
db := test_config.NewTestDB(test_config.NewTestNode())
|
db = test_config.NewTestDB(test_config.NewTestNode())
|
||||||
queue := storage.NewStorageQueue(db)
|
test_config.CleanTestDB(db)
|
||||||
|
queue = storage.NewStorageQueue(db)
|
||||||
addErr := queue.Add(row)
|
addErr := queue.Add(row)
|
||||||
|
|
||||||
Expect(addErr).NotTo(HaveOccurred())
|
Expect(addErr).NotTo(HaveOccurred())
|
||||||
var result utils.StorageDiffRow
|
})
|
||||||
getErr := db.Get(&result, `SELECT contract, block_hash, block_height, storage_key, storage_value FROM public.queued_storage`)
|
|
||||||
|
Describe("Add", func() {
|
||||||
|
It("adds a storage row to the db", func() {
|
||||||
|
var result utils.StorageDiffRow
|
||||||
|
getErr := db.Get(&result, `SELECT contract, block_hash, block_height, storage_key, storage_value FROM public.queued_storage`)
|
||||||
|
Expect(getErr).NotTo(HaveOccurred())
|
||||||
|
Expect(result).To(Equal(row))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("does not duplicate storage rows", func() {
|
||||||
|
addErr := queue.Add(row)
|
||||||
|
Expect(addErr).NotTo(HaveOccurred())
|
||||||
|
var count int
|
||||||
|
getErr := db.Get(&count, `SELECT count(*) FROM public.queued_storage`)
|
||||||
|
Expect(getErr).NotTo(HaveOccurred())
|
||||||
|
Expect(count).To(Equal(1))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
It("deletes storage row from db", func() {
|
||||||
|
rows, getErr := queue.GetAll()
|
||||||
Expect(getErr).NotTo(HaveOccurred())
|
Expect(getErr).NotTo(HaveOccurred())
|
||||||
Expect(result).To(Equal(row))
|
Expect(len(rows)).To(Equal(1))
|
||||||
|
|
||||||
|
err := queue.Delete(rows[0].Id)
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
remainingRows, secondGetErr := queue.GetAll()
|
||||||
|
Expect(secondGetErr).NotTo(HaveOccurred())
|
||||||
|
Expect(len(remainingRows)).To(BeZero())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("gets all storage rows from db", func() {
|
||||||
|
rowTwo := utils.StorageDiffRow{
|
||||||
|
Contract: common.HexToAddress("0x123456"),
|
||||||
|
BlockHash: common.HexToHash("0x678902"),
|
||||||
|
BlockHeight: 988,
|
||||||
|
StorageKey: common.HexToHash("0x654322"),
|
||||||
|
StorageValue: common.HexToHash("0x198766"),
|
||||||
|
}
|
||||||
|
addErr := queue.Add(rowTwo)
|
||||||
|
Expect(addErr).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
rows, err := queue.GetAll()
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(len(rows)).To(Equal(2))
|
||||||
|
Expect(rows[0]).NotTo(Equal(rows[1]))
|
||||||
|
Expect(rows[0].Id).NotTo(BeZero())
|
||||||
|
Expect(rows[0].Contract).To(Or(Equal(row.Contract), Equal(rowTwo.Contract)))
|
||||||
|
Expect(rows[0].BlockHash).To(Or(Equal(row.BlockHash), Equal(rowTwo.BlockHash)))
|
||||||
|
Expect(rows[0].BlockHeight).To(Or(Equal(row.BlockHeight), Equal(rowTwo.BlockHeight)))
|
||||||
|
Expect(rows[0].StorageKey).To(Or(Equal(row.StorageKey), Equal(rowTwo.StorageKey)))
|
||||||
|
Expect(rows[0].StorageValue).To(Or(Equal(row.StorageValue), Equal(rowTwo.StorageValue)))
|
||||||
|
Expect(rows[1].Id).NotTo(BeZero())
|
||||||
|
Expect(rows[1].Contract).To(Or(Equal(row.Contract), Equal(rowTwo.Contract)))
|
||||||
|
Expect(rows[1].BlockHash).To(Or(Equal(row.BlockHash), Equal(rowTwo.BlockHash)))
|
||||||
|
Expect(rows[1].BlockHeight).To(Or(Equal(row.BlockHeight), Equal(rowTwo.BlockHeight)))
|
||||||
|
Expect(rows[1].StorageKey).To(Or(Equal(row.StorageKey), Equal(rowTwo.StorageKey)))
|
||||||
|
Expect(rows[1].StorageValue).To(Or(Equal(row.StorageValue), Equal(rowTwo.StorageValue)))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import (
|
|||||||
const ExpectedRowLength = 5
|
const ExpectedRowLength = 5
|
||||||
|
|
||||||
type StorageDiffRow struct {
|
type StorageDiffRow struct {
|
||||||
|
Id int
|
||||||
Contract common.Address
|
Contract common.Address
|
||||||
BlockHash common.Hash `db:"block_hash"`
|
BlockHash common.Hash `db:"block_hash"`
|
||||||
BlockHeight int `db:"block_height"`
|
BlockHeight int `db:"block_height"`
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
// VulcanizeDB
|
||||||
|
// Copyright © 2019 Vulcanize
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package transactions
|
package transactions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -28,6 +44,9 @@ func NewTransactionsSyncer(db *postgres.DB, blockChain core.BlockChain) Transact
|
|||||||
|
|
||||||
func (syncer TransactionsSyncer) SyncTransactions(headerID int64, logs []types.Log) error {
|
func (syncer TransactionsSyncer) SyncTransactions(headerID int64, logs []types.Log) error {
|
||||||
transactionHashes := getUniqueTransactionHashes(logs)
|
transactionHashes := getUniqueTransactionHashes(logs)
|
||||||
|
if len(transactionHashes) < 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
transactions, transactionErr := syncer.BlockChain.GetTransactions(transactionHashes)
|
transactions, transactionErr := syncer.BlockChain.GetTransactions(transactionHashes)
|
||||||
if transactionErr != nil {
|
if transactionErr != nil {
|
||||||
return transactionErr
|
return transactionErr
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
// VulcanizeDB
|
||||||
|
// Copyright © 2019 Vulcanize
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package transactions_test
|
package transactions_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -24,12 +40,19 @@ var _ = Describe("Transaction syncer", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("fetches transactions for logs", func() {
|
It("fetches transactions for logs", func() {
|
||||||
err := syncer.SyncTransactions(0, []types.Log{})
|
err := syncer.SyncTransactions(0, []types.Log{{TxHash: fakes.FakeHash}})
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(blockChain.GetTransactionsCalled).To(BeTrue())
|
Expect(blockChain.GetTransactionsCalled).To(BeTrue())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("does not fetch transactions if no logs", func() {
|
||||||
|
err := syncer.SyncTransactions(0, []types.Log{})
|
||||||
|
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(blockChain.GetTransactionsCalled).To(BeFalse())
|
||||||
|
})
|
||||||
|
|
||||||
It("only fetches transactions with unique hashes", func() {
|
It("only fetches transactions with unique hashes", func() {
|
||||||
err := syncer.SyncTransactions(0, []types.Log{{
|
err := syncer.SyncTransactions(0, []types.Log{{
|
||||||
TxHash: fakes.FakeHash,
|
TxHash: fakes.FakeHash,
|
||||||
@@ -44,7 +67,7 @@ var _ = Describe("Transaction syncer", func() {
|
|||||||
It("returns error if fetching transactions fails", func() {
|
It("returns error if fetching transactions fails", func() {
|
||||||
blockChain.GetTransactionsError = fakes.FakeError
|
blockChain.GetTransactionsError = fakes.FakeError
|
||||||
|
|
||||||
err := syncer.SyncTransactions(0, []types.Log{})
|
err := syncer.SyncTransactions(0, []types.Log{{TxHash: fakes.FakeHash}})
|
||||||
|
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(err).To(MatchError(fakes.FakeError))
|
Expect(err).To(MatchError(fakes.FakeError))
|
||||||
@@ -55,7 +78,7 @@ var _ = Describe("Transaction syncer", func() {
|
|||||||
mockHeaderRepository := fakes.NewMockHeaderRepository()
|
mockHeaderRepository := fakes.NewMockHeaderRepository()
|
||||||
syncer.Repository = mockHeaderRepository
|
syncer.Repository = mockHeaderRepository
|
||||||
|
|
||||||
err := syncer.SyncTransactions(0, []types.Log{})
|
err := syncer.SyncTransactions(0, []types.Log{{TxHash: fakes.FakeHash}})
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(mockHeaderRepository.CreateTransactionsCalled).To(BeTrue())
|
Expect(mockHeaderRepository.CreateTransactionsCalled).To(BeTrue())
|
||||||
@@ -67,7 +90,7 @@ var _ = Describe("Transaction syncer", func() {
|
|||||||
mockHeaderRepository.CreateTransactionsError = fakes.FakeError
|
mockHeaderRepository.CreateTransactionsError = fakes.FakeError
|
||||||
syncer.Repository = mockHeaderRepository
|
syncer.Repository = mockHeaderRepository
|
||||||
|
|
||||||
err := syncer.SyncTransactions(0, []types.Log{})
|
err := syncer.SyncTransactions(0, []types.Log{{TxHash: fakes.FakeHash}})
|
||||||
|
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(err).To(MatchError(fakes.FakeError))
|
Expect(err).To(MatchError(fakes.FakeError))
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
// VulcanizeDB
|
||||||
|
// Copyright © 2019 Vulcanize
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package transactions_test
|
package transactions_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -20,13 +20,12 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/constants"
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EventTransformer interface {
|
type EventTransformer interface {
|
||||||
Execute(logs []types.Log, header core.Header, recheckHeaders constants.TransformerExecution) error
|
Execute(logs []types.Log, header core.Header) error
|
||||||
GetConfig() EventTransformerConfig
|
GetConfig() EventTransformerConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,10 +54,10 @@ func (watcher *ContractWatcher) AddTransformers(inits interface{}) error {
|
|||||||
watcher.Transformers = append(watcher.Transformers, t)
|
watcher.Transformers = append(watcher.Transformers, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, transformer := range watcher.Transformers {
|
for _, contractTransformer := range watcher.Transformers {
|
||||||
err := transformer.Init()
|
err := contractTransformer.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print("Unable to initialize transformer:", transformer.GetConfig().Name, err)
|
log.Print("Unable to initialize transformer:", contractTransformer.GetConfig().Name, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,10 +65,10 @@ func (watcher *ContractWatcher) AddTransformers(inits interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (watcher *ContractWatcher) Execute() error {
|
func (watcher *ContractWatcher) Execute() error {
|
||||||
for _, transformer := range watcher.Transformers {
|
for _, contractTransformer := range watcher.Transformers {
|
||||||
err := transformer.Execute()
|
err := contractTransformer.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Unable to execute transformer:", transformer.GetConfig().Name, err)
|
log.Error("Unable to execute transformer:", contractTransformer.GetConfig().Name, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ type EventWatcher struct {
|
|||||||
Transformers []transformer.EventTransformer
|
Transformers []transformer.EventTransformer
|
||||||
BlockChain core.BlockChain
|
BlockChain core.BlockChain
|
||||||
DB *postgres.DB
|
DB *postgres.DB
|
||||||
Fetcher fetcher.LogFetcher
|
Fetcher fetcher.ILogFetcher
|
||||||
Chunker chunker.Chunker
|
Chunker chunker.Chunker
|
||||||
Addresses []common.Address
|
Addresses []common.Address
|
||||||
Topics []common.Hash
|
Topics []common.Hash
|
||||||
@@ -47,7 +47,7 @@ type EventWatcher struct {
|
|||||||
|
|
||||||
func NewEventWatcher(db *postgres.DB, bc core.BlockChain) EventWatcher {
|
func NewEventWatcher(db *postgres.DB, bc core.BlockChain) EventWatcher {
|
||||||
logChunker := chunker.NewLogChunker()
|
logChunker := chunker.NewLogChunker()
|
||||||
logFetcher := fetcher.NewFetcher(bc)
|
logFetcher := fetcher.NewLogFetcher(bc)
|
||||||
transactionSyncer := transactions.NewTransactionsSyncer(db, bc)
|
transactionSyncer := transactions.NewTransactionsSyncer(db, bc)
|
||||||
return EventWatcher{
|
return EventWatcher{
|
||||||
BlockChain: bc,
|
BlockChain: bc,
|
||||||
@@ -96,7 +96,7 @@ func (watcher *EventWatcher) Execute(recheckHeaders constants.TransformerExecuti
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
notCheckedSQL := repository.CreateNotCheckedSQL(checkedColumnNames, recheckHeaders)
|
notCheckedSQL := repository.CreateHeaderCheckedPredicateSQL(checkedColumnNames, recheckHeaders)
|
||||||
|
|
||||||
missingHeaders, err := repository.MissingHeaders(*watcher.StartingBlock, -1, watcher.DB, notCheckedSQL)
|
missingHeaders, err := repository.MissingHeaders(*watcher.StartingBlock, -1, watcher.DB, notCheckedSQL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -134,7 +134,7 @@ func (watcher *EventWatcher) transformLogs(logs []types.Log, header core.Header)
|
|||||||
for _, t := range watcher.Transformers {
|
for _, t := range watcher.Transformers {
|
||||||
transformerName := t.GetConfig().TransformerName
|
transformerName := t.GetConfig().TransformerName
|
||||||
logChunk := chunkedLogs[transformerName]
|
logChunk := chunkedLogs[transformerName]
|
||||||
err := t.Execute(logChunk, header, constants.HeaderMissing)
|
err := t.Execute(logChunk, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("%v transformer failed to execute in watcher: %v", transformerName, err)
|
logrus.Errorf("%v transformer failed to execute in watcher: %v", transformerName, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -17,73 +17,100 @@
|
|||||||
package watcher
|
package watcher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/vulcanize/vulcanizedb/libraries/shared/fetcher"
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/storage"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/storage"
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/fs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type StorageWatcher struct {
|
type StorageWatcher struct {
|
||||||
db *postgres.DB
|
db *postgres.DB
|
||||||
tailer fs.Tailer
|
StorageFetcher fetcher.IStorageFetcher
|
||||||
Queue storage.IStorageQueue
|
Queue storage.IStorageQueue
|
||||||
Transformers map[common.Address]transformer.StorageTransformer
|
Transformers map[common.Address]transformer.StorageTransformer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStorageWatcher(tailer fs.Tailer, db *postgres.DB) StorageWatcher {
|
func NewStorageWatcher(fetcher fetcher.IStorageFetcher, db *postgres.DB) StorageWatcher {
|
||||||
transformers := make(map[common.Address]transformer.StorageTransformer)
|
transformers := make(map[common.Address]transformer.StorageTransformer)
|
||||||
queue := storage.NewStorageQueue(db)
|
queue := storage.NewStorageQueue(db)
|
||||||
return StorageWatcher{
|
return StorageWatcher{
|
||||||
db: db,
|
db: db,
|
||||||
tailer: tailer,
|
StorageFetcher: fetcher,
|
||||||
Queue: queue,
|
Queue: queue,
|
||||||
Transformers: transformers,
|
Transformers: transformers,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (watcher StorageWatcher) AddTransformers(initializers []transformer.StorageTransformerInitializer) {
|
func (storageWatcher StorageWatcher) AddTransformers(initializers []transformer.StorageTransformerInitializer) {
|
||||||
for _, initializer := range initializers {
|
for _, initializer := range initializers {
|
||||||
storageTransformer := initializer(watcher.db)
|
storageTransformer := initializer(storageWatcher.db)
|
||||||
watcher.Transformers[storageTransformer.ContractAddress()] = storageTransformer
|
storageWatcher.Transformers[storageTransformer.ContractAddress()] = storageTransformer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (watcher StorageWatcher) Execute() error {
|
func (storageWatcher StorageWatcher) Execute(rows chan utils.StorageDiffRow, errs chan error, queueRecheckInterval time.Duration) {
|
||||||
t, tailErr := watcher.tailer.Tail()
|
ticker := time.NewTicker(queueRecheckInterval)
|
||||||
if tailErr != nil {
|
go storageWatcher.StorageFetcher.FetchStorageDiffs(rows, errs)
|
||||||
return tailErr
|
for {
|
||||||
}
|
select {
|
||||||
for line := range t.Lines {
|
case fetchErr := <-errs:
|
||||||
row, parseErr := utils.FromStrings(strings.Split(line.Text, ","))
|
logrus.Warn(fmt.Sprintf("error fetching storage diffs: %s", fetchErr))
|
||||||
if parseErr != nil {
|
case row := <-rows:
|
||||||
return parseErr
|
storageWatcher.processRow(row)
|
||||||
|
case <-ticker.C:
|
||||||
|
storageWatcher.processQueue()
|
||||||
}
|
}
|
||||||
storageTransformer, ok := watcher.Transformers[row.Contract]
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (storageWatcher StorageWatcher) processRow(row utils.StorageDiffRow) {
|
||||||
|
storageTransformer, ok := storageWatcher.Transformers[row.Contract]
|
||||||
|
if !ok {
|
||||||
|
// ignore rows from unwatched contracts
|
||||||
|
return
|
||||||
|
}
|
||||||
|
executeErr := storageTransformer.Execute(row)
|
||||||
|
if executeErr != nil {
|
||||||
|
logrus.Warn(fmt.Sprintf("error executing storage transformer: %s", executeErr))
|
||||||
|
queueErr := storageWatcher.Queue.Add(row)
|
||||||
|
if queueErr != nil {
|
||||||
|
logrus.Warn(fmt.Sprintf("error queueing storage diff: %s", queueErr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (storageWatcher StorageWatcher) processQueue() {
|
||||||
|
rows, fetchErr := storageWatcher.Queue.GetAll()
|
||||||
|
if fetchErr != nil {
|
||||||
|
logrus.Warn(fmt.Sprintf("error getting queued storage: %s", fetchErr))
|
||||||
|
}
|
||||||
|
for _, row := range rows {
|
||||||
|
storageTransformer, ok := storageWatcher.Transformers[row.Contract]
|
||||||
if !ok {
|
if !ok {
|
||||||
logrus.Warn(utils.ErrContractNotFound{Contract: row.Contract.Hex()}.Error())
|
// delete row from queue if address no longer watched
|
||||||
|
storageWatcher.deleteRow(row.Id)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
executeErr := storageTransformer.Execute(row)
|
executeErr := storageTransformer.Execute(row)
|
||||||
if executeErr != nil {
|
if executeErr == nil {
|
||||||
if isKeyNotFound(executeErr) {
|
storageWatcher.deleteRow(row.Id)
|
||||||
queueErr := watcher.Queue.Add(row)
|
|
||||||
if queueErr != nil {
|
|
||||||
logrus.Warn(queueErr.Error())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logrus.Warn(executeErr.Error())
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
}
|
||||||
|
|
||||||
|
func (storageWatcher StorageWatcher) deleteRow(id int) {
|
||||||
|
deleteErr := storageWatcher.Queue.Delete(id)
|
||||||
|
if deleteErr != nil {
|
||||||
|
logrus.Warn(fmt.Sprintf("error deleting persisted row from queue: %s", deleteErr))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isKeyNotFound(executeErr error) bool {
|
func isKeyNotFound(executeErr error) bool {
|
||||||
|
|||||||
@@ -17,15 +17,11 @@
|
|||||||
package watcher_test
|
package watcher_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/hpcloud/tail"
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@@ -34,7 +30,6 @@ import (
|
|||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
||||||
"github.com/vulcanize/vulcanizedb/libraries/shared/watcher"
|
"github.com/vulcanize/vulcanizedb/libraries/shared/watcher"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
||||||
"github.com/vulcanize/vulcanizedb/test_config"
|
"github.com/vulcanize/vulcanizedb/test_config"
|
||||||
)
|
)
|
||||||
@@ -43,170 +38,207 @@ var _ = Describe("Storage Watcher", func() {
|
|||||||
It("adds transformers", func() {
|
It("adds transformers", func() {
|
||||||
fakeAddress := common.HexToAddress("0x12345")
|
fakeAddress := common.HexToAddress("0x12345")
|
||||||
fakeTransformer := &mocks.MockStorageTransformer{Address: fakeAddress}
|
fakeTransformer := &mocks.MockStorageTransformer{Address: fakeAddress}
|
||||||
w := watcher.NewStorageWatcher(&fakes.MockTailer{}, test_config.NewTestDB(core.Node{}))
|
w := watcher.NewStorageWatcher(mocks.NewMockStorageFetcher(), test_config.NewTestDB(test_config.NewTestNode()))
|
||||||
|
|
||||||
w.AddTransformers([]transformer.StorageTransformerInitializer{fakeTransformer.FakeTransformerInitializer})
|
w.AddTransformers([]transformer.StorageTransformerInitializer{fakeTransformer.FakeTransformerInitializer})
|
||||||
|
|
||||||
Expect(w.Transformers[fakeAddress]).To(Equal(fakeTransformer))
|
Expect(w.Transformers[fakeAddress]).To(Equal(fakeTransformer))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("reads the tail of the storage diffs file", func() {
|
Describe("executing watcher", func() {
|
||||||
mockTailer := fakes.NewMockTailer()
|
var (
|
||||||
w := watcher.NewStorageWatcher(mockTailer, test_config.NewTestDB(core.Node{}))
|
errs chan error
|
||||||
|
mockFetcher *mocks.MockStorageFetcher
|
||||||
|
mockQueue *mocks.MockStorageQueue
|
||||||
|
mockTransformer *mocks.MockStorageTransformer
|
||||||
|
row utils.StorageDiffRow
|
||||||
|
rows chan utils.StorageDiffRow
|
||||||
|
storageWatcher watcher.StorageWatcher
|
||||||
|
)
|
||||||
|
|
||||||
assert(func(err error) {
|
BeforeEach(func() {
|
||||||
Expect(err).To(BeNil())
|
errs = make(chan error)
|
||||||
Expect(mockTailer.TailCalled).To(BeTrue())
|
rows = make(chan utils.StorageDiffRow)
|
||||||
}, w, mockTailer, []*tail.Line{})
|
address := common.HexToAddress("0x0123456789abcdef")
|
||||||
})
|
mockFetcher = mocks.NewMockStorageFetcher()
|
||||||
|
mockQueue = &mocks.MockStorageQueue{}
|
||||||
It("returns error if row parsing fails", func() {
|
mockTransformer = &mocks.MockStorageTransformer{Address: address}
|
||||||
mockTailer := fakes.NewMockTailer()
|
row = utils.StorageDiffRow{
|
||||||
w := watcher.NewStorageWatcher(mockTailer, test_config.NewTestDB(core.Node{}))
|
Id: 1337,
|
||||||
line := &tail.Line{Text: "oops"}
|
Contract: address,
|
||||||
|
BlockHash: common.HexToHash("0xfedcba9876543210"),
|
||||||
assert(func(err error) {
|
BlockHeight: 0,
|
||||||
Expect(err).To(HaveOccurred())
|
StorageKey: common.HexToHash("0xabcdef1234567890"),
|
||||||
Expect(err).To(MatchError(utils.ErrRowMalformed{Length: 1}))
|
StorageValue: common.HexToHash("0x9876543210abcdef"),
|
||||||
}, w, mockTailer, []*tail.Line{line})
|
}
|
||||||
})
|
|
||||||
|
|
||||||
It("logs error if no transformer can parse storage row", func() {
|
|
||||||
mockTailer := fakes.NewMockTailer()
|
|
||||||
address := common.HexToAddress("0x12345")
|
|
||||||
line := getFakeLine(address.Bytes())
|
|
||||||
w := watcher.NewStorageWatcher(mockTailer, test_config.NewTestDB(core.Node{}))
|
|
||||||
tempFile, err := ioutil.TempFile("", "log")
|
|
||||||
defer os.Remove(tempFile.Name())
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
logrus.SetOutput(tempFile)
|
|
||||||
|
|
||||||
assert(func(err error) {
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
logContent, readErr := ioutil.ReadFile(tempFile.Name())
|
|
||||||
Expect(readErr).NotTo(HaveOccurred())
|
|
||||||
Expect(string(logContent)).To(ContainSubstring(utils.ErrContractNotFound{Contract: address.Hex()}.Error()))
|
|
||||||
}, w, mockTailer, []*tail.Line{line})
|
|
||||||
})
|
|
||||||
|
|
||||||
It("executes transformer with storage row", func() {
|
|
||||||
address := []byte{1, 2, 3}
|
|
||||||
line := getFakeLine(address)
|
|
||||||
mockTailer := fakes.NewMockTailer()
|
|
||||||
w := watcher.NewStorageWatcher(mockTailer, test_config.NewTestDB(core.Node{}))
|
|
||||||
fakeTransformer := &mocks.MockStorageTransformer{Address: common.BytesToAddress(address)}
|
|
||||||
w.AddTransformers([]transformer.StorageTransformerInitializer{fakeTransformer.FakeTransformerInitializer})
|
|
||||||
|
|
||||||
assert(func(err error) {
|
|
||||||
Expect(err).To(BeNil())
|
|
||||||
expectedRow, err := utils.FromStrings(strings.Split(line.Text, ","))
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
Expect(fakeTransformer.PassedRow).To(Equal(expectedRow))
|
|
||||||
}, w, mockTailer, []*tail.Line{line})
|
|
||||||
})
|
|
||||||
|
|
||||||
Describe("when executing transformer fails", func() {
|
|
||||||
It("queues row when error is storage key not found", func() {
|
|
||||||
address := []byte{1, 2, 3}
|
|
||||||
line := getFakeLine(address)
|
|
||||||
mockTailer := fakes.NewMockTailer()
|
|
||||||
w := watcher.NewStorageWatcher(mockTailer, test_config.NewTestDB(core.Node{}))
|
|
||||||
mockQueue := &mocks.MockStorageQueue{}
|
|
||||||
w.Queue = mockQueue
|
|
||||||
keyNotFoundError := utils.ErrStorageKeyNotFound{Key: "unknown_storage_key"}
|
|
||||||
fakeTransformer := &mocks.MockStorageTransformer{Address: common.BytesToAddress(address), ExecuteErr: keyNotFoundError}
|
|
||||||
w.AddTransformers([]transformer.StorageTransformerInitializer{fakeTransformer.FakeTransformerInitializer})
|
|
||||||
|
|
||||||
assert(func(err error) {
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
Expect(mockQueue.AddCalled).To(BeTrue())
|
|
||||||
}, w, mockTailer, []*tail.Line{line})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
It("logs error if queuing row fails", func() {
|
It("logs error if fetching storage diffs fails", func(done Done) {
|
||||||
address := []byte{1, 2, 3}
|
mockFetcher.ErrsToReturn = []error{fakes.FakeError}
|
||||||
line := getFakeLine(address)
|
storageWatcher = watcher.NewStorageWatcher(mockFetcher, test_config.NewTestDB(test_config.NewTestNode()))
|
||||||
mockTailer := fakes.NewMockTailer()
|
storageWatcher.Queue = mockQueue
|
||||||
w := watcher.NewStorageWatcher(mockTailer, test_config.NewTestDB(core.Node{}))
|
storageWatcher.AddTransformers([]transformer.StorageTransformerInitializer{mockTransformer.FakeTransformerInitializer})
|
||||||
mockQueue := &mocks.MockStorageQueue{}
|
tempFile, fileErr := ioutil.TempFile("", "log")
|
||||||
mockQueue.AddError = fakes.FakeError
|
Expect(fileErr).NotTo(HaveOccurred())
|
||||||
w.Queue = mockQueue
|
|
||||||
keyNotFoundError := utils.ErrStorageKeyNotFound{Key: "unknown_storage_key"}
|
|
||||||
fakeTransformer := &mocks.MockStorageTransformer{Address: common.BytesToAddress(address), ExecuteErr: keyNotFoundError}
|
|
||||||
w.AddTransformers([]transformer.StorageTransformerInitializer{fakeTransformer.FakeTransformerInitializer})
|
|
||||||
tempFile, err := ioutil.TempFile("", "log")
|
|
||||||
defer os.Remove(tempFile.Name())
|
defer os.Remove(tempFile.Name())
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
logrus.SetOutput(tempFile)
|
logrus.SetOutput(tempFile)
|
||||||
|
|
||||||
assert(func(err error) {
|
go storageWatcher.Execute(rows, errs, time.Hour)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
Expect(mockQueue.AddCalled).To(BeTrue())
|
Eventually(func() (string, error) {
|
||||||
logContent, readErr := ioutil.ReadFile(tempFile.Name())
|
logContent, err := ioutil.ReadFile(tempFile.Name())
|
||||||
Expect(readErr).NotTo(HaveOccurred())
|
return string(logContent), err
|
||||||
Expect(string(logContent)).To(ContainSubstring(fakes.FakeError.Error()))
|
}).Should(ContainSubstring(fakes.FakeError.Error()))
|
||||||
}, w, mockTailer, []*tail.Line{line})
|
close(done)
|
||||||
})
|
})
|
||||||
|
|
||||||
It("logs any other error", func() {
|
Describe("transforming new storage diffs", func() {
|
||||||
address := []byte{1, 2, 3}
|
BeforeEach(func() {
|
||||||
line := getFakeLine(address)
|
mockFetcher.RowsToReturn = []utils.StorageDiffRow{row}
|
||||||
mockTailer := fakes.NewMockTailer()
|
storageWatcher = watcher.NewStorageWatcher(mockFetcher, test_config.NewTestDB(test_config.NewTestNode()))
|
||||||
w := watcher.NewStorageWatcher(mockTailer, test_config.NewTestDB(core.Node{}))
|
storageWatcher.Queue = mockQueue
|
||||||
executionError := errors.New("storage watcher failed attempting to execute transformer")
|
storageWatcher.AddTransformers([]transformer.StorageTransformerInitializer{mockTransformer.FakeTransformerInitializer})
|
||||||
fakeTransformer := &mocks.MockStorageTransformer{Address: common.BytesToAddress(address), ExecuteErr: executionError}
|
})
|
||||||
w.AddTransformers([]transformer.StorageTransformerInitializer{fakeTransformer.FakeTransformerInitializer})
|
|
||||||
tempFile, err := ioutil.TempFile("", "log")
|
|
||||||
defer os.Remove(tempFile.Name())
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
logrus.SetOutput(tempFile)
|
|
||||||
|
|
||||||
assert(func(err error) {
|
It("executes transformer for recognized storage row", func(done Done) {
|
||||||
Expect(err).NotTo(HaveOccurred())
|
go storageWatcher.Execute(rows, errs, time.Hour)
|
||||||
logContent, readErr := ioutil.ReadFile(tempFile.Name())
|
|
||||||
Expect(readErr).NotTo(HaveOccurred())
|
Eventually(func() utils.StorageDiffRow {
|
||||||
Expect(string(logContent)).To(ContainSubstring(executionError.Error()))
|
return mockTransformer.PassedRow
|
||||||
}, w, mockTailer, []*tail.Line{line})
|
}).Should(Equal(row))
|
||||||
|
close(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("queues row for later processing if transformer execution fails", func(done Done) {
|
||||||
|
mockTransformer.ExecuteErr = fakes.FakeError
|
||||||
|
|
||||||
|
go storageWatcher.Execute(rows, errs, time.Hour)
|
||||||
|
|
||||||
|
Expect(<-errs).To(BeNil())
|
||||||
|
Eventually(func() bool {
|
||||||
|
return mockQueue.AddCalled
|
||||||
|
}).Should(BeTrue())
|
||||||
|
Eventually(func() utils.StorageDiffRow {
|
||||||
|
return mockQueue.AddPassedRow
|
||||||
|
}).Should(Equal(row))
|
||||||
|
close(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("logs error if queueing row fails", func(done Done) {
|
||||||
|
mockTransformer.ExecuteErr = utils.ErrStorageKeyNotFound{}
|
||||||
|
mockQueue.AddError = fakes.FakeError
|
||||||
|
tempFile, fileErr := ioutil.TempFile("", "log")
|
||||||
|
Expect(fileErr).NotTo(HaveOccurred())
|
||||||
|
defer os.Remove(tempFile.Name())
|
||||||
|
logrus.SetOutput(tempFile)
|
||||||
|
|
||||||
|
go storageWatcher.Execute(rows, errs, time.Hour)
|
||||||
|
|
||||||
|
Eventually(func() bool {
|
||||||
|
return mockQueue.AddCalled
|
||||||
|
}).Should(BeTrue())
|
||||||
|
Eventually(func() (string, error) {
|
||||||
|
logContent, err := ioutil.ReadFile(tempFile.Name())
|
||||||
|
return string(logContent), err
|
||||||
|
}).Should(ContainSubstring(fakes.FakeError.Error()))
|
||||||
|
close(done)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Describe("transforming queued storage diffs", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
mockQueue.RowsToReturn = []utils.StorageDiffRow{row}
|
||||||
|
storageWatcher = watcher.NewStorageWatcher(mockFetcher, test_config.NewTestDB(test_config.NewTestNode()))
|
||||||
|
storageWatcher.Queue = mockQueue
|
||||||
|
storageWatcher.AddTransformers([]transformer.StorageTransformerInitializer{mockTransformer.FakeTransformerInitializer})
|
||||||
|
})
|
||||||
|
|
||||||
|
It("logs error if getting queued storage fails", func(done Done) {
|
||||||
|
mockQueue.GetAllErr = fakes.FakeError
|
||||||
|
tempFile, fileErr := ioutil.TempFile("", "log")
|
||||||
|
Expect(fileErr).NotTo(HaveOccurred())
|
||||||
|
defer os.Remove(tempFile.Name())
|
||||||
|
logrus.SetOutput(tempFile)
|
||||||
|
|
||||||
|
go storageWatcher.Execute(rows, errs, time.Nanosecond)
|
||||||
|
|
||||||
|
Eventually(func() (string, error) {
|
||||||
|
logContent, err := ioutil.ReadFile(tempFile.Name())
|
||||||
|
return string(logContent), err
|
||||||
|
}).Should(ContainSubstring(fakes.FakeError.Error()))
|
||||||
|
close(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("executes transformer for storage row", func(done Done) {
|
||||||
|
go storageWatcher.Execute(rows, errs, time.Nanosecond)
|
||||||
|
|
||||||
|
Eventually(func() utils.StorageDiffRow {
|
||||||
|
return mockTransformer.PassedRow
|
||||||
|
}).Should(Equal(row))
|
||||||
|
close(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("deletes row from queue if transformer execution successful", func(done Done) {
|
||||||
|
go storageWatcher.Execute(rows, errs, time.Nanosecond)
|
||||||
|
|
||||||
|
Eventually(func() int {
|
||||||
|
return mockQueue.DeletePassedId
|
||||||
|
}).Should(Equal(row.Id))
|
||||||
|
close(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("logs error if deleting persisted row fails", func(done Done) {
|
||||||
|
mockQueue.DeleteErr = fakes.FakeError
|
||||||
|
tempFile, fileErr := ioutil.TempFile("", "log")
|
||||||
|
Expect(fileErr).NotTo(HaveOccurred())
|
||||||
|
defer os.Remove(tempFile.Name())
|
||||||
|
logrus.SetOutput(tempFile)
|
||||||
|
|
||||||
|
go storageWatcher.Execute(rows, errs, time.Nanosecond)
|
||||||
|
|
||||||
|
Eventually(func() (string, error) {
|
||||||
|
logContent, err := ioutil.ReadFile(tempFile.Name())
|
||||||
|
return string(logContent), err
|
||||||
|
}).Should(ContainSubstring(fakes.FakeError.Error()))
|
||||||
|
close(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("deletes obsolete row from queue if contract not recognized", func(done Done) {
|
||||||
|
obsoleteRow := utils.StorageDiffRow{
|
||||||
|
Id: row.Id + 1,
|
||||||
|
Contract: common.HexToAddress("0xfedcba9876543210"),
|
||||||
|
}
|
||||||
|
mockQueue.RowsToReturn = []utils.StorageDiffRow{obsoleteRow}
|
||||||
|
|
||||||
|
go storageWatcher.Execute(rows, errs, time.Nanosecond)
|
||||||
|
|
||||||
|
Eventually(func() int {
|
||||||
|
return mockQueue.DeletePassedId
|
||||||
|
}).Should(Equal(obsoleteRow.Id))
|
||||||
|
close(done)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("logs error if deleting obsolete row fails", func(done Done) {
|
||||||
|
obsoleteRow := utils.StorageDiffRow{
|
||||||
|
Id: row.Id + 1,
|
||||||
|
Contract: common.HexToAddress("0xfedcba9876543210"),
|
||||||
|
}
|
||||||
|
mockQueue.RowsToReturn = []utils.StorageDiffRow{obsoleteRow}
|
||||||
|
mockQueue.DeleteErr = fakes.FakeError
|
||||||
|
tempFile, fileErr := ioutil.TempFile("", "log")
|
||||||
|
Expect(fileErr).NotTo(HaveOccurred())
|
||||||
|
defer os.Remove(tempFile.Name())
|
||||||
|
logrus.SetOutput(tempFile)
|
||||||
|
|
||||||
|
go storageWatcher.Execute(rows, errs, time.Nanosecond)
|
||||||
|
|
||||||
|
Eventually(func() (string, error) {
|
||||||
|
logContent, err := ioutil.ReadFile(tempFile.Name())
|
||||||
|
return string(logContent), err
|
||||||
|
}).Should(ContainSubstring(fakes.FakeError.Error()))
|
||||||
|
close(done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
func assert(assertion func(err error), watcher watcher.StorageWatcher, mockTailer *fakes.MockTailer, lines []*tail.Line) {
|
|
||||||
errs := make(chan error, 1)
|
|
||||||
done := make(chan bool, 1)
|
|
||||||
go execute(watcher, errs, done)
|
|
||||||
for _, line := range lines {
|
|
||||||
mockTailer.Lines <- line
|
|
||||||
}
|
|
||||||
close(mockTailer.Lines)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case err := <-errs:
|
|
||||||
assertion(err)
|
|
||||||
break
|
|
||||||
case <-done:
|
|
||||||
assertion(nil)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func execute(w watcher.StorageWatcher, errs chan error, done chan bool) {
|
|
||||||
err := w.Execute()
|
|
||||||
if err != nil {
|
|
||||||
errs <- err
|
|
||||||
} else {
|
|
||||||
done <- true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFakeLine(address []byte) *tail.Line {
|
|
||||||
blockHash := []byte{4, 5, 6}
|
|
||||||
blockHeight := int64(789)
|
|
||||||
storageKey := []byte{9, 8, 7}
|
|
||||||
storageValue := []byte{6, 5, 4}
|
|
||||||
return &tail.Line{
|
|
||||||
Text: fmt.Sprintf("%s,%s,%d,%s,%s", common.Bytes2Hex(address), common.Bytes2Hex(blockHash), blockHeight, common.Bytes2Hex(storageKey), common.Bytes2Hex(storageValue)),
|
|
||||||
Time: time.Time{},
|
|
||||||
Err: nil,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ func (tr *Transformer) Init() error {
|
|||||||
// Iterates through stored, initialized contract objects
|
// Iterates through stored, initialized contract objects
|
||||||
// Iterates through contract's event filters, grabbing watched event logs
|
// Iterates through contract's event filters, grabbing watched event logs
|
||||||
// Uses converter to convert logs into custom log type
|
// Uses converter to convert logs into custom log type
|
||||||
// Persists converted logs into custuom postgres tables
|
// Persists converted logs into custom postgres tables
|
||||||
// Calls selected methods, using token holder address generated during event log conversion
|
// Calls selected methods, using token holder address generated during event log conversion
|
||||||
func (tr *Transformer) Execute() error {
|
func (tr *Transformer) Execute() error {
|
||||||
if len(tr.Contracts) == 0 {
|
if len(tr.Contracts) == 0 {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ var _ = Describe("Transformer", func() {
|
|||||||
|
|
||||||
Describe("Init", func() {
|
Describe("Init", func() {
|
||||||
It("Initializes transformer's contract objects", func() {
|
It("Initializes transformer's contract objects", func() {
|
||||||
blockRetriever := &fakes.MockFullBlockRetriever{}
|
blockRetriever := &fakes.MockFullSyncBlockRetriever{}
|
||||||
firstBlock := int64(1)
|
firstBlock := int64(1)
|
||||||
mostRecentBlock := int64(2)
|
mostRecentBlock := int64(2)
|
||||||
blockRetriever.FirstBlock = firstBlock
|
blockRetriever.FirstBlock = firstBlock
|
||||||
@@ -74,7 +74,7 @@ var _ = Describe("Transformer", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("Fails to initialize if first and most recent blocks cannot be fetched from vDB", func() {
|
It("Fails to initialize if first and most recent blocks cannot be fetched from vDB", func() {
|
||||||
blockRetriever := &fakes.MockFullBlockRetriever{}
|
blockRetriever := &fakes.MockFullSyncBlockRetriever{}
|
||||||
blockRetriever.FirstBlockErr = fakes.FakeError
|
blockRetriever.FirstBlockErr = fakes.FakeError
|
||||||
t := getTransformer(blockRetriever, &fakes.MockParser{}, &fakes.MockPoller{})
|
t := getTransformer(blockRetriever, &fakes.MockParser{}, &fakes.MockPoller{})
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ import (
|
|||||||
|
|
||||||
func TestConverter(t *testing.T) {
|
func TestConverter(t *testing.T) {
|
||||||
RegisterFailHandler(Fail)
|
RegisterFailHandler(Fail)
|
||||||
RunSpecs(t, "Light Converter Suite Test")
|
RunSpecs(t, "Header Sync Converter Suite Test")
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ = BeforeSuite(func() {
|
var _ = BeforeSuite(func() {
|
||||||
+1
-1
@@ -22,7 +22,7 @@ import (
|
|||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/light/converter"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/converter"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/contract"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/contract"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers"
|
||||||
+1
-1
@@ -22,7 +22,7 @@ import (
|
|||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/light/fetcher"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/fetcher"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
||||||
)
|
)
|
||||||
+2
-2
@@ -23,7 +23,7 @@ import (
|
|||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/light/repository"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/repository"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers/mocks"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers/mocks"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
@@ -32,7 +32,7 @@ import (
|
|||||||
|
|
||||||
var _ = Describe("Repository", func() {
|
var _ = Describe("Repository", func() {
|
||||||
var db *postgres.DB
|
var db *postgres.DB
|
||||||
var contractHeaderRepo repository.HeaderRepository // contract_watcher light header repository
|
var contractHeaderRepo repository.HeaderRepository // contract_watcher headerSync header repository
|
||||||
var coreHeaderRepo repositories.HeaderRepository // pkg/datastore header repository
|
var coreHeaderRepo repositories.HeaderRepository // pkg/datastore header repository
|
||||||
var eventIDs = []string{
|
var eventIDs = []string{
|
||||||
"eventName_contractAddr",
|
"eventName_contractAddr",
|
||||||
+1
-1
@@ -27,7 +27,7 @@ import (
|
|||||||
|
|
||||||
func TestRepository(t *testing.T) {
|
func TestRepository(t *testing.T) {
|
||||||
RegisterFailHandler(Fail)
|
RegisterFailHandler(Fail)
|
||||||
RunSpecs(t, "Light Repository Suite Test")
|
RunSpecs(t, "Header Sync Repository Suite Test")
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ = BeforeSuite(func() {
|
var _ = BeforeSuite(func() {
|
||||||
+1
-1
@@ -20,7 +20,7 @@ import (
|
|||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/light/retriever"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/retriever"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers/mocks"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers/mocks"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
+1
-1
@@ -27,7 +27,7 @@ import (
|
|||||||
|
|
||||||
func TestRetriever(t *testing.T) {
|
func TestRetriever(t *testing.T) {
|
||||||
RegisterFailHandler(Fail)
|
RegisterFailHandler(Fail)
|
||||||
RunSpecs(t, "Light Block Number Retriever Suite Test")
|
RunSpecs(t, "Header Sync Block Number Retriever Suite Test")
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ = BeforeSuite(func() {
|
var _ = BeforeSuite(func() {
|
||||||
+7
-7
@@ -24,10 +24,10 @@ import (
|
|||||||
gethTypes "github.com/ethereum/go-ethereum/core/types"
|
gethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/config"
|
"github.com/vulcanize/vulcanizedb/pkg/config"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/light/converter"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/converter"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/light/fetcher"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/fetcher"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/light/repository"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/repository"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/light/retriever"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/retriever"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/contract"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/contract"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/parser"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/parser"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/poller"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/poller"
|
||||||
@@ -37,7 +37,7 @@ import (
|
|||||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Requires a light synced vDB (headers) and a running eth node (or infura)
|
// Requires a header synced vDB (headers) and a running eth node (or infura)
|
||||||
type Transformer struct {
|
type Transformer struct {
|
||||||
// Database interfaces
|
// Database interfaces
|
||||||
EventRepository srep.EventRepository // Holds transformed watched event log data
|
EventRepository srep.EventRepository // Holds transformed watched event log data
|
||||||
@@ -77,14 +77,14 @@ type Transformer struct {
|
|||||||
func NewTransformer(con config.ContractConfig, bc core.BlockChain, db *postgres.DB) *Transformer {
|
func NewTransformer(con config.ContractConfig, bc core.BlockChain, db *postgres.DB) *Transformer {
|
||||||
|
|
||||||
return &Transformer{
|
return &Transformer{
|
||||||
Poller: poller.NewPoller(bc, db, types.LightSync),
|
Poller: poller.NewPoller(bc, db, types.HeaderSync),
|
||||||
Fetcher: fetcher.NewFetcher(bc),
|
Fetcher: fetcher.NewFetcher(bc),
|
||||||
Parser: parser.NewParser(con.Network),
|
Parser: parser.NewParser(con.Network),
|
||||||
HeaderRepository: repository.NewHeaderRepository(db),
|
HeaderRepository: repository.NewHeaderRepository(db),
|
||||||
Retriever: retriever.NewBlockRetriever(db),
|
Retriever: retriever.NewBlockRetriever(db),
|
||||||
Converter: &converter.Converter{},
|
Converter: &converter.Converter{},
|
||||||
Contracts: map[string]*contract.Contract{},
|
Contracts: map[string]*contract.Contract{},
|
||||||
EventRepository: srep.NewEventRepository(db, types.LightSync),
|
EventRepository: srep.NewEventRepository(db, types.HeaderSync),
|
||||||
Config: con,
|
Config: con,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -27,7 +27,7 @@ import (
|
|||||||
|
|
||||||
func TestTransformer(t *testing.T) {
|
func TestTransformer(t *testing.T) {
|
||||||
RegisterFailHandler(Fail)
|
RegisterFailHandler(Fail)
|
||||||
RunSpecs(t, "Light Transformer Suite Test")
|
RunSpecs(t, "Header Sync Transformer Suite Test")
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ = BeforeSuite(func() {
|
var _ = BeforeSuite(func() {
|
||||||
+7
-7
@@ -20,8 +20,8 @@ import (
|
|||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/light/retriever"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/retriever"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/light/transformer"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/transformer"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/contract"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/contract"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers/mocks"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers/mocks"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/parser"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/parser"
|
||||||
@@ -33,7 +33,7 @@ var _ = Describe("Transformer", func() {
|
|||||||
var fakeAddress = "0x1234567890abcdef"
|
var fakeAddress = "0x1234567890abcdef"
|
||||||
Describe("Init", func() {
|
Describe("Init", func() {
|
||||||
It("Initializes transformer's contract objects", func() {
|
It("Initializes transformer's contract objects", func() {
|
||||||
blockRetriever := &fakes.MockLightBlockRetriever{}
|
blockRetriever := &fakes.MockHeaderSyncBlockRetriever{}
|
||||||
firstBlock := int64(1)
|
firstBlock := int64(1)
|
||||||
blockRetriever.FirstBlock = firstBlock
|
blockRetriever.FirstBlock = firstBlock
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ var _ = Describe("Transformer", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("Fails to initialize if first block cannot be fetched from vDB headers table", func() {
|
It("Fails to initialize if first block cannot be fetched from vDB headers table", func() {
|
||||||
blockRetriever := &fakes.MockLightBlockRetriever{}
|
blockRetriever := &fakes.MockHeaderSyncBlockRetriever{}
|
||||||
blockRetriever.FirstBlockErr = fakes.FakeError
|
blockRetriever.FirstBlockErr = fakes.FakeError
|
||||||
t := getFakeTransformer(blockRetriever, &fakes.MockParser{}, &fakes.MockPoller{})
|
t := getFakeTransformer(blockRetriever, &fakes.MockParser{}, &fakes.MockPoller{})
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ var _ = Describe("Transformer", func() {
|
|||||||
|
|
||||||
Describe("Execute", func() {
|
Describe("Execute", func() {
|
||||||
It("Executes contract transformations", func() {
|
It("Executes contract transformations", func() {
|
||||||
blockRetriever := &fakes.MockLightBlockRetriever{}
|
blockRetriever := &fakes.MockHeaderSyncBlockRetriever{}
|
||||||
firstBlock := int64(1)
|
firstBlock := int64(1)
|
||||||
blockRetriever.FirstBlock = firstBlock
|
blockRetriever.FirstBlock = firstBlock
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ var _ = Describe("Transformer", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("Fails to initialize if first block cannot be fetched from vDB headers table", func() {
|
It("Fails to initialize if first block cannot be fetched from vDB headers table", func() {
|
||||||
blockRetriever := &fakes.MockLightBlockRetriever{}
|
blockRetriever := &fakes.MockHeaderSyncBlockRetriever{}
|
||||||
blockRetriever.FirstBlockErr = fakes.FakeError
|
blockRetriever.FirstBlockErr = fakes.FakeError
|
||||||
t := getFakeTransformer(blockRetriever, &fakes.MockParser{}, &fakes.MockPoller{})
|
t := getFakeTransformer(blockRetriever, &fakes.MockParser{}, &fakes.MockPoller{})
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ func getFakeTransformer(blockRetriever retriever.BlockRetriever, parsr parser.Pa
|
|||||||
Parser: parsr,
|
Parser: parsr,
|
||||||
Retriever: blockRetriever,
|
Retriever: blockRetriever,
|
||||||
Poller: pollr,
|
Poller: pollr,
|
||||||
HeaderRepository: &fakes.MockLightHeaderRepository{},
|
HeaderRepository: &fakes.MockHeaderSyncHeaderRepository{},
|
||||||
Contracts: map[string]*contract.Contract{},
|
Contracts: map[string]*contract.Contract{},
|
||||||
Config: mocks.MockConfig,
|
Config: mocks.MockConfig,
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ type NewOwnerLog struct {
|
|||||||
Owner string `db:"owner_"`
|
Owner string `db:"owner_"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LightTransferLog struct {
|
type HeaderSyncTransferLog struct {
|
||||||
Id int64 `db:"id"`
|
Id int64 `db:"id"`
|
||||||
HeaderID int64 `db:"header_id"`
|
HeaderID int64 `db:"header_id"`
|
||||||
TokenName string `db:"token_name"`
|
TokenName string `db:"token_name"`
|
||||||
@@ -71,7 +71,7 @@ type LightTransferLog struct {
|
|||||||
RawLog []byte `db:"raw_log"`
|
RawLog []byte `db:"raw_log"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LightNewOwnerLog struct {
|
type HeaderSyncNewOwnerLog struct {
|
||||||
Id int64 `db:"id"`
|
Id int64 `db:"id"`
|
||||||
HeaderID int64 `db:"header_id"`
|
HeaderID int64 `db:"header_id"`
|
||||||
TokenName string `db:"token_name"`
|
TokenName string `db:"token_name"`
|
||||||
@@ -240,13 +240,13 @@ func TearDown(db *postgres.DB) {
|
|||||||
_, err = tx.Exec(`DELETE FROM full_sync_transactions`)
|
_, err = tx.Exec(`DELETE FROM full_sync_transactions`)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
_, err = tx.Exec("DELETE FROM light_sync_transactions")
|
_, err = tx.Exec("DELETE FROM header_sync_transactions")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
_, err = tx.Exec(`DELETE FROM full_sync_receipts`)
|
_, err = tx.Exec(`DELETE FROM full_sync_receipts`)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
_, err = tx.Exec(`DELETE FROM light_sync_receipts`)
|
_, err = tx.Exec(`DELETE FROM header_sync_receipts`)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
_, err = tx.Exec(`DROP TABLE checked_headers`)
|
_, err = tx.Exec(`DROP TABLE checked_headers`)
|
||||||
@@ -258,13 +258,13 @@ func TearDown(db *postgres.DB) {
|
|||||||
_, err = tx.Exec(`DROP SCHEMA IF EXISTS full_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e CASCADE`)
|
_, err = tx.Exec(`DROP SCHEMA IF EXISTS full_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e CASCADE`)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
_, err = tx.Exec(`DROP SCHEMA IF EXISTS light_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e CASCADE`)
|
_, err = tx.Exec(`DROP SCHEMA IF EXISTS header_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e CASCADE`)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
_, err = tx.Exec(`DROP SCHEMA IF EXISTS full_0x314159265dd8dbb310642f98f50c066173c1259b CASCADE`)
|
_, err = tx.Exec(`DROP SCHEMA IF EXISTS full_0x314159265dd8dbb310642f98f50c066173c1259b CASCADE`)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
_, err = tx.Exec(`DROP SCHEMA IF EXISTS light_0x314159265dd8dbb310642f98f50c066173c1259b CASCADE`)
|
_, err = tx.Exec(`DROP SCHEMA IF EXISTS header_0x314159265dd8dbb310642f98f50c066173c1259b CASCADE`)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
err = tx.Commit()
|
err = tx.Commit()
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ func (r *eventRepository) PersistLogs(logs []types.Log, eventInfo types.Event, c
|
|||||||
func (r *eventRepository) persistLogs(logs []types.Log, eventInfo types.Event, contractAddr, contractName string) error {
|
func (r *eventRepository) persistLogs(logs []types.Log, eventInfo types.Event, contractAddr, contractName string) error {
|
||||||
var err error
|
var err error
|
||||||
switch r.mode {
|
switch r.mode {
|
||||||
case types.LightSync:
|
case types.HeaderSync:
|
||||||
err = r.persistLightSyncLogs(logs, eventInfo, contractAddr, contractName)
|
err = r.persistHeaderSyncLogs(logs, eventInfo, contractAddr, contractName)
|
||||||
case types.FullSync:
|
case types.FullSync:
|
||||||
err = r.persistFullSyncLogs(logs, eventInfo, contractAddr, contractName)
|
err = r.persistFullSyncLogs(logs, eventInfo, contractAddr, contractName)
|
||||||
default:
|
default:
|
||||||
@@ -95,8 +95,8 @@ func (r *eventRepository) persistLogs(logs []types.Log, eventInfo types.Event, c
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a custom postgres command to persist logs for the given event (compatible with light synced vDB)
|
// Creates a custom postgres command to persist logs for the given event (compatible with header synced vDB)
|
||||||
func (r *eventRepository) persistLightSyncLogs(logs []types.Log, eventInfo types.Event, contractAddr, contractName string) error {
|
func (r *eventRepository) persistHeaderSyncLogs(logs []types.Log, eventInfo types.Event, contractAddr, contractName string) error {
|
||||||
tx, err := r.db.Beginx()
|
tx, err := r.db.Beginx()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -232,7 +232,7 @@ func (r *eventRepository) newEventTable(tableID string, event types.Event) error
|
|||||||
pgStr = pgStr + fmt.Sprintf(" %s_ %s NOT NULL,", strings.ToLower(field.Name), field.PgType)
|
pgStr = pgStr + fmt.Sprintf(" %s_ %s NOT NULL,", strings.ToLower(field.Name), field.PgType)
|
||||||
}
|
}
|
||||||
pgStr = pgStr + " CONSTRAINT log_index_fk FOREIGN KEY (vulcanize_log_id) REFERENCES logs (id) ON DELETE CASCADE)"
|
pgStr = pgStr + " CONSTRAINT log_index_fk FOREIGN KEY (vulcanize_log_id) REFERENCES logs (id) ON DELETE CASCADE)"
|
||||||
case types.LightSync:
|
case types.HeaderSync:
|
||||||
pgStr = pgStr + "(id SERIAL, header_id INTEGER NOT NULL REFERENCES headers (id) ON DELETE CASCADE, token_name CHARACTER VARYING(66) NOT NULL, raw_log JSONB, log_idx INTEGER NOT NULL, tx_idx INTEGER NOT NULL,"
|
pgStr = pgStr + "(id SERIAL, header_id INTEGER NOT NULL REFERENCES headers (id) ON DELETE CASCADE, token_name CHARACTER VARYING(66) NOT NULL, raw_log JSONB, log_idx INTEGER NOT NULL, tx_idx INTEGER NOT NULL,"
|
||||||
|
|
||||||
for _, field := range event.Fields {
|
for _, field := range event.Fields {
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ import (
|
|||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
fc "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/full/converter"
|
fc "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/full/converter"
|
||||||
lc "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/light/converter"
|
lc "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/converter"
|
||||||
lr "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/light/repository"
|
lr "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/repository"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/constants"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/constants"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/contract"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/contract"
|
||||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers"
|
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers"
|
||||||
@@ -208,9 +208,9 @@ var _ = Describe("Repository", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("Light sync mode", func() {
|
Describe("Header sync mode", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
dataStore = sr.NewEventRepository(db, types.LightSync)
|
dataStore = sr.NewEventRepository(db, types.HeaderSync)
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("CreateContractSchema", func() {
|
Describe("CreateContractSchema", func() {
|
||||||
@@ -242,7 +242,7 @@ var _ = Describe("Repository", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(created).To(Equal(true))
|
Expect(created).To(Equal(true))
|
||||||
|
|
||||||
tableID := fmt.Sprintf("%s_%s.%s_event", types.LightSync, strings.ToLower(con.Address), strings.ToLower(event.Name))
|
tableID := fmt.Sprintf("%s_%s.%s_event", types.HeaderSync, strings.ToLower(con.Address), strings.ToLower(event.Name))
|
||||||
_, ok := dataStore.CheckTableCache(tableID)
|
_, ok := dataStore.CheckTableCache(tableID)
|
||||||
Expect(ok).To(Equal(false))
|
Expect(ok).To(Equal(false))
|
||||||
|
|
||||||
@@ -292,12 +292,12 @@ var _ = Describe("Repository", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
var count int
|
var count int
|
||||||
err = db.Get(&count, fmt.Sprintf("SELECT COUNT(*) FROM light_%s.transfer_event", constants.TusdContractAddress))
|
err = db.Get(&count, fmt.Sprintf("SELECT COUNT(*) FROM header_%s.transfer_event", constants.TusdContractAddress))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(count).To(Equal(2))
|
Expect(count).To(Equal(2))
|
||||||
|
|
||||||
scanLog := test_helpers.LightTransferLog{}
|
scanLog := test_helpers.HeaderSyncTransferLog{}
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.transfer_event LIMIT 1", constants.TusdContractAddress)).StructScan(&scanLog)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.transfer_event LIMIT 1", constants.TusdContractAddress)).StructScan(&scanLog)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(scanLog.HeaderID).To(Equal(headerID))
|
Expect(scanLog.HeaderID).To(Equal(headerID))
|
||||||
Expect(scanLog.TokenName).To(Equal("TrueUSD"))
|
Expect(scanLog.TokenName).To(Equal("TrueUSD"))
|
||||||
@@ -334,7 +334,7 @@ var _ = Describe("Repository", func() {
|
|||||||
|
|
||||||
// Show that no new logs were entered
|
// Show that no new logs were entered
|
||||||
var count int
|
var count int
|
||||||
err = db.Get(&count, fmt.Sprintf("SELECT COUNT(*) FROM light_%s.transfer_event", constants.TusdContractAddress))
|
err = db.Get(&count, fmt.Sprintf("SELECT COUNT(*) FROM header_%s.transfer_event", constants.TusdContractAddress))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(count).To(Equal(2))
|
Expect(count).To(Equal(2))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -149,9 +149,9 @@ var _ = Describe("Repository", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("Light Sync Mode", func() {
|
Describe("Header Sync Mode", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
dataStore = repository.NewMethodRepository(db, types.LightSync)
|
dataStore = repository.NewMethodRepository(db, types.HeaderSync)
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("CreateContractSchema", func() {
|
Describe("CreateContractSchema", func() {
|
||||||
@@ -199,7 +199,7 @@ var _ = Describe("Repository", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(created).To(Equal(true))
|
Expect(created).To(Equal(true))
|
||||||
|
|
||||||
tableID := fmt.Sprintf("%s_%s.%s_method", types.LightSync, strings.ToLower(con.Address), strings.ToLower(method.Name))
|
tableID := fmt.Sprintf("%s_%s.%s_method", types.HeaderSync, strings.ToLower(con.Address), strings.ToLower(method.Name))
|
||||||
_, ok := dataStore.CheckTableCache(tableID)
|
_, ok := dataStore.CheckTableCache(tableID)
|
||||||
Expect(ok).To(Equal(false))
|
Expect(ok).To(Equal(false))
|
||||||
|
|
||||||
@@ -214,13 +214,13 @@ var _ = Describe("Repository", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Describe("PersistResult", func() {
|
Describe("PersistResult", func() {
|
||||||
It("Persists result from method polling in custom pg table for light sync mode vDB", func() {
|
It("Persists result from method polling in custom pg table for header sync mode vDB", func() {
|
||||||
err = dataStore.PersistResults([]types.Result{mockResult}, method, con.Address, con.Name)
|
err = dataStore.PersistResults([]types.Result{mockResult}, method, con.Address, con.Name)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
scanStruct := test_helpers.BalanceOf{}
|
scanStruct := test_helpers.BalanceOf{}
|
||||||
|
|
||||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM light_%s.balanceof_method", constants.TusdContractAddress)).StructScan(&scanStruct)
|
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||||
expectedLog := test_helpers.BalanceOf{
|
expectedLog := test_helpers.BalanceOf{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
TokenName: "TrueUSD",
|
TokenName: "TrueUSD",
|
||||||
|
|||||||
@@ -38,14 +38,14 @@ type Field struct {
|
|||||||
|
|
||||||
// Struct to hold instance of an event log data
|
// Struct to hold instance of an event log data
|
||||||
type Log struct {
|
type Log struct {
|
||||||
Id int64 // VulcanizeIdLog for full sync and header ID for light sync contract watcher
|
Id int64 // VulcanizeIdLog for full sync and header ID for header sync contract watcher
|
||||||
Values map[string]string // Map of event input names to their values
|
Values map[string]string // Map of event input names to their values
|
||||||
|
|
||||||
// Used for full sync only
|
// Used for full sync only
|
||||||
Block int64
|
Block int64
|
||||||
Tx string
|
Tx string
|
||||||
|
|
||||||
// Used for lightSync only
|
// Used for headerSync only
|
||||||
LogIndex uint
|
LogIndex uint
|
||||||
TransactionIndex uint
|
TransactionIndex uint
|
||||||
Raw []byte // json.Unmarshalled byte array of geth/core/types.Log{}
|
Raw []byte // json.Unmarshalled byte array of geth/core/types.Log{}
|
||||||
|
|||||||
@@ -21,18 +21,18 @@ import "fmt"
|
|||||||
type Mode int
|
type Mode int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LightSync Mode = iota
|
HeaderSync Mode = iota
|
||||||
FullSync
|
FullSync
|
||||||
)
|
)
|
||||||
|
|
||||||
func (mode Mode) IsValid() bool {
|
func (mode Mode) IsValid() bool {
|
||||||
return mode >= LightSync && mode <= FullSync
|
return mode >= HeaderSync && mode <= FullSync
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mode Mode) String() string {
|
func (mode Mode) String() string {
|
||||||
switch mode {
|
switch mode {
|
||||||
case LightSync:
|
case HeaderSync:
|
||||||
return "light"
|
return "header"
|
||||||
case FullSync:
|
case FullSync:
|
||||||
return "full"
|
return "full"
|
||||||
default:
|
default:
|
||||||
@@ -42,23 +42,23 @@ func (mode Mode) String() string {
|
|||||||
|
|
||||||
func (mode Mode) MarshalText() ([]byte, error) {
|
func (mode Mode) MarshalText() ([]byte, error) {
|
||||||
switch mode {
|
switch mode {
|
||||||
case LightSync:
|
case HeaderSync:
|
||||||
return []byte("light"), nil
|
return []byte("header"), nil
|
||||||
case FullSync:
|
case FullSync:
|
||||||
return []byte("full"), nil
|
return []byte("full"), nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("contract watcher: unknown mode %d, want LightSync or FullSync", mode)
|
return nil, fmt.Errorf("contract watcher: unknown mode %d, want HeaderSync or FullSync", mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mode *Mode) UnmarshalText(text []byte) error {
|
func (mode *Mode) UnmarshalText(text []byte) error {
|
||||||
switch string(text) {
|
switch string(text) {
|
||||||
case "light":
|
case "header":
|
||||||
*mode = LightSync
|
*mode = HeaderSync
|
||||||
case "full":
|
case "full":
|
||||||
*mode = FullSync
|
*mode = FullSync
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf(`contract watcher: unknown mode %q, want "light" or "full"`, text)
|
return fmt.Errorf(`contract watcher: unknown mode %q, want "header" or "full"`, text)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ type BlockChain interface {
|
|||||||
GetBlockByNumber(blockNumber int64) (Block, error)
|
GetBlockByNumber(blockNumber int64) (Block, error)
|
||||||
GetEthLogsWithCustomQuery(query ethereum.FilterQuery) ([]types.Log, error)
|
GetEthLogsWithCustomQuery(query ethereum.FilterQuery) ([]types.Log, error)
|
||||||
GetHeaderByNumber(blockNumber int64) (Header, error)
|
GetHeaderByNumber(blockNumber int64) (Header, error)
|
||||||
GetHeaderByNumbers(blockNumbers []int64) ([]Header, error)
|
GetHeadersByNumbers(blockNumbers []int64) ([]Header, error)
|
||||||
GetLogs(contract Contract, startingBlockNumber *big.Int, endingBlockNumber *big.Int) ([]Log, error)
|
GetLogs(contract Contract, startingBlockNumber *big.Int, endingBlockNumber *big.Int) ([]Log, error)
|
||||||
GetTransactions(transactionHashes []common.Hash) ([]TransactionModel, error)
|
GetTransactions(transactionHashes []common.Hash) ([]TransactionModel, error)
|
||||||
LastBlock() (*big.Int, error)
|
LastBlock() (*big.Int, error)
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func (repository HeaderRepository) CreateOrUpdateHeader(header core.Header) (int
|
|||||||
|
|
||||||
func (repository HeaderRepository) CreateTransactions(headerID int64, transactions []core.TransactionModel) error {
|
func (repository HeaderRepository) CreateTransactions(headerID int64, transactions []core.TransactionModel) error {
|
||||||
for _, transaction := range transactions {
|
for _, transaction := range transactions {
|
||||||
_, err := repository.database.Exec(`INSERT INTO public.light_sync_transactions
|
_, err := repository.database.Exec(`INSERT INTO public.header_sync_transactions
|
||||||
(header_id, hash, gas_limit, gas_price, input_data, nonce, raw, tx_from, tx_index, tx_to, "value")
|
(header_id, hash, gas_limit, gas_price, input_data, nonce, raw, tx_from, tx_index, tx_to, "value")
|
||||||
VALUES ($1, $2, $3::NUMERIC, $4::NUMERIC, $5, $6::NUMERIC, $7, $8, $9::NUMERIC, $10, $11::NUMERIC)
|
VALUES ($1, $2, $3::NUMERIC, $4::NUMERIC, $5, $6::NUMERIC, $7, $8, $9::NUMERIC, $10, $11::NUMERIC)
|
||||||
ON CONFLICT DO NOTHING`, headerID, transaction.Hash, transaction.GasLimit, transaction.GasPrice,
|
ON CONFLICT DO NOTHING`, headerID, transaction.Hash, transaction.GasLimit, transaction.GasPrice,
|
||||||
@@ -69,7 +69,7 @@ func (repository HeaderRepository) CreateTransactions(headerID int64, transactio
|
|||||||
|
|
||||||
func (repository HeaderRepository) CreateTransactionInTx(tx *sqlx.Tx, headerID int64, transaction core.TransactionModel) (int64, error) {
|
func (repository HeaderRepository) CreateTransactionInTx(tx *sqlx.Tx, headerID int64, transaction core.TransactionModel) (int64, error) {
|
||||||
var txId int64
|
var txId int64
|
||||||
err := tx.QueryRowx(`INSERT INTO public.light_sync_transactions
|
err := tx.QueryRowx(`INSERT INTO public.header_sync_transactions
|
||||||
(header_id, hash, gas_limit, gas_price, input_data, nonce, raw, tx_from, tx_index, tx_to, "value")
|
(header_id, hash, gas_limit, gas_price, input_data, nonce, raw, tx_from, tx_index, tx_to, "value")
|
||||||
VALUES ($1, $2, $3::NUMERIC, $4::NUMERIC, $5, $6::NUMERIC, $7, $8, $9::NUMERIC, $10, $11::NUMERIC)
|
VALUES ($1, $2, $3::NUMERIC, $4::NUMERIC, $5, $6::NUMERIC, $7, $8, $9::NUMERIC, $10, $11::NUMERIC)
|
||||||
ON CONFLICT (header_id, hash) DO UPDATE
|
ON CONFLICT (header_id, hash) DO UPDATE
|
||||||
@@ -87,7 +87,7 @@ func (repository HeaderRepository) CreateTransactionInTx(tx *sqlx.Tx, headerID i
|
|||||||
|
|
||||||
func (repository HeaderRepository) CreateReceiptInTx(tx *sqlx.Tx, headerID, transactionID int64, receipt core.Receipt) (int64, error) {
|
func (repository HeaderRepository) CreateReceiptInTx(tx *sqlx.Tx, headerID, transactionID int64, receipt core.Receipt) (int64, error) {
|
||||||
var receiptId int64
|
var receiptId int64
|
||||||
err := tx.QueryRowx(`INSERT INTO public.light_sync_receipts
|
err := tx.QueryRowx(`INSERT INTO public.header_sync_receipts
|
||||||
(header_id, transaction_id, contract_address, cumulative_gas_used, gas_used, state_root, status, tx_hash, rlp)
|
(header_id, transaction_id, contract_address, cumulative_gas_used, gas_used, state_root, status, tx_hash, rlp)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||||
ON CONFLICT (header_id, transaction_id) DO UPDATE
|
ON CONFLICT (header_id, transaction_id) DO UPDATE
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ var _ = Describe("Block header repository", func() {
|
|||||||
var dbReceipt idModel
|
var dbReceipt idModel
|
||||||
err = db.Get(&dbReceipt,
|
err = db.Get(&dbReceipt,
|
||||||
`SELECT transaction_id, contract_address, cumulative_gas_used, gas_used, state_root, status, tx_hash, rlp
|
`SELECT transaction_id, contract_address, cumulative_gas_used, gas_used, state_root, status, tx_hash, rlp
|
||||||
FROM public.light_sync_receipts WHERE header_id = $1`, headerID)
|
FROM public.header_sync_receipts WHERE header_id = $1`, headerID)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(dbReceipt.TransactionId).To(Equal(txId))
|
Expect(dbReceipt.TransactionId).To(Equal(txId))
|
||||||
Expect(dbReceipt.TxHash).To(Equal(txHash.Hex()))
|
Expect(dbReceipt.TxHash).To(Equal(txHash.Hex()))
|
||||||
@@ -288,7 +288,7 @@ var _ = Describe("Block header repository", func() {
|
|||||||
var dbTransactions []core.TransactionModel
|
var dbTransactions []core.TransactionModel
|
||||||
err = db.Select(&dbTransactions,
|
err = db.Select(&dbTransactions,
|
||||||
`SELECT hash, gas_limit, gas_price, input_data, nonce, raw, tx_from, tx_index, tx_to, "value"
|
`SELECT hash, gas_limit, gas_price, input_data, nonce, raw, tx_from, tx_index, tx_to, "value"
|
||||||
FROM public.light_sync_transactions WHERE header_id = $1`, headerID)
|
FROM public.header_sync_transactions WHERE header_id = $1`, headerID)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(dbTransactions).To(ConsistOf(transactions))
|
Expect(dbTransactions).To(ConsistOf(transactions))
|
||||||
})
|
})
|
||||||
@@ -300,7 +300,7 @@ var _ = Describe("Block header repository", func() {
|
|||||||
var dbTransactions []core.TransactionModel
|
var dbTransactions []core.TransactionModel
|
||||||
err = db.Select(&dbTransactions,
|
err = db.Select(&dbTransactions,
|
||||||
`SELECT hash, gas_limit, gas_price, input_data, nonce, raw, tx_from, tx_index, tx_to, "value"
|
`SELECT hash, gas_limit, gas_price, input_data, nonce, raw, tx_from, tx_index, tx_to, "value"
|
||||||
FROM public.light_sync_transactions WHERE header_id = $1`, headerID)
|
FROM public.header_sync_transactions WHERE header_id = $1`, headerID)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(len(dbTransactions)).To(Equal(2))
|
Expect(len(dbTransactions)).To(Equal(2))
|
||||||
})
|
})
|
||||||
@@ -337,7 +337,7 @@ var _ = Describe("Block header repository", func() {
|
|||||||
var dbTransaction core.TransactionModel
|
var dbTransaction core.TransactionModel
|
||||||
err = db.Get(&dbTransaction,
|
err = db.Get(&dbTransaction,
|
||||||
`SELECT hash, gas_limit, gas_price, input_data, nonce, raw, tx_from, tx_index, tx_to, "value"
|
`SELECT hash, gas_limit, gas_price, input_data, nonce, raw, tx_from, tx_index, tx_to, "value"
|
||||||
FROM public.light_sync_transactions WHERE header_id = $1`, headerID)
|
FROM public.header_sync_transactions WHERE header_id = $1`, headerID)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(dbTransaction).To(Equal(transaction))
|
Expect(dbTransaction).To(Equal(transaction))
|
||||||
})
|
})
|
||||||
@@ -381,7 +381,7 @@ var _ = Describe("Block header repository", func() {
|
|||||||
var dbTransactions []core.TransactionModel
|
var dbTransactions []core.TransactionModel
|
||||||
err = db.Select(&dbTransactions,
|
err = db.Select(&dbTransactions,
|
||||||
`SELECT hash, gas_limit, gas_price, input_data, nonce, raw, tx_from, tx_index, tx_to, "value"
|
`SELECT hash, gas_limit, gas_price, input_data, nonce, raw, tx_from, tx_index, tx_to, "value"
|
||||||
FROM public.light_sync_transactions WHERE header_id = $1`, headerID)
|
FROM public.header_sync_transactions WHERE header_id = $1`, headerID)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(len(dbTransactions)).To(Equal(1))
|
Expect(len(dbTransactions)).To(Equal(1))
|
||||||
})
|
})
|
||||||
|
|||||||
+5
-1
@@ -43,11 +43,15 @@ var FakeHeader = core.Header{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetFakeHeader(blockNumber int64) core.Header {
|
func GetFakeHeader(blockNumber int64) core.Header {
|
||||||
|
return GetFakeHeaderWithTimestamp(fakeTimestamp, blockNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFakeHeaderWithTimestamp(timestamp, blockNumber int64) core.Header {
|
||||||
return core.Header{
|
return core.Header{
|
||||||
Hash: FakeHash.String(),
|
Hash: FakeHash.String(),
|
||||||
BlockNumber: blockNumber,
|
BlockNumber: blockNumber,
|
||||||
Raw: rawFakeHeader,
|
Raw: rawFakeHeader,
|
||||||
Timestamp: strconv.FormatInt(fakeTimestamp, 10),
|
Timestamp: strconv.FormatInt(timestamp, 10),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ func (chain *MockBlockChain) GetHeaderByNumber(blockNumber int64) (core.Header,
|
|||||||
return core.Header{BlockNumber: blockNumber}, nil
|
return core.Header{BlockNumber: blockNumber}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (chain *MockBlockChain) GetHeaderByNumbers(blockNumbers []int64) ([]core.Header, error) {
|
func (chain *MockBlockChain) GetHeadersByNumbers(blockNumbers []int64) ([]core.Header, error) {
|
||||||
var headers []core.Header
|
var headers []core.Header
|
||||||
for _, blockNumber := range blockNumbers {
|
for _, blockNumber := range blockNumbers {
|
||||||
var header = core.Header{BlockNumber: int64(blockNumber)}
|
var header = core.Header{BlockNumber: int64(blockNumber)}
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
// VulcanizeDB
|
||||||
|
// Copyright © 2019 Vulcanize
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package fakes
|
package fakes
|
||||||
|
|
||||||
import "github.com/vulcanize/vulcanizedb/pkg/filters"
|
import "github.com/vulcanize/vulcanizedb/pkg/filters"
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
package fakes
|
|
||||||
|
|
||||||
type MockFullBlockRetriever struct {
|
|
||||||
FirstBlock int64
|
|
||||||
FirstBlockErr error
|
|
||||||
MostRecentBlock int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (retriever *MockFullBlockRetriever) RetrieveFirstBlock(contractAddr string) (int64, error) {
|
|
||||||
return retriever.FirstBlock, retriever.FirstBlockErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (retriever *MockFullBlockRetriever) RetrieveMostRecentBlock() (int64, error) {
|
|
||||||
return retriever.MostRecentBlock, nil
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package fakes
|
||||||
|
|
||||||
|
type MockFullSyncBlockRetriever struct {
|
||||||
|
FirstBlock int64
|
||||||
|
FirstBlockErr error
|
||||||
|
MostRecentBlock int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (retriever *MockFullSyncBlockRetriever) RetrieveFirstBlock(contractAddr string) (int64, error) {
|
||||||
|
return retriever.FirstBlock, retriever.FirstBlockErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (retriever *MockFullSyncBlockRetriever) RetrieveMostRecentBlock() (int64, error) {
|
||||||
|
return retriever.MostRecentBlock, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package fakes
|
||||||
|
|
||||||
|
type MockHeaderSyncBlockRetriever struct {
|
||||||
|
FirstBlock int64
|
||||||
|
FirstBlockErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (retriever *MockHeaderSyncBlockRetriever) RetrieveFirstBlock() (int64, error) {
|
||||||
|
return retriever.FirstBlock, retriever.FirstBlockErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (retriever *MockHeaderSyncBlockRetriever) RetrieveMostRecentBlock() (int64, error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package fakes
|
||||||
|
|
||||||
|
import "github.com/vulcanize/vulcanizedb/pkg/core"
|
||||||
|
|
||||||
|
type MockHeaderSyncHeaderRepository struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*MockHeaderSyncHeaderRepository) AddCheckColumn(id string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*MockHeaderSyncHeaderRepository) AddCheckColumns(ids []string) error {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*MockHeaderSyncHeaderRepository) MarkHeaderChecked(headerID int64, eventID string) error {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*MockHeaderSyncHeaderRepository) MarkHeaderCheckedForAll(headerID int64, ids []string) error {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*MockHeaderSyncHeaderRepository) MarkHeadersCheckedForAll(headers []core.Header, ids []string) error {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*MockHeaderSyncHeaderRepository) MissingHeaders(startingBlockNumber int64, endingBlockNumber int64, eventID string) ([]core.Header, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*MockHeaderSyncHeaderRepository) MissingMethodsCheckedEventsIntersection(startingBlockNumber, endingBlockNumber int64, methodIds, eventIds []string) ([]core.Header, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*MockHeaderSyncHeaderRepository) MissingHeadersForAll(startingBlockNumber, endingBlockNumber int64, ids []string) ([]core.Header, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*MockHeaderSyncHeaderRepository) CheckCache(key string) (interface{}, bool) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package fakes
|
|
||||||
|
|
||||||
type MockLightBlockRetriever struct {
|
|
||||||
FirstBlock int64
|
|
||||||
FirstBlockErr error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (retriever *MockLightBlockRetriever) RetrieveFirstBlock() (int64, error) {
|
|
||||||
return retriever.FirstBlock, retriever.FirstBlockErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (retriever *MockLightBlockRetriever) RetrieveMostRecentBlock() (int64, error) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package fakes
|
|
||||||
|
|
||||||
import "github.com/vulcanize/vulcanizedb/pkg/core"
|
|
||||||
|
|
||||||
type MockLightHeaderRepository struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*MockLightHeaderRepository) AddCheckColumn(id string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*MockLightHeaderRepository) AddCheckColumns(ids []string) error {
|
|
||||||
panic("implement me")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*MockLightHeaderRepository) MarkHeaderChecked(headerID int64, eventID string) error {
|
|
||||||
panic("implement me")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*MockLightHeaderRepository) MarkHeaderCheckedForAll(headerID int64, ids []string) error {
|
|
||||||
panic("implement me")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*MockLightHeaderRepository) MarkHeadersCheckedForAll(headers []core.Header, ids []string) error {
|
|
||||||
panic("implement me")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*MockLightHeaderRepository) MissingHeaders(startingBlockNumber int64, endingBlockNumber int64, eventID string) ([]core.Header, error) {
|
|
||||||
panic("implement me")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*MockLightHeaderRepository) MissingMethodsCheckedEventsIntersection(startingBlockNumber, endingBlockNumber int64, methodIds, eventIds []string) ([]core.Header, error) {
|
|
||||||
panic("implement me")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*MockLightHeaderRepository) MissingHeadersForAll(startingBlockNumber, endingBlockNumber int64, ids []string) ([]core.Header, error) {
|
|
||||||
panic("implement me")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*MockLightHeaderRepository) CheckCache(key string) (interface{}, bool) {
|
|
||||||
panic("implement me")
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,19 @@
|
|||||||
|
// VulcanizeDB
|
||||||
|
// Copyright © 2019 Vulcanize
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package fakes
|
package fakes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
// VulcanizeDB
|
||||||
|
// Copyright © 2019 Vulcanize
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package fakes
|
package fakes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
// VulcanizeDB
|
||||||
|
// Copyright © 2019 Vulcanize
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package fakes
|
package fakes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -6,24 +22,22 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type MockTailer struct {
|
type MockTailer struct {
|
||||||
Lines chan *tail.Line
|
Lines chan *tail.Line
|
||||||
TailCalled bool
|
TailErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMockTailer() *MockTailer {
|
func NewMockTailer() *MockTailer {
|
||||||
return &MockTailer{
|
return &MockTailer{
|
||||||
Lines: make(chan *tail.Line, 1),
|
Lines: make(chan *tail.Line, 1),
|
||||||
TailCalled: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mock *MockTailer) Tail() (*tail.Tail, error) {
|
func (mock *MockTailer) Tail() (*tail.Tail, error) {
|
||||||
mock.TailCalled = true
|
|
||||||
fakeTail := &tail.Tail{
|
fakeTail := &tail.Tail{
|
||||||
Filename: "",
|
Filename: "",
|
||||||
Lines: mock.Lines,
|
Lines: mock.Lines,
|
||||||
Config: tail.Config{},
|
Config: tail.Config{},
|
||||||
Tomb: tomb.Tomb{},
|
Tomb: tomb.Tomb{},
|
||||||
}
|
}
|
||||||
return fakeTail, nil
|
return fakeTail, mock.TailErr
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
// VulcanizeDB
|
||||||
|
// Copyright © 2019 Vulcanize
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package fakes
|
package fakes
|
||||||
|
|
||||||
import "github.com/ethereum/go-ethereum/core/types"
|
import "github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package geth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -80,7 +79,7 @@ func (blockChain *BlockChain) GetHeaderByNumber(blockNumber int64) (header core.
|
|||||||
return blockChain.getPOWHeader(blockNumber)
|
return blockChain.getPOWHeader(blockNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (blockChain *BlockChain) GetHeaderByNumbers(blockNumbers []int64) (header []core.Header, err error) {
|
func (blockChain *BlockChain) GetHeadersByNumbers(blockNumbers []int64) (header []core.Header, err error) {
|
||||||
if blockChain.node.NetworkID == core.KOVAN_NETWORK_ID {
|
if blockChain.node.NetworkID == core.KOVAN_NETWORK_ID {
|
||||||
return blockChain.getPOAHeaders(blockNumbers)
|
return blockChain.getPOAHeaders(blockNumbers)
|
||||||
}
|
}
|
||||||
@@ -122,7 +121,6 @@ func (blockChain *BlockChain) GetTransactions(transactionHashes []common.Hash) (
|
|||||||
|
|
||||||
rpcErr := blockChain.rpcClient.BatchCall(batch)
|
rpcErr := blockChain.rpcClient.BatchCall(batch)
|
||||||
if rpcErr != nil {
|
if rpcErr != nil {
|
||||||
fmt.Println("rpc err")
|
|
||||||
return []core.TransactionModel{}, rpcErr
|
return []core.TransactionModel{}, rpcErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ var _ = Describe("Geth blockchain", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("fetches headers with multiple blocks", func() {
|
It("fetches headers with multiple blocks", func() {
|
||||||
_, err := blockChain.GetHeaderByNumbers([]int64{100, 99})
|
_, err := blockChain.GetHeadersByNumbers([]int64{100, 99})
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
mockRpcClient.AssertBatchCalledWith("eth_getBlockByNumber", 2)
|
mockRpcClient.AssertBatchCalledWith("eth_getBlockByNumber", 2)
|
||||||
@@ -139,7 +139,7 @@ var _ = Describe("Geth blockchain", func() {
|
|||||||
blockNumber := hexutil.Big(*big.NewInt(100))
|
blockNumber := hexutil.Big(*big.NewInt(100))
|
||||||
mockRpcClient.SetReturnPOAHeaders([]vulcCore.POAHeader{{Number: &blockNumber}})
|
mockRpcClient.SetReturnPOAHeaders([]vulcCore.POAHeader{{Number: &blockNumber}})
|
||||||
|
|
||||||
_, err := blockChain.GetHeaderByNumbers([]int64{100, 99})
|
_, err := blockChain.GetHeadersByNumbers([]int64{100, 99})
|
||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
mockRpcClient.AssertBatchCalledWith("eth_getBlockByNumber", 2)
|
mockRpcClient.AssertBatchCalledWith("eth_getBlockByNumber", 2)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user