Skip to content

Commit

Permalink
fix: state sync on v2.x (#3808)
Browse files Browse the repository at this point in the history
Closes #3804 with a
hacky approach that infers the app version based on the snapshot height
and mounts KV stores accordingly.

Kinda related to #3804
but we didn't hit this because we don't have tests that try to state
sync so I created
#3807

## Testing

Using both the `arabica.sh` and `mocha.sh` scripts in this PR, I can
start state syncing. I let the Arabica node fully state sync to the tip
of the chain and it worked.

---------

Co-authored-by: Callum Waters <cmwaters19@gmail.com>
  • Loading branch information
rootulp and cmwaters authored Aug 22, 2024
1 parent 3d90004 commit fb79af5
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 0 deletions.
27 changes: 27 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,7 @@ func (app *App) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain
// mountKeysAndInit mounts the keys for the provided app version and then
// invokes baseapp.Init().
func (app *App) mountKeysAndInit(appVersion uint64) {
app.BaseApp.Logger().Debug(fmt.Sprintf("mounting KV stores for app version %v", appVersion))
app.MountKVStores(app.versionedKeys(appVersion))

// Invoke load latest version for it's side-effect of invoking baseapp.Init()
Expand Down Expand Up @@ -784,3 +785,29 @@ func (app *App) InitializeAppVersion(ctx sdk.Context) {
app.SetAppVersion(ctx, appVersion)
}
}

// OfferSnapshot is a wrapper around the baseapp's OfferSnapshot method. It is
// needed to mount stores for the appropriate app version.
func (app *App) OfferSnapshot(req abci.RequestOfferSnapshot) abci.ResponseOfferSnapshot {
if app.IsSealed() {
// If the app is sealed, keys have already been mounted so this can
// delegate to the baseapp's OfferSnapshot.
return app.BaseApp.OfferSnapshot(req)
}

if app.upgradeHeightV2 == 0 {
app.Logger().Debug("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.mountKeysAndInit(v2)
return app.BaseApp.OfferSnapshot(req)
}

app.Logger().Debug("snapshot height is less than upgrade height, assuming app version 1")
app.mountKeysAndInit(v1)
return app.BaseApp.OfferSnapshot(req)
}
44 changes: 44 additions & 0 deletions app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import (
"github.com/celestiaorg/celestia-app/v2/app"
"github.com/celestiaorg/celestia-app/v2/app/encoding"
"github.com/celestiaorg/celestia-app/v2/x/minfee"
"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"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
tmdb "github.com/tendermint/tm-db"
)
Expand Down Expand Up @@ -47,6 +52,45 @@ func TestNew(t *testing.T) {
})
}

func TestOfferSnapshot(t *testing.T) {
logger := log.NewNopLogger()
db := tmdb.NewMemDB()
traceStore := &NoopWriter{}
invCheckPeriod := uint(1)
encodingConfig := encoding.MakeConfig(app.ModuleEncodingRegisters...)
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"),
}
want := abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT}
got := app.OfferSnapshot(request)
assert.Equal(t, want, got)
})
}

func getSnapshotOption(t *testing.T) func(*baseapp.BaseApp) {
snapshotDir := t.TempDir()
snapshotDB, err := tmdb.NewDB("metadata", tmdb.GoLevelDBBackend, t.TempDir())
require.NoError(t, err)
snapshotStore, err := snapshots.NewStore(snapshotDB, snapshotDir)
require.NoError(t, err)
interval := uint64(10)
keepRecent := uint32(10)
return baseapp.SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(interval, keepRecent))
}

// NoopWriter is a no-op implementation of a writer.
type NoopWriter struct{}

Expand Down
59 changes: 59 additions & 0 deletions scripts/arabica.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/bin/sh

# This script starts a consensus node on Arabica and state syncs to the tip of
# the chain.

# Stop script execution if an error is encountered
set -o errexit
# Stop script execution if an undefined variable is used
set -o nounset

CHAIN_ID="arabica-11"
NODE_NAME="node-name"
SEEDS="827583022cc6ce65cf762115642258f937c954cd@validator-1.celestia-arabica-11.com:26656,74e42b39f512f844492ff09e30af23d54579b7bc@validator-2.celestia-arabica-11.com:26656,00d577159b2eb1f524ef9c37cb389c020a2c38d2@validator-3.celestia-arabica-11.com:26656,b2871b6dc2e18916d07264af0e87c456c2bba04f@validator-4.celestia-arabica-11.com:26656"
RPC="https://rpc.celestia-arabica-11.com:443"

CELESTIA_APP_HOME="${HOME}/.celestia-app"
CELESTIA_APP_VERSION=$(celestia-appd version 2>&1)

echo "celestia-app home: ${CELESTIA_APP_HOME}"
echo "celestia-app version: ${CELESTIA_APP_VERSION}"
echo ""

# Ask the user for confirmation before deleting the existing celestia-app home
# directory.
read -p "Are you sure you want to delete: $CELESTIA_APP_HOME? [y/n] " response

# Check the user's response
if [ "$response" != "y" ]; then
# Exit if the user did not respond with "y"
echo "You must delete $CELESTIA_APP_HOME to continue."
exit 1
fi

echo "Deleting $CELESTIA_APP_HOME..."
rm -r "$CELESTIA_APP_HOME"

echo "Initializing config files..."
celestia-appd init ${NODE_NAME} --chain-id ${CHAIN_ID} > /dev/null 2>&1 # Hide output to reduce terminal noise

echo "Settings seeds in config.toml..."
sed -i.bak -e "s/^seeds *=.*/seeds = \"$SEEDS\"/" $CELESTIA_APP_HOME/config/config.toml

# LATEST_HEIGHT=$(curl -s $RPC/block | jq -r .result.block.header.height);
BLOCK_HEIGHT=1751700
TRUST_HASH=$(curl -s "$RPC/block?height=$BLOCK_HEIGHT" | jq -r .result.block_id.hash)

echo "Block height: $BLOCK_HEIGHT"
echo "Trust hash: $TRUST_HASH"
echo "Enabling state sync in config.toml..."
sed -i.bak -E "s|^(enable[[:space:]]+=[[:space:]]+).*$|\1true| ; \
s|^(rpc_servers[[:space:]]+=[[:space:]]+).*$|\1\"$RPC,$RPC\"| ; \
s|^(trust_height[[:space:]]+=[[:space:]]+).*$|\1$BLOCK_HEIGHT| ; \
s|^(trust_hash[[:space:]]+=[[:space:]]+).*$|\1\"$TRUST_HASH\"|" $HOME/.celestia-app/config/config.toml

echo "Downloading genesis file..."
celestia-appd download-genesis ${CHAIN_ID}

echo "Starting celestia-appd..."
celestia-appd start --v2-upgrade-height 1751707
59 changes: 59 additions & 0 deletions scripts/mocha.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/bin/sh

# This script starts a consensus node on Mocha and state syncs to the tip of the
# chain.

# Stop script execution if an error is encountered
set -o errexit
# Stop script execution if an undefined variable is used
set -o nounset

CHAIN_ID="mocha-4"
NODE_NAME="node-name"
SEEDS="ee9f90974f85c59d3861fc7f7edb10894f6ac3c8@seed-mocha.pops.one:26656,258f523c96efde50d5fe0a9faeea8a3e83be22ca@seed.mocha-4.celestia.aviaone.com:20279,5d0bf034d6e6a8b5ee31a2f42f753f1107b3a00e@celestia-testnet-seed.itrocket.net:11656,7da0fb48d6ef0823bc9770c0c8068dd7c89ed4ee@celest-test-seed.theamsolutions.info:443"
RPC="https://celestia-testnet-rpc.itrocket.net:443"

CELESTIA_APP_HOME="${HOME}/.celestia-app"
CELESTIA_APP_VERSION=$(celestia-appd version 2>&1)

echo "celestia-app home: ${CELESTIA_APP_HOME}"
echo "celestia-app version: ${CELESTIA_APP_VERSION}"
echo ""

# Ask the user for confirmation before deleting the existing celestia-app home
# directory.
read -p "Are you sure you want to delete: $CELESTIA_APP_HOME? [y/n] " response

# Check the user's response
if [ "$response" != "y" ]; then
# Exit if the user did not respond with "y"
echo "You must delete $CELESTIA_APP_HOME to continue."
exit 1
fi

echo "Deleting $CELESTIA_APP_HOME..."
rm -r "$CELESTIA_APP_HOME"

echo "Initializing config files..."
celestia-appd init ${NODE_NAME} --chain-id ${CHAIN_ID} > /dev/null 2>&1 # Hide output to reduce terminal noise

echo "Settings seeds in config.toml..."
sed -i.bak -e "s/^seeds *=.*/seeds = \"$SEEDS\"/" $CELESTIA_APP_HOME/config/config.toml

LATEST_HEIGHT=$(curl -s $RPC/block | jq -r .result.block.header.height);
BLOCK_HEIGHT=$((LATEST_HEIGHT - 2000)); \
TRUST_HASH=$(curl -s "$RPC/block?height=$BLOCK_HEIGHT" | jq -r .result.block_id.hash)

echo "Block height: $BLOCK_HEIGHT"
echo "Trust hash: $TRUST_HASH"
echo "Enabling state sync in config.toml..."
sed -i.bak -E "s|^(enable[[:space:]]+=[[:space:]]+).*$|\1true| ; \
s|^(rpc_servers[[:space:]]+=[[:space:]]+).*$|\1\"$RPC,$RPC\"| ; \
s|^(trust_height[[:space:]]+=[[:space:]]+).*$|\1$BLOCK_HEIGHT| ; \
s|^(trust_hash[[:space:]]+=[[:space:]]+).*$|\1\"$TRUST_HASH\"|" $HOME/.celestia-app/config/config.toml

echo "Downloading genesis file..."
celestia-appd download-genesis ${CHAIN_ID} > /dev/null 2>&1 # Hide output to reduce terminal noise

echo "Starting celestia-appd..."
celestia-appd start --v2-upgrade-height 2585031

0 comments on commit fb79af5

Please sign in to comment.