diff --git a/cmd/common.go b/cmd/common.go new file mode 100644 index 00000000..3e575621 --- /dev/null +++ b/cmd/common.go @@ -0,0 +1,37 @@ +// Copyright © 2021 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/spf13/cobra" + "github.com/spf13/viper" +) + +func addDatabaseFlags(command *cobra.Command) { + // database flags + command.PersistentFlags().String("database-name", "vulcanize_public", "database name") + command.PersistentFlags().Int("database-port", 5432, "database port") + command.PersistentFlags().String("database-hostname", "localhost", "database hostname") + command.PersistentFlags().String("database-user", "", "database user") + command.PersistentFlags().String("database-password", "", "database password") + + // database flag bindings + viper.BindPFlag("database.name", command.PersistentFlags().Lookup("database-name")) + viper.BindPFlag("database.port", command.PersistentFlags().Lookup("database-port")) + viper.BindPFlag("database.hostname", command.PersistentFlags().Lookup("database-hostname")) + viper.BindPFlag("database.user", command.PersistentFlags().Lookup("database-user")) + viper.BindPFlag("database.password", command.PersistentFlags().Lookup("database-password")) +} diff --git a/cmd/serve.go b/cmd/serve.go index 10b672b0..2ec3adfa 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -263,22 +263,20 @@ func startStateTrieValidator(server s.Server, validateEveryNthBlock uint64) { if (blockNumber > lastBlockNumber) && (blockNumber%validateEveryNthBlock == 0) { // The validate trie call will take a long time on mainnet, e.g. a few hours. - err = backend.ValidateTrie(stateRoot) - if err != nil { - // Log an error and exit. - log.Fatalf("Error validating state trie for block number %d hash %s state root %s", - blockNumber, - blockHash, - stateRoot, - ) - } else { - log.Infof("Successfully validated state trie for block number %d hash %s state root %s", + if err = backend.ValidateTrie(stateRoot); err != nil { + log.Fatalf("Error validating trie for block number %d hash %s state root %s", blockNumber, blockHash, stateRoot, ) } + log.Infof("Successfully validated trie for block number %d hash %s state root %s", + blockNumber, + blockHash, + stateRoot, + ) + lastBlockNumber = blockNumber } } @@ -308,12 +306,7 @@ func parseRpcAddresses(value string) ([]*rpc.Client, error) { func init() { rootCmd.AddCommand(serveCmd) - // database credentials - serveCmd.PersistentFlags().String("database-name", "vulcanize_public", "database name") - serveCmd.PersistentFlags().Int("database-port", 5432, "database port") - serveCmd.PersistentFlags().String("database-hostname", "localhost", "database hostname") - serveCmd.PersistentFlags().String("database-user", "", "database user") - serveCmd.PersistentFlags().String("database-password", "", "database password") + addDatabaseFlags(serveCmd) // flags for all config variables // eth graphql and json-rpc parameters @@ -357,13 +350,6 @@ func init() { serveCmd.PersistentFlags().Uint("validator-every-nth-block", 1500, "only validate every Nth block") // and their bindings - // database - viper.BindPFlag("database.name", serveCmd.PersistentFlags().Lookup("database-name")) - viper.BindPFlag("database.port", serveCmd.PersistentFlags().Lookup("database-port")) - viper.BindPFlag("database.hostname", serveCmd.PersistentFlags().Lookup("database-hostname")) - viper.BindPFlag("database.user", serveCmd.PersistentFlags().Lookup("database-user")) - viper.BindPFlag("database.password", serveCmd.PersistentFlags().Lookup("database-password")) - // eth graphql server viper.BindPFlag("eth.server.graphql", serveCmd.PersistentFlags().Lookup("eth-server-graphql")) viper.BindPFlag("eth.server.graphqlPath", serveCmd.PersistentFlags().Lookup("eth-server-graphql-path")) diff --git a/cmd/validate.go b/cmd/validate.go new file mode 100644 index 00000000..f23b9412 --- /dev/null +++ b/cmd/validate.go @@ -0,0 +1,86 @@ +// Copyright © 2021 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 ( + "time" + + "github.com/ethereum/go-ethereum/common" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/spf13/viper" + validator "github.com/vulcanize/eth-ipfs-state-validator/pkg" + ipfsethdb "github.com/vulcanize/ipfs-ethdb/postgres" + s "github.com/vulcanize/ipld-eth-server/pkg/serve" +) + +const GroupName = "statedb-validate" +const CacheExpiryInMins = 8 * 60 // 8 hours +const CacheSizeInMB = 16 // 16 MB + +var validateCmd = &cobra.Command{ + Use: "validate", + Short: "valdiate state", + Long: `This command validates the trie for the given state root`, + Run: func(cmd *cobra.Command, args []string) { + subCommand = cmd.CalledAs() + logWithCommand = *log.WithField("SubCommand", subCommand) + validate() + }, +} + +func validate() { + config, err := s.NewConfig() + if err != nil { + logWithCommand.Fatal(err) + } + + stateRootStr := viper.GetString("stateRoot") + if stateRootStr == "" { + logWithCommand.Fatal("must provide a state root for state validation") + } + + stateRoot := common.HexToHash(stateRootStr) + cacheSize := viper.GetInt("cacheSize") + + ethDB := ipfsethdb.NewDatabase(config.DB.DB, ipfsethdb.CacheConfig{ + Name: GroupName, + Size: cacheSize * 1024 * 1024, + ExpiryDuration: time.Minute * time.Duration(CacheExpiryInMins), + }) + + validator := validator.NewValidator(nil, ethDB) + if err = validator.ValidateTrie(stateRoot); err != nil { + log.Fatalln("Error validating state root") + } + + stats := ethDB.GetCacheStats() + log.Debugf("groupcache stats %+v", stats) + + log.Infoln("Successfully validated state root") +} + +func init() { + rootCmd.AddCommand(validateCmd) + + addDatabaseFlags(validateCmd) + + validateCmd.PersistentFlags().String("state-root", "", "root of the state trie we wish to validate") + viper.BindPFlag("stateRoot", validateCmd.PersistentFlags().Lookup("state-root")) + + validateCmd.PersistentFlags().Int("cache-size", CacheSizeInMB, "cache size in MB") + viper.BindPFlag("cacheSize", validateCmd.PersistentFlags().Lookup("cache-size")) +}