diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..fdab53e --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,30 @@ +name: lint +on: + pull_request: + +jobs: + golangci-lint: + name: golangci-lint + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + - uses: golangci/golangci-lint-action@v6.1.1 + with: + version: v1.61.0 + args: --timeout 10m + github-token: ${{ secrets.github_token }} + # hadolint lints the Dockerfile + hadolint: + uses: celestiaorg/.github/.github/workflows/reusable_dockerfile_lint.yml@v0.5.0 + with: + dockerfile: "docker/Dockerfile" + + yamllint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: celestiaorg/.github/.github/actions/yamllint@v0.5.0 diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000..aca5422 --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,7 @@ +"default": true # Default state for all rules +"MD010": + "code_blocks": false # Disable rule for hard tabs in code blocks +"MD013": false # Disable rule for line length +"MD033": false # Disable rule banning inline HTML +"MD055": + "style": "leading_and_trailing" # Instead of "consistent" (default) which can vary per file, use one style across the entire project diff --git a/.markdownlintignore b/.markdownlintignore new file mode 100644 index 0000000..7368c96 --- /dev/null +++ b/.markdownlintignore @@ -0,0 +1 @@ +solidity-ibc-eureka/ diff --git a/.yamllint.yml b/.yamllint.yml new file mode 100644 index 0000000..c80af7c --- /dev/null +++ b/.yamllint.yml @@ -0,0 +1,12 @@ +--- +# Built from docs https://yamllint.readthedocs.io/en/stable/configuration.html +extends: default + +rules: + # 120 chars should be enough, but don't fail if a line is longer + line-length: + max: 120 + level: warning + +ignore: + - solidity-ibc-eureka/ diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 45a9f36..41ba4b9 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,6 +1,7 @@ # IBC ZK-EVM Architecture Document -*This is a work in progress designed to describe all the components needed for transferring tokens from Celestia to a ZK proven EVM* +> [!NOTE] +> This is a work in progress designed to describe all the components needed for transferring tokens from Celestia to a ZK proven EVM To provide some context on what we’re trying to achieve, we’re going to start with describing the user flow for transferring TIA to a ZK EVM (Based of the current flow) @@ -9,14 +10,14 @@ To provide some context on what we’re trying to achieve, we’re going to star 1. A user submits a `MsgTransfer` to the Celestia chain. Celestia performs some validation checks on the message, then transfers the user’s funds to a module account, effectively locking the funds. The chain then creates a `Commitment` , a receipt of the successful execution of that message as well as an event emitted containing a `Packet` with information to send to the EVM rollup 2. A relayer listens for the event. It queries a Celestia consensus node for the `Commitment` in the state tree along with the merkle proof, proving the inclusion of that data. The relayer submits the packet along with the inclusion proofs to the rollups namespace. 3. The rollup full node validates the Celestia header as part of it’s STF, thus it does not require an `UpdateClient` message. It can be assumed that it always has the latest state root for verifying Celestia state. Reading the namespace it validates the `Commitment` and it’s inclusion in Celestia state. It then mints TIA (or the IBC denomination equivalent) and sends it to the recipient address. Similar to Celestia, it saves an `AcknowledgementCommitment` in state. -4. The relayer listens to the acknowledgement event, queries the state of the EVM Rollup alongside the 1) STF proof in groth16 form, 2) State inclusion proof of the `AcknowledgementCommitment` (as a Merkle Patricia Trie Proof). It submits a transaction with the first proof and often multiple second proofs batched together to Celestia. +4. The relayer listens to the acknowledgement event, queries the state of the EVM Rollup alongside the 1) STF proof in groth16 form, 2) State inclusion proof of the `AcknowledgementCommitment` (as a Merkle Patricia Trie Proof). It submits a transaction with the first proof and often multiple second proofs batched together to Celestia. 5. Celestia validates the groth16 proof to update the EVM state root it has stored, it verifies the `Acknowledgement` in the EVM state using the inclusion proof. It then cancels the pending timeout thus confirming the transfer. Alternatively, after a timeout period, a timeout message can be sent to Celestia which will unlock the funds returning them to the user. To transfer back follows a similar process except, instead of minting tokens, the EVM proves that they burned the tokens and Celestia proves back to the EVM that it released the tokens and gave it back to the user. ## Architecture -The architecture involves several different logical components: +The architecture involves several different logical components: - User + Light Node: Performs actions on either chain or rollup and uses the light client to verify every interaction - Data Availability Layer (DA): Publishes a canonical serialised stream of transactions for rollup full nodes and light nodes alike to read and execute. Here we have a convention of separating the STF proofs and the rollup data into separate namespaces. The Celestia namespace can be one or more namespaces that Celestia validators read and execute over. @@ -31,7 +32,7 @@ The architecture involves several different logical components: Celestia will track the state roots of the ZK EVM through a ZK IBC Client. To update it, the relayer needs to submit a transaction with a groth16 proof and the public inputs (trusted reference height, new height, new state root etc.) -RSP has a circuit for proving the execution of a single block. +RSP has a circuit for proving the execution of a single block. We need to write a circuit that proves the following computation: @@ -54,7 +55,7 @@ type PublicWitness struct { } ``` -**API** +## API The prover needs to provide an API for the relayer to query aggregated proofs to submit to Celestia to update the IBC client state. diff --git a/ibc/lightclients/groth16/changes.md b/ibc/lightclients/groth16/changes.md deleted file mode 100644 index 000eb93..0000000 --- a/ibc/lightclients/groth16/changes.md +++ /dev/null @@ -1,10 +0,0 @@ -some of the work i did in order to reflect recent changes in the ibc: - -- Removed genesis.go because - The [`ExportMetadata` interface function](https://github.com/cosmos/ibc-go/blob/v8.0.0/modules/core/exported/client.go#L59) has been removed from the `ClientState` interface. Core IBC will export all key/value's within the 02-client store. - -- Keeper function `CheckMisbehaviourAndUpdateState` has been removed since function `UpdateClient` can now handle updating `ClientState` on `ClientMessage` type which can be any `Misbehaviour` implementations. - -- VerifyClientState and VerifyClientConsensusState were removed. -// https://github.com/cosmos/ibc/issues/1121 - -- Have to add module.go in order to initialize the client in the cosmos-sdk app. \ No newline at end of file diff --git a/ibc/lightclients/groth16/client_state.go b/ibc/lightclients/groth16/client_state.go index e99f4b8..4774d0c 100644 --- a/ibc/lightclients/groth16/client_state.go +++ b/ibc/lightclients/groth16/client_state.go @@ -8,7 +8,6 @@ import ( sdkerrors "cosmossdk.io/errors" storetypes "cosmossdk.io/store/types" "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/celestiaorg/celestia-zkevm-ibc-demo/ibc/mpt" clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types" @@ -32,10 +31,10 @@ func NewClientState( genesisStateRoot []byte, ) *ClientState { return &ClientState{ - LatestHeight: latestHeight, + LatestHeight: latestHeight, // StateTransitionVerifierKey: stateTransitionVerifierKey, - CodeCommitment: codeCommitment, - GenesisStateRoot: genesisStateRoot, + CodeCommitment: codeCommitment, + GenesisStateRoot: genesisStateRoot, } } @@ -94,36 +93,6 @@ func (cs ClientState) initialize(ctx context.Context, cdc codec.BinaryCodec, cli return nil } -// verifyDelayPeriodPassed will ensure that at least delayTimePeriod amount of time and delayBlockPeriod number of blocks have passed -// since consensus state was submitted before allowing verification to continue. -func verifyDelayPeriodPassed(ctx sdk.Context, store storetypes.KVStore, proofHeight exported.Height, delayTimePeriod, delayBlockPeriod uint64) error { - // check that executing chain's timestamp has passed consensusState's processed time + delay time period - processedTime, ok := GetProcessedTime(store, proofHeight) - if !ok { - return sdkerrors.Wrapf(ErrProcessedTimeNotFound, "processed time not found for height: %s", proofHeight) - } - currentTimestamp := uint64(ctx.BlockTime().UnixNano()) - validTime := processedTime + delayTimePeriod - // NOTE: delay time period is inclusive, so if currentTimestamp is validTime, then we return no error - if currentTimestamp < validTime { - return sdkerrors.Wrapf(ErrDelayPeriodNotPassed, "cannot verify packet until time: %d, current time: %d", - validTime, currentTimestamp) - } - // check that executing chain's height has passed consensusState's processed height + delay block period - processedHeight, ok := GetProcessedHeight(store, proofHeight) - if !ok { - return sdkerrors.Wrapf(ErrProcessedHeightNotFound, "processed height not found for height: %s", proofHeight) - } - currentHeight := clienttypes.GetSelfHeight(ctx) - validHeight := clienttypes.NewHeight(processedHeight.GetRevisionNumber(), processedHeight.GetRevisionHeight()+delayBlockPeriod) - // NOTE: delay block period is inclusive, so if currentHeight is validHeight, then we return no error - if currentHeight.LT(validHeight) { - return sdkerrors.Wrapf(ErrDelayPeriodNotPassed, "cannot verify packet until height: %s, current height: %s", - validHeight, currentHeight) - } - return nil -} - //------------------------------------ // The following are modified methods from the v9 IBC Client interface. The idea is to make diff --git a/ibc/lightclients/groth16/integration_test.go b/ibc/lightclients/groth16/integration_test.go index 7148742..38ac53e 100644 --- a/ibc/lightclients/groth16/integration_test.go +++ b/ibc/lightclients/groth16/integration_test.go @@ -30,10 +30,9 @@ const ( ) var ( - height = clienttypes.NewHeight(0, 4) - newClientHeight = clienttypes.NewHeight(1, 1) - upgradePath = []string{"upgrade", "upgradedIBCState"} - invalidUpgradePath = []string{"upgrade", ""} + height = clienttypes.NewHeight(0, 4) + newClientHeight = clienttypes.NewHeight(1, 1) + upgradePath = []string{"upgrade", "upgradedIBCState"} ) type ClientConfig interface { @@ -48,8 +47,7 @@ type Grtoth16Config struct { } func NewGrtoth16Config() *Grtoth16Config { - return &Grtoth16Config{ - } + return &Grtoth16Config{} } func (*Grtoth16Config) GetClientType() string { @@ -115,22 +113,6 @@ func (suite *Groth16TestSuite) SetupTest() { suite.ctx = app.BaseApp.NewContext(checkTx) } -func getAltSigners(altVal *cmttypes.Validator, altPrivVal cmttypes.PrivValidator) map[string]cmttypes.PrivValidator { - return map[string]cmttypes.PrivValidator{altVal.Address.String(): altPrivVal} -} - -func getBothSigners(suite *Groth16TestSuite, altVal *cmttypes.Validator, altPrivVal cmttypes.PrivValidator) (*cmttypes.ValidatorSet, map[string]cmttypes.PrivValidator) { - // Create bothValSet with both suite validator and altVal. Would be valid update - bothValSet := cmttypes.NewValidatorSet(append(suite.valSet.Validators, altVal)) - // Create signer array and ensure it is in same order as bothValSet - _, suiteVal := suite.valSet.GetByIndex(0) - bothSigners := map[string]cmttypes.PrivValidator{ - suiteVal.Address.String(): suite.privVal, - altVal.Address.String(): altPrivVal, - } - return bothValSet, bothSigners -} - func TestGroth16TestSuite(t *testing.T) { testifysuite.Run(t, new(Groth16TestSuite)) } diff --git a/ibc/lightclients/groth16/light_client_module.go b/ibc/lightclients/groth16/light_client_module.go index 09f4772..c2bab72 100644 --- a/ibc/lightclients/groth16/light_client_module.go +++ b/ibc/lightclients/groth16/light_client_module.go @@ -8,7 +8,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" - "github.com/celestiaorg/celestia-zkevm-ibc-demo/x/header" clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types" ibcerrors "github.com/cosmos/ibc-go/v9/modules/core/errors" "github.com/cosmos/ibc-go/v9/modules/core/exported" @@ -20,7 +19,6 @@ var _ exported.LightClientModule = (*LightClientModule)(nil) type LightClientModule struct { cdc codec.BinaryCodec storeProvider clienttypes.StoreProvider - headerKeeper header.Keeper } // NewLightClientModule creates and returns a new zk LightClientModule. diff --git a/ibc/lightclients/groth16/light_client_module_test.go b/ibc/lightclients/groth16/light_client_module_test.go index 335ece1..a3d0a55 100644 --- a/ibc/lightclients/groth16/light_client_module_test.go +++ b/ibc/lightclients/groth16/light_client_module_test.go @@ -13,7 +13,6 @@ import ( "github.com/celestiaorg/celestia-zkevm-ibc-demo/ibc/lightclients/groth16" "github.com/celestiaorg/celestia-zkevm-ibc-demo/ibc/mpt" clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v9/modules/core/23-commitment/types" commitmenttypes "github.com/cosmos/ibc-go/v9/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v9/modules/core/24-host" ibcerrors "github.com/cosmos/ibc-go/v9/modules/core/errors" @@ -234,11 +233,10 @@ func (suite *Groth16TestSuite) TestVerifyMembership() { testingpath.Setup() // suite.chainA.NewEndpoint(testingpath.EndpointA.ClientID, testingpath.EndpointA.ConnectionID, testingpath.EndpointA.ChannelID) - + // endpointB := ibctesting.NewEndpoint(suite.chainB, NewGrtoth16Config(), ibctesting.NewConnectionConfig(), ibctesting.NewChannelConfig()) // endpointA := ibctesting.NewEndpoint(suite.chainA, NewGrtoth16Config(), testingpath.EndpointA.ChannelID) - latestHeight := testingpath.EndpointB.GetClientLatestHeight() key := host.FullConsensusStateKey(testingpath.EndpointB.ClientID, latestHeight) merklePath := commitmenttypes.NewMerklePath(key) @@ -255,7 +253,8 @@ func (suite *Groth16TestSuite) TestVerifyMembership() { // get the proof for the key proof := mpt.ProofList{} - trie.Prove(mptKey, &proof) + err := trie.Prove(mptKey, &proof) + suite.Require().NoError(err) proofBytes, err := mpt.ProofListToBytes(proof) suite.Require().NoError(err) @@ -276,7 +275,7 @@ func (suite *Groth16TestSuite) TestVerifyMembership() { // set consensus state to the trie root consensusState := testingpath.EndpointB.GetConsensusState(latestHeight).(*ibctm.ConsensusState) - root := types.NewMerkleRoot(trie.Hash().Bytes()) + root := commitmenttypes.NewMerkleRoot(trie.Hash().Bytes()) newConsState := ibctm.ConsensusState{ Timestamp: consensusState.Timestamp, Root: root, diff --git a/ibc/lightclients/groth16/update.go b/ibc/lightclients/groth16/update.go index 79b759c..e8bdb9b 100644 --- a/ibc/lightclients/groth16/update.go +++ b/ibc/lightclients/groth16/update.go @@ -13,7 +13,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types" "github.com/cosmos/ibc-go/v9/modules/core/exported" - ) // VerifyClientMessage checks if the clientMessage is of type Header @@ -29,7 +28,7 @@ func (cs *ClientState) VerifyClientMessage( } } -func (cs ClientState) verifyHeader(ctx context.Context, clientStore storetypes.KVStore, cdc codec.BinaryCodec, +func (cs ClientState) verifyHeader(_ context.Context, clientStore storetypes.KVStore, cdc codec.BinaryCodec, header *Header) error { // sdkCtx := sdk.UnwrapSDKContext(ctx) // TODO: https://github.com/cosmos/ibc-go/issues/5917 diff --git a/ibc/lightclients/groth16/upgrade_test.go b/ibc/lightclients/groth16/upgrade_test.go index b50ec17..1b8c127 100644 --- a/ibc/lightclients/groth16/upgrade_test.go +++ b/ibc/lightclients/groth16/upgrade_test.go @@ -37,13 +37,15 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.Require().NoError(err) // commit upgrade store changes and update clients suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) @@ -70,13 +72,15 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.Require().NoError(err) // commit upgrade store changes and update clients suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) @@ -97,13 +101,15 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+10)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.Require().NoError(err) // commit upgrade store changes and update clients suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) @@ -128,13 +134,15 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.Require().NoError(err) // commit upgrade store changes and update clients suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) @@ -154,14 +162,16 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.Require().NoError(err) // change upgradedClient client-specified parameters upgradedClient = ibctm.NewClientState("wrongchainID", ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath) suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) @@ -178,14 +188,16 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { name: "unsuccessful upgrade: client-specified parameters do not match previous client", setup: func() { // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.Require().NoError(err) // change upgradedClient client-specified parameters upgradedClient = ibctm.NewClientState(newChainId, ibctm.DefaultTrustLevel, ubdPeriod, ubdPeriod+trustingPeriod, maxClockDrift+5, lastHeight, commitmenttypes.GetSDKSpecs(), upgradePath) suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) @@ -205,8 +217,10 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.Require().NoError(err) // change submitted upgradedConsensusState upgradedConsState = &ibctm.ConsensusState{ @@ -216,7 +230,7 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { // commit upgrade store changes and update clients suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) @@ -232,7 +246,8 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { { name: "unsuccessful upgrade: client proof unmarshal failed", setup: func() { - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) suite.Require().True(found) @@ -248,7 +263,8 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { { name: "unsuccessful upgrade: consensus state proof unmarshal failed", setup: func() { - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) suite.Require().True(found) @@ -269,7 +285,8 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { // upgrade Height is at next block lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) suite.Require().True(found) @@ -289,7 +306,8 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { // upgrade Height is at next block lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) suite.Require().True(found) @@ -308,12 +326,13 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) // commit upgrade store changes and update clients suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) @@ -338,12 +357,13 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) // commit upgrade store changes and update clients suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) @@ -363,12 +383,13 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+100)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) // commit upgrade store changes and update clients suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) @@ -385,12 +406,13 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { name: "unsuccessful upgrade: client is expired", setup: func() { // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) // commit upgrade store changes and update clients suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) // expire chainB's client @@ -413,12 +435,13 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) // commit upgrade store changes and update clients suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) @@ -443,13 +466,15 @@ func (suite *Groth16TestSuite) TestVerifyUpgrade() { lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.Require().NoError(err) // commit upgrade store changes and update clients suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) diff --git a/ibc/mpt/mpt.go b/ibc/mpt/mpt.go index beb2fc1..0c6e61f 100644 --- a/ibc/mpt/mpt.go +++ b/ibc/mpt/mpt.go @@ -17,6 +17,9 @@ import ( func VerifyMerklePatriciaTrieProof(stateRoot []byte, key []byte, proof []byte) (value []byte, err error) { rootHash := common.BytesToHash(stateRoot) bytesToProofList, err := bytesToProofList(proof) + if err != nil { + return nil, fmt.Errorf("failed to convert cytes to proof list: %w", err) + } proofDB, err := ReconstructProofDB(bytesToProofList) if err != nil { return nil, fmt.Errorf("failed to decode proof: %w", err) diff --git a/ibc/mpt/mpt_test.go b/ibc/mpt/mpt_test.go index 5579012..fe4c23e 100644 --- a/ibc/mpt/mpt_test.go +++ b/ibc/mpt/mpt_test.go @@ -15,14 +15,12 @@ func TestVerifyMerklePatriciaTrieProof(t *testing.T) { for i, prover := range makeProvers(trie) { for _, kv := range vals { - proof := prover(kv.k) + proof, err := prover(kv.k) + require.NoError(t, err) + require.NotNil(t, proof) proofBytes, err := ProofListToBytes(*proof) require.NoError(t, err) - if proof == nil { - t.Fatalf("prover %d: missing key %x while constructing proof", i, kv.k) - } - val, err := VerifyMerklePatriciaTrieProof(root.Bytes(), kv.k, proofBytes) if err != nil { t.Fatalf("prover %d: failed to verify proof for key %x: %v\nraw proof: %x", i, kv.k, err, proof) @@ -36,26 +34,30 @@ func TestVerifyMerklePatriciaTrieProof(t *testing.T) { // makeProvers creates Merkle trie provers based on different implementations to // test all variations. -func makeProvers(trie *gethtrie.Trie) []func(key []byte) *ProofList { - var provers []func(key []byte) *ProofList +func makeProvers(trie *gethtrie.Trie) []func(key []byte) (*ProofList, error) { + var provers []func(key []byte) (*ProofList, error) // Create a direct trie based Merkle prover - provers = append(provers, func(key []byte) *ProofList { + provers = append(provers, func(key []byte) (*ProofList, error) { var proof ProofList - trie.Prove(key, &proof) - return &proof + err := trie.Prove(key, &proof) + if err != nil { + return nil, err + } + return &proof, nil }) // Create a leaf iterator based Merkle prover - provers = append(provers, func(key []byte) *ProofList { + provers = append(provers, func(key []byte) (*ProofList, error) { var proof ProofList if it := gethtrie.NewIterator(trie.MustNodeIterator(key)); it.Next() && bytes.Equal(key, it.Key) { for _, p := range it.Prove() { - proof.Put(crypto.Keccak256(p), p) + err := proof.Put(crypto.Keccak256(p), p) + if err != nil { + return nil, err + } } } - return &proof + return &proof, nil }) return provers } - - diff --git a/ibc/mpt/test_helpers.go b/ibc/mpt/test_helpers.go index 9e623eb..3f559be 100644 --- a/ibc/mpt/test_helpers.go +++ b/ibc/mpt/test_helpers.go @@ -42,7 +42,11 @@ func RandomTrie(n int) (trie *gethtrie.Trie, vals map[string]*kv) { func initRnd() *mrand.Rand { var seed [8]byte - crand.Read(seed[:]) + _, err := crand.Read(seed[:]) + if err != nil { + panic(fmt.Sprintf("failed to read random seed: %v", err)) + } + rnd := mrand.New(mrand.NewSource(int64(binary.LittleEndian.Uint64(seed[:])))) fmt.Printf("Seed: %x\n", seed) return rnd diff --git a/simapp/README.md b/simapp/README.md index 86efd02..5f4aaa1 100644 --- a/simapp/README.md +++ b/simapp/README.md @@ -33,7 +33,7 @@ If you've run `simd` in the past, you may need to reset your database before sta $ ./simd init [moniker] --chain-id [chain-id] ``` -The command should initialize a new working directory at the `~simapp` location. +The command should initialize a new working directory at the `~simapp` location. The `moniker` and `chain-id` can be anything, but you must use the same `chain-id` subsequently. ### 2. Create a New Key @@ -69,7 +69,7 @@ The amount should be at least `1000000000stake`. Providing too much or too littl ### 5. Create the Genesis File -A participant must create the genesis file `genesis.json` with every participant's transaction. +A participant must create the genesis file `genesis.json` with every participant's transaction. You can do this by gathering all the Genesis transactions under `config/gentx` and then executing this command: ```sh @@ -106,5 +106,5 @@ Finally, execute this command to start your nodes: Now you have a small testnet that you can use to try out changes to the Cosmos SDK or CometBFT! > ⚠️ NOTE: Sometimes, creating the network through the `collect-gentxs` will fail, and validators will start in a funny state (and then panic). -> +> > If this happens, you can try to create and start the network first with a single validator and then add additional validators using a `create-validator` transaction. diff --git a/testing/demo/pkg/test/main.go b/testing/demo/pkg/test/main.go index cc6c7a0..ce21b2d 100644 --- a/testing/demo/pkg/test/main.go +++ b/testing/demo/pkg/test/main.go @@ -5,7 +5,6 @@ import ( "crypto/ecdsa" "encoding/json" "fmt" - "math/big" "os" "os/exec" @@ -73,7 +72,7 @@ func DeployContracts() error { // Parse JSON into a map var runLatest map[string]interface{} if err := json.Unmarshal(file, &runLatest); err != nil { - fmt.Errorf("Error unmarshalling JSON: %v", err) + return fmt.Errorf("Error unmarshalling JSON: %w", err) } // Extract and print the contract addresses @@ -97,7 +96,7 @@ func DeployContracts() error { var addresses ContractAddresses if err := json.Unmarshal([]byte(unescapedValue), &addresses); err != nil { - return fmt.Errorf("error unmarshalling contract addresses:", err) + return fmt.Errorf("error unmarshalling contract addresses: %w", err) } fmt.Println("Contract Addresses:", addresses) @@ -265,4 +264,7 @@ func main() { fmt.Println(err) os.Exit(1) } + if err := InitializeLightClient(); err != nil { + fmt.Println(err) + } } diff --git a/x/header/README.md b/x/header/README.md index 0a58a9e..d78e116 100644 --- a/x/header/README.md +++ b/x/header/README.md @@ -1,3 +1,3 @@ # `x/header` -The header module allows for header storage for the retention window period. \ No newline at end of file +The header module allows for header storage for the retention window period. diff --git a/x/header/keeper.go b/x/header/keeper.go index e4e4e01..f489808 100644 --- a/x/header/keeper.go +++ b/x/header/keeper.go @@ -26,19 +26,27 @@ func NewKeeper( } } -func (k Keeper) BeginBlocker(ctx sdk.Context) { +func (k Keeper) BeginBlocker(ctx sdk.Context) error { // Prune headers that are older than the retention period - k.PruneHeaders(ctx) + err := k.PruneHeaders(ctx) + if err != nil { + return err + } // Save the block header height := ctx.BlockHeight() headerHash := ctx.HeaderHash() - k.SaveHeaderHash(ctx, height, headerHash) + err = k.SaveHeaderHash(ctx, height, headerHash) + if err != nil { + return err + } + return nil } -func (k Keeper) SaveHeaderHash(ctx context.Context, height int64, headerHash []byte) { +func (k Keeper) SaveHeaderHash(ctx context.Context, height int64, headerHash []byte) error { store := k.storeService.OpenKVStore(ctx) - store.Set(sdk.Uint64ToBigEndian(uint64(height)), headerHash) + err := store.Set(sdk.Uint64ToBigEndian(uint64(height)), headerHash) + return err } func (k Keeper) GetHeaderHash(ctx context.Context, height int64) ([]byte, bool) { @@ -56,7 +64,7 @@ func (k Keeper) GetHeaderHash(ctx context.Context, height int64) ([]byte, bool) } // PruneHeaders prunes block headers that are older than the retention window. -func (k Keeper) PruneHeaders(ctx sdk.Context) { +func (k Keeper) PruneHeaders(ctx sdk.Context) error { store := k.storeService.OpenKVStore(ctx) iterator, err := store.Iterator(nil, nil) // Start from the lowest key if err != nil { @@ -66,7 +74,7 @@ func (k Keeper) PruneHeaders(ctx sdk.Context) { latestHeight, ok := k.GetLatestSavedBlockHeight(ctx) if !ok { - return + return nil } // Calculate the minimum height to retain @@ -78,12 +86,16 @@ func (k Keeper) PruneHeaders(ctx sdk.Context) { // If the height is below the minimum height to retain, delete it if height < minHeightToRetain { - store.Delete(iterator.Key()) + err := store.Delete(iterator.Key()) + if err != nil { + return err + } } else { // Since entries are sorted by height, we can break early break } } + return nil } func (k Keeper) GetLatestSavedBlockHeight(ctx context.Context) (uint64, bool) { diff --git a/x/header/module.go b/x/header/module.go index 1b6f7da..28c17ef 100644 --- a/x/header/module.go +++ b/x/header/module.go @@ -82,9 +82,13 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { // IsAppModule implements the appmodule.AppModule interface. func (am AppModule) IsAppModule() {} -func (am AppModule) BeginBlock(ctx context.Context) { +func (am AppModule) BeginBlock(ctx context.Context) error { c := sdk.UnwrapSDKContext(ctx) - am.keeper.BeginBlocker(c) + err := am.keeper.BeginBlocker(c) + if err != nil { + return err + } + return nil } // InitGenesis does nothing because no predefined state is required for this module and no params are set.