mukan-ibc/modules/light-clients/07-tendermint/upgrade.go
Mukan Erkin Törük 6852832fe8
Some checks failed
CodeQL / Analyze (push) Waiting to run
Docker Build & Push Simapp (main) / docker-build (push) Waiting to run
golangci-lint / lint (push) Waiting to run
Tests / Code Coverage / build (amd64) (push) Waiting to run
Tests / Code Coverage / build (arm64) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[additional-args:-tags="test_e2e" name:e2e path:./e2e]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:08-wasm path:./modules/light-clients/08-wasm]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:ibc-go path:.]) (push) Waiting to run
Deploy to GitHub Pages / Deploy to GitHub Pages (push) Has been cancelled
Buf-Push / push (push) Has been cancelled
initial: sovereign Mukan Network fork
2026-05-11 03:18:28 +03:00

188 lines
9.3 KiB
Go

package tendermint
import (
"fmt"
"time"
errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"
upgradetypes "cosmossdk.io/x/upgrade/types"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types"
commitmenttypes "github.com/cosmos/ibc-go/v10/modules/core/23-commitment/types"
commitmenttypesv2 "github.com/cosmos/ibc-go/v10/modules/core/23-commitment/types/v2"
"github.com/cosmos/ibc-go/v10/modules/core/exported"
)
// VerifyUpgradeAndUpdateState checks if the upgraded client has been committed by the current client
// It will zero out all client-specific fields and verify all data in client state that must
// be the same across all valid Tendermint clients for the new chain.
// Note, if there is a decrease in the UnbondingPeriod, then the TrustingPeriod, despite being a client-specific field
// is scaled down by the same ratio.
// VerifyUpgrade will return an error if:
// - the upgradedClient is not a Tendermint ClientState
// - the latest height of the client state does not have the same revision number or has a greater
// height than the committed client.
// - the height of upgraded client is not greater than that of current client
// - the latest height of the new client does not match or is greater than the height in committed client
// - any Tendermint chain specified parameter in upgraded client such as ChainID, UnbondingPeriod,
// and ProofSpecs do not match parameters set by committed client
func (cs ClientState) VerifyUpgradeAndUpdateState(
ctx sdk.Context, cdc codec.BinaryCodec, clientStore storetypes.KVStore,
upgradedClient exported.ClientState, upgradedConsState exported.ConsensusState,
upgradeClientProof, upgradeConsStateProof []byte,
) error {
if len(cs.UpgradePath) == 0 {
return errorsmod.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade client, no upgrade path set")
}
// upgraded client state and consensus state must be IBC tendermint client state and consensus state
// this may be modified in the future to upgrade to a new IBC tendermint type
// counterparty must also commit to the upgraded consensus state at a sub-path under the upgrade path specified
tmUpgradeClient, ok := upgradedClient.(*ClientState)
if !ok {
return errorsmod.Wrapf(clienttypes.ErrInvalidClientType, "upgraded client must be Tendermint client. expected: %T got: %T",
&ClientState{}, upgradedClient)
}
tmUpgradeConsState, ok := upgradedConsState.(*ConsensusState)
if !ok {
return errorsmod.Wrapf(clienttypes.ErrInvalidConsensus, "upgraded consensus state must be Tendermint consensus state. expected %T, got: %T",
&ConsensusState{}, upgradedConsState)
}
// unmarshal proofs
var merkleProofClient, merkleProofConsState commitmenttypes.MerkleProof
if err := cdc.Unmarshal(upgradeClientProof, &merkleProofClient); err != nil {
return errorsmod.Wrapf(commitmenttypes.ErrInvalidProof, "could not unmarshal client merkle proof: %v", err)
}
if err := cdc.Unmarshal(upgradeConsStateProof, &merkleProofConsState); err != nil {
return errorsmod.Wrapf(commitmenttypes.ErrInvalidProof, "could not unmarshal consensus state merkle proof: %v", err)
}
// last height of current counterparty chain must be client's latest height
lastHeight := cs.LatestHeight
// Must prove against latest consensus state to ensure we are verifying against latest upgrade plan
// This verifies that upgrade is intended for the provided revision, since committed client must exist
// at this consensus state
consState, found := GetConsensusState(clientStore, cdc, lastHeight)
if !found {
return errorsmod.Wrap(clienttypes.ErrConsensusStateNotFound, "could not retrieve consensus state for lastHeight")
}
// Verify client proof
bz, err := cdc.MarshalInterface(tmUpgradeClient.ZeroCustomFields())
if err != nil {
return errorsmod.Wrapf(clienttypes.ErrInvalidClient, "could not marshal client state: %v", err)
}
// construct clientState Merkle path
upgradeClientPath := constructUpgradeClientMerklePath(cs.UpgradePath, lastHeight)
if err := merkleProofClient.VerifyMembership(cs.ProofSpecs, consState.GetRoot(), upgradeClientPath, bz); err != nil {
return errorsmod.Wrapf(err, "client state proof failed. Path: %s", upgradeClientPath.GetKeyPath())
}
// Verify consensus state proof
bz, err = cdc.MarshalInterface(upgradedConsState)
if err != nil {
return errorsmod.Wrapf(clienttypes.ErrInvalidConsensus, "could not marshal consensus state: %v", err)
}
// construct consensus state Merkle path
upgradeConsStatePath := constructUpgradeConsStateMerklePath(cs.UpgradePath, lastHeight)
if err := merkleProofConsState.VerifyMembership(cs.ProofSpecs, consState.GetRoot(), upgradeConsStatePath, bz); err != nil {
return errorsmod.Wrapf(err, "consensus state proof failed. Path: %s", upgradeConsStatePath.GetKeyPath())
}
trustingPeriod := cs.TrustingPeriod
if tmUpgradeClient.UnbondingPeriod < cs.UnbondingPeriod {
trustingPeriod = calculateNewTrustingPeriod(trustingPeriod, cs.UnbondingPeriod, tmUpgradeClient.UnbondingPeriod)
}
// Construct new client state and consensus state
// Relayer chosen client parameters are ignored.
// All chain-chosen parameters come from committed client, all client-chosen parameters
// come from current client.
newClientState := NewClientState(
tmUpgradeClient.ChainId, cs.TrustLevel, trustingPeriod, tmUpgradeClient.UnbondingPeriod,
cs.MaxClockDrift, tmUpgradeClient.LatestHeight, tmUpgradeClient.ProofSpecs, tmUpgradeClient.UpgradePath,
)
if err := newClientState.Validate(); err != nil {
return errorsmod.Wrap(err, "updated client state failed basic validation")
}
// The new consensus state is merely used as a trusted kernel against which headers on the new
// chain can be verified. The root is just a stand-in sentinel value as it cannot be known in advance, thus no proof verification will pass.
// The timestamp and the NextValidatorsHash of the consensus state is the blocktime and NextValidatorsHash
// of the last block committed by the old chain. This will allow the first block of the new chain to be verified against
// the last validators of the old chain so long as it is submitted within the TrustingPeriod of this client.
// NOTE: We do not set processed time for this consensus state since this consensus state should not be used for packet verification
// as the root is empty. The next consensus state submitted using update will be usable for packet-verification.
newConsState := NewConsensusState(
tmUpgradeConsState.Timestamp, commitmenttypes.NewMerkleRoot([]byte(SentinelRoot)), tmUpgradeConsState.NextValidatorsHash,
)
setClientState(clientStore, cdc, newClientState)
setConsensusState(clientStore, cdc, newConsState, newClientState.LatestHeight)
setConsensusMetadata(ctx, clientStore, tmUpgradeClient.LatestHeight)
return nil
}
// construct MerklePath for the committed client from upgradePath
func constructUpgradeClientMerklePath(upgradePath []string, lastHeight exported.Height) commitmenttypesv2.MerklePath {
// copy all elements from upgradePath except final element
clientPath := make([]string, len(upgradePath)-1)
copy(clientPath, upgradePath)
// append lastHeight and `upgradedClient` to last key of upgradePath and use as lastKey of clientPath
// this will create the IAVL key that is used to store client in upgrade store
lastKey := upgradePath[len(upgradePath)-1]
appendedKey := fmt.Sprintf("%s/%d/%s", lastKey, lastHeight.GetRevisionHeight(), upgradetypes.KeyUpgradedClient)
clientPath = append(clientPath, appendedKey)
var clientKey [][]byte
for _, part := range clientPath {
clientKey = append(clientKey, []byte(part))
}
return commitmenttypes.NewMerklePath(clientKey...)
}
// construct MerklePath for the committed consensus state from upgradePath
func constructUpgradeConsStateMerklePath(upgradePath []string, lastHeight exported.Height) commitmenttypesv2.MerklePath {
// copy all elements from upgradePath except final element
consPath := make([]string, len(upgradePath)-1)
copy(consPath, upgradePath)
// append lastHeight and `upgradedClient` to last key of upgradePath and use as lastKey of clientPath
// this will create the IAVL key that is used to store client in upgrade store
lastKey := upgradePath[len(upgradePath)-1]
appendedKey := fmt.Sprintf("%s/%d/%s", lastKey, lastHeight.GetRevisionHeight(), upgradetypes.KeyUpgradedConsState)
consPath = append(consPath, appendedKey)
var consStateKey [][]byte
for _, part := range consPath {
consStateKey = append(consStateKey, []byte(part))
}
return commitmenttypes.NewMerklePath(consStateKey...)
}
// calculateNewTrustingPeriod converts the provided durations to decimal representation to avoid floating-point precision issues
// and calculates the new trusting period, decreasing it by the ratio between the original and new unbonding period.
func calculateNewTrustingPeriod(trustingPeriod, originalUnbonding, newUnbonding time.Duration) time.Duration {
origUnbondingDec := sdkmath.LegacyNewDec(originalUnbonding.Nanoseconds())
newUnbondingDec := sdkmath.LegacyNewDec(newUnbonding.Nanoseconds())
trustingPeriodDec := sdkmath.LegacyNewDec(trustingPeriod.Nanoseconds())
// compute new trusting period: trustingPeriod * newUnbonding / originalUnbonding
newTrustingPeriodDec := trustingPeriodDec.Mul(newUnbondingDec).Quo(origUnbondingDec)
return time.Duration(newTrustingPeriodDec.TruncateInt64())
}