Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(statesync): extract app version from snapshot (backport #3871) #3936

Merged
merged 11 commits into from
Oct 4, 2024
27 changes: 23 additions & 4 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ func setDefaultAppVersion(req abci.RequestInitChain) abci.RequestInitChain {
// mountKeysAndInit mounts the keys for the provided app version and then
// invokes baseapp.Init().
func (app *App) mountKeysAndInit(appVersion uint64) {
app.BaseApp.Logger().Info(fmt.Sprintf("mounting KV stores for app version %v", appVersion))
app.Logger().Info(fmt.Sprintf("mounting KV stores for app version %v", appVersion))
app.MountKVStores(app.versionedKeys(appVersion))

// Invoke load latest version for its side-effect of invoking baseapp.Init()
Expand Down Expand Up @@ -805,19 +805,38 @@ func (app *App) OfferSnapshot(req abci.RequestOfferSnapshot) abci.ResponseOfferS
return app.BaseApp.OfferSnapshot(req)
}

app.Logger().Info("offering snapshot", "height", req.Snapshot.Height, "app_version", req.AppVersion)
if req.AppVersion != 0 {
if !isSupportedAppVersion(req.AppVersion) {
app.Logger().Info("rejecting snapshot because unsupported app version", "app_version", req.AppVersion)
return abci.ResponseOfferSnapshot{
Result: abci.ResponseOfferSnapshot_REJECT,
}
}

app.Logger().Info("mounting keys for snapshot", "app_version", req.AppVersion)
app.mountKeysAndInit(req.AppVersion)
return app.BaseApp.OfferSnapshot(req)
}

// If the app version is not set in the snapshot, this falls back to inferring the app version based on the upgrade height.
if app.upgradeHeightV2 == 0 {
app.Logger().Debug("v2 upgrade height not set, assuming app version 2")
app.Logger().Info("v2 upgrade height not set, assuming app version 2")
app.mountKeysAndInit(v2)
return app.BaseApp.OfferSnapshot(req)
}

if req.Snapshot.Height >= uint64(app.upgradeHeightV2) {
app.Logger().Debug("snapshot height is greater than or equal to upgrade height, assuming app version 2")
app.Logger().Info("snapshot height is greater than or equal to upgrade height, assuming app version 2")
app.mountKeysAndInit(v2)
return app.BaseApp.OfferSnapshot(req)
}

app.Logger().Debug("snapshot height is less than upgrade height, assuming app version 1")
app.Logger().Info("snapshot height is less than upgrade height, assuming app version 1")
app.mountKeysAndInit(v1)
return app.BaseApp.OfferSnapshot(req)
}

func isSupportedAppVersion(appVersion uint64) bool {
return appVersion == v1 || appVersion == v2 || appVersion == v3
}
64 changes: 51 additions & 13 deletions app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,23 +120,61 @@ func TestOfferSnapshot(t *testing.T) {
upgradeHeight := int64(0)
appOptions := NoopAppOptions{}
snapshotOption := getSnapshotOption(t)
app := app.New(logger, db, traceStore, invCheckPeriod, encodingConfig, upgradeHeight, appOptions, snapshotOption)

t.Run("should return ACCEPT", func(t *testing.T) {
request := abci.RequestOfferSnapshot{
Snapshot: &abci.Snapshot{
Height: 0x1b07ec,
Format: 0x2,
Chunks: 0x1,
Hash: []uint8{0xaf, 0xa5, 0xe, 0x16, 0x45, 0x4, 0x2e, 0x45, 0xd3, 0x49, 0xdf, 0x83, 0x2a, 0x57, 0x9d, 0x64, 0xc8, 0xad, 0xa5, 0xb, 0x65, 0x1b, 0x46, 0xd6, 0xc3, 0x85, 0x6, 0x51, 0xd7, 0x45, 0x8e, 0xb8},
Metadata: []uint8{0xa, 0x20, 0xaf, 0xa5, 0xe, 0x16, 0x45, 0x4, 0x2e, 0x45, 0xd3, 0x49, 0xdf, 0x83, 0x2a, 0x57, 0x9d, 0x64, 0xc8, 0xad, 0xa5, 0xb, 0x65, 0x1b, 0x46, 0xd6, 0xc3, 0x85, 0x6, 0x51, 0xd7, 0x45, 0x8e, 0xb8},
},
AppHash: []byte("apphash"),
}

t.Run("should ACCEPT a valid snapshot", func(t *testing.T) {
app := app.New(logger, db, traceStore, invCheckPeriod, encodingConfig, upgradeHeight, appOptions, snapshotOption)
request := validSnapshot()
want := abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT}
got := app.OfferSnapshot(request)
assert.Equal(t, want, got)
})
t.Run("should ACCEPT a snapshot with app version 1", func(t *testing.T) {
app := app.New(logger, db, traceStore, invCheckPeriod, encodingConfig, upgradeHeight, appOptions, snapshotOption)
request := validSnapshot()
request.AppVersion = 1
want := abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT}
got := app.OfferSnapshot(request)
assert.Equal(t, want, got)
})
t.Run("should ACCEPT a snapshot with app version 2", func(t *testing.T) {
app := app.New(logger, db, traceStore, invCheckPeriod, encodingConfig, upgradeHeight, appOptions, snapshotOption)
request := validSnapshot()
request.AppVersion = 2
want := abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT}
got := app.OfferSnapshot(request)
assert.Equal(t, want, got)
})
t.Run("should ACCEPT a snapshot with app version 3", func(t *testing.T) {
app := app.New(logger, db, traceStore, invCheckPeriod, encodingConfig, upgradeHeight, appOptions, snapshotOption)
request := validSnapshot()
request.AppVersion = 3
want := abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT}
got := app.OfferSnapshot(request)
assert.Equal(t, want, got)
})
t.Run("should REJECT a snapshot with unsupported app version", func(t *testing.T) {
app := app.New(logger, db, traceStore, invCheckPeriod, encodingConfig, upgradeHeight, appOptions, snapshotOption)
request := validSnapshot()
request.AppVersion = 4 // unsupported app version
want := abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT}
got := app.OfferSnapshot(request)
assert.Equal(t, want, got)
})
}

func validSnapshot() abci.RequestOfferSnapshot {
return abci.RequestOfferSnapshot{
// Snapshot was created by logging the contents of OfferSnapshot on a
// node that was syncing via state sync.
Snapshot: &abci.Snapshot{
Height: 0x1b07ec,
Format: 0x2,
Chunks: 0x1,
Hash: []uint8{0xaf, 0xa5, 0xe, 0x16, 0x45, 0x4, 0x2e, 0x45, 0xd3, 0x49, 0xdf, 0x83, 0x2a, 0x57, 0x9d, 0x64, 0xc8, 0xad, 0xa5, 0xb, 0x65, 0x1b, 0x46, 0xd6, 0xc3, 0x85, 0x6, 0x51, 0xd7, 0x45, 0x8e, 0xb8},
Metadata: []uint8{0xa, 0x20, 0xaf, 0xa5, 0xe, 0x16, 0x45, 0x4, 0x2e, 0x45, 0xd3, 0x49, 0xdf, 0x83, 0x2a, 0x57, 0x9d, 0x64, 0xc8, 0xad, 0xa5, 0xb, 0x65, 0x1b, 0x46, 0xd6, 0xc3, 0x85, 0x6, 0x51, 0xd7, 0x45, 0x8e, 0xb8},
},
AppHash: []byte("apphash"),
}
}

func getSnapshotOption(t *testing.T) func(*baseapp.BaseApp) {
Expand Down
68 changes: 68 additions & 0 deletions app/test/state_sync_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package app_test

import (
"path/filepath"
"testing"

"github.com/celestiaorg/celestia-app/v3/app"
"github.com/celestiaorg/celestia-app/v3/app/encoding"
"github.com/celestiaorg/celestia-app/v3/test/util"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/snapshots"
snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abcitypes "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
dbm "github.com/tendermint/tm-db"
)

func TestOfferSnapshot(t *testing.T) {
t.Run("v1", func(t *testing.T) {
testApp := createTestApp(t)
request := createRequest()
request.AppVersion = 1
resp := testApp.OfferSnapshot(request)
assert.Equal(t, abcitypes.ResponseOfferSnapshot{Result: abcitypes.ResponseOfferSnapshot_ACCEPT}, resp)
})
t.Run("v2", func(t *testing.T) {
testApp := createTestApp(t)
request := createRequest()
request.AppVersion = 2
resp := testApp.OfferSnapshot(request)
assert.Equal(t, abcitypes.ResponseOfferSnapshot{Result: abcitypes.ResponseOfferSnapshot_ACCEPT}, resp)
rootulp marked this conversation as resolved.
Show resolved Hide resolved
})
}

func createTestApp(t *testing.T) *app.App {
db := dbm.NewMemDB()
config := encoding.MakeConfig(app.ModuleEncodingRegisters...)
upgradeHeight := int64(3)
snapshotDir := filepath.Join(t.TempDir(), "data", "snapshots")
snapshotDB, err := dbm.NewDB("metadata", dbm.GoLevelDBBackend, snapshotDir)
require.NoError(t, err)
snapshotStore, err := snapshots.NewStore(snapshotDB, snapshotDir)
require.NoError(t, err)
baseAppOption := baseapp.SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(10, 10))
testApp := app.New(log.NewNopLogger(), db, nil, 0, config, upgradeHeight, util.EmptyAppOptions{}, baseAppOption)
require.NoError(t, err)
response := testApp.Info(abcitypes.RequestInfo{})
require.Equal(t, uint64(0), response.AppVersion)
return testApp
}

func createRequest() abcitypes.RequestOfferSnapshot {
return abcitypes.RequestOfferSnapshot{
// Snapshot was created by logging the contents of OfferSnapshot on a
// node that was syncing via state sync.
Snapshot: &abcitypes.Snapshot{
Height: 0x290400,
Format: 0x2,
Chunks: 0x15,
Hash: []uint8{0xdd, 0x39, 0x7a, 0xb, 0xf7, 0x48, 0xb1, 0xb2, 0x40, 0x9a, 0x5f, 0x9, 0xd3, 0x59, 0x6f, 0x26, 0x85, 0x1f, 0x39, 0xb4, 0x69, 0x4e, 0x30, 0x45, 0x45, 0xc, 0x61, 0x65, 0x58, 0x19, 0xbd, 0x13},
Metadata: []uint8{0xa, 0x20, 0xe5, 0xd2, 0x9f, 0xa6, 0xad, 0xa9, 0xf5, 0x19, 0xb3, 0xd1, 0x60, 0xe4, 0x9d, 0x36, 0xfa, 0x85, 0x7c, 0x5, 0x1d, 0x63, 0xa6, 0x92, 0x12, 0xb5, 0xf8, 0x1e, 0x0, 0x78, 0x57, 0xef, 0xc9, 0xdb, 0xa, 0x20, 0x35, 0xcb, 0x42, 0xaa, 0x7e, 0x18, 0x1e, 0x2d, 0xb3, 0xf2, 0x60, 0xca, 0x42, 0xf8, 0xa9, 0x56, 0x17, 0x1, 0xdd, 0x2f, 0xdd, 0x3c, 0xb2, 0x24, 0x4, 0xc9, 0x1, 0xd4, 0x76, 0x50, 0xd3, 0xa5, 0xa, 0x20, 0xaf, 0xf9, 0xde, 0x28, 0x23, 0x3d, 0x5f, 0x5b, 0xb0, 0x2b, 0xb0, 0xb1, 0x2b, 0x40, 0xc4, 0xf6, 0xa7, 0xb4, 0xbc, 0x91, 0x20, 0xa5, 0x27, 0x59, 0x81, 0x68, 0x10, 0x2c, 0x8c, 0x90, 0x12, 0x66, 0xa, 0x20, 0xff, 0x15, 0x78, 0x38, 0xaf, 0x28, 0x9e, 0x9b, 0x9d, 0xf, 0x43, 0x9f, 0x81, 0x78, 0xc8, 0xe8, 0x33, 0x88, 0x4f, 0x60, 0x15, 0x82, 0x1f, 0x3b, 0x4b, 0x9d, 0xcb, 0xfe, 0xdd, 0x33, 0xab, 0x9d, 0xa, 0x20, 0x2c, 0xb7, 0x98, 0x72, 0x76, 0x74, 0x74, 0xad, 0xf4, 0x57, 0x80, 0xc, 0xbb, 0x8c, 0x38, 0xb3, 0xf4, 0x81, 0xdc, 0x5c, 0xa4, 0x6, 0xd1, 0x85, 0x90, 0xbb, 0x1b, 0x5d, 0xf2, 0x16, 0x4a, 0x14, 0xa, 0x20, 0x86, 0x54, 0xa6, 0x90, 0x55, 0x66, 0x28, 0x58, 0x5f, 0xaf, 0x96, 0xcb, 0xda, 0x1b, 0x5c, 0x12, 0x5a, 0xe0, 0x2b, 0x9f, 0x56, 0xaa, 0x0, 0x41, 0x95, 0x3, 0x95, 0xe3, 0xad, 0xb8, 0x49, 0x9e, 0xa, 0x20, 0xcb, 0x93, 0x9, 0xaf, 0xe, 0xfd, 0xf1, 0xa9, 0xe4, 0xd9, 0xb3, 0x63, 0x3b, 0x1e, 0xca, 0x85, 0x2c, 0xa3, 0xd8, 0x9a, 0x19, 0xfa, 0xe8, 0xea, 0xc6, 0xd4, 0x87, 0x44, 0x82, 0xcf, 0x1, 0x4c, 0xa, 0x20, 0x1d, 0x27, 0x30, 0x95, 0xd8, 0xf3, 0xd6, 0x2, 0xe0, 0x45, 0xc5, 0x30, 0x87, 0x30, 0xb9, 0x7e, 0x38, 0xee, 0x6a, 0xe1, 0xfe, 0x60, 0x5e, 0x58, 0xdf, 0x22, 0xf6, 0xf5, 0x13, 0xf6, 0xa1, 0xd6, 0xa, 0x20, 0x22, 0x47, 0x84, 0x25, 0x35, 0xf0, 0x11, 0xcd, 0x24, 0x26, 0x11, 0x9c, 0xde, 0x75, 0xff, 0x11, 0xe9, 0x12, 0xa1, 0xfc, 0xd1, 0x1, 0xe2, 0xf7, 0x4, 0xb0, 0x10, 0x2c, 0xd4, 0x18, 0xcf, 0x84, 0xa, 0x20, 0x9a, 0x85, 0xe9, 0x18, 0xc8, 0xdc, 0x3e, 0x38, 0x13, 0xe3, 0x97, 0x64, 0xbd, 0x4b, 0xb1, 0xc4, 0x47, 0x9e, 0x68, 0xa5, 0xf7, 0x32, 0x85, 0x56, 0x98, 0x25, 0x2f, 0x92, 0x9d, 0xdc, 0x4a, 0x2, 0xa, 0x20, 0xc2, 0xe0, 0xea, 0x18, 0xe2, 0x4c, 0x9c, 0x87, 0xbb, 0x88, 0xe8, 0x7, 0xb3, 0x99, 0x85, 0x4d, 0xda, 0x44, 0xd9, 0x3f, 0xa8, 0xb6, 0x21, 0xfb, 0xd7, 0x98, 0xe6, 0xe4, 0x55, 0x91, 0xd, 0x97, 0xa, 0x20, 0x37, 0x75, 0x7f, 0x7, 0xa6, 0x59, 0xe4, 0x4, 0x2, 0xb3, 0x8e, 0xb2, 0xfd, 0x81, 0x5a, 0x99, 0xce, 0x48, 0x2d, 0x39, 0xa6, 0xba, 0xe8, 0x26, 0xeb, 0xca, 0x8d, 0xae, 0x16, 0x21, 0x6d, 0xe0, 0xa, 0x20, 0x28, 0xa0, 0xb7, 0x37, 0xaa, 0x4c, 0x8c, 0x5, 0x25, 0x70, 0x96, 0x29, 0x2a, 0x1a, 0x60, 0x51, 0x9d, 0xd0, 0xc6, 0x32, 0x4c, 0x59, 0xc0, 0xf4, 0xb7, 0xad, 0x19, 0x21, 0xe, 0x20, 0x7, 0x50, 0xa, 0x20, 0x32, 0x32, 0x29, 0x6c, 0x7e, 0xfc, 0x8, 0x79, 0x24, 0x72, 0xfa, 0x69, 0x29, 0xe0, 0x1, 0xb8, 0xa3, 0xac, 0x90, 0x1b, 0x37, 0x96, 0x95, 0xff, 0x4e, 0xd2, 0xd0, 0xcf, 0x2a, 0xad, 0x44, 0xd3, 0xa, 0x20, 0xc9, 0x38, 0xe0, 0xaa, 0xa2, 0x9a, 0x69, 0xd3, 0xa6, 0xd3, 0x72, 0x7, 0xe6, 0xf6, 0xbb, 0x81, 0x42, 0x59, 0xf3, 0xae, 0x3d, 0xfc, 0xc0, 0x51, 0xe8, 0x2, 0x1c, 0x80, 0x3f, 0xc1, 0xf3, 0x38, 0xa, 0x20, 0x5a, 0xc1, 0x9b, 0x0, 0x8, 0x1e, 0xee, 0xd0, 0x5, 0x34, 0xc8, 0xfd, 0x22, 0x96, 0x7e, 0xa0, 0x9a, 0x60, 0x55, 0x84, 0x55, 0xbc, 0x1d, 0xef, 0xfe, 0xfd, 0xbe, 0xe6, 0x2e, 0x90, 0xce, 0xbc, 0xa, 0x20, 0x5d, 0x40, 0x89, 0xac, 0xdf, 0x90, 0x52, 0x70, 0xf9, 0xd, 0xab, 0x3d, 0x6d, 0xe3, 0xa8, 0x65, 0xa9, 0xe6, 0xdc, 0x6d, 0x72, 0x71, 0x38, 0x8d, 0x56, 0xaa, 0x22, 0xc, 0x46, 0xf2, 0xfd, 0x15, 0xa, 0x20, 0xab, 0x91, 0x4e, 0xb2, 0x97, 0x1d, 0xc0, 0xe6, 0x4d, 0x4b, 0x48, 0x1a, 0xbb, 0x39, 0x9f, 0xa2, 0xdc, 0x1, 0x7c, 0x8, 0xc5, 0xe8, 0x16, 0x2d, 0xce, 0xcd, 0xa2, 0xa7, 0x22, 0x30, 0x2b, 0xad, 0xa, 0x20, 0xf0, 0x96, 0x1d, 0x71, 0x7f, 0xa1, 0xc0, 0xab, 0xbf, 0xb8, 0x90, 0xf0, 0xe1, 0x64, 0x1f, 0x6e, 0xdd, 0xbd, 0xe9, 0xb8, 0xc2, 0xf6, 0xf0, 0x2e, 0x8c, 0x76, 0xf3, 0x1a, 0xfe, 0x40, 0x93, 0x70, 0xa, 0x20, 0x1c, 0x51, 0xe9, 0xdf, 0x1d, 0x6, 0x28, 0xdf, 0x17, 0xda, 0x75, 0xf7, 0x83, 0x4e, 0xe6, 0x6e, 0xf1, 0x73, 0xcf, 0x3a, 0x55, 0x90, 0xb4, 0x28, 0x15, 0x13, 0x9e, 0x79, 0x5d, 0xd1, 0xd0, 0x33, 0xa, 0x20, 0xf2, 0xd2, 0x2f, 0x94, 0x1a, 0xf9, 0x61, 0x25, 0xb5, 0xb0, 0x82, 0xf9, 0x80, 0x11, 0x29, 0xf0, 0x38, 0xda, 0xb9, 0xb9, 0xe2, 0x6, 0x7b, 0x6a, 0x15, 0x87, 0xc4, 0x1d, 0x54, 0x2e, 0xca, 0x27},
},
AppHash: []byte("apphash"),
AppVersion: 0, // unit tests will override this
}
}
Loading
Loading