GithubHelp home page GithubHelp logo

stride-labs / stride Goto Github PK

View Code? Open in Web Editor NEW
184.0 11.0 201.0 173.36 MB

Stride: Multichain Liquid Staking

Home Page: https://stride.zone/

License: Apache License 2.0

Go 98.37% Makefile 0.07% Shell 1.46% Python 0.07% Dockerfile 0.01% TypeScript 0.02%
cosmos

stride's Introduction

Multichain Liquid Staking

Twitter | Discord | Website

What is Stride?

Stride is a blockchain ("zone") that provides liquidity for staked assets. Using Stride, you can earn both staking and DeFi yields across the Cosmos IBC ecosystem. Read our "Meet Stride" blog post to learn more about why we built Stride.

Stride is built using Cosmos SDK and Tendermint. Stride allows users to liquid stake any IBC-compatible cosmos SDK native appchain token. Under the hood, Stride leverages the Inter-Blockchain Communication protocol, Interchain Accounts and Interchain Queries.

How does Multichain Liquid Staking work?

Running a Mainnet Node

If you want to setup your node for the Stride mainnet, find the relevant files and instructions here

Getting Started as a Developer

Developing on Stride

Developers who wish to develop on Stride can easily spin up 3 Stride nodes, 3 Gaia nodes, 1 Hermes relayer and 1 interchain-queries relayer. The Gaia nodes simulate the Cosmos Hub (Gaia) zone in local development node, and the relayers allow Stride zone to interact with that instance of Gaia.

The fastest way to develop on Stride is local development mode.

Set up local development mode

Install the required git submodule dependencies (various chains, relayers, bats).

git submodule update --init --recursive

Build executables, initialize state, and start the network with

make start-docker build=sgjotr

You can optionally pass build arguments to specify which binary to rebuild

  1. s This will re-build the Stride binary (default)
  2. g This will re-build the Gaia binary
  3. j This will re-build the Juno binary
  4. o This will re-build the Osmo binary
  5. t This will re-build the Stargaze binary
  6. e This will re-build the Evmos binary
  7. r This will re-build the Go Relayer binary
  8. h This will re-build the Hermes binary

Example: make start-docker build=sg, this will:

  • Rebuild the Stride and Gaia binaries
  • Start 1 Stride and 1 Gaia node in the docker
  • Start Relayers

To bring down the chain, execute:

make stop-docker

To test the chain with a mnemonic that has tokens (e.g. sending Stride transactions), you can use the VAL_MNEMONIC_1, which is

close soup mirror crew erode defy knock trigger gather eyebrow tent farm gym gloom base lemon sleep weekend rich forget diagram hurt prize fly

This mnemonic will have tokens on every chain running locally.

Running integration tests

Ensure submodules are updated

git submodule update --init --recursive

Build Stride, Gaia, Evmos, and the go relayer

make start-docker build=sger

Run integration tests

make test-integration-docker

Making changes to this repository

Summary

Add summary of the pull request here (E.g. This pull request adds XYZ feature to the x/ABC module and associated unit tests.)

Unit tests

To run unit tests for the whole project, execute: make unit-test To run unit tests for a particular module (e.g. the stakeibc module), execute: make unit-test path=stakeibc To run unit tests for a particular package (e.g. the stakeibc module), execute: make unit-test path=stakeibc/types To inspect unit test coverage, execute: make test-cover

Configure

Your blockchain in development can be configured with config.yml. To learn more, see the Starport docs.

Release

To release a new version of your blockchain, create and push a new tag with v prefix. A new draft release with the configured targets will be created.

git tag v0.1
git push origin v0.1

After a draft release is created, make your final changes from the release page and publish it.

Stride's Technical Architecture

Users stake their tokens on Stride from any Cosmos chain. Rewards accumulate in real time. No minimum. They will receive staked tokens immediately when they liquid stake. These staked tokens can be freely traded, and can be redeemed with Stride at any time to receive your original tokens plus staking rewards.

On the backend, Stride permissionly stakes these tokens on the host chain and compounds user rewards. Stride lets users use your staked assets to compound their yields. Continue to earn staking yield, and earn additional yield by lending, LPing, and more. They can set their own risk tolerance in Cosmos DeFi.

Users can always redeem from Stride. When they select "redeem" on the Stride website, Stride will initiate unbonding on the host zone. Once the unbonding period elapses, the users will receive native tokens in their wallets.

Attribution

Stride is proud to be an open-source project, and we welcome all other projects to use our repo. We use modules from the cosmos-sdk and other open source projects.

We operate under the Apache 2.0 License, and have used the following modules from fellow Cosmos projects. Huge thank you to these projects!

We use the following modules from Osmosis provided under this License:

x/epochs
x/mint
x/ratelimit

We use the following module (marketed as public infra) from Quicksilver provided under this License:

x/interchainqueries

Relevant licenses with full attribution can be found in the subdirectories.

stride's People

Contributors

agouin avatar antstalepresh avatar asalzmann avatar assafmo avatar chillyvee avatar cryptomaniats avatar dependabot[bot] avatar dev-stride avatar dylanschultzie avatar ethan-stride avatar faddat avatar hellobevi avatar hieuvubk avatar jstr1121 avatar kevinstubbs avatar khdegraaf avatar leonoorscryptoman avatar nghuyenthevinh2000 avatar omahs avatar riley-stride avatar sampocs avatar shellvish avatar sontrinh16 avatar srph avatar strbrian avatar svv28 avatar thanhnhann avatar viroot27 avatar vuittont60 avatar zakarialounes avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stride's Issues

AUDIT: Error handling should be reviewed

Surfaced from @informalsystems audit of Stride at commit f9736fe


finding:
id: IF-STRIDE-STAKEIBC-ERRORHANDLING
title: Error handling should be reviewed
severity: Informational
impact: None
exploitability: None
type: Implementation
issue:
status: Unresolved

Involved artifacts

Description

InitiateAllHostZoneUnbondings and SweepAllUnbondedTokens functions have return values defined but these are not handled in the if block:

if epochIdentifier == "day" {
    // here, we process everything we need to for redemptions
    k.Logger(ctx).Info(fmt.Sprintf("Day %d Beginning", epochNumber))
    // first we initiate unbondings from any hostZone where it's appropriate
    k.Logger(ctx).Info("InitiateAllHostZoneUnbondings")
    k.InitiateAllHostZoneUnbondings(ctx, epochNumber)
    // then we check previous epochs to see if unbondings finished, and sweep the tokens if so
    k.Logger(ctx).Info("SweepAllUnbondedTokens")
    k.SweepAllUnbondedTokens(ctx)
    ...
}

On the other hand, CallRegisteredICACallback returns nil in both cases, the successful one (callback is processed and callback data is removed), and when there is no actual callback data. It's clear that some IBC or ICA transactions don't need to have callback defined but the caller of the function couldn't know if the callback was called and processed or the callback data hasn't been defined.

func (k Keeper) CallRegisteredICACallback(ctx sdk.Context, modulePacket channeltypes.Packet, ack *channeltypes.Acknowledgement) error {
	callbackDataKey := types.PacketID(modulePacket.GetSourcePort(), modulePacket.GetSourceChannel(), modulePacket.Sequence)
	callbackData, found := k.GetCallbackDataFromPacket(ctx, modulePacket, callbackDataKey)
	if !found {
		return nil
	}
	callbackHandler, err := k.GetICACallbackHandlerFromPacket(ctx, modulePacket)
	if err != nil {
		k.Logger(ctx).Error(fmt.Sprintf("GetICACallbackHandlerFromPacket %s", err.Error()))
		return err
	}

	// call the callback
	if (*callbackHandler).HasICACallback(callbackData.CallbackId) {
		k.Logger(ctx).Info(fmt.Sprintf("Calling callback for %s", callbackData.CallbackId))
		// if acknowledgement is empty, then it is a timeout
		err := (*callbackHandler).CallICACallback(ctx, callbackData.CallbackId, modulePacket, ack, callbackData.CallbackArgs)
		if err != nil {
			errMsg := fmt.Sprintf("Error occured while calling ICACallback (%s) | err: %s", callbackData.CallbackId, err.Error())
			k.Logger(ctx).Error(errMsg)
			return sdkerrors.Wrapf(types.ErrCallbackFailed, errMsg)
		}
	} else {
		k.Logger(ctx).Error(fmt.Sprintf("Callback %v has no associated callback", callbackData))
	}

	// remove the callback data
	k.RemoveCallbackData(ctx, callbackDataKey)
	return nil
}

Problem Scenarios

Caller functions could have misleading information based on return value(s) of the calling functions.

Recommendation

For InitiateAllHostZoneUnbondings think about handling the case when there are some failed unbondings, or similar in SweepAllUnbondedTokens if there are failed sweeps from Delegation ICA to Redemption ICA.

In the CallRegisteredICACallback at least consider adding an info log before returning nil if it's expected that some acks do not have an associated callback. The other aproach is to add dummy callback for every IBC tx.

Informal Systems audit results

Informal systems 2022 audit results

Informational issues

  • IF-STRIDE-STAKEIBC-ZONEISOLATION #416
  • IF-STRIDE-STAKEIBC-HOST_ZONE_UNBOINDING #417
  • IF-STRIDE-STAKEIBC-HOST_ZONE_UNBOINDING_2 #426
  • IF-STRIDE-STAKEIBC-ERRORHANDLING #418
  • IF-STRIDE-STAKEIBC-REBALANCEVALIDATORS #419
  • IF-STRIDE-STAKEIBC-REDEMPTIONRATELIMITS #420
  • IF-STRIDE-DOCUMENTATION #421
  • IF-STRIDE-CODINGSTYLE #422
  • IF-STRIDE-TESTING-DEFICIENCY #423
  • IF-STRIDE-OBSERVATIONS #424

Medium issues

  • IF-STRIDE-STAKEIBC-UNBONDINGRECORDS #427
  • IF-STRIDE-STAKEIBC-STAKE_EXISTING_DEPOSITS #425

Add Transaction Boilerplates

Subticket of #414

Overview

Add the following transactions (but leave them unimplemented)

  • MsgAddRateLimit [DO NOT IMPLEMENT]
  • MsgRemoveRateLimit
  • MsgResetRateLimit [DO NOT IMPLEMENT]
  • MsgAddQuota
  • MsgRemoveQuota

Acceptance Criteria

  • Transaction proto types
  • Keeper functions
  • CLI commands
  • Unit tests for implemented functions
  • Debugging logs to unimplemented functions to test

Add LSM support

Background

When LSM goes live on a Cosmos chain, the rate of liquid staking adoption will likely jump. It does not look like LSM will be adopted very soon, but Stride should be ready. Stride does not currently have support for LSM. This issue outlines how we might add support for LSM. This will allow users to liquid stake to Stride without unbonding their current delegation from the LSM-enabled host chain.

Suggested Design

Time estimate: ~14d to MVP

Converting non-LSM hz to LSM

  • add an admin convertToLsm fn
    • sets usesLsm : true
    • issues ICA call to each val to roll delegation into an LSM delegation up to the exemption limit
  • Hooks.go changes:
    • branch on usesLsm (mostly in iterateAllHzs):
      • Delegate: add lsm version
      • undelegate: add lsm version
      • Redelegate: add lsm version
      • all denoms change from native token to any of the lsm share denoms
    • Store sharesToTokens for all vals on hz structs, updated at every slash, and use that to convert LSM shares to numTokens

Delegating

  • protos: stakeibc hz struct needs bool โ€œusesLsmโ€ field
  • msgLiquidStake switches on usesLsm.
    • If hz usesLsm:
      • Steps:
          1. On userโ€™s account tokenonizeShares their delegation using LSM
          1. IBC transfer those shares to Stride
          1. Atomically:
          • Transfer LSM shares to moduleAcct
          • Mult by validatorโ€™s stale tokensToShares rate to get stTokens and send to user
        • Alterative solution (non-atomic): icq the hz validator to get tokensToShares (not atomic)
        • Open question: Can you tokenize shares on a tombstoned validator? Ans: yes, and Jun is going to submit a PR to LSM to fix this.

RedeemStake

  • msgUndelegate now issues an LSM share unbonding. The rest of the unbonding/claim flow is unchanged

Rebalancing

  • Need to add LSM rebalancing logic:
    • unwraps LSM shares,
    • Rebalance normally
    • Re-wrap into LSM shares up to the LSM exemption share limits of each validator

Action items to start work on this

  1. Add LSM as a module to a hz in dockernet (test (natively on hz) lsm versions of del, undel, redel etc)
  2. Find out if hzs need new allow msgs for lsm versions of staking messages (probably!)
  3. Implement solution outlined above

Acceptance Criteria

  • all existing and new tests should pass
  • new tests for converting a zone from non-LSM to LSM should pass
  • new tests for delegating LSM shares on an LSM-zone should pass
  • new tests for rebalancing on an LSM zone should pass

IBC by LEDGER error

IBC by LEDGER error
Do not send tx to ledger device when I want to stake Atom to stAtom

Ubuntu 22.10, Chromium 105, Keplr 0.10.16

AUDIT: Stride lacks various test cases

Surfaced from @informalsystems audit of Stride at commit f9736fe


finding:
id: IF-STRIDE-TESTING-DEFICIENCY
title: Stride lacks various test cases
severity: Informational
impact: None
exploitability: None
type: Test
issue:
status: Unresolved

Involved artifacts

Description

Stride has a decent unit test coverage, but lacks some further testing such as automated fuzz testing. For example, "liquid stake" and "redeem stake" methods are good candidates for fuzzing.
Integration tests are written sequentially, so that each test depends on the previous tests and examines the chain state at a point in time. The written integration tests are passing which means main functionalities are working fine, but it looks like only "happy paths" are covered.
scripts/tests/gaia_tests.bats

There are no tests which simulate the situations when something could potentially go wrong, such as system behaviour when timeouts happen, etc.
A lot of code in Stride is running periodically using epochs and basically all the logic is dependent on epochs. There are no tests which checks the behaviour of the system when configuring various epoch durations even though there are warnings issued:

epochs/README.md

Recommendation

The recommendation is to add fuzz testing which will provide invalid, unexpected or random data as inputs. This could help exposing vulnerabilities and corner cases which weren't considered when writing unit tests. Consider testing stride functionalities with different epoch durations. Also, it looks like there is an assumption that relayers will always work very efficiently. It would be great if there is a possibility to check the behaviour of system with adding tests based on possible arbitrary behaviour of relayers. Finally, there should be some tests which are a result of scalability analysis, since there is a plan to add more Host Zones. Try to check the behaviour of the system with increasing number of Host Zones.

AUDIT: Redemption rate limits are hardcoded

Surfaced from @informalsystems audit of Stride at commit f9736fe


finding:
id: IF-STRIDE-STAKEIBC-REDEMPTIONRATELIMITS
title: Redemption rate limits are hardcoded
severity: Informational
impact: None
exploitability: None
type: Implementation
issue:
status: Unresolved

Involved artifacts

Description

The way redemption rate works is it uses formula:

redemptionRate = (undelegatedBalance + stakedBalance + moduleAcctBalance)/stSupply

where the amount inside parentheses represents all the native tokens that Stride protocol owns (ATOMs for Cosmos Hub), and stSupply
is staked tokens amount (stATOMs for Cosmos Hub). In the downside, slashing should be limited to ~5% so redemption rate could go down for 5% (normalized to 0.95). On the upside it's limited by the inflation (for Cosmos Hub it's 20% per year), so the expectation is for redemption rate to go up to 1.2 after one year.

Right now, the limit range is hardcoded in stakeibc/types/params.go to be between 0.9 and 1.5.

Problem Scenarios

Different chains have different inflation rates and slashing mechanisms. Also, the redemption rate limits are by nature time dependent.

Recommendation

Each host zone should dynamically specify redemption rate limits. So, consider shifting them through time.

AUDIT: Coding style recommendations

Surfaced from @informalsystems audit of Stride at commit f9736fe


finding:
id: IF-STRIDE-CODINGSTYLE
title: Coding style recommendations
severity: Informational
impact: None
exploitability: None
type: Implementation
issue:
status: Unresolved

Involved artifacts

Description

  • Redundant casting:
    stTokenAmount, err := cast.ToUint64E(msg.Amount)
	if err != nil {
		errMsg := fmt.Sprintf("Could not convert redemption amount to int64 in redeem stake | %s", err.Error())
		k.Logger(ctx).Error(errMsg)
		return nil, sdkerrors.Wrapf(types.ErrIntCast, errMsg)
	}
	hostZoneUnbonding.StTokenAmount += stTokenAmount

Both msg.Amount and hostZoneUnbonding.StTokenAmount are already uint64.

  • Redundant else keyword:
    if amt >= 0 {
        amt, err := cast.ToUint64E(amt)
        if err != nil {
            k.Logger(ctx).Error(fmt.Sprintf("Error converting %d to uint64", amt))
            return false
        }
        val.DelegationAmt = val.GetDelegationAmt() + amt
        return true
    } else {
        absAmt, err := cast.ToUint64E(-amt)
        if err != nil {
            k.Logger(ctx).Error(fmt.Sprintf("Error converting %d to uint64", amt))
            return false
        }
        if absAmt > val.GetDelegationAmt() {
            k.Logger(ctx).Error(fmt.Sprintf("Delegation amount %d is greater than validator %s delegation amount %d", absAmt, valAddr, val.GetDelegationAmt()))
            return false
        }
        val.DelegationAmt = val.GetDelegationAmt() - absAmt
        return true
    }
  • Using hardcoded string:
    if epochIdentifier == "day" {
  • Code duplication:
sender, err := sdk.AccAddressFromBech32(msg.Creator)
	if err != nil {
		return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "creator address is invalid: %s. err: %s", msg.Creator, err.Error())
	}

The code above repeats in ValidatorBasic() as well as in msgServer's implementation of RedeemStake.

  • Naming optimization:
    amount := reinvestCallback.ReinvestAmount.Amount
    denom := reinvestCallback.ReinvestAmount.Denom
  • MintStAsset does two things: minting and sending tokens.

Problem Scenarios

Recommendation

  • Remove redundant type conversion statement.
  • In the above, the else statement can be safely removed because its if clause returns from the method. Thus, even without the else, thereโ€™s no way youโ€™ll be able to proceed past the if clause body.
  • Avoid using hardcoded strings ("day"), make use of named constants instead (DAY_EPOCH).
  • Remove error checking in msgServer's implementation of RedeemStake. It's been already checked in ValidatorBasic().
  • Change the name of ReinvestAmount type to ReinvestCoin, because it contains Amount and Denom fields.
  • According to SRP sending stTokens to user's Stride account shouldn't be the responsibility of MintStAsset function. Take the sending part out of the function and place it inside LiquidStake.

AUDIT: Documentation is not updated (including Linux support)

Surfaced from @informalsystems audit of Stride at commit f9736fe


finding:
id: IF-STRIDE-DOCUMENTATION
title: Documentation is not updated (including Linux support)
severity: Informational
impact: None
exploitability: None
type: Implementation
issue:
status: Unresolved

Involved artifacts

Description

The documentation contains pretty well technical architecture diagrams which explain three main data flow processes:

  • deposit and liquid stake
  • epoch delegation and reinvestment
  • unbonding

But some parts of the epoch delegation and reinvestment diagram need to be updated:

  • Reward Account probably should be removed and arrow no.3 may point to Withdraw ICA directly.

    Alt text

  • Function SetWithdrawalAddressOnHost changes default withdraw address (Delegation ICA address) to Withdraw ICA using MsgSetWithdrawAddress from x/distribution module:

    msgs = append(msgs, &distributiontypes.MsgSetWithdrawAddress{DelegatorAddress: delegationIca.GetAddress(), WithdrawAddress: withdrawalIcaAddr})

    so the "Reward Account" is an internal account in the dist module and is not important for the big picture.

  • There are deprecated enums: TRANSFER and STAKE

Up to this point, dockernet has only been tested on MacOS. In other words, nothing hasn't been done or tested on Linux. The audit team experienced a lot of installing problems using Linux machines so the solution rapidly appeared; the stride team provided a dedicated branch for Linux users: linux-dockernet and PR which introduces changes to make dockernet compatible with Linux.

Problem Scenarios

Out-of-date technical diagrams could lead to wrong conclusions about the data flow. Additionally, Linux users may feel some inconvenience while installing the chain.

Recommendation

  • Rename Revenue EOA to either Revenue ICA or Fee ICA to be consistent with other ICA accounts on the host zone
  • Remove the first sentence under the "Claim and Reinvest Rewards" section. There is no such thing as a "claim rewards" function anymore.
  • On the epoch delegation diagram make use of newly added enums for deposit records:
    • TRANSFER_QUEUE
    • TRANSFER_IN_PROGRESS
    • DELEGATION_QUEUE
    • DELEGATION_IN_PROGRESS

to clearly distinguish the current state of each deposit record instead of the former ones (TRANSFER and STAKE).

  • Similar thing for unbonding flow; mention the HostZoneUnbonding possible status:

    • UNBONDING_QUEUE
    • UNBONDING_IN_PROGRESS
    • EXIT_TRANSFER_QUEUE
    • EXIT_TRANSFER_IN_PROGRESS
    • CLAIMABLE
  • Replace the non-used ignite chain serve command which belongs to the
    Installing Stride section from docs with the appropriate one.

  • Dockernet linux support : already done.

Remove sdkerror

Background

  • Changing the ordering/numbering of sdkerrors can cause state breaking changes

Suggested Design

  • Replace sdkerrors with regular go errors

Acceptance Criteria

  • Unit and integration tests pass

AUDIT: Various kinds of observations

Surfaced from @informalsystems audit of Stride at commit f9736fe


finding:
id: IF-STRIDE-OBSERVATIONS
title: Various kinds of observations
severity: Informational
impact: None
exploitability: None
type: Implementation
issue:
status: Unresolved

Description

  • There are no invariants defined in Stride codebase. In the context of the Cosmos SDK, an Invariant is a function that checks for a particular invariant at the end of each block. These functions are useful to detect bugs early on and act upon them to limit their potential consequences (e.g. by halting the chain). They are also useful in the development process of the application to detect bugs via simulations.
  • Simulation tests not implemented. The Cosmos SDK offers a full-fledged simulation framework to fuzz-test every message defined by a module. Although Stride modules contain scaffolded skeletons for simulation messages, they are not implemented (e.g. LiquidStake msg):
    func SimulateMsgLiquidStake(
  ak types.AccountKeeper,
  bk types.BankKeeper,
  k keeper.Keeper,
    ) simtypes.Operation {
  return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
      ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
    simAccount, _ := simtypes.RandomAcc(r, accs)
    msg := &types.MsgLiquidStake{
      Creator: simAccount.Address.String(),
    }

    // TODO: Handling the LiquidStake simulation

    return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "LiquidStake simulation not implemented"), nil, nil
      }
    }
  • HostZoneUnbonding's status EXIT_TRANSFER_QUEUE was set twice, before and after burning stTokens during UndelegateCallback.

  • SetWithdrawalAddressOnHost is being called on every epoch.

  • Flags: HostZoneUnbonding_UNBONDING_IN_PROGRESS and HostZoneUnbonding_EXIT_TRANSFER_IN_PROGRESS seem to be unused.

Problem Scenarios

Recommendation

  • Think about adding some invariants. Describe and check conditions on Stride which should be fulfilled at every block execution or on every epoch.
  • If there is a need for fuzzy testing, you could utilize cosmos sdk simulator by implementing messages of interest. These are simulated with random field values. The sender of the operation is also assigned randomly. The simulator can test parameter changes at random as well. Finally, you could create a randomized genesis file.
  • Consider representing amount fields with string type (if their type int64 or uint64). The way protobuf json marshaling works, it encodes the uint64 as a string. This also allows for larger amounts such as uint256 to be sent across chains. An example of this approach can be found in proto/ibc/applications/transfer/v2/packet.proto:
message FungibleTokenPacketData {
  // the token denomination to be transferred
  string denom = 1;
  // the token amount to be transferred
  string amount = 2;
  // the sender address
  string sender = 3;
  // the recipient address on the destination chain
  string receiver = 4;
}
  • Set HostZoneUnbonding's status EXIT_TRANSFER_QUEUE either before or after burning stTokens during UndelegateCallback.
  • SetWithdrawalAddressOnHost should not be called on every epoch.
  • Define only necessary flags for HostZoneUnbonding_Status. Probably HostZoneUnbonding_UNBONDING_IN_PROGRESS and HostZoneUnbonding_EXIT_TRANSFER_IN_PROGRESS should be removed.

Migrate Integration Tests to ibctest

Background

Strangelove has built an integration test framework called ibctest that allows you to write integration tests in Go that run with a docker backend. This framework would be an improvement over our bash integration tests and would make it easier to run via CI.

Authz grant query fails

Summary of Bug

Unable to query a granter/grantee for authz grants: https://rest.cosmos.directory/stride/cosmos/authz/v1beta1/grants?granter=stride1cvncg08uc6nxuem2w2wkkd74kd4n6gzf4q94vd&grantee=stride1f49xq0rmah39sk58aaxq6gnqcvupee7jaxxgcr

Which returns:

{ "code": 2, "message": "runtime error: invalid memory address or nil pointer dereference: panic", "details": [ ] }

Even though a couple of grants happened between the above granter/grantee pair and should exist: https://www.mintscan.io/stride/txs/89F698563A2F4D651FAF522927872526A122255C721573B7226031C33D804363

Version

Current mainnet

Steps to Reproduce

Grant some authz grants and try to query them.

For Admin Use

  • Not duplicate issue
  • Appropriate labels applied
  • Appropriate contributors tagged
  • Contributor assigned/self-assigned

Checklist for remove sdkerror

Background

Checklist for removing sdkerror of Stride (to solve #371 )

We should solve all the list of modules below

  • claim
  • epochs
  • icacallbacks
  • interchainquery
  • mint
  • records
  • stakeibc

Main pull request: #379

Acceptance Criteria

  • All existing and new tests should pass

RATE-LIMITER: Add Flow type and helper functions

Subticket of #414

Suggested Design

  • Spec for Flow data structure (not persisted in store)
Flow
   Attributes
        Inflow int
	Outflow int
	PeriodEnd int
	ChannelValue int

	NewFlow(currentTime, channelValue) -> flow
	IsExpired() -> bool 
             checks relative to block time

	AddInflow(amount, quota) โ†’ error [DO NOT IMPLEMENT]
	AddOutflow(amount, quota) -> error [DO NOT IMPLEMENT]

Acceptance Criteria

  • Flow proto definition
  • Flow proto-generated go file
  • Flow helper functions implemented
  • Unit tests for all functions

IBC client still get expired

Summary of Bug

I know that Stride relayer has updated to this cosmos/relayer#853 to make sure that client stays alive. However, I still get error client expired when trying to ibc-transfer.

I found this out when have to debug scripts/tests/gaia-tests.bats for proto

To make sure that what I found is correct, I have run ibc-transfer right after relayer starts and wait a long time before sending a second one.

Here is the result:

  1. Right after (running)

Screenshot 2022-11-18 at 14 19 05

  1. After a while (expired)

Screenshot 2022-11-18 at 13 04 35

Version

main

Steps to Reproduce

docker exec stride-stride1-1 strided tx ibc-transfer transfer transfer channel-0 cosmos1pcag0cj4ttxg8l7pcg0q4ksuglswuuedcextl2 3000ustrd --from val1 --chain-id STRIDE -y --keyring-backend test

run this at different time after relayer started.


For Admin Use

  • Not duplicate issue
  • Appropriate labels applied
  • Appropriate contributors tagged
  • Contributor assigned/self-assigned

more composable/interoperable protos

Hello! I'm working on tooling to generate clients, and here to help with packaging protos in a canonical way that other chains are doing

For example,

To make this more interoperable, can we move

proto/epochs            -> proto/stride/epochs
proto/icacallbacks      -> proto/stride/icacallbacks
proto/interchainquery   -> proto/stride/interchainquery
proto/mint              -> proto/stride/mint
proto/records           -> proto/stride/records
proto/stakeibc          -> proto/stride/stakeibc

It looks like a pretty small change, but would be HUGE in terms of interoperability.

You may also want to consider the package names also, I think there are two packages: stride and stridelabs, not sure if it's good to consolidate or not depending on the use case.

cc @nghuyenthevinh2000

AUDIT: Consider isolating host zone operations

Surfaced from @informalsystems audit of Stride at commit f9736fe

Involved artifacts

Description

In the current Stride StakeIBC architecture, in particular in the functions BeforeEpochStart() and BeginBlocker(), many actions are done via iteration over all host zones; we may call this architecture horizontal: all operations of a particular kind are tightly coupled together for all host zones.

As Stride plans to expand to ~50 host zones in the near future, we would like to propose the following isolation property, as desirable for the Stride implementation:

Isolation property: whatever faults happen on one of the Stride host zones, should not influence Stride operations on other host zones.

The isolation property can be implemented via refactoring the Stride architecture from horizontal to vertical: here, the set of all operations for each host zone is done independently from the other host zones, and in particular they should be done in different blocks; thus e.g. the epochs for each host zone should be shifted wrt. each other. The difference between horizontal vs. vertical architecture is illustrated graphically in the image below.
zoneisolation

Problem Scenarios

One of the main problems the current architecture poses is the existence of implicit assumptions that should be satisfied by the large volume of Stride code. E.g.:

  • the code should not panic (because it's executed in BeginBlocker/EndBlocker);
  • the code called from inside host zone iteration loops should not return prematurely, or break from those loops.

While the current codebase seem to adhere to those assumptions, as it is written by a few code developers, the assumptions may become violated unintentionally as the team grows. And if any of the assumptions is ever violated, the whole Stride operation for all host zones may break.

We rate the impact of this finding as High, because an unhandled error, or premature exit for any operation for one of the host zones will make the whole set of operations for all host zones to fail. We rate the exploitability of this finding as None, because in the current implementation we were not able to identify the way to trigger the problem. We would like to stress though that the exhaustive search and identification of all potential ways to exploit the problem is not feasible; e.g., an error may occur in other parts of the stack of dependencies: encoding/decoding, IBC, ICA, etc., which are outside of the scope of this report. Thus, we assign this finding the overall severity score of Informational: while not crucial now, we recommend to address it within the next year.

Recommendation

Consider refactoring the Stride StakeIBC implementation from horizontal to vertical architecture wrt. the handing of host zones. Besides that, consider documenting explicitly whatever important assumptions the code should satisfy (such as absence of panics).

Add txfees module and add anteHandler gas filter for liquidStake/redeemStake and IbcTransfer

Background

Some gas-free cosmos chains have been attacked by spam transactions that clutter blocks and crowd out legitimate users and app logic. Stride has no transactios fees today, so that the UX is seamless for liquid stakers, but this makes Stride potentially vulnerable to spam transactions.

We could achieve the same seamless UX by setting a small non-zero default tx price, but allowing exceptions: liquidStake and redeemStake txs are gas-free below some threshold value (e.g. less than 1000atom), and ibcTransfers of st assets are gas free. This is the only relevant user flow we need to keep gas-free for the sake of UX; we can charge gas for other types of transactions - that won't impact the UX since it's outside the typical user flow.

Credit to Osmosis for writing the x/txfees module and writing the first

Suggested Design

High level

  1. Add the x/txfees module (and greenlight st assets)
  2. Set default gas to small non-zero value
  3. Add detection logic to AnteHandler for charging custom gas based on message type and value (pioneered by Osmosis)

Detailed Design (full credit to Osmosis for pioneering this)

Transaction Fees
x/txfees is implemented in Osmosis' repo here

AnteHandler work

  • Using FeeDecoratorFilter architecture, we could implement it like this:
    • Write GetMinBaseGasPriceForTx() like this
    • Write IsZeroGasTx() like this (checking for msg type and num tokens)
    • Define gasPrice like this and initialize like this
    • In AnteHandle(), invoke GetMinBaseGasPriceForTx() like this

As a reference, see Devโ€™s AnteHandler work for arb-tx detection + custom gas prices:
- Written in osmosis-labs/osmosis#741
- Refactored in https://github.com/osmosis-labs/osmosis/pull/777

Acceptance Criteria

  • all existing and new tests should pass
  • add tests for x/txfees: can pay gas in st assets
  • add tests for antehandler: zero-gas works only for specified transactions
  • get a review from someone on the osmosis team

Error on installing poolParty

Installing poolParty throws this error at the final step:

Fetching Stride's code... Done

Where do you want to install your stride and cosmovisor binaries? [default: /root/go/bin] y

Building Stride... Done

============================================================================================

Almost there! You'll also need cosmosvisor which will enable automatic upgrades.

Installing now!
This one might take a few minutes...
mv: cannot stat '/root/.stride/cosmovisor/cosmovisor': No such file or directory

Refactor redemption rate safety bound invariants to be specific to each host zone

Background

We currently upper and lower bound all redemption rates by fixed, hardcoded constants [0.9,1.5]. We should instead vary these bounds by host zones. We also should put these bounds up to governance as parameters.

In this way

  • zones with higher inflation rates have wider bounds, while those with lower inflation have tighter bounds
  • a simple param change gov vote can update the bounds, rather than requiring a software upgrade

Suggested Design

  1. Add two new maps in stakeibc params:
  • maxrrs = {chainidA: 1.5, chainidB: 2.0,โ€ฆ}
  • minrrs = {chainidA: 0.6, chainidB: 0.8,โ€ฆ}
  1. Use the proper hostzone's bounds (indexed from these maps) everywhere the current bounds are used

Acceptance Criteria

  • all existing and new tests should pass
  • write two new unit tests, they should pass:
    - the redemption rate on a host zone is more than the upper bound and halts the chain
    - the redemption rate on a host zone is less than the upper bound and halts the chain
  • manually verify we can change the params with a gov vote
  • make sure we initialize these params in the software upgrade

Add Additional Test Coverage to SweepAllUnbondedTokens

Goal

Get more granular coverage on SweepAllUnbondedTokens

Context

SweepAllUnbondedTokens iterates through each host zone and calls SweepAllUnbondedTokensForHostZone for each. The unit tests in unbonding_records_sweep_unbonded_tokens_test.go test only SweepAllUnbondedTokens. However we want to add coverage for SweepAllUnbondedTokensForHostZone since itโ€™s more granular and will let us test errors thrown in the middle of the function, rather than just a binary pass/fail for each host zone.

Suggested Design

Replace the tests in unbonding_records_sweep_unbonded_tokens_test.go with the following:

  • TestSweepUnbondedTokens_Successful (no change necessary)
    • Should test SweepAllUnbondedTokens with multiple host zones all succeeding
  • TestSweepUnbondedTokens_SingleHostFailure (similar to TestSweepUnbondedTokens_RedemptionAccountMissing)
    • Should test SweepAllUnbondedTokens with multiple host zones where one of them fails
  • TestSweepUnbondedTokensForHostZone_Successful
    • Tests SweepUnbondedTokensForHostZone with a successful setup
  • TestSweepUnbondedTokensForHostZone_RedemptionAccountMissing
  • TestSweepUnbondedTokensForHostZone_DelegatoinAccountMissing
  • Any other errors that are thrown by SweepUnbondedTokensForHostZone

Acceptance Criteria

  • Tests above added with all tests passing

Add new governance proposal for modifying multiple host zone validators

Background

Currently, host zone validators are proposed one at a time through governance. That's tedious. It also means that the council would have to submit N proposals to alter N validators. We'd prefer to use a single proposal to set a full new N-validator set (with weights) on the host zone.

Suggested Design

  • add a new valWgts struct to x/stakeibc: {cosmosvaloper1234:5, cosmosvaloper5678:2,โ€ฆ}
  • new gov handler: proposeValidators
    • inputs: valWgts struct
    • validateBasic (which executes when gov prop is submitted): check addr formattings, check number of vals < limit, check no single val wgt exceeds maxConcentration
      • add a maxConcentration param to stakeibc (start at 0.1): in the new proposed set, no validator can have over 10% weight.
    • add a gov prop handler to execute before gov prop added onchain
      • I think this doesn't need any real functionality
    • add a method that executes once gov prop passes:
      • add new vals to hz struct and set wgts
        1. Set weight on any vals leaving the old set to 0
        2. Add new vals w new wgts
        3. Update wgts on existing vals
        4. Invoke rebalance

Acceptance Criteria

  • all existing and new tests should pass
  • new test for proposing the gov prop, and for it executing
  • new test for proposing a val set with N-1 existing validators and one non-existent

Questions

  • how should we handle the case where one validator in the proposed set does not exist on the host zone, or is tombstoned, or falls out of the active set before the gov prop passes? -
    • (maybe) icq the hz to check proposed val addresses against active host zone set before (a) writing the gov proposal to stride governance and (b) executing addValidator for each validator once the gov prop passes

Upgrade ibc-go to v5.1.0

Background

  • We are currently on ibc-go v3. Upgrading to v5 will bring some improvements to ICA and it will enable us to switch to the main version of the go relayer (we are currently using a side branch that has ibc v3 and new features backported)

Suggested Design

  • Update the package definition in go.mod, update the imports, and then make code changes as required

Acceptance Criteria

  • Unit and integration tests pass

Epic: IBC rate limiting module

heavily inspired by the Osmosis implementation: https://github.com/osmosis-labs/osmosis/tree/main/x/ibc-rate-limit

IBC Rate Limit

The IBC Rate Limit module is responsible for adding a governance-configurable rate limit to IBC transfers.
This is a safety control, intended to protect assets on Stride in event of:

  • a bug/hack on Stride
  • a bug/hack on the counter-party chain
  • a bug/hack in IBC itself

This is done in exchange for a potential (one-way) bridge liveness tradeoff, in periods of high deposits or withdrawals.

The architecture of this package is a minimal go package which implements an IBC Middleware that wraps the ICS20 transfer app, and calls a new module, ibc-rate-limit.

The ibc-rate-limit module then has all of the actual IBC rate limiting logic.

The status of the module is being in a state suitable for some initial governance settable rate limits for high value bridged assets.
Its not in its long term / end state for all channels by any means, but does act as a strong protection we
can instantiate today for high value IBC connections.

Motivation

The motivation of IBC-rate-limit comes from the empirical observations of blockchain bridge hacks that a rate limit would have massively reduced the stolen amount of assets in:

In the presence of a software bug on Stride, IBC itself, or on a counterparty chain, we would like to prevent the bridge from being fully depegged.
This stems from the idea that a 30% asset depeg is ~infinitely better than a 100% depeg.
Its crazy that today these complex bridged assets can instantly go to 0 in event of bug.
The goal of a rate limit is to raise an alert that something has potentially gone wrong, allowing validators and developers to have time to analyze, react, and protect larger portions of user funds.

The thesis of this is that, it is worthwile to sacrifice liveness in the case of legitimate demand to send extreme amounts of funds, to prevent the terrible long-tail full fund risks.
Rate limits aren't the end-all of safety controls, they're merely the simplest automated one. More should be explored and added onto IBC!

Implementation overview

  • Add ibc-rate-limit module
    • Must be an IBC middleware
    • Should sit in the records stack
    • SendPacket should limit outflows
    • ReceivePacket should limit inflows
    • If a transfer fails, the normal ICS-20 logic should be invoked (refund) and the rate-limits should not be updated
  • Open questions
    • How should we add / remove / update rate-limits? It may be easiest to start with a multisig and move to governance
    • What are the necessary data structures and types we'll need to store?
    • What functions are exposed externally?

Improve Github pull request process and guardrails

Background

Our current PR process is subpar. Of all the desired checks/guardrails/requirements, some are:

  1. UNSPECIFIED/UNENFORCED: not specifed or enforced (includes changelog entries, gofumpt, checking package versions, SLAs for reviewers, adding/editing documentation)
  2. ENFORCED SOCIALLY: enforced socially at the PR template layer (incl. "did you add new tests?" "did you run integration tests?")
  3. ENFORCED BY CODE: enforced at the github layer (linting, unit tests, verify code compiles on various distros, gosec)

We need to move as many requirements as possible from UNSPECIFIED/UNENFORCED to ENFORCED SOCIALLY to ENFORCED BY CODE.

IMO this effort should be ongoing - this issue should not be closed until we are satisfied with our process, which will take time. Rather, let's comment on the issue, update it as we move items towards (2) and iterate.

Suggested Design

adding documentation to explain code changes and new features

  • new โ€œdocsโ€ checkbox to PR template
  • (ideally) in GH CI, block merges unless they make a change to a markdown file (either new docs or update existing docs or update changelog)
    adding to changelog.md
  • create this file and enforce socially
  • (ideally) block merge if it has changes to proto files and no changes to changelog.md
    (notional can help) add GH CI for:
    gofumpt: add to github CI
    autocheck package versions (eg ibc) against the latest/expected versions: add to github CI
    SLA / process: enforce socially
  • keep all prs as draft until ready for review
    need description, tests etc and 2 tagged reviewers to bring out of draft
  • 48hr reviewer SLA enforced socially + with axolo slack reminders
    (maybe) shorten the PR autoclose setting on GH to 6d: why? Bc if not merged after 3 loops with reviewers, should move go to draft, prob reqโ€™s more involved changes

Acceptance Criteria

Social agreement by the eng team

AUDIT: GetHostZoneUnbondingMsgs need to be refactored

Surfaced from @informalsystems audit of Stride at commit f9736fe


finding:
id: IF-STRIDE-STAKEIBC-HOST_ZONE_UNBOINDING
title: GetHostZoneUnbondingMsgs need to be refactored
severity: Informational
impact: None
exploitability: None
type: Implementation
issue:
status: PR created

Involved artifacts

Description

The job of GetHostZoneUnbondingMsgs function is to populate and return all MsgUndelegate messages as outlined below:

for _, valAddr := range utils.StringToIntMapKeys(valAddrToUnbondAmt) {
		valUnbondAmt := valAddrToUnbondAmt[valAddr]
		stakeAmt := sdk.NewInt64Coin(hostZone.HostDenom, valUnbondAmt)

		msgs = append(msgs, &stakingtypes.MsgUndelegate{
			DelegatorAddress: delegationAccount.GetAddress(),
			ValidatorAddress: valAddr,
			Amount:           stakeAmt,
		})

		splitDelegations = append(splitDelegations, &types.SplitDelegation{
			Validator: valAddr,
			Amount:    stakeAmt.Amount.Uint64(),
		})
	}
	...
	return msgs, totalAmtToUnbond, marshalledCallbackArgs, epochUnbondingRecordIds, nil

DelegatorAddress is the address of Delegation ICA on host zone, and ValidatorAddress is the address of particular validator from which the token Amount will be undelegated.

The problematic part is the complexity that follows creating those messages.

GetHostZoneUnbondingMsgs:

  • returns 5 different results (including error). On many places there are nasty returns like:
    • return nil, 0, nil, nil, sdkerrors.Wrap(types.ErrIntCast, errMsg)
      return nil, 0, nil, nil, sdkerrors.Wrap(types.ErrNoValidatorAmts, errMsg)
      return nil, 0, nil, nil, nil
      ...
  • has 4 for loops (including 2x iterations over the validator set)
  • doesn't have single responsibility; instead it's doing many things:
    • Calculate total unbonding amount
    • Populate mapping (validator address -> unbond amount)
    • Dealing with overflows
    • Creating MsgUndelegate for each (delegator, validator) pair
    • Creating undelegate callback

Problem Scenarios

Code maintenance might become difficult.

Recommendation

  • Pull the code that calculates total amount to unbond out from the function. The function is only interested in the final result: totalAmtToUnbond.
  for _, epochUnbonding := range k.RecordsKeeper.GetAllEpochUnbondingRecord(ctx) {
		hostZoneRecord, found := k.RecordsKeeper.GetHostZoneUnbondingByChainId(ctx, epochUnbonding.EpochNumber, hostZone.ChainId)
		if !found {
			errMsg := fmt.Sprintf("Host zone unbonding record not found for hostZoneId %s in epoch %d",
				hostZone.ChainId, epochUnbonding.GetEpochNumber())
			k.Logger(ctx).Error(errMsg)
			continue
		}
		// mark the epoch unbonding record for processing if it's bonded and the host zone unbonding has an amount g.t. zero
		if hostZoneRecord.Status == recordstypes.HostZoneUnbonding_UNBONDING_QUEUE && hostZoneRecord.NativeTokenAmount > 0 {
			totalAmtToUnbond += hostZoneRecord.NativeTokenAmount
			epochUnbondingRecordIds = append(epochUnbondingRecordIds, epochUnbonding.EpochNumber)
			k.Logger(ctx).Info(fmt.Sprintf("[SendHostZoneUnbondings] Sending unbondings, host zone: %s, epochUnbonding: %v", hostZone.ChainId, epochUnbonding))

		}
	}
  • Calculating and handling the overflow should also be moved from the function:
  if overflowAmt > 0 { // if we need to reallocate any weights
		for _, validator := range validators {
			valAddr := validator.GetAddress()
			...
		}
	}
	if overflowAmt > 0 { 
		errMsg := fmt.Sprintf("Could not unbond %d on Host Zone %s, unable to balance the unbond amount across validators",
			totalAmtToUnbond, hostZone.ChainId)
		...
	}

It's not responsibility of the GetHostZoneUnbondingMsgs to deal with unbonding amount overflows.

  • Rename stakeAmt to unstakeAmt:
  for _, valAddr := range utils.StringToIntMapKeys(valAddrToUnbondAmt) {
		valUnbondAmt := valAddrToUnbondAmt[valAddr]
		stakeAmt := sdk.NewInt64Coin(hostZone.HostDenom, valUnbondAmt)

		msgs = append(msgs, &stakingtypes.MsgUndelegate{
			DelegatorAddress: delegationAccount.GetAddress(),
			ValidatorAddress: valAddr,
			Amount:           stakeAmt,
		})

		splitDelegations = append(splitDelegations, &types.SplitDelegation{
			Validator: valAddr,
			Amount:    stakeAmt.Amount.Uint64(),
		})
	}

Add v3 package versioning to stride module

Background

  • In go, packages with a version greater than v1 must have a suffix with the version (e.g. github.com/.../v3)
  • in order to import Stride v3 in other go projects, we need to modify the module definition to include the suffix

Suggested Design

  • Change the module definition in go.mod to github.com/Stride-Labs/stride/v3 and update imports accordingly

Acceptance Criteria

  • Unit and integration tests pass
  • Stride v3 is importable in other go projects

AUDIT: Failure to send IBC packets may lead to user funds freeze

Surfaced from @informalsystems audit of Stride at commit f9736fe


finding:
id: IF-STRIDE-STAKEIBC-UNBONDINGRECORDS
title: Failure to send IBC packets may lead to user funds freeze
severity: Medium
impact: High
exploitability: Low
type: Implementation
issue:
status: Unresolved

Involved artifacts

Description

In the function SweepAllUnbondedTokensForHostZone(), which is called at the beginning of every Stride "day" epoch from BeforeEpochStart() and SweepAllUnbondedTokens(); we find the following code fragment:

// Send the transaction through SubmitTx
_, err = k.SubmitTxsDayEpoch(ctx, hostZone.ConnectionId, msgs, *delegationAccount, REDEMPTION, marshalledCallbackArgs)
if err != nil {
    k.Logger(ctx).Info(fmt.Sprintf("Failed to SubmitTxs, transfer to redemption account on %s", hostZone.ChainId))
}
err = k.RecordsKeeper.SetHostZoneUnbondings(ctx, hostZone, epochUnbondingRecordIds, recordstypes.HostZoneUnbonding_EXIT_TRANSFER_IN_PROGRESS)
if err != nil {
    k.Logger(ctx).Error(err.Error())
    return false, 0
}
k.Logger(ctx).Info(fmt.Sprintf("Successfully completed unbonded token sweep ICA call for %s, %s, %v", hostZone.ConnectionId, hostZone.ChainId, msgs))

As can be seen, if an error is received from SubmitTxsDayEpoch(), the error is logged, but the execution continues; in particular, it updates the state of the relevant epoch unbonding records to HostZoneUnbonding_EXIT_TRANSFER_IN_PROGRESS. SubmitTxsDayEpoch() goes via ICAControllerKeeper.SendTx(), and eventually calls IBC send. There are many places in the long chain of calls that may fails due to variant reasons, and thus SubmitTxsDayEpoch() will fail as well.

Problem Scenarios

The above error will result in the following consequences:

  • Interchain Accounts IBC transaction won't be sent;
  • The unbonded funds won't be transferred to the Redemption ICA account;
  • RedemptionCallback() won't be called; and thus the status of unbonding records won't be set to HostZoneUnbonding_CLAIMABLE;
  • The funds that should have been transferred from Delegation ICA account to Redemption ICA account will be stuck at Delegation ICA account, and users won't be able to claim them.

We rate the impact of this finding as High, because it may lead to funds of many users locked, and users won't be able to claim them. We rate the exploitability of this finding as Low, as the possibility of ICA/IBC transfer failing is relatively low. Due to the combination of the above two scores, vulnerability receives the overall severity score of Medium.

Recommendation

In the above code fragment, interrupt execution upon receiving an error from the SubmitTxsDayEpoch() function.

RATE-LIMITER: Add Path type and helper functions

Subticket of #414

Suggested Design

  • Spec for Path datastructure (persisted in store)
Attributes
    Id string (f'{Denom}-{ChannelId}')
    Denom string (native denom)
    ChannelId string

Functions
    AddPath(denom, channel) โ†’ string (id)
        Adds to store with ID as the key, returns an ID string

    RemovePath(id)
       Deletes from store

    GetPath(id) โ†’ path 
       Grabs a path object from the store using the key and returns it 

    GetAllPaths() โ†’ List[path]
       Iterates through each path object and returns a list

    IsNative() โ†’ bool 
       Returns true if denom is ustrd, false otherwise

    GetIbcDenomHash()
      Returns the IBC hash of a denom using the Denom, ChannelId, and "transfer" as the port

    GetChannelValue() [DO NOT IMPLEMENT]

Acceptance Criteria

  • Path proto definition
  • Path proto-generated go file
  • Helper functions implemented
  • Unit tests for all functions

Protobuf error

Summary of Bug

.proto files refer to v3

Version

v4.0.0 / main

Steps to Reproduce

make proto-gen

Screenshots

image

Additional context

PR upcoming

additionally the protocgen.sh script referred to v3.


For Admin Use

  • Not duplicate issue
  • Appropriate labels applied
  • Appropriate contributors tagged
  • Contributor assigned/self-assigned

app-router module

Context
Currently, it takes two transactions to liquid stake. Given this is such a common action on Stride, it would improve UX to reduce the number of transactions it takes to liquid stake. Furthermore, for DAOs that want to liquid stake their treasury, it is easier

Spec
Create an IBC middleware module app-router that parses incoming ICS-20 transfers and executes logic on Stride, if criteria are met.

  • The way we parse the data out of the recipient field should be robust - the current approach uses a delimiter and looks for a special character sequence to trigger a liquid stake
  • The middleware should not change any of the functionality of the transfer module

NOTE: As of IBC v3.4.0, a memo field is now supported in ICS-20 transfers. Release https://github.com/cosmos/ibc-go/releases/tag/v3.4.0 We should support liquid stakes that use this memo field and liquid stakes that we parse from the receive field. The logic should be as follows

if ibc_transfer_packet.memo != "":
    process_with_memo(ibc_transfer_packet)
else:
    process_from_recipient(ibc_transfer_packet)

Work so far
A draft of the module was created here, inspired by routing module created by strangelove.

Future work and improvement ideas

  • Route LSD tokens back to an external address
  • Generalize the module so that it can process arbitrary function calls
  • Use IBC memos (release in IBCv5) instead of parsing data from the receiver address

AUDIT: Variable err not assigned but used inside if condition

Surfaced from @informalsystems audit of Stride at commit f9736fe


finding:
id: IF-STRIDE-STAKEIBC-HOST_ZONE_UNBOINDING_2
title: Variable err not assigned but used inside if condition
severity: Informational
impact: None
exploitability: None
type: Implementation
issue:
status: Unresolved

Involved artifacts

Description

Variable err doesnโ€™t have associated value but it is used inside if condition:

for _, validator := range validators {
		valAddr := validator.GetAddress()
		valUnbondAmt := newUnbondingToValidator[valAddr]
		currentAmtStaked := validator.GetDelegationAmt()
		if err != nil {
			errMsg := fmt.Sprintf("Error fetching validator staked amount %d: %s", currentAmtStaked, err.Error())
			k.Logger(ctx).Error(errMsg)
			return nil, 0, nil, nil, sdkerrors.Wrap(types.ErrNoValidatorAmts, errMsg)
		}
		...
	}

Function GetDelegationAmt() doesn't return any errors.

Problem Scenarios

Existing of dead code inside for loop. Even though err should never be different than nil in the current implementation, reading the code could lead to wrong conclusions.

Recommendation

Remove if condition from the code in Description. Handle the 0 delegation amount (currentAmtStaked) case if there is a need for that.

RATE-LIMITER: Add RateLimit type and helper functions

Subticket of #414
Blocked by #440, #437, #439

Suggested Design

  • Spec for RateLimit data structure (persisted in store)
Attributes
	Path path
	Flow flow
	Quota quota

Functions
	AddRateLimit(path, flow, quota) 
            Adds a rate limit object to the store, using the pathId as the key

        RemoveRateLimit(pathId)
            Removes from the store 

	GetRateLimit(pathId)
            Grabs a RateLimit object from the store using the path id as the key and returns it

	GetAllRateLimits()
           Iterates through each rate limit object and returns a list

Acceptance Criteria

  • RateLimit proto definition
  • RateLimit proto-generated go file
  • Helper functions implemented
  • Unit tests for all functions

RATE-LIMITER: Add Quota type and helper functions

Subticket of #414

Suggested Design

  • Spec for Quota data structure (persisted in store)
Quota 
    Attributes
        Name string
	MaxPercentSend int
	MaxPercentRecv int
	DurationHours int

   Functions
        AddQuota(name, send, recv, duration)
            Adds to store with name as the key

        RemoveQuota(name)
            Removes from the store

        SetQuota(quota)
           Updates a quota item

        GetQuota(name) โ†’ quota
           Grabs a quota object from the store using the key and returns it 

        GetAllQuotas() โ†’ List[quota]
           Iterates through each quota object and returns a list

	CheckExceedsQuota(SEND/RECV, amount, totalValue) โ†’ bool
            if SEND: returns true if (amount / totalValue) > MaxPercentSend
            if RECV: returns true if (amount / totalValue) > MaxPercentRecv

Acceptance Criteria

  • Quota proto definition
  • Quota proto-generated go file
  • Helper functions implemented
  • Unit tests for all functions

error in fetching state

Hi there,
I'm trying to run testnet node validator but it seems that url for fetching chain state is not correct in the script:

fetched_state="$(curl -s https://stride-library.$TESTNET.stridenet.co/commit

This script needs to be updated because addresses are outdated:
bash -c "$(curl -sSL install.poolparty.stridelabs.co)"
what is the correct address of testnet state? is there anywhere I can find latest correct addresses?

Also `ignite chain serve' doesn't work and here is the log:

โžœ  stride git:(main) โœ— ignite chain serve -v

[STARPORT] Cosmos SDK's version is: stargate - v0.45.5
[STARPORT]
[STARPORT] ๐Ÿ› ๏ธ  Building proto...
[STARPORT] cannot build app:
[STARPORT]
[STARPORT] 	open /root/stride/vue/package.json: no such file or directory
[STARPORT] Waiting for a fix before retrying...

AUDIT: StakeExistingDepositsOnHostZones could be a bottleneck in case of many host zones

Surfaced from @informalsystems audit of Stride at commit f9736fe


finding:
id: IF-STRIDE-STAKEIBC-STAKE_EXISTING_DEPOSITS
title: StakeExistingDepositsOnHostZones could be a bottleneck in case of many host zones
severity: Medium
impact: Low
exploitability: High
type: Implementation
issue:
status: Unresolved

Involved artifacts

Description

Staking of existing deposits is not done by host zones but per epoch which contains all deposit records filtered by DELEGATION_QUEUE status. There is limitation of the number of staking deposit records to process per epoch:

maxDepositRecordsToStake := utils.Min(len(stakeDepositRecords), cast.ToInt(k.GetParam(ctx, types.KeyMaxStakeICACallsPerEpoch)))

for _, depositRecord := range stakeDepositRecords[:maxDepositRecordsToStake] {
...
}

where KeyMaxStakeICACallsPerEpoch is set to 100 by default.

Problem Scenarios

Probably there could be situations when the total number of deposits records per epoch may be greater than 100 (in the case of 40+ host zones e.g.). Then, some of them may not be processed and in the case of timeouts or failed acks, the processed ones will also get back to the delegation queue. This may cause some of deposit records being stuck and accumulated through previous epochs.

Recommendation

Consider doing the staking deposit records by host zone to make it easier to track the process and stats.
Also think about having different limitations of staking ICA calls per each chain.

RATE-LIMITER: Scaffold ibc-rate-limit module

Subticket of #414

Overview

Scaffold ibc-rate-limit module and connect to Stride as middleware. Use osmosis as an example (and include attribution)

Design

  • Add module boilerplate
  • Add middleware to wrap ICS20 transfer - should sit in records stack between records and transfer

Acceptance Criteria

  • Add debugging logs to middleware and confirm middleware is reached by sending test packets between stride and cosmos
  • Osmosis attribution
  • Unit tests (if applicable)

When redemptionRate invariants are violated, pause one host zone instead of halting the chain

Background

Today, if Stride detects a redemption rate outside the upper/lower bound invariants, Stride panics, halting the chain.

The desired behavior in this scenario, however, is pausing app logic related to that host zone and pausing IBC withdrawals of the relevant asset, without pausing block production or app logic of other host zones.

We can achieve this by using per-host-zone flags that flip when a host zone's bounds are violated and pauses that host zone's app logic, but leaves the chain running and other leaves host zones' app logic unaltered.

Suggested Design

  • Add a โ€œhaltedโ€ field to hostZone, set to false in initialization and state migration
  • Replace panics with setting halted=true in RR invariant checks
  • At the top of each of the following, log, emit event and return if halted=true
    • Liquid staking
    • Redeeming
    • IBCโ€™ing the relevant asset off Stride

Acceptance Criteria

  • all existing and new tests should pass
  • new test that verifies that when bounds are violated, other host zones are still functional, but current hz is paused (check each relevant function)

Modularize `GetHostZoneUnbondingMsgs`

Background

Every "day" epoch (every 3 days, typically), module stakeibc's hooks.go initiates unbonding ATOM (and other tokens that Stride supports) if any users invoked msgRedeemStake in the preceding 3 days. Specifically, InitiateAllHostZoneUnbondings submits ICA (interchain account, or cross-chain) transactions to each host zone to unbond from its validators, according to a set of weights. Redemptions, unbondings in process and completed unbondings are tracked using data structures called records (defined in the x/records module).

InitiateAllHostZoneUnbondings uses a helper function GetHostZoneUnbondingMsgs to generate the unbonding transactions that need to be issued. This function

  • Gets the total amount to unbond from EpochUnbondingRecords
  • Calculates amounts to unbond from each validator, according to the defined set of weights (including doing some rounding and overflow math)
  • Creates, marshal and return the interchain-account msgUndelegate messages
  • Stores callback data, so that when the ack from the ICA call returns, Stride can execute cleanup logic

As an aside, the way that IBC transactions work is by processing the following 3 transactions

  • SendPacket (on Stride)
  • OnRecvPacket (on another chain)
  • OnAcknowledgementPacket (on Stride)
    An โ€œackโ€ returning just means that a relayer has executed an OnAcknowledgementPacket transaction on Stride (which can only happen after SendPacket and OnRecvPacket execute). This is an implementation detail of IBC, the ICA transaction is triggered simply by calling SubmitTxsDayEpoch.

InitiateAllHostZoneUnbondings has the outputs from GetHostZoneUnbondingMsgs, it submits the messages and marks the unbonding records "in progress".

**That's a LOT of logic in one single function. It's one of Stride's busiest methods. InitiateAllHostZoneUnbondings and GetHostZoneUnbondingMsgs are unclear and bloated.

To make this code more readable and easier to test, we should modularize it (and generally clean it up, using idiomatic golang).**

Getting started

Please create a new branch in the Stride repo, open a draft pull request and push code incrementally (hereโ€™s an example of a thorough pull request description to the Stride repo: #146)

You might want to take a look at the unit tests in unbonding_records_initiate_all_unbondings_test.go. Theyโ€™ll help you understand the record logic and explore edge cases as you go.

Goals

All unit tests should pass. All integration tests should pass across multiple zones.

InitiateAllHostZoneUnbondings and GetHostZoneUnbondingMsgs should be refactored and modularized (feel free to define new functions or change these functionsโ€™ names as needed). Weโ€™re hoping to use this refactor as an example for refactoring other portions of the codebase.

Best golang practices should be followed. Weโ€™re hoping to come away from this exercise with some golang learnings (how to write clean, idiomatic golang) that can be applied throughout the Stride codebase.

AUDIT: Different conditions for max number of validators to rebalance

Surfaced from @informalsystems audit of Stride at commit f9736fe


finding:
id: IF-STRIDE-STAKEIBC-REBALANCEVALIDATORS
title: Different conditions for max number of validators to rebalance
severity: Informational
impact: None
exploitability: None
type: Implementation
issue:
status: Unresolved

Involved artifacts

Description

There are two different places in the code where conditions for maximum number of validators to rebalance are checked and they don't really match. First one is in the function ValidateBasic():

if (msg.NumRebalance < 1) || (msg.NumRebalance > 10) {
		return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, fmt.Sprintf("invalid number of validators to rebalance (%d)", msg.NumRebalance))
	}

and the other one is part of RebalanceValidators() implementation.

if maxNumRebalance < 1 {
		k.Logger(ctx).Error(fmt.Sprintf("Invalid number of validators to rebalance %d", maxNumRebalance))
		return nil, types.ErrInvalidNumValidator
	}
	if maxNumRebalance > 4 {
		k.Logger(ctx).Error(fmt.Sprintf("Invalid number of validators to rebalance %d", maxNumRebalance))
		return nil, types.ErrInvalidNumValidator
	}

"Magic numbers" are also used inside if conditions.

Problem Scenarios

MsgRebalanceValidators will be processed successfully by ValidateBasic() for any NumRebalance value in {5..10}, but it will fail later on msg execution.

Recommendation

Choose which values could be taken for number of validators to rebalance and do the check only at one place (e.g. in ValidateBasic()).

Replace numbers with named constants (e.g. MIN_NUM_REBALANCE and MAX_NUM_REBALANCE)

Combine two conditions into single expression:

if (msg.NumRebalance < MIN_NUM_REBALANCE) || (msg.NumRebalance > MAX_NUM_REBALANCE) {
		return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, fmt.Sprintf("invalid number of validators to rebalance (%d)", msg.NumRebalance))
	}

Update test case for Stride

Background

Checklist for updated test cases for Stride #423

We should update test cases for the list function below( some functions misses some test cases and another don't have)

Unit-test

Acceptance Criteria

  • All existing and new tests should pass

RegisterAminoMsg preferred over RegisterConcrete

Background

There are RegisterCodec funcs like this one which use RegisterConcrete. It's preferred to use RegisterLegacyAminoCodec because it has a built-in check for the msg name length which will prevent against a hard to debug issue that can happen with Ledger. (cosmos/cosmos-sdk#10870). The cosmos-sdk switched to using this at the same time that this function was introduced.

Example from Stride codebase

func RegisterCodec(cdc *codec.LegacyAmino) {
	cdc.RegisterConcrete(&MsgLiquidStake{}, "stakeibc/LiquidStake", nil)
	cdc.RegisterConcrete(&MsgRegisterHostZone{}, "stakeibc/RegisterHostZone", nil)
	cdc.RegisterConcrete(&MsgRedeemStake{}, "stakeibc/RedeemStake", nil)
	cdc.RegisterConcrete(&MsgClaimUndelegatedTokens{}, "stakeibc/ClaimUndelegatedTokens", nil)
	cdc.RegisterConcrete(&MsgRebalanceValidators{}, "stakeibc/RebalanceValidators", nil)
	cdc.RegisterConcrete(&MsgAddValidator{}, "stakeibc/AddValidator", nil)
	cdc.RegisterConcrete(&MsgChangeValidatorWeight{}, "stakeibc/ChangeValidatorWeight", nil)
	cdc.RegisterConcrete(&MsgDeleteValidator{}, "stakeibc/DeleteValidator", nil)
	cdc.RegisterConcrete(&MsgRestoreInterchainAccount{}, "stakeibc/RestoreInterchainAccount", nil)
	cdc.RegisterConcrete(&MsgUpdateValidatorSharesExchRate{}, "stakeibc/UpdateValidatorSharesExchRate", nil)
	// this line is used by starport scaffolding # 2
}

Example from cosmos-sdk (https://github.com/cosmos/cosmos-sdk/blob/main/x/gov/types/v1/codec.go)

func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
	legacy.RegisterAminoMsg(cdc, &MsgSubmitProposal{}, "cosmos-sdk/v1/MsgSubmitProposal")
	legacy.RegisterAminoMsg(cdc, &MsgDeposit{}, "cosmos-sdk/v1/MsgDeposit")
	legacy.RegisterAminoMsg(cdc, &MsgVote{}, "cosmos-sdk/v1/MsgVote")
	legacy.RegisterAminoMsg(cdc, &MsgVoteWeighted{}, "cosmos-sdk/v1/MsgVoteWeighted")
	legacy.RegisterAminoMsg(cdc, &MsgExecLegacyContent{}, "cosmos-sdk/v1/MsgExecLegacyContent")
	legacy.RegisterAminoMsg(cdc, &MsgUpdateParams{}, "cosmos-sdk/x/gov/v1/MsgUpdateParams")
}

So I recommend us to switch to RegisterAminoMsg and also to change the func names from RegisterCodec to RegisterLegacyAminoCodec to also match the cosmos-sdk's pattern.

RATE-LIMITER: Implement Queries

Subticket of #414

Overview

Add the following queries

  • Paths: Returns all path objects
  • Path/{Id}: Returns a path given the path ID ({Denom-Channel})
  • Quotas: Returns all quota objects
  • Quota/{Name}: Returns a quota given a name
  • RateLimits: Returns all rate limit objects
  • RateLimit/{PathId}: Returns a rate limit object given the path ID

Acceptance Criteria

  • Query keeper functions
  • CLI commands
  • Unit tests
  • Tested with dockernet

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.