Skip to content

Commit

Permalink
Re-implement removal of genesis persistence in state db (#1295)
Browse files Browse the repository at this point in the history
* Self-contained solution

* Retrieve new UT from #1017

* Keep changelog from #1017

* Updated changelog

* Appease linter

* Appease linter 2

---------

Co-authored-by: Jasmina Malicevic <jasmina.dustinac@gmail.com>
  • Loading branch information
2 people authored and melekes committed Jan 9, 2024
1 parent 1e434f1 commit 142a39c
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- `[node]` Remove genesis persistence in state db, replaced by a hash
([cometbft/cometbft\#1017](https://github.com/cometbft/cometbft/pull/1017),
[cometbft/cometbft\#1295](https://github.com/cometbft/cometbft/pull/1295))
70 changes: 70 additions & 0 deletions node/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ import (
"github.com/cometbft/cometbft/abci/example/kvstore"
cfg "github.com/cometbft/cometbft/config"
"github.com/cometbft/cometbft/crypto/ed25519"
"github.com/cometbft/cometbft/crypto/tmhash"
"github.com/cometbft/cometbft/evidence"
"github.com/cometbft/cometbft/internal/test"
cmtjson "github.com/cometbft/cometbft/libs/json"
"github.com/cometbft/cometbft/libs/log"
cmtos "github.com/cometbft/cometbft/libs/os"
cmtrand "github.com/cometbft/cometbft/libs/rand"
mempl "github.com/cometbft/cometbft/mempool"
"github.com/cometbft/cometbft/p2p"
Expand Down Expand Up @@ -464,6 +467,73 @@ func TestNodeNewNodeCustomReactors(t *testing.T) {
assert.Contains(t, channels, cr.Channels[0].ID)
}

func TestNodeNewNodeGenesisHashMismatch(t *testing.T) {
config := test.ResetTestRoot("node_new_node_genesis_hash")
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)

n, 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)

// Start and stop to close the db for later reading
err = n.Start()
require.NoError(t, err)

err = n.Stop()
require.NoError(t, err)

// Ensure the genesis doc hash is saved to db
stateDB, err := cfg.DefaultDBProvider(&cfg.DBContext{ID: "state", Config: config})
require.NoError(t, err)

genDocHash, err := stateDB.Get(genesisDocHashKey)
require.NoError(t, err)
require.NotNil(t, genDocHash, "genesis doc hash should be saved in db")
require.Len(t, genDocHash, tmhash.Size)

err = stateDB.Close()
require.NoError(t, err)

// Modify the genesis file chain ID to get a different hash
genBytes := cmtos.MustReadFile(config.GenesisFile())
var genesisDoc types.GenesisDoc
err = cmtjson.Unmarshal(genBytes, &genesisDoc)
require.NoError(t, err)

genesisDoc.ChainID = "different-chain-id"
err = genesisDoc.SaveAs(config.GenesisFile())
require.NoError(t, err)

_, 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, "NewNode should error when genesisDoc is changed")
require.Equal(t, "genesis doc hash in db does not match loaded genesis doc", err.Error())
}

func state(nVals int, height int64) (sm.State, dbm.DB, []types.PrivValidator) {
privVals := make([]types.PrivValidator, nVals)
vals := make([]types.GenesisValidator, nVals)
Expand Down
69 changes: 28 additions & 41 deletions node/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package node
import (
"bytes"
"context"
"errors"
"fmt"
"net"
"strings"
Expand All @@ -18,6 +17,7 @@ import (
cfg "github.com/cometbft/cometbft/config"
cs "github.com/cometbft/cometbft/consensus"
"github.com/cometbft/cometbft/crypto"
"github.com/cometbft/cometbft/crypto/tmhash"
"github.com/cometbft/cometbft/evidence"
"github.com/cometbft/cometbft/statesync"

Expand Down Expand Up @@ -543,7 +543,8 @@ func startStateSync(

//------------------------------------------------------------------------------

var genesisDocKey = []byte("genesisDoc")
// var genesisDocKey = []byte("genesisDoc")
var genesisDocHashKey = []byte("genesisDocHash")

// LoadStateFromDBOrGenesisDocProvider attempts to load the state from the
// database, or creates one using the given genesisDocProvider. On success this also
Expand All @@ -552,22 +553,34 @@ func LoadStateFromDBOrGenesisDocProvider(
stateDB dbm.DB,
genesisDocProvider GenesisDocProvider,
) (sm.State, *types.GenesisDoc, error) {
// Get genesis doc
genDoc, err := loadGenesisDoc(stateDB)
// Get genesis doc hash
genDocHash, err := stateDB.Get(genesisDocHashKey)
if err != nil {
genDoc, err = genesisDocProvider()
if err != nil {
return sm.State{}, nil, err
}
return sm.State{}, nil, fmt.Errorf("error retrieving genesis doc hash: %w", err)
}
genDoc, err := genesisDocProvider()
if err != nil {
return sm.State{}, nil, err
}

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

genDocBytes, err := cmtjson.Marshal(genDoc)
if err != nil {
return sm.State{}, nil, fmt.Errorf("failed to save genesis doc hash due to marshaling error: %w", err)
}

incomingGenDocHash := tmhash.Sum(genDocBytes)
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, incomingGenDocHash); err != nil {
return sm.State{}, nil, fmt.Errorf("failed to save genesis doc hash to db: %w", err)
}
// save genesis doc to prevent a certain class of user errors (e.g. when it
// was changed, accidentally or not). Also good for audit trail.
if err := saveGenesisDoc(stateDB, genDoc); err != nil {
return sm.State{}, nil, err
} else {
if !bytes.Equal(genDocHash, incomingGenDocHash) {
return sm.State{}, nil, fmt.Errorf("genesis doc hash in db does not match loaded genesis doc")
}
}
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
Expand All @@ -580,32 +593,6 @@ func LoadStateFromDBOrGenesisDocProvider(
return state, genDoc, nil
}

// panics if failed to unmarshal bytes
func loadGenesisDoc(db dbm.DB) (*types.GenesisDoc, error) {
b, err := db.Get(genesisDocKey)
if err != nil {
panic(err)
}
if len(b) == 0 {
return nil, errors.New("genesis doc not found")
}
var genDoc *types.GenesisDoc
err = cmtjson.Unmarshal(b, &genDoc)
if err != nil {
panic(fmt.Sprintf("Failed to load genesis doc due to unmarshaling error: %v (bytes: %X)", err, b))
}
return genDoc, nil
}

// panics if failed to marshal the given genesis document
func saveGenesisDoc(db dbm.DB, genDoc *types.GenesisDoc) error {
b, err := cmtjson.Marshal(genDoc)
if err != nil {
return fmt.Errorf("failed to save genesis doc due to marshaling error: %w", err)
}
return db.SetSync(genesisDocKey, b)
}

func createAndStartPrivValidatorSocketClient(
listenAddr,
chainID string,
Expand Down

0 comments on commit 142a39c

Please sign in to comment.