Skip to content

Commit

Permalink
Merge branch 'main' into feature/adr101-pull-companion
Browse files Browse the repository at this point in the history
  • Loading branch information
jmalicevic committed Sep 19, 2023
2 parents 4f0d5b4 + 7e63417 commit 6a770e1
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- `[node]` Go-API breaking: Change the signature of `LoadStateFromDBOrGenesisDocProvider`
to accept an optional operator provided hash of the genesis file
([\#1324](https://github.com/cometbft/cometbft/pull/1324)).
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- `[cli/node]` The genesis hash provided with the `--genesis-hash` is now
forwarded to the node, instead of reading the file.
([\#1324](https://github.com/cometbft/cometbft/pull/1324)).
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ terraform.tfstate.d
test/app/grpc_client
test/loadtime/build
test/e2e/build
test/e2e/data/
test/e2e/networks/*/
test/logs
test/p2p/data/
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ looking for, see [our security policy](SECURITY.md).
| main | Go version | Go 1.21 or higher |
| v0.38.x | Go version | Go 1.20 or higher |
| v0.37.x | Go version | Go 1.20 or higher |
| v0.34.x | Go version | Go 1.19 or higher |
| v0.34.x | Go version | Go 1.20 or higher |

### Install

Expand Down
37 changes: 3 additions & 34 deletions cmd/cometbft/commands/run_node.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package commands

import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"

"github.com/spf13/cobra"

cfg "github.com/cometbft/cometbft/config"
cmtos "github.com/cometbft/cometbft/libs/os"
nm "github.com/cometbft/cometbft/node"
)
Expand Down Expand Up @@ -94,8 +90,8 @@ func NewRunNodeCmd(nodeProvider nm.Provider) *cobra.Command {
Aliases: []string{"node", "run"},
Short: "Run the CometBFT node",
RunE: func(cmd *cobra.Command, args []string) error {
if err := checkGenesisHash(config); err != nil {
return err
if len(genesisHash) != 0 {
config.Storage.GenesisHash = hex.EncodeToString(genesisHash)
}

n, err := nodeProvider(config, logger)
Expand Down Expand Up @@ -126,30 +122,3 @@ func NewRunNodeCmd(nodeProvider nm.Provider) *cobra.Command {
AddNodeFlags(cmd)
return cmd
}

func checkGenesisHash(config *cfg.Config) error {
if len(genesisHash) == 0 || config.Genesis == "" {
return nil
}

// Calculate SHA-256 hash of the genesis file.
f, err := os.Open(config.GenesisFile())
if err != nil {
return fmt.Errorf("can't open genesis file: %w", err)
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return fmt.Errorf("error when hashing genesis file: %w", err)
}
actualHash := h.Sum(nil)

// Compare with the flag.
if !bytes.Equal(genesisHash, actualHash) {
return fmt.Errorf(
"--genesis_hash=%X does not match %s hash: %X",
genesisHash, config.GenesisFile(), actualHash)
}

return nil
}
10 changes: 10 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1228,6 +1228,14 @@ type StorageConfig struct {
DiscardABCIResponses bool `mapstructure:"discard_abci_responses"`
// Configuration related to storage pruning.
Pruning *PruningConfig `mapstructure:"pruning"`

// Hex representation of the hash of the genesis file.
// This is an optional parameter set when an operator provides
// a hash via the command line.
// It is used to verify the hash of the actual genesis file.
// Note that if the provided has does not match the hash of the genesis file
// the node will report an error and not boot.
GenesisHash string `mapstructure:"genesis_hash"`
}

// DefaultStorageConfig returns the default configuration options relating to
Expand All @@ -1236,6 +1244,7 @@ func DefaultStorageConfig() *StorageConfig {
return &StorageConfig{
DiscardABCIResponses: false,
Pruning: DefaultPruningConfig(),
GenesisHash: "",
}
}

Expand All @@ -1245,6 +1254,7 @@ func TestStorageConfig() *StorageConfig {
return &StorageConfig{
DiscardABCIResponses: false,
Pruning: TestPruningConfig(),
GenesisHash: "",
}
}

Expand Down
6 changes: 6 additions & 0 deletions config/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,12 @@ initial_block_retain_height = {{ .Storage.Pruning.DataCompanion.InitialBlockReta
# already set a block results retain height, this is ignored.
initial_block_results_retain_height = {{ .Storage.Pruning.DataCompanion.InitialBlockResultsRetainHeight }}
# Hash of the Genesis file (as hex string), passed to CometBFT via the command line.
# If this hash mismatches the hash that CometBFT computes on the genesis file,
# the node is not able to boot.
genesis_hash = "{{ .Storage.GenesisHash }}"
#######################################################
### Transaction Indexer Configuration Options ###
#######################################################
Expand Down
4 changes: 2 additions & 2 deletions node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func BootstrapState(ctx context.Context, config *cfg.Config, dbProvider cfg.DBPr
return fmt.Errorf("state not empty, trying to initialize non empty state")
}

genState, _, err := LoadStateFromDBOrGenesisDocProvider(stateDB, DefaultGenesisDocProviderFunc(config))
genState, _, err := LoadStateFromDBOrGenesisDocProvider(stateDB, DefaultGenesisDocProviderFunc(config), config.Storage.GenesisHash)
if err != nil {
return err
}
Expand Down Expand Up @@ -280,7 +280,7 @@ func NewNode(ctx context.Context,
DiscardABCIResponses: config.Storage.DiscardABCIResponses,
})

state, genDoc, err := LoadStateFromDBOrGenesisDocProvider(stateDB, genesisDocProvider)
state, genDoc, err := LoadStateFromDBOrGenesisDocProvider(stateDB, genesisDocProvider, config.Storage.GenesisHash)
if err != nil {
return nil, err
}
Expand Down
73 changes: 72 additions & 1 deletion node/node_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package node

import (
"bytes"
"context"
"encoding/hex"

"fmt"
"net"
"net/http"
Expand Down Expand Up @@ -487,7 +490,6 @@ func TestNodeNewNodeCustomReactors(t *testing.T) {
func TestNodeNewNodeDeleteGenesisFileFromDB(t *testing.T) {
config := test.ResetTestRoot("node_new_node_delete_genesis_from_db")
defer os.RemoveAll(config.RootDir)
fmt.Println(config.RootDir)
// Use goleveldb so we can reuse the same db for the second NewNode()
config.DBBackend = string(dbm.GoLevelDBBackend)
// Ensure the genesis doc hash is saved to db
Expand Down Expand Up @@ -605,6 +607,75 @@ func TestNodeNewNodeGenesisHashMismatch(t *testing.T) {
require.Equal(t, "genesis doc hash in db does not match loaded genesis doc", err.Error())
}

func TestNodeGenesisHashFlagMatch(t *testing.T) {
config := test.ResetTestRoot("node_new_node_genesis_hash_flag_match")
defer os.RemoveAll(config.RootDir)

config.DBBackend = string(dbm.GoLevelDBBackend)
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
require.NoError(t, err)
// Get correct hash of correct genesis file
jsonBlob, err := os.ReadFile(config.GenesisFile())
require.NoError(t, err)

incomingChecksum := tmhash.Sum(jsonBlob)
// Set genesis flag value to incorrect hash
config.Storage.GenesisHash = hex.EncodeToString(incomingChecksum)
_, err = NewNode(
context.Background(),
config,
privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()),
nodeKey,
proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()),
DefaultGenesisDocProviderFunc(config),
cfg.DefaultDBProvider,
DefaultMetricsProvider(config.Instrumentation),
log.TestingLogger(),
)
require.NoError(t, err)

}

func TestNodeGenesisHashFlagMismatch(t *testing.T) {
config := test.ResetTestRoot("node_new_node_genesis_hash_flag_mismatch")
defer os.RemoveAll(config.RootDir)

// Use goleveldb so we can reuse the same db for the second NewNode()
config.DBBackend = string(dbm.GoLevelDBBackend)

nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
require.NoError(t, err)

// Generate hash of wrong file
f, err := os.ReadFile(config.PrivValidatorKeyFile())
require.NoError(t, err)
flagHash := tmhash.Sum(f)

// Set genesis flag value to incorrect hash
config.Storage.GenesisHash = hex.EncodeToString(flagHash)

_, err = NewNode(
context.Background(),
config,
privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()),
nodeKey,
proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()),
DefaultGenesisDocProviderFunc(config),
cfg.DefaultDBProvider,
DefaultMetricsProvider(config.Instrumentation),
log.TestingLogger(),
)
require.Error(t, err)

f, err = os.ReadFile(config.GenesisFile())
require.NoError(t, err)

genHash := tmhash.Sum(f)

genHashMismatch := bytes.Equal(genHash, flagHash)
require.False(t, genHashMismatch)
}

func state(nVals int, height int64) (sm.State, dbm.DB, []types.PrivValidator) {
privVals := make([]types.PrivValidator, nVals)
vals := make([]types.GenesisValidator, nVals)
Expand Down
18 changes: 16 additions & 2 deletions node/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package node
import (
"bytes"
"context"
"encoding/hex"
"fmt"
"net"
"os"
Expand Down Expand Up @@ -560,6 +561,7 @@ var genesisDocHashKey = []byte("genesisDocHash")
func LoadStateFromDBOrGenesisDocProvider(
stateDB dbm.DB,
genesisDocProvider GenesisDocProvider,
operatorGenesisHashHex string,
) (sm.State, *types.GenesisDoc, error) {
// Get genesis doc hash
genDocHash, err := stateDB.Get(genesisDocHashKey)
Expand All @@ -571,20 +573,32 @@ func LoadStateFromDBOrGenesisDocProvider(
return sm.State{}, nil, err
}

if err := csGenDoc.GenesisDoc.ValidateAndComplete(); err != nil {
if err = csGenDoc.GenesisDoc.ValidateAndComplete(); err != nil {
return sm.State{}, nil, fmt.Errorf("error in genesis doc: %w", err)
}

// Validate that existing or recently saved genesis file hash matches optional --genesis_hash passed by operator
if operatorGenesisHashHex != "" {
decodedOperatorGenesisHash, err := hex.DecodeString(operatorGenesisHashHex)
if err != nil {
return sm.State{}, nil, fmt.Errorf("genesis hash provided by operator cannot be decoded")
}
if !bytes.Equal(csGenDoc.Sha256Checksum, decodedOperatorGenesisHash) {
return sm.State{}, nil, fmt.Errorf("genesis doc hash in db does not match passed --genesis_hash value")
}
}

if len(genDocHash) == 0 {
// Save the genDoc hash in the store if it doesn't already exist for future verification
if err := stateDB.SetSync(genesisDocHashKey, csGenDoc.Sha256Checksum); err != nil {
if err = stateDB.SetSync(genesisDocHashKey, csGenDoc.Sha256Checksum); err != nil {
return sm.State{}, nil, fmt.Errorf("failed to save genesis doc hash to db: %w", err)
}
} else {
if !bytes.Equal(genDocHash, csGenDoc.Sha256Checksum) {
return sm.State{}, nil, fmt.Errorf("genesis doc hash in db does not match loaded genesis doc")
}
}

stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
})
Expand Down

0 comments on commit 6a770e1

Please sign in to comment.