From ca8b3066d156b24b43318ae048b9e6aa86d900bd Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Tue, 10 Nov 2020 17:07:59 +0800 Subject: [PATCH] Write directly to PostgreSQL (port from geth statediff) --- cmd/env.go | 49 +++ cmd/root.go | 63 ++- cmd/serve.go | 21 +- cmd/write.go | 103 +++++ environments/example.toml | 14 +- go.mod | 7 +- go.sum | 98 ++++- pkg/api.go | 8 +- pkg/builder.go | 644 +++++++++++++++++-------------- pkg/builder_test.go | 2 +- pkg/service.go | 76 +++- pkg/testhelpers/mocks/builder.go | 24 +- pkg/types.go | 2 +- 13 files changed, 782 insertions(+), 329 deletions(-) create mode 100644 cmd/env.go create mode 100644 cmd/write.go diff --git a/cmd/env.go b/cmd/env.go new file mode 100644 index 0000000..eb5f7aa --- /dev/null +++ b/cmd/env.go @@ -0,0 +1,49 @@ +// 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 . + +package cmd + +import ( + "github.com/spf13/viper" + + pg "github.com/ethereum/go-ethereum/statediff/indexer/postgres" +) + +const ( + ETH_NODE_ID = "ETH_NODE_ID" + ETH_CLIENT_NAME = "ETH_CLIENT_NAME" + ETH_GENESIS_BLOCK = "ETH_GENESIS_BLOCK" + ETH_NETWORK_ID = "ETH_NETWORK_ID" + ETH_CHAIN_ID = "ETH_CHAIN_ID" +) + +// Bind env vars for eth node and DB configuration +func init() { + viper.BindEnv("ethereum.nodeID", ETH_NODE_ID) + viper.BindEnv("ethereum.clientName", ETH_CLIENT_NAME) + viper.BindEnv("ethereum.genesisBlock", ETH_GENESIS_BLOCK) + viper.BindEnv("ethereum.networkID", ETH_NETWORK_ID) + viper.BindEnv("ethereum.chainID", ETH_CHAIN_ID) + + viper.BindEnv("database.name", pg.DATABASE_NAME) + viper.BindEnv("database.hostname", pg.DATABASE_HOSTNAME) + viper.BindEnv("database.port", pg.DATABASE_PORT) + viper.BindEnv("database.user", pg.DATABASE_USER) + viper.BindEnv("database.password", pg.DATABASE_PASSWORD) + viper.BindEnv("database.maxIdle", pg.DATABASE_MAX_IDLE_CONNECTIONS) + viper.BindEnv("database.maxOpen", pg.DATABASE_MAX_OPEN_CONNECTIONS) + viper.BindEnv("database.maxLifetime", pg.DATABASE_MAX_CONN_LIFETIME) +} diff --git a/cmd/root.go b/cmd/root.go index baf7747..c2c6811 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -21,6 +21,8 @@ import ( "os" "strings" + "github.com/ethereum/go-ethereum/statediff/indexer/node" + "github.com/ethereum/go-ethereum/statediff/indexer/postgres" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -45,7 +47,7 @@ func Execute() { } func initFuncs(cmd *cobra.Command, args []string) { - logfile := viper.GetString("logfile") + logfile := viper.GetString("log.file") if logfile != "" { file, err := os.OpenFile(logfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) @@ -85,12 +87,41 @@ func init() { rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file location") rootCmd.PersistentFlags().String("log-file", "", "file path for logging") - rootCmd.PersistentFlags().String("log-level", log.InfoLevel.String(), "log level (trace, debug, info, warn, error, fatal, panic") + rootCmd.PersistentFlags().String("log-level", log.InfoLevel.String(), + "log level (trace, debug, info, warn, error, fatal, panic") + + rootCmd.PersistentFlags().String("leveldb-path", "", "path to primary datastore") + rootCmd.PersistentFlags().String("ancient-path", "", "path to ancient datastore") rootCmd.PersistentFlags().Int("workers", 0, "number of concurrent workers to use") + rootCmd.PersistentFlags().String("database-name", "vulcanize_public", "database name") + rootCmd.PersistentFlags().Int("database-port", 5432, "database port") + rootCmd.PersistentFlags().String("database-hostname", "localhost", "database hostname") + rootCmd.PersistentFlags().String("database-user", "", "database user") + rootCmd.PersistentFlags().String("database-password", "", "database password") + + rootCmd.PersistentFlags().String("eth-node-id", "", "eth node id") + rootCmd.PersistentFlags().String("eth-client-name", "Geth", "eth client name") + rootCmd.PersistentFlags().String("eth-genesis-block", + "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3", "eth genesis block hash") + rootCmd.PersistentFlags().String("eth-network-id", "1", "eth network id") + rootCmd.PersistentFlags().String("eth-chain-id", "1", "eth chain id") + viper.BindPFlag("log.file", rootCmd.PersistentFlags().Lookup("log-file")) viper.BindPFlag("log.level", rootCmd.PersistentFlags().Lookup("log-level")) viper.BindPFlag("statediff.workers", rootCmd.PersistentFlags().Lookup("workers")) + viper.BindPFlag("leveldb.path", rootCmd.PersistentFlags().Lookup("leveldb-path")) + viper.BindPFlag("leveldb.ancient", rootCmd.PersistentFlags().Lookup("ancient-path")) + viper.BindPFlag("database.name", rootCmd.PersistentFlags().Lookup("database-name")) + viper.BindPFlag("database.port", rootCmd.PersistentFlags().Lookup("database-port")) + viper.BindPFlag("database.hostname", rootCmd.PersistentFlags().Lookup("database-hostname")) + viper.BindPFlag("database.user", rootCmd.PersistentFlags().Lookup("database-user")) + viper.BindPFlag("database.password", rootCmd.PersistentFlags().Lookup("database-password")) + viper.BindPFlag("ethereum.nodeID", rootCmd.PersistentFlags().Lookup("eth-node-id")) + viper.BindPFlag("ethereum.clientName", rootCmd.PersistentFlags().Lookup("eth-client-name")) + viper.BindPFlag("ethereum.genesisBlock", rootCmd.PersistentFlags().Lookup("eth-genesis-block")) + viper.BindPFlag("ethereum.networkID", rootCmd.PersistentFlags().Lookup("eth-network-id")) + viper.BindPFlag("ethereum.chainID", rootCmd.PersistentFlags().Lookup("eth-chain-id")) } func initConfig() { @@ -105,3 +136,31 @@ func initConfig() { log.Warn("No config file passed with --config flag") } } + +func GetEthNodeInfo() node.Info { + return node.Info{ + ID: viper.GetString("ethereum.nodeID"), + ClientName: viper.GetString("ethereum.clientName"), + GenesisBlock: viper.GetString("ethereum.genesisBlock"), + NetworkID: viper.GetString("ethereum.networkID"), + ChainID: viper.GetUint64("ethereum.chainID"), + } +} + +func GetDBParams() postgres.ConnectionParams { + return postgres.ConnectionParams{ + Name: viper.GetString("database.name"), + Hostname: viper.GetString("database.hostname"), + Port: viper.GetInt("database.port"), + User: viper.GetString("database.user"), + Password: viper.GetString("database.password"), + } +} + +func GetDBConfig() postgres.ConnectionConfig { + return postgres.ConnectionConfig{ + MaxIdle: viper.GetInt("database.maxIdle"), + MaxOpen: viper.GetInt("database.maxOpen"), + MaxLifetime: viper.GetInt("database.maxLifetime"), + } +} diff --git a/cmd/serve.go b/cmd/serve.go index 2897769..f918c5a 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -27,6 +27,8 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" + ind "github.com/ethereum/go-ethereum/statediff/indexer" + "github.com/ethereum/go-ethereum/statediff/indexer/postgres" sd "github.com/vulcanize/eth-statediff-service/pkg" ) @@ -53,8 +55,9 @@ func serve() { if path == "" || ancientPath == "" { logWithCommand.Fatal("require a valid eth leveldb primary datastore path and ancient datastore path") } - chainID := viper.GetUint64("eth.chainID") - config, err := chainConfig(chainID) + + nodeInfo := GetEthNodeInfo() + config, err := chainConfig(nodeInfo.ChainID) if err != nil { logWithCommand.Fatal(err) } @@ -68,8 +71,12 @@ func serve() { // create statediff service logWithCommand.Info("creating statediff service") - statediffService, err := sd.NewStateDiffService( - lvlDBReader, sd.Config{Workers: viper.GetUint("statediff.workers")}) + db, err := postgres.NewDB(GetDBParams(), GetDBConfig(), nodeInfo) + if err != nil { + logWithCommand.Fatal(err) + } + indexer := ind.NewStateDiffIndexer(config, db) + statediffService, err := sd.NewStateDiffService(lvlDBReader, indexer, viper.GetUint("statediff.workers")) if err != nil { logWithCommand.Fatal(err) } @@ -96,15 +103,9 @@ func serve() { func init() { rootCmd.AddCommand(serveCmd) - serveCmd.PersistentFlags().String("leveldb-path", "", "path to primary datastore") - serveCmd.PersistentFlags().String("ancient-path", "", "path to ancient datastore") - serveCmd.PersistentFlags().Uint("chain-id", 1, "ethereum chain id (mainnet = 1)") serveCmd.PersistentFlags().String("http-path", "", "vdb server http path") serveCmd.PersistentFlags().String("ipc-path", "", "vdb server ipc path") - viper.BindPFlag("leveldb.path", serveCmd.PersistentFlags().Lookup("leveldb-path")) - viper.BindPFlag("leveldb.ancient", serveCmd.PersistentFlags().Lookup("ancient-path")) - viper.BindPFlag("eth.chainID", serveCmd.PersistentFlags().Lookup("chain-id")) viper.BindPFlag("server.httpPath", serveCmd.PersistentFlags().Lookup("http-path")) viper.BindPFlag("server.ipcPath", serveCmd.PersistentFlags().Lookup("ipc-path")) } diff --git a/cmd/write.go b/cmd/write.go new file mode 100644 index 0000000..8240dd0 --- /dev/null +++ b/cmd/write.go @@ -0,0 +1,103 @@ +// Copyright © 2019 Vulcanize, Inc +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package cmd + +import ( + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + ind "github.com/ethereum/go-ethereum/statediff/indexer" + "github.com/ethereum/go-ethereum/statediff/indexer/postgres" + sdtypes "github.com/ethereum/go-ethereum/statediff/types" + sd "github.com/vulcanize/eth-statediff-service/pkg" +) + +var writeCmd = &cobra.Command{ + Use: "write", + Short: "Write statediffs directly to DB for preconfigured block ranges", + Long: `Usage + +./eth-statediff-service write --config={path to toml config file}`, + Run: func(cmd *cobra.Command, args []string) { + subCommand = cmd.CalledAs() + logWithCommand = *logrus.WithField("SubCommand", subCommand) + write() + }, +} + +func init() { + rootCmd.AddCommand(writeCmd) +} + +func write() { + logWithCommand.Info("Starting statediff writer") + + // load params + path := viper.GetString("leveldb.path") + ancientPath := viper.GetString("leveldb.ancient") + if path == "" || ancientPath == "" { + logWithCommand.Fatal("require a valid eth leveldb primary datastore path and ancient datastore path") + } + + nodeInfo := GetEthNodeInfo() + config, err := chainConfig(nodeInfo.ChainID) + if err != nil { + logWithCommand.Fatal(err) + } + + // create leveldb reader + logWithCommand.Info("creating leveldb reader") + lvlDBReader, err := sd.NewLvlDBReader(path, ancientPath, config) + if err != nil { + logWithCommand.Fatal(err) + } + + // create statediff service + logWithCommand.Info("creating statediff service") + db, err := postgres.NewDB(GetDBParams(), GetDBConfig(), nodeInfo) + if err != nil { + logWithCommand.Fatal(err) + } + indexer := ind.NewStateDiffIndexer(config, db) + statediffService, err := sd.NewStateDiffService(lvlDBReader, indexer, viper.GetUint("statediff.workers")) + if err != nil { + logWithCommand.Fatal(err) + } + + // Read all defined block ranges, write statediffs to database + var blockRanges [][2]uint64 + diffParams := sdtypes.Params{ // todo: configurable? + IntermediateStateNodes: true, + IntermediateStorageNodes: true, + IncludeBlock: true, + IncludeReceipts: true, + IncludeTD: true, + IncludeCode: true, + } + viper.UnmarshalKey("write.ranges", &blockRanges) + // viper.UnmarshalKey("write.params", &diffParams) + + for _, rng := range blockRanges { + if rng[1] < rng[0] { + logWithCommand.Fatal("range ending block number needs to be greater than starting block number") + } + logrus.Infof("writing statediffs from %d to %d", rng[0], rng[1]) + for height := uint64(0); height <= rng[1]; height++ { + statediffService.WriteStateDiffAt(height, diffParams) + } + } +} diff --git a/environments/example.toml b/environments/example.toml index f8d3fee..4ece698 100644 --- a/environments/example.toml +++ b/environments/example.toml @@ -9,9 +9,21 @@ [statediff] workers = 4 +[write] + ranges = [ + [0, 0] + ] + [log] file = "" level = "info" [eth] - chainID = 1 \ No newline at end of file + chainID = 1 + +[database] + name = "vulcanize_test" + hostname = "localhost" + port = 5432 + user = "vulcanize" + password = "..." diff --git a/go.mod b/go.mod index 6009df9..329b26c 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,12 @@ module github.com/vulcanize/eth-statediff-service go 1.13 require ( - github.com/ethereum/go-ethereum v1.9.11 - github.com/sirupsen/logrus v1.6.0 + github.com/ethereum/go-ethereum v1.9.24 + github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect + github.com/sirupsen/logrus v1.7.0 github.com/spf13/cobra v1.0.0 github.com/spf13/viper v1.7.1 github.com/vulcanize/go-eth-state-node-iterator v0.0.1-alpha ) -replace github.com/ethereum/go-ethereum v1.9.11 => github.com/vulcanize/go-ethereum v1.9.11-statediff-0.0.8 +replace github.com/ethereum/go-ethereum v1.9.24 => github.com/vulcanize/go-ethereum v1.9.24-statediff-0.0.11 diff --git a/go.sum b/go.sum index bc9c3ef..726a7a2 100644 --- a/go.sum +++ b/go.sum @@ -42,6 +42,7 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= @@ -77,6 +78,7 @@ github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c h1:JHHhtb9XWJrGNMcr github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa h1:XKAhUk/dtp+CV0VO6mhG2V7jA9vbcGcnYF/Ay9NjZrY= github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= +github.com/ethereum/go-ethereum v1.9.11/go.mod h1:7oC0Ni6dosMv5pxMigm6s0hN8g4haJMBnqmmo0D9YfQ= github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc h1:jtW8jbpkO4YirRSyepBOH8E+2HEw6/hKkBvFPwhUN8c= @@ -92,9 +94,11 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -117,6 +121,8 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -128,6 +134,8 @@ github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= +github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -145,6 +153,8 @@ github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -156,9 +166,38 @@ github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3 h1:DqD8eigqlUm0+znmx7z github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= +github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= +github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= +github.com/ipfs/go-block-format v0.0.2 h1:qPDvcP19izTjU8rgo6p7gTXZlkMkF5bz5G3fqIsSCPE= +github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= +github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog= +github.com/ipfs/go-cid v0.0.7 h1:ysQJVJA3fNDF1qigJbsSQOdjhVLsOEoPdh0+R97k3jY= +github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= +github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= +github.com/ipfs/go-datastore v0.4.2 h1:h8/n7WPzhp239kkLws+epN3Ic7YtcBPgcaXfEfdVDWM= +github.com/ipfs/go-datastore v0.4.2/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= +github.com/ipfs/go-ipfs-blockstore v1.0.1 h1:fnuVj4XdZp4yExhd0CnUwAiMNJHiPnfInhiuwz4lW1w= +github.com/ipfs/go-ipfs-blockstore v1.0.1/go.mod h1:MGNZlHNEnR4KGgPHM3/k8lBySIOK2Ve+0KjZubKlaOE= +github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= +github.com/ipfs/go-ipfs-ds-help v1.0.0 h1:bEQ8hMGs80h0sR8O4tfDgV6B01aaF9qeTrujrTLYV3g= +github.com/ipfs/go-ipfs-ds-help v1.0.0/go.mod h1:ujAbkeIgkKAWtxxNkoZHWLCyk5JpPoKnGyCcsoF6ueE= +github.com/ipfs/go-ipfs-util v0.0.1 h1:Wz9bL2wB2YBJqggkA4dD7oSmqB4cAnpNbGrlHJulv50= +github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= +github.com/ipfs/go-ipld-format v0.2.0 h1:xGlJKkArkmBvowr+GMCX0FEZtkro71K1AwiKnL37mwA= +github.com/ipfs/go-ipld-format v0.2.0/go.mod h1:3l3C1uKoadTPbeNfrDi+xMInYKlx2Cvg1BuydPSdzQs= +github.com/ipfs/go-log v0.0.1 h1:9XTUN/rW64BCG1YhPK9Hoy3q8nr4gOmHHBpgFdfw6Lc= +github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= +github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg= +github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 h1:6OvNmYgJyexcZ3pYbTI9jWx5tHo1Dee/tWbLMfPe2TA= github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8 h1:bspPhN+oKYFk5fcGNuQzp6IGzYQSenLEgH3s6jkXrWw= +github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -176,22 +215,37 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg= +github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.0 h1:v2XXALHHh6zHfYTJ+cSkwtyffnaOyR1MXaA91mTrb8o= github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035 h1:USWjF42jDCSEeikX/G1g40ZWnsPXN5WkZ4jMHZWyBK4= github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771 h1:MHkK1uRtFbVqvAgvWxafZe54+5uBxLluGylDiKgdhwo= +github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -203,6 +257,22 @@ github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQz github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= +github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc= +github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= +github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= +github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= +github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= +github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= +github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk= +github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= +github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= +github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= +github.com/multiformats/go-multihash v0.0.14 h1:QoBceQYQQtNUuf6s7wHxnE2c8bhbMqhfGzNI032se/I= +github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= +github.com/multiformats/go-varint v0.0.5 h1:XVZwSo04Cs3j/jS0uAEPpT3JY6DzMcVLLoWOSnCxOjg= +github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= @@ -213,6 +283,8 @@ github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222 h1:goeTyGkArOZIVOMA0dQbyuPWGNQJZGPwPu/QS9GlpnA= @@ -226,12 +298,16 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= @@ -251,11 +327,15 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.0.1-0.20190317074736-539464a789e9/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= @@ -293,8 +373,8 @@ github.com/vulcanize/go-eth-state-node-iterator v0.0.1-alpha h1:obQGU35NWJjxR9UT github.com/vulcanize/go-eth-state-node-iterator v0.0.1-alpha/go.mod h1:tjtesuYyYOHhG1Xq7BExdsHhRy6pi92/gc1NkZ+ojSY= github.com/vulcanize/go-ethereum v1.9.11-statediff-0.0.5 h1:U+BqhjRLR22e9OEm8cgWC3Eq3bh8G6azjNpXeenfCG4= github.com/vulcanize/go-ethereum v1.9.11-statediff-0.0.5/go.mod h1:7oC0Ni6dosMv5pxMigm6s0hN8g4haJMBnqmmo0D9YfQ= -github.com/vulcanize/go-ethereum v1.9.11-statediff-0.0.8 h1:7TK52k55uvSl+1SCKYYFelzH1NrpvEcDrpeU9nUDIpI= -github.com/vulcanize/go-ethereum v1.9.11-statediff-0.0.8/go.mod h1:7oC0Ni6dosMv5pxMigm6s0hN8g4haJMBnqmmo0D9YfQ= +github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc h1:9lDbC6Rz4bwmou+oE6Dt4Cb2BGMur5eR/GYptkKUVHo= +github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 h1:1cngl9mPEoITZG8s8cVcUy5CeIBYhEESkOB7m6Gmkrk= github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= @@ -303,14 +383,19 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -338,6 +423,7 @@ golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -363,6 +449,8 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -372,11 +460,14 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 h1:LepdCS8Gf/MVejFIt8lsiexZATdoGVyp5bcyS+rYoUI= golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -394,6 +485,7 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -421,11 +513,13 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= +gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772 h1:hhsSf/5z74Ck/DJYc+R8zpq8KGm7uJvpdLRQED/IedA= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= diff --git a/pkg/api.go b/pkg/api.go index 334268e..7250b2a 100644 --- a/pkg/api.go +++ b/pkg/api.go @@ -17,8 +17,7 @@ package statediff import ( "context" - - sd "github.com/ethereum/go-ethereum/statediff" + sd "github.com/ethereum/go-ethereum/statediff/types" ) // APIName is the namespace used for the state diffing service API @@ -49,3 +48,8 @@ func (api *PublicStateDiffAPI) StateDiffAt(ctx context.Context, blockNumber uint func (api *PublicStateDiffAPI) StateTrieAt(ctx context.Context, blockNumber uint64, params sd.Params) (*sd.Payload, error) { return api.sds.StateTrieAt(blockNumber, params) } + +// WriteStateDiffAt writes a state diff object directly to DB at the specific blockheight +func (api *PublicStateDiffAPI) WriteStateDiffAt(ctx context.Context, blockNumber uint64, params sd.Params) error { + return api.sds.WriteStateDiffAt(blockNumber, params) +} diff --git a/pkg/builder.go b/pkg/builder.go index 927b48f..d9458e2 100644 --- a/pkg/builder.go +++ b/pkg/builder.go @@ -33,7 +33,8 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" - sd "github.com/ethereum/go-ethereum/statediff" + "github.com/ethereum/go-ethereum/statediff" + . "github.com/ethereum/go-ethereum/statediff/types" iter "github.com/vulcanize/go-eth-state-node-iterator" ) @@ -46,8 +47,9 @@ var ( // Builder interface exposes the method for building a state diff between two blocks type Builder interface { - BuildStateDiffObject(args sd.Args, params sd.Params) (sd.StateObject, error) - BuildStateTrieObject(current *types.Block) (sd.StateObject, error) + BuildStateDiffObject(args Args, params Params) (StateObject, error) + BuildStateTrieObject(current *types.Block) (StateObject, error) + WriteStateDiffObject(args StateRoots, params Params, output StateNodeSink, codeOutput CodeSink) error } type builder struct { @@ -59,6 +61,48 @@ type iterPair struct { older, newer trie.NodeIterator } +func resolveNode(it trie.NodeIterator, trieDB *trie.Database) (StateNode, []interface{}, error) { + nodePath := make([]byte, len(it.Path())) + copy(nodePath, it.Path()) + node, err := trieDB.Node(it.Hash()) + if err != nil { + return StateNode{}, nil, err + } + var nodeElements []interface{} + if err := rlp.DecodeBytes(node, &nodeElements); err != nil { + return StateNode{}, nil, err + } + ty, err := statediff.CheckKeyType(nodeElements) + if err != nil { + return StateNode{}, nil, err + } + return StateNode{ + NodeType: ty, + Path: nodePath, + NodeValue: node, + }, nodeElements, nil +} + +// convenience +func stateNodeAppender(nodes *[]StateNode) StateNodeSink { + return func(node StateNode) error { + *nodes = append(*nodes, node) + return nil + } +} +func storageNodeAppender(nodes *[]StorageNode) StorageNodeSink { + return func(node StorageNode) error { + *nodes = append(*nodes, node) + return nil + } +} +func codeMappingAppender(data *[]CodeAndCodeHash) CodeSink { + return func(c CodeAndCodeHash) error { + *data = append(*data, c) + return nil + } +} + // NewBuilder is used to create a statediff builder func NewBuilder(stateCache state.Database, workers uint) (Builder, error) { if workers == 0 { @@ -74,17 +118,17 @@ func NewBuilder(stateCache state.Database, workers uint) (Builder, error) { } // BuildStateTrieObject builds a state trie object from the provided block -func (sdb *builder) BuildStateTrieObject(current *types.Block) (sd.StateObject, error) { +func (sdb *builder) BuildStateTrieObject(current *types.Block) (StateObject, error) { currentTrie, err := sdb.stateCache.OpenTrie(current.Root()) if err != nil { - return sd.StateObject{}, fmt.Errorf("error creating trie for block %d: %v", current.Number(), err) + return StateObject{}, fmt.Errorf("error creating trie for block %d: %v", current.Number(), err) } it := currentTrie.NodeIterator([]byte{}) stateNodes, codeAndCodeHashes, err := sdb.buildStateTrie(it) if err != nil { - return sd.StateObject{}, fmt.Errorf("error collecting state nodes for block %d: %v", current.Number(), err) + return StateObject{}, fmt.Errorf("error collecting state nodes for block %d: %v", current.Number(), err) } - return sd.StateObject{ + return StateObject{ BlockNumber: current.Number(), BlockHash: current.Hash(), Nodes: stateNodes, @@ -92,68 +136,32 @@ func (sdb *builder) BuildStateTrieObject(current *types.Block) (sd.StateObject, }, nil } -func resolveNode(it trie.NodeIterator, trieDB *trie.Database) (sd.StateNode, []interface{}, error) { - nodePath := make([]byte, len(it.Path())) - copy(nodePath, it.Path()) - node, err := trieDB.Node(it.Hash()) - if err != nil { - return sd.StateNode{}, nil, err - } - var nodeElements []interface{} - if err := rlp.DecodeBytes(node, &nodeElements); err != nil { - return sd.StateNode{}, nil, err - } - ty, err := sd.CheckKeyType(nodeElements) - if err != nil { - return sd.StateNode{}, nil, err - } - return sd.StateNode{ - NodeType: ty, - Path: nodePath, - NodeValue: node, - }, nodeElements, nil -} - -func (sdb *builder) buildStateTrie(it trie.NodeIterator) ([]sd.StateNode, []sd.CodeAndCodeHash, error) { - stateNodes := make([]sd.StateNode, 0) - codeAndCodeHashes := make([]sd.CodeAndCodeHash, 0) +func (sdb *builder) buildStateTrie(it trie.NodeIterator) ([]StateNode, []CodeAndCodeHash, error) { + stateNodes := make([]StateNode, 0) + codeAndCodeHashes := make([]CodeAndCodeHash, 0) for it.Next(true) { - // skip value nodes and null nodes + // skip value nodes if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) { continue } - nodePath := make([]byte, len(it.Path())) - copy(nodePath, it.Path()) - node, err := sdb.stateCache.TrieDB().Node(it.Hash()) + node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB()) if err != nil { return nil, nil, err } - var nodeElements []interface{} - if err := rlp.DecodeBytes(node, &nodeElements); err != nil { - return nil, nil, err - } - ty, err := sd.CheckKeyType(nodeElements) - if err != nil { - return nil, nil, err - } - switch ty { - case sd.Leaf: + switch node.NodeType { + case Leaf: var account state.Account if err := rlp.DecodeBytes(nodeElements[1].([]byte), &account); err != nil { - return nil, nil, fmt.Errorf("error decoding account for leaf node at path %x nerror: %v", nodePath, err) + return nil, nil, fmt.Errorf("error decoding account for leaf node at path %x nerror: %v", node.Path, err) } partialPath := trie.CompactToHex(nodeElements[0].([]byte)) - valueNodePath := append(nodePath, partialPath...) + valueNodePath := append(node.Path, partialPath...) encodedPath := trie.HexToCompact(valueNodePath) leafKey := encodedPath[1:] - node := sd.StateNode{ - NodeType: ty, - Path: nodePath, - LeafKey: leafKey, - NodeValue: node, - } + node.LeafKey = leafKey if !bytes.Equal(account.CodeHash, nullCodeHash) { - storageNodes, err := sdb.buildStorageNodesEventual(account.Root, nil, true) + var storageNodes []StorageNode + err := sdb.buildStorageNodesEventual(account.Root, nil, true, storageNodeAppender(&storageNodes)) if err != nil { return nil, nil, fmt.Errorf("failed building eventual storage diffs for account %+v\r\nerror: %v", account, err) } @@ -164,27 +172,41 @@ func (sdb *builder) buildStateTrie(it trie.NodeIterator) ([]sd.StateNode, []sd.C if err != nil { return nil, nil, fmt.Errorf("failed to retrieve code for codehash %s\r\n error: %v", codeHash.String(), err) } - codeAndCodeHashes = append(codeAndCodeHashes, sd.CodeAndCodeHash{ + codeAndCodeHashes = append(codeAndCodeHashes, CodeAndCodeHash{ Hash: codeHash, Code: code, }) } stateNodes = append(stateNodes, node) - case sd.Extension, sd.Branch: - stateNodes = append(stateNodes, sd.StateNode{ - NodeType: ty, - Path: nodePath, - NodeValue: node, - }) + case Extension, Branch: + stateNodes = append(stateNodes, node) default: - return nil, nil, fmt.Errorf("unexpected node type %s", ty) + return nil, nil, fmt.Errorf("unexpected node type %s", node.NodeType) } } return stateNodes, codeAndCodeHashes, it.Error() } -// BuildStateDiff builds a statediff object from two blocks and the provided parameters -func (sdb *builder) BuildStateDiffObject(args sd.Args, params sd.Params) (sd.StateObject, error) { +// BuildStateDiffObject builds a statediff object from two blocks and the provided parameters +func (sdb *builder) BuildStateDiffObject(args Args, params Params) (StateObject, error) { + var stateNodes []StateNode + var codeAndCodeHashes []CodeAndCodeHash + err := sdb.WriteStateDiffObject( + StateRoots{OldStateRoot: args.OldStateRoot, NewStateRoot: args.NewStateRoot}, + params, stateNodeAppender(&stateNodes), codeMappingAppender(&codeAndCodeHashes)) + if err != nil { + return StateObject{}, err + } + return StateObject{ + BlockHash: args.BlockHash, + BlockNumber: args.BlockNumber, + Nodes: stateNodes, + CodeAndCodeHashes: codeAndCodeHashes, + }, nil +} + +// Writes a statediff object to output callback +func (sdb *builder) WriteStateDiffObject(args StateRoots, params Params, output StateNodeSink, codeOutput CodeSink) error { if len(params.WatchedAddresses) > 0 { // if we are watching only specific accounts then we are only diffing leaf nodes log.Info("Ignoring intermediate state nodes because WatchedAddresses was passed") @@ -194,95 +216,95 @@ func (sdb *builder) BuildStateDiffObject(args sd.Args, params sd.Params) (sd.Sta // Load tries for old and new states oldTrie, err := sdb.stateCache.OpenTrie(args.OldStateRoot) if err != nil { - return sd.StateObject{}, fmt.Errorf("error creating trie for old state root: %v", err) + return fmt.Errorf("error creating trie for old state root: %v", err) } newTrie, err := sdb.stateCache.OpenTrie(args.NewStateRoot) if err != nil { - return sd.StateObject{}, fmt.Errorf("error creating trie for new state root: %v", err) + return fmt.Errorf("error creating trie for new state root: %v", err) } // Split old and new tries into corresponding subtrie iterators oldIterFac := iter.NewSubtrieIteratorFactory(oldTrie, sdb.numWorkers) newIterFac := iter.NewSubtrieIteratorFactory(newTrie, sdb.numWorkers) - iterChan := make(chan []iterPair, sdb.numWorkers) // Create iterators ahead of time to avoid race condition in state.Trie access + // We do two state iterations per subtrie: one for new/updated nodes, + // one for deleted/updated nodes; prepare 2 iterator instances for each task + var iterPairs [][]iterPair for i := uint(0); i < sdb.numWorkers; i++ { - // two state iterations per diff build - iterChan <- []iterPair{ - iterPair{ - older: oldIterFac.IteratorAt(i), - newer: newIterFac.IteratorAt(i), - }, - iterPair{ - older: oldIterFac.IteratorAt(i), - newer: newIterFac.IteratorAt(i), - }, + iterPairs = append(iterPairs, []iterPair{ + iterPair{older: oldIterFac.IteratorAt(i), newer: newIterFac.IteratorAt(i)}, + iterPair{older: oldIterFac.IteratorAt(i), newer: newIterFac.IteratorAt(i)}, + }) + } + + // Dispatch workers to process trie data; sync and collect results here via channels + nodeChan := make(chan StateNode) + codeChan := make(chan CodeAndCodeHash) + + go func() { + nodeSender := func(node StateNode) error { nodeChan <- node; return nil } + codeSender := func(code CodeAndCodeHash) error { codeChan <- code; return nil } + var wg sync.WaitGroup + + for w := uint(0); w < sdb.numWorkers; w++ { + wg.Add(1) + go func(worker uint) { + defer wg.Done() + sdb.buildStateDiff(iterPairs[worker], params, nodeSender, codeSender) + }(w) + } + wg.Wait() + close(nodeChan) + close(codeChan) + }() + + for nodeChan != nil || codeChan != nil { + select { + case node, more := <-nodeChan: + if more { + if err := output(node); err != nil { + return err + } + } else { + nodeChan = nil + } + case codeAndCodeHash, more := <-codeChan: + if more { + if err := codeOutput(codeAndCodeHash); err != nil { + return err + } + } else { + codeChan = nil + } } } - type packet struct { - nodes []sd.StateNode - codes []sd.CodeAndCodeHash - } - - packetChan := make(chan packet) - var wg sync.WaitGroup - - for w := uint(0); w < sdb.numWorkers; w++ { - wg.Add(1) - go func(iterChan <-chan []iterPair) error { - defer wg.Done() - if iters, more := <-iterChan; more { - subtrieNodes, subtrieCodes, err := sdb.buildStateDiff(iters, params) - if err != nil { - return err - } - packetChan <- packet{ - nodes: subtrieNodes, - codes: subtrieCodes, - } - } - return nil - }(iterChan) - } - - go func() { - defer close(packetChan) - defer close(iterChan) - wg.Wait() - }() - - stateNodes := make([]sd.StateNode, 0) - codeAndCodeHashes := make([]sd.CodeAndCodeHash, 0) - for packet := range packetChan { - stateNodes = append(stateNodes, packet.nodes...) - codeAndCodeHashes = append(codeAndCodeHashes, packet.codes...) - } - - return sd.StateObject{ - BlockHash: args.BlockHash, - BlockNumber: args.BlockNumber, - Nodes: stateNodes, - CodeAndCodeHashes: codeAndCodeHashes, - }, nil + return nil } -func (sdb *builder) buildStateDiff(args []iterPair, params sd.Params) ([]sd.StateNode, []sd.CodeAndCodeHash, error) { +func (sdb *builder) buildStateDiff(args []iterPair, params Params, output StateNodeSink, codeOutput CodeSink) error { // collect a slice of all the intermediate nodes that were touched and exist at B // a map of their leafkey to all the accounts that were touched and exist at B // and a slice of all the paths for the nodes in both of the above sets - createdOrUpdatedIntermediateNodes, diffAccountsAtB, diffPathsAtB, err := sdb.createdAndUpdatedState( - args[0], params.WatchedAddresses, params.IntermediateStateNodes) + var diffAccountsAtB AccountMap + var diffPathsAtB map[string]bool + var err error + if params.IntermediateStateNodes { + diffAccountsAtB, diffPathsAtB, err = sdb.createdAndUpdatedStateWithIntermediateNodes(args[0], output) + } else { + diffAccountsAtB, diffPathsAtB, err = sdb.createdAndUpdatedState(args[0], params.WatchedAddresses) + } + if err != nil { - return nil, nil, fmt.Errorf("error collecting createdAndUpdatedNodes: %v", err) + return fmt.Errorf("error collecting createdAndUpdatedNodes: %v", err) } // collect a slice of all the nodes that existed at a path in A that doesn't exist in B // a map of their leafkey to all the accounts that were touched and exist at A - emptiedPaths, diffAccountsAtA, err := sdb.deletedOrUpdatedState(args[1], diffPathsAtB) + diffAccountsAtA, err := sdb.deletedOrUpdatedState(args[1], diffPathsAtB, output) if err != nil { - return nil, nil, fmt.Errorf("error collecting deletedOrUpdatedNodes: %v", err) + return fmt.Errorf("error collecting deletedOrUpdatedNodes: %v", err) } // collect and sort the leafkey keys for both account mappings into a slice @@ -295,57 +317,43 @@ func (sdb *builder) buildStateDiff(args []iterPair, params sd.Params) ([]sd.Stat // and leaving the truly created or deleted keys in place updatedKeys := findIntersection(createKeys, deleteKeys) - // build the diff nodes for the updated accounts using the mappings at both A and B - // as directed by the keys found as the intersection of the two - updatedAccounts, err := sdb.buildAccountUpdates( + // build the diff nodes for the updated accounts using the mappings at both A and B as directed by the keys found as the intersection of the two + err = sdb.buildAccountUpdates( diffAccountsAtB, diffAccountsAtA, updatedKeys, - params.WatchedStorageSlots, params.IntermediateStorageNodes) + params.WatchedStorageSlots, params.IntermediateStorageNodes, output) if err != nil { - return nil, nil, fmt.Errorf("error building diff for updated accounts: %v", err) + return fmt.Errorf("error building diff for updated accounts: %v", err) } // build the diff nodes for created accounts - createdAccounts, codeAndCodeHashes, err := sdb.buildAccountCreations( - diffAccountsAtB, params.WatchedStorageSlots, params.IntermediateStorageNodes) + err = sdb.buildAccountCreations(diffAccountsAtB, params.WatchedStorageSlots, params.IntermediateStorageNodes, output, codeOutput) if err != nil { - return nil, nil, fmt.Errorf("error building diff for created accounts: %v", err) + return fmt.Errorf("error building diff for created accounts: %v", err) } - - // assemble all of the nodes into the statediff object, including the intermediate nodes - nodes := append( - append( - append(updatedAccounts, createdAccounts...), - createdOrUpdatedIntermediateNodes..., - ), - emptiedPaths...) - - return nodes, codeAndCodeHashes, nil + return nil } // createdAndUpdatedState returns -// a slice of all the intermediate nodes that exist in a different state at B than A // a mapping of their leafkeys to all the accounts that exist in a different state at B than A // and a slice of the paths for all of the nodes included in both -func (sdb *builder) createdAndUpdatedState(iters iterPair, watchedAddresses []common.Address, intermediates bool) ([]sd.StateNode, AccountMap, map[string]bool, error) { - createdOrUpdatedIntermediateNodes := make([]sd.StateNode, 0) +func (sdb *builder) createdAndUpdatedState(iters iterPair, watchedAddresses []common.Address) (AccountMap, map[string]bool, error) { diffPathsAtB := make(map[string]bool) diffAcountsAtB := make(AccountMap) it, _ := trie.NewDifferenceIterator(iters.older, iters.newer) for it.Next(true) { - // skip value nodes and null nodes if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) { continue } node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB()) if err != nil { - return nil, nil, nil, err + return nil, nil, err } switch node.NodeType { - case sd.Leaf: + case Leaf: // created vs updated is important for leaf nodes since we need to diff their storage // so we need to map all changed accounts at B to their leafkey, since account can change pathes but not leafkey var account state.Account if err := rlp.DecodeBytes(nodeElements[1].([]byte), &account); err != nil { - return nil, nil, nil, fmt.Errorf("error decoding account for leaf node at path %x nerror: %v", node.Path, err) + return nil, nil, fmt.Errorf("error decoding account for leaf node at path %x nerror: %v", node.Path, err) } partialPath := trie.CompactToHex(nodeElements[0].([]byte)) valueNodePath := append(node.Path, partialPath...) @@ -360,33 +368,22 @@ func (sdb *builder) createdAndUpdatedState(iters iterPair, watchedAddresses []co Account: &account, } } - case sd.Extension, sd.Branch: - // create a diff for any intermediate node that has changed at b - // created vs updated makes no difference for intermediate nodes since we do not need to diff storage - if intermediates { - createdOrUpdatedIntermediateNodes = append(createdOrUpdatedIntermediateNodes, sd.StateNode{ - NodeType: node.NodeType, - Path: node.Path, - NodeValue: node.NodeValue, - }) - } - default: - return nil, nil, nil, fmt.Errorf("unexpected node type %s", node.NodeType) } // add both intermediate and leaf node paths to the list of diffPathsAtB diffPathsAtB[common.Bytes2Hex(node.Path)] = true } - return createdOrUpdatedIntermediateNodes, diffAcountsAtB, diffPathsAtB, it.Error() + return diffAcountsAtB, diffPathsAtB, it.Error() } -// deletedOrUpdatedState returns a slice of all the paths that are emptied at B -// and a mapping of their leafkeys to all the accounts that exist in a different state at A than B -func (sdb *builder) deletedOrUpdatedState(iters iterPair, diffPathsAtB map[string]bool) ([]sd.StateNode, AccountMap, error) { - emptiedPaths := make([]sd.StateNode, 0) - diffAccountAtA := make(AccountMap) - it, _ := trie.NewDifferenceIterator(iters.newer, iters.older) +// createdAndUpdatedStateWithIntermediateNodes returns +// a slice of all the intermediate nodes that exist in a different state at B than A +// a mapping of their leafkeys to all the accounts that exist in a different state at B than A +// and a slice of the paths for all of the nodes included in both +func (sdb *builder) createdAndUpdatedStateWithIntermediateNodes(iters iterPair, output StateNodeSink) (AccountMap, map[string]bool, error) { + diffPathsAtB := make(map[string]bool) + diffAcountsAtB := make(AccountMap) + it, _ := trie.NewDifferenceIterator(iters.older, iters.newer) for it.Next(true) { - // skip value nodes and null nodes if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) { continue } @@ -394,19 +391,10 @@ func (sdb *builder) deletedOrUpdatedState(iters iterPair, diffPathsAtB map[strin if err != nil { return nil, nil, err } - // if this nodePath did not show up in diffPathsAtB - // that means the node at this path was deleted (or moved) in B - // emit an empty "removed" diff to signify as such - if _, ok := diffPathsAtB[common.Bytes2Hex(node.Path)]; !ok { - emptiedPaths = append(emptiedPaths, sd.StateNode{ - Path: node.Path, - NodeValue: []byte{}, - NodeType: sd.Removed, - }) - } switch node.NodeType { - case sd.Leaf: - // map all different accounts at A to their leafkey + case Leaf: + // created vs updated is important for leaf nodes since we need to diff their storage + // so we need to map all changed accounts at B to their leafkey, since account can change paths but not leafkey var account state.Account if err := rlp.DecodeBytes(nodeElements[1].([]byte), &account); err != nil { return nil, nil, fmt.Errorf("error decoding account for leaf node at path %x nerror: %v", node.Path, err) @@ -415,6 +403,68 @@ func (sdb *builder) deletedOrUpdatedState(iters iterPair, diffPathsAtB map[strin valueNodePath := append(node.Path, partialPath...) encodedPath := trie.HexToCompact(valueNodePath) leafKey := encodedPath[1:] + diffAcountsAtB[common.Bytes2Hex(leafKey)] = accountWrapper{ + NodeType: node.NodeType, + Path: node.Path, + NodeValue: node.NodeValue, + LeafKey: leafKey, + Account: &account, + } + case Extension, Branch: + // create a diff for any intermediate node that has changed at b + // created vs updated makes no difference for intermediate nodes since we do not need to diff storage + if err := output(StateNode{ + NodeType: node.NodeType, + Path: node.Path, + NodeValue: node.NodeValue, + }); err != nil { + return nil, nil, err + } + default: + return nil, nil, fmt.Errorf("unexpected node type %s", node.NodeType) + } + // add both intermediate and leaf node paths to the list of diffPathsAtB + diffPathsAtB[common.Bytes2Hex(node.Path)] = true + } + return diffAcountsAtB, diffPathsAtB, it.Error() +} + +// deletedOrUpdatedState returns a slice of all the paths that are emptied at B +// and a mapping of their leafkeys to all the accounts that exist in a different state at A than B +func (sdb *builder) deletedOrUpdatedState(iters iterPair, diffPathsAtB map[string]bool, output StateNodeSink) (AccountMap, error) { + diffAccountAtA := make(AccountMap) + it, _ := trie.NewDifferenceIterator(iters.newer, iters.older) + for it.Next(true) { + if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) { + continue + } + node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB()) + if err != nil { + return nil, err + } + // if this node's path did not show up in diffPathsAtB + // that means the node at this path was deleted (or moved) in B + // emit an empty "removed" diff to signify as such + if _, ok := diffPathsAtB[common.Bytes2Hex(node.Path)]; !ok { + if err := output(StateNode{ + Path: node.Path, + NodeValue: []byte{}, + NodeType: Removed, + }); err != nil { + return nil, err + } + } + switch node.NodeType { + case Leaf: + // map all different accounts at A to their leafkey + var account state.Account + if err := rlp.DecodeBytes(nodeElements[1].([]byte), &account); err != nil { + return nil, fmt.Errorf("error decoding account for leaf node at path %x nerror: %v", node.Path, err) + } + partialPath := trie.CompactToHex(nodeElements[0].([]byte)) + valueNodePath := append(node.Path, partialPath...) + encodedPath := trie.HexToCompact(valueNodePath) + leafKey := encodedPath[1:] diffAccountAtA[common.Bytes2Hex(leafKey)] = accountWrapper{ NodeType: node.NodeType, Path: node.Path, @@ -422,55 +472,54 @@ func (sdb *builder) deletedOrUpdatedState(iters iterPair, diffPathsAtB map[strin LeafKey: leafKey, Account: &account, } - case sd.Extension, sd.Branch: + case Extension, Branch: // fall through, we did everything we need to do with these node types default: - return nil, nil, fmt.Errorf("unexpected node type %s", node.NodeType) + return nil, fmt.Errorf("unexpected node type %s", node.NodeType) } } - return emptiedPaths, diffAccountAtA, it.Error() + return diffAccountAtA, it.Error() } // buildAccountUpdates uses the account diffs maps for A => B and B => A and the known intersection of their leafkeys // to generate the statediff node objects for all of the accounts that existed at both A and B but in different states // needs to be called before building account creations and deletions as this mutates // those account maps to remove the accounts which were updated -func (sdb *builder) buildAccountUpdates(creations, deletions AccountMap, updatedKeys []string, watchedStorageKeys []common.Hash, intermediateStorageNodes bool) ([]sd.StateNode, error) { - updatedAccounts := make([]sd.StateNode, 0, len(updatedKeys)) +func (sdb *builder) buildAccountUpdates(creations, deletions AccountMap, updatedKeys []string, watchedStorageKeys []common.Hash, intermediateStorageNodes bool, output StateNodeSink) error { var err error for _, key := range updatedKeys { createdAcc := creations[key] deletedAcc := deletions[key] - var storageDiffs []sd.StorageNode + var storageDiffs []StorageNode if deletedAcc.Account != nil && createdAcc.Account != nil { oldSR := deletedAcc.Account.Root newSR := createdAcc.Account.Root - storageDiffs, err = sdb.buildStorageNodesIncremental(oldSR, newSR, watchedStorageKeys, intermediateStorageNodes) + err = sdb.buildStorageNodesIncremental(oldSR, newSR, watchedStorageKeys, intermediateStorageNodes, storageNodeAppender(&storageDiffs)) if err != nil { - return nil, fmt.Errorf("failed building incremental storage diffs for account with leafkey %s\r\nerror: %v", key, err) + return fmt.Errorf("failed building incremental storage diffs for account with leafkey %s\r\nerror: %v", key, err) } } - updatedAccounts = append(updatedAccounts, sd.StateNode{ + if err = output(StateNode{ NodeType: createdAcc.NodeType, Path: createdAcc.Path, NodeValue: createdAcc.NodeValue, LeafKey: createdAcc.LeafKey, StorageNodes: storageDiffs, - }) + }); err != nil { + return err + } delete(creations, key) delete(deletions, key) } - return updatedAccounts, nil + return nil } // buildAccountCreations returns the statediff node objects for all the accounts that exist at B but not at A // it also returns the code and codehash for created contract accounts -func (sdb *builder) buildAccountCreations(accounts AccountMap, watchedStorageKeys []common.Hash, intermediateStorageNodes bool) ([]sd.StateNode, []sd.CodeAndCodeHash, error) { - accountDiffs := make([]sd.StateNode, 0, len(accounts)) - codeAndCodeHashes := make([]sd.CodeAndCodeHash, 0) +func (sdb *builder) buildAccountCreations(accounts AccountMap, watchedStorageKeys []common.Hash, intermediateStorageNodes bool, output StateNodeSink, codeOutput CodeSink) error { for _, val := range accounts { - diff := sd.StateNode{ + diff := StateNode{ NodeType: val.NodeType, Path: val.Path, LeafKey: val.LeafKey, @@ -478,161 +527,128 @@ func (sdb *builder) buildAccountCreations(accounts AccountMap, watchedStorageKey } if !bytes.Equal(val.Account.CodeHash, nullCodeHash) { // For contract creations, any storage node contained is a diff - storageDiffs, err := sdb.buildStorageNodesEventual(val.Account.Root, watchedStorageKeys, intermediateStorageNodes) + var storageDiffs []StorageNode + err := sdb.buildStorageNodesEventual(val.Account.Root, watchedStorageKeys, intermediateStorageNodes, storageNodeAppender(&storageDiffs)) if err != nil { - return nil, nil, fmt.Errorf("failed building eventual storage diffs for node %x\r\nerror: %v", val.Path, err) + return fmt.Errorf("failed building eventual storage diffs for node %x\r\nerror: %v", val.Path, err) } diff.StorageNodes = storageDiffs - // emit codehash => code mappings for new contracts + // emit codehash => code mappings for cod codeHash := common.BytesToHash(val.Account.CodeHash) code, err := sdb.stateCache.ContractCode(common.Hash{}, codeHash) if err != nil { - return nil, nil, fmt.Errorf("failed to retrieve code for codehash %s\r\n error: %v", codeHash.String(), err) + return fmt.Errorf("failed to retrieve code for codehash %s\r\n error: %v", codeHash.String(), err) } - codeAndCodeHashes = append(codeAndCodeHashes, sd.CodeAndCodeHash{ + if err := codeOutput(CodeAndCodeHash{ Hash: codeHash, Code: code, - }) + }); err != nil { + return err + } + } + if err := output(diff); err != nil { + return err } - accountDiffs = append(accountDiffs, diff) } - return accountDiffs, codeAndCodeHashes, nil + return nil } // buildStorageNodesEventual builds the storage diff node objects for a created account // i.e. it returns all the storage nodes at this state, since there is no previous state -func (sdb *builder) buildStorageNodesEventual(sr common.Hash, watchedStorageKeys []common.Hash, intermediateNodes bool) ([]sd.StorageNode, error) { +func (sdb *builder) buildStorageNodesEventual(sr common.Hash, watchedStorageKeys []common.Hash, intermediateNodes bool, output StorageNodeSink) error { if bytes.Equal(sr.Bytes(), emptyContractRoot.Bytes()) { - return nil, nil + return nil } - log.Debug("Storage Root For Eventual Diff", "root", sr, sr.Hex()) + log.Debug("Storage Root For Eventual Diff", "root", sr.Hex()) sTrie, err := sdb.stateCache.OpenTrie(sr) if err != nil { log.Info("error in build storage diff eventual", "error", err) - return nil, err + return err } it := sTrie.NodeIterator(make([]byte, 0)) - return sdb.buildStorageNodesFromTrie(it, watchedStorageKeys, intermediateNodes) + err = sdb.buildStorageNodesFromTrie(it, watchedStorageKeys, intermediateNodes, output) + if err != nil { + return err + } + return nil } // buildStorageNodesFromTrie returns all the storage diff node objects in the provided node iterator // if any storage keys are provided it will only return those leaf nodes // including intermediate nodes can be turned on or off -func (sdb *builder) buildStorageNodesFromTrie(it trie.NodeIterator, watchedStorageKeys []common.Hash, intermediateNodes bool) ([]sd.StorageNode, error) { - storageDiffs := make([]sd.StorageNode, 0) +func (sdb *builder) buildStorageNodesFromTrie(it trie.NodeIterator, watchedStorageKeys []common.Hash, intermediateNodes bool, output StorageNodeSink) error { for it.Next(true) { - // skip value nodes and null nodes if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) { continue } node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB()) if err != nil { - return nil, err + return err } switch node.NodeType { - case sd.Leaf: + case Leaf: partialPath := trie.CompactToHex(nodeElements[0].([]byte)) valueNodePath := append(node.Path, partialPath...) encodedPath := trie.HexToCompact(valueNodePath) leafKey := encodedPath[1:] if isWatchedStorageKey(watchedStorageKeys, leafKey) { - storageDiffs = append(storageDiffs, sd.StorageNode{ + if err := output(StorageNode{ NodeType: node.NodeType, Path: node.Path, NodeValue: node.NodeValue, LeafKey: leafKey, - }) + }); err != nil { + return err + } } - case sd.Extension, sd.Branch: + case Extension, Branch: if intermediateNodes { - storageDiffs = append(storageDiffs, sd.StorageNode{ + if err := output(StorageNode{ NodeType: node.NodeType, Path: node.Path, NodeValue: node.NodeValue, - }) + }); err != nil { + return err + } } default: - return nil, fmt.Errorf("unexpected node type %s", node.NodeType) + return fmt.Errorf("unexpected node type %s", node.NodeType) } } - return storageDiffs, it.Error() + return it.Error() } // buildStorageNodesIncremental builds the storage diff node objects for all nodes that exist in a different state at B than A -func (sdb *builder) buildStorageNodesIncremental(oldSR common.Hash, newSR common.Hash, watchedStorageKeys []common.Hash, intermediateNodes bool) ([]sd.StorageNode, error) { +func (sdb *builder) buildStorageNodesIncremental(oldSR common.Hash, newSR common.Hash, watchedStorageKeys []common.Hash, intermediateNodes bool, output StorageNodeSink) error { if bytes.Equal(newSR.Bytes(), oldSR.Bytes()) { - return nil, nil + return nil } log.Debug("Storage Roots for Incremental Diff", "old", oldSR.Hex(), "new", newSR.Hex()) oldTrie, err := sdb.stateCache.OpenTrie(oldSR) if err != nil { - return nil, err + return err } newTrie, err := sdb.stateCache.OpenTrie(newSR) if err != nil { - return nil, err + return err } - createdOrUpdatedStorage, diffPathsAtB, err := sdb.createdAndUpdatedStorage(oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{}), watchedStorageKeys, intermediateNodes) + diffPathsAtB, err := sdb.createdAndUpdatedStorage(oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{}), watchedStorageKeys, intermediateNodes, output) if err != nil { - return nil, err + return err } - deletedStorage, err := sdb.deletedOrUpdatedStorage(oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{}), diffPathsAtB, watchedStorageKeys, intermediateNodes) + err = sdb.deletedOrUpdatedStorage(oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{}), diffPathsAtB, watchedStorageKeys, intermediateNodes, output) if err != nil { - return nil, err + return err } - return append(createdOrUpdatedStorage, deletedStorage...), nil + return nil } -func (sdb *builder) createdAndUpdatedStorage(a, b trie.NodeIterator, watchedKeys []common.Hash, intermediateNodes bool) ([]sd.StorageNode, map[string]bool, error) { - createdOrUpdatedStorage := make([]sd.StorageNode, 0) +func (sdb *builder) createdAndUpdatedStorage(a, b trie.NodeIterator, watchedKeys []common.Hash, intermediateNodes bool, output StorageNodeSink) (map[string]bool, error) { diffPathsAtB := make(map[string]bool) it, _ := trie.NewDifferenceIterator(a, b) for it.Next(true) { - // skip value nodes and null nodes - if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) { - continue - } - node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB()) - if err != nil { - return nil, nil, err - } - switch node.NodeType { - case sd.Leaf: - partialPath := trie.CompactToHex(nodeElements[0].([]byte)) - valueNodePath := append(node.Path, partialPath...) - encodedPath := trie.HexToCompact(valueNodePath) - leafKey := encodedPath[1:] - if isWatchedStorageKey(watchedKeys, leafKey) { - createdOrUpdatedStorage = append(createdOrUpdatedStorage, sd.StorageNode{ - NodeType: node.NodeType, - Path: node.Path, - NodeValue: node.NodeValue, - LeafKey: leafKey, - }) - } - case sd.Extension, sd.Branch: - if intermediateNodes { - createdOrUpdatedStorage = append(createdOrUpdatedStorage, sd.StorageNode{ - NodeType: node.NodeType, - Path: node.Path, - NodeValue: node.NodeValue, - }) - } - default: - return nil, nil, fmt.Errorf("unexpected node type %s", node.NodeType) - } - diffPathsAtB[common.Bytes2Hex(node.Path)] = true - } - return createdOrUpdatedStorage, diffPathsAtB, it.Error() -} - -func (sdb *builder) deletedOrUpdatedStorage(a, b trie.NodeIterator, diffPathsAtB map[string]bool, watchedKeys []common.Hash, intermediateNodes bool) ([]sd.StorageNode, error) { - deletedStorage := make([]sd.StorageNode, 0) - it, _ := trie.NewDifferenceIterator(b, a) - for it.Next(true) { - // skip value nodes and null nodes if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) { continue } @@ -640,6 +656,50 @@ func (sdb *builder) deletedOrUpdatedStorage(a, b trie.NodeIterator, diffPathsAtB if err != nil { return nil, err } + switch node.NodeType { + case Leaf: + partialPath := trie.CompactToHex(nodeElements[0].([]byte)) + valueNodePath := append(node.Path, partialPath...) + encodedPath := trie.HexToCompact(valueNodePath) + leafKey := encodedPath[1:] + if isWatchedStorageKey(watchedKeys, leafKey) { + if err := output(StorageNode{ + NodeType: node.NodeType, + Path: node.Path, + NodeValue: node.NodeValue, + LeafKey: leafKey, + }); err != nil { + return nil, err + } + } + case Extension, Branch: + if intermediateNodes { + if err := output(StorageNode{ + NodeType: node.NodeType, + Path: node.Path, + NodeValue: node.NodeValue, + }); err != nil { + return nil, err + } + } + default: + return nil, fmt.Errorf("unexpected node type %s", node.NodeType) + } + diffPathsAtB[common.Bytes2Hex(node.Path)] = true + } + return diffPathsAtB, it.Error() +} + +func (sdb *builder) deletedOrUpdatedStorage(a, b trie.NodeIterator, diffPathsAtB map[string]bool, watchedKeys []common.Hash, intermediateNodes bool, output StorageNodeSink) error { + it, _ := trie.NewDifferenceIterator(b, a) + for it.Next(true) { + if it.Leaf() || bytes.Equal(nullHashBytes, it.Hash().Bytes()) { + continue + } + node, nodeElements, err := resolveNode(it, sdb.stateCache.TrieDB()) + if err != nil { + return err + } // if this node path showed up in diffPathsAtB // that means this node was updated at B and we already have the updated diff for it // otherwise that means this node was deleted in B and we need to add a "removed" diff to represent that event @@ -647,31 +707,35 @@ func (sdb *builder) deletedOrUpdatedStorage(a, b trie.NodeIterator, diffPathsAtB continue } switch node.NodeType { - case sd.Leaf: + case Leaf: partialPath := trie.CompactToHex(nodeElements[0].([]byte)) valueNodePath := append(node.Path, partialPath...) encodedPath := trie.HexToCompact(valueNodePath) leafKey := encodedPath[1:] if isWatchedStorageKey(watchedKeys, leafKey) { - deletedStorage = append(deletedStorage, sd.StorageNode{ - NodeType: sd.Removed, + if err := output(StorageNode{ + NodeType: Removed, Path: node.Path, NodeValue: []byte{}, - }) + }); err != nil { + return err + } } - case sd.Extension, sd.Branch: + case Extension, Branch: if intermediateNodes { - deletedStorage = append(deletedStorage, sd.StorageNode{ - NodeType: sd.Removed, + if err := output(StorageNode{ + NodeType: Removed, Path: node.Path, NodeValue: []byte{}, - }) + }); err != nil { + return err + } } default: - return nil, fmt.Errorf("unexpected node type %s", node.NodeType) + return fmt.Errorf("unexpected node type %s", node.NodeType) } } - return deletedStorage, it.Error() + return it.Error() } // isWatchedAddress is used to check if a state account corresponds to one of the addresses the builder is configured to watch diff --git a/pkg/builder_test.go b/pkg/builder_test.go index 770be60..60be0c8 100644 --- a/pkg/builder_test.go +++ b/pkg/builder_test.go @@ -27,7 +27,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" - statediff "github.com/ethereum/go-ethereum/statediff" + statediff "github.com/ethereum/go-ethereum/statediff/types" pkg "github.com/vulcanize/eth-statediff-service/pkg" "github.com/vulcanize/eth-statediff-service/pkg/testhelpers" diff --git a/pkg/service.go b/pkg/service.go index 31a4fb8..b5d2c5f 100644 --- a/pkg/service.go +++ b/pkg/service.go @@ -28,8 +28,10 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" - sd "github.com/ethereum/go-ethereum/statediff" + sd "github.com/ethereum/go-ethereum/statediff/types" "github.com/sirupsen/logrus" + + ind "github.com/ethereum/go-ethereum/statediff/indexer" ) // lvlDBReader are the db interfaces required by the statediffing service @@ -51,12 +53,8 @@ type IService interface { StateDiffAt(blockNumber uint64, params sd.Params) (*sd.Payload, error) // Method to get state trie object at specific block StateTrieAt(blockNumber uint64, params sd.Params) (*sd.Payload, error) -} - -// Server configuration -type Config struct { - // Number of goroutines to use - Workers uint + // Method to write state diff object directly to DB + WriteStateDiffAt(blockNumber uint64, params sd.Params) error } // Service is the underlying struct for the state diffing service @@ -67,11 +65,13 @@ type Service struct { lvlDBReader lvlDBReader // Used to signal shutdown of the service QuitChan chan bool + // Interface for publishing statediffs as PG-IPLD objects + indexer ind.Indexer } // NewStateDiffService creates a new Service -func NewStateDiffService(lvlDBReader lvlDBReader, cfg Config) (*Service, error) { - builder, err := NewBuilder(lvlDBReader.StateDB(), cfg.Workers) +func NewStateDiffService(lvlDBReader lvlDBReader, indexer ind.Indexer, workers uint) (*Service, error) { + builder, err := NewBuilder(lvlDBReader.StateDB(), workers) if err != nil { return nil, err } @@ -79,6 +79,7 @@ func NewStateDiffService(lvlDBReader lvlDBReader, cfg Config) (*Service, error) lvlDBReader: lvlDBReader, Builder: builder, QuitChan: make(chan bool), + indexer: indexer, }, nil } @@ -218,3 +219,60 @@ func (sds *Service) Stop() error { close(sds.QuitChan) return nil } + +// WriteStateDiffAt writes a state diff at the specific blockheight directly to the database +// This operation cannot be performed back past the point of db pruning; it requires an archival node +// for historical data +func (sds *Service) WriteStateDiffAt(blockNumber uint64, params sd.Params) error { + logrus.Info(fmt.Sprintf("writing state diff at block %d", blockNumber)) + currentBlock, err := sds.lvlDBReader.GetBlockByNumber(blockNumber) + if err != nil { + return err + } + parentRoot := common.Hash{} + if blockNumber != 0 { + parentBlock, err := sds.lvlDBReader.GetBlockByHash(currentBlock.ParentHash()) + if err != nil { + return err + } + parentRoot = parentBlock.Root() + } + return sds.writeStateDiff(currentBlock, parentRoot, params) +} + +// Writes a state diff from the current block, parent state root, and provided params +func (sds *Service) writeStateDiff(block *types.Block, parentRoot common.Hash, params sd.Params) error { + var totalDifficulty *big.Int + var receipts types.Receipts + var err error + if params.IncludeTD { + totalDifficulty, err = sds.lvlDBReader.GetTdByHash(block.Hash()) + } + if err != nil { + return err + } + if params.IncludeReceipts { + receipts, err = sds.lvlDBReader.GetReceiptsByHash(block.Hash()) + } + if err != nil { + return err + } + + tx, err := sds.indexer.PushBlock(block, receipts, totalDifficulty) + if err != nil { + return err + } + // defer handling of commit/rollback for any return case + defer tx.Close() + output := func(node sd.StateNode) error { + return sds.indexer.PushStateNode(tx, node) + } + codeOutput := func(c sd.CodeAndCodeHash) error { + return sds.indexer.PushCodeAndCodeHash(tx, c) + } + err = sds.Builder.WriteStateDiffObject(sd.StateRoots{ + NewStateRoot: block.Root(), + OldStateRoot: parentRoot, + }, params, output, codeOutput) + return nil +} diff --git a/pkg/testhelpers/mocks/builder.go b/pkg/testhelpers/mocks/builder.go index 4ca0729..55833a0 100644 --- a/pkg/testhelpers/mocks/builder.go +++ b/pkg/testhelpers/mocks/builder.go @@ -18,36 +18,44 @@ package mocks import ( "github.com/ethereum/go-ethereum/core/types" - statediff "github.com/vulcanize/eth-statediff-service/pkg" + sd "github.com/ethereum/go-ethereum/statediff/types" ) // Builder is a mock state diff builder type Builder struct { - Args statediff.Args - Params statediff.Params - stateDiff statediff.StateObject + Args sd.Args + Params sd.Params + stateDiff sd.StateObject block *types.Block - stateTrie statediff.StateObject + stateTrie sd.StateObject builderError error } // BuildStateDiffObject mock method -func (builder *Builder) BuildStateDiffObject(args statediff.Args, params statediff.Params) (statediff.StateObject, error) { +func (builder *Builder) BuildStateDiffObject(args sd.Args, params sd.Params) (sd.StateObject, error) { builder.Args = args builder.Params = params return builder.stateDiff, builder.builderError } +// BuildStateDiffObject mock method +func (builder *Builder) WriteStateDiffObject(args sd.StateRoots, params sd.Params, output sd.StateNodeSink, codeOutput sd.CodeSink) error { + builder.StateRoots = args + builder.Params = params + + return builder.builderError +} + // BuildStateTrieObject mock method -func (builder *Builder) BuildStateTrieObject(block *types.Block) (statediff.StateObject, error) { +func (builder *Builder) BuildStateTrieObject(block *types.Block) (sd.StateObject, error) { builder.block = block return builder.stateTrie, builder.builderError } // SetStateDiffToBuild mock method -func (builder *Builder) SetStateDiffToBuild(stateDiff statediff.StateObject) { +func (builder *Builder) SetStateDiffToBuild(stateDiff sd.StateObject) { builder.stateDiff = stateDiff } diff --git a/pkg/types.go b/pkg/types.go index 867d011..8985017 100644 --- a/pkg/types.go +++ b/pkg/types.go @@ -21,7 +21,7 @@ package statediff import ( "github.com/ethereum/go-ethereum/core/state" - sd "github.com/ethereum/go-ethereum/statediff" + sd "github.com/ethereum/go-ethereum/statediff/types" ) // AccountMap is a mapping of hex encoded path => account wrapper