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"))
+}