Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

chore: osmoutils, twap, downtime-detector, ibc-hooks cherry-pick to v14 #3925

Merged
merged 30 commits into from
Jan 5, 2023
Merged
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ad0c311
Upgrade IBC to v4.2.0 (#3838)
nicolaslara Dec 23, 2022
e8c3c6c
feat(x/twap): modify cli to add geometric option (#3812)
p0mvn Dec 22, 2022
a2fc2c8
Make wasm hooks importable (#3850)
nicolaslara Dec 23, 2022
129cbcb
refactor(x/twap): handle spot price error case in the context of geom…
p0mvn Dec 23, 2022
8196036
test(x/twap): add randomized geometric twap test on a balancer pool (…
p0mvn Dec 23, 2022
c7c0222
Basic geometric twap e2e test (#3835)
hieuvubk Dec 23, 2022
1afb23e
feat(x/twap): whitelist GeometricTwap and GeometricTwapToNow (#3852)
p0mvn Dec 23, 2022
c12bfa3
fix(scripts): proto gen for osmoutils (#3854)
p0mvn Dec 24, 2022
168ccda
fix(scripts): proto gen osmoutils path (#3859)
p0mvn Dec 27, 2022
de76db0
added packet timeouts to wasm hooks (#3862)
nicolaslara Dec 29, 2022
8f88f7d
add negative to cli (#3888)
czarcas7ic Dec 30, 2022
f4c7d47
Making osmoutils compile on chains that don't use our SDK fork (#3899)
nicolaslara Jan 2, 2023
19e4a9d
remove deprecation from arithmetic & geometric twap to now query (#3917)
iboss-ptk Jan 4, 2023
952b2d7
Add types & boilerplate for the Downtime detector module (#3609)
ValarDragon Dec 6, 2022
7ec5e4e
Add downtime detector module (#3688)
ValarDragon Dec 14, 2022
ecde767
Initial by hand fixes
czarcas7ic Jan 4, 2023
1500176
feat(osmomath): Exp2 function (#3708)
p0mvn Dec 16, 2022
048a2a5
[x/TWAP] Expose a geometric TWAP API (#3529)
stackman27 Dec 1, 2022
7ade440
fix twap by hand
czarcas7ic Jan 4, 2023
ea3af42
change to gamm
czarcas7ic Jan 4, 2023
813a6e8
fix balancer test
czarcas7ic Jan 4, 2023
640dfb9
bump to v14 upgrade
czarcas7ic Jan 4, 2023
f7ded6c
e2e fix
czarcas7ic Jan 4, 2023
da63fa8
add remaining diff from main to ibc-rate-limit
czarcas7ic Jan 5, 2023
7d25928
update contracts test
czarcas7ic Jan 5, 2023
f864dcf
osmomath: `AddMut` and `QuoMut` (#3779)
pysel Dec 22, 2022
35ab94e
Modify CHANGELOG
mattverse Jan 5, 2023
09ccc37
Whitelist EstimateSwapExactAmountOut (#3693)
byeongsu-hong Dec 13, 2022
3a4aac2
updated rate limit contract
nicolaslara Jan 5, 2023
d5dda29
Fix rust checks (#3576)
nicolaslara Nov 30, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
added packet timeouts to wasm hooks (#3862)
nicolaslara authored and czarcas7ic committed Jan 4, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit de76db0fc544ea269e39c7d5d7068296ea57f962
Binary file added tests/ibc-hooks/bytecode/counter.wasm
Binary file not shown.
34 changes: 34 additions & 0 deletions tests/ibc-hooks/ibc_middleware_test.go
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"testing"
"time"

wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"

@@ -460,6 +461,39 @@ func (suite *HooksTestSuite) TestAcks() {

}

func (suite *HooksTestSuite) TestTimeouts() {
suite.chainA.StoreContractCode(&suite.Suite, "./bytecode/counter.wasm")
addr := suite.chainA.InstantiateContract(&suite.Suite, `{"count": 0}`, 1)

// Generate swap instructions for the contract
callbackMemo := fmt.Sprintf(`{"ibc_callback":"%s"}`, addr)
// Send IBC transfer with the memo with crosschain-swap instructions
transferMsg := NewMsgTransfer(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000)), suite.chainA.SenderAccount.GetAddress().String(), addr.String(), callbackMemo)
transferMsg.TimeoutTimestamp = uint64(suite.coordinator.CurrentTime.Add(time.Minute).UnixNano())
sendResult, err := suite.chainA.SendMsgsNoCheck(transferMsg)
suite.Require().NoError(err)

packet, err := ibctesting.ParsePacketFromEvents(sendResult.GetEvents())
suite.Require().NoError(err)

// Move chainB forward one block
suite.chainB.NextBlock()
// One month later
suite.coordinator.IncrementTimeBy(time.Hour)
err = suite.path.EndpointA.UpdateClient()
suite.Require().NoError(err)

err = suite.path.EndpointA.TimeoutPacket(packet)
suite.Require().NoError(err)

// The test contract will increment the counter for itself by 10 when a packet times out
state := suite.chainA.QueryContract(
&suite.Suite, addr,
[]byte(fmt.Sprintf(`{"get_count": {"addr": "%s"}}`, addr)))
suite.Require().Equal(`{"count":10}`, state)

}

func (suite *HooksTestSuite) TestSendWithoutMemo() {
// Sending a packet without memo to ensure that the ibc_callback middleware doesn't interfere with a regular send
transferMsg := NewMsgTransfer(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000)), suite.chainA.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), "")
65 changes: 50 additions & 15 deletions x/ibc-hooks/testutils/contracts/counter/src/contract.rs
Original file line number Diff line number Diff line change
@@ -76,25 +76,60 @@ pub mod execute {
}

pub fn reset(deps: DepsMut, info: MessageInfo, count: i32) -> Result<Response, ContractError> {
COUNTERS.update(
deps.storage,
info.sender.clone(),
|state| -> Result<_, ContractError> {
match state {
None => Err(ContractError::Unauthorized {}),
Some(state) if state.owner != info.sender.clone() => {
Err(ContractError::Unauthorized {})
}
_ => Ok(Counter {
count,
total_funds: vec![],
owner: info.sender.clone(),
}),
}
utils::update_counter(deps, info.sender, &|_counter| count, &|_counter| vec![])?;
Ok(Response::new().add_attribute("action", "reset"))
}
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn sudo(deps: DepsMut, env: Env, msg: SudoMsg) -> Result<Response, ContractError> {
match msg {
SudoMsg::ReceiveAck {
channel: _,
sequence: _,
ack: _,
success,
} => sudo::receive_ack(deps, env.contract.address, success),
SudoMsg::IBCTimeout {
channel: _,
sequence: _,
} => sudo::ibc_timeout(deps, env.contract.address),
}
}

pub mod sudo {
use cosmwasm_std::Addr;

use super::*;

pub fn receive_ack(
deps: DepsMut,
contract: Addr,
_success: bool,
) -> Result<Response, ContractError> {
utils::update_counter(
deps,
contract,
&|counter| match counter {
None => 1,
Some(counter) => counter.count + 1,
},
)?;
Ok(Response::new().add_attribute("action", "reset"))
}

pub(crate) fn ibc_timeout(deps: DepsMut, contract: Addr) -> Result<Response, ContractError> {
utils::update_counter(
deps,
contract,
&|counter| match counter {
None => 10,
Some(counter) => counter.count + 10,
},
&|_counter| vec![],
)?;
Ok(Response::new().add_attribute("action", "timeout"))
}
}

pub fn naive_add_coins(lhs: &Vec<Coin>, rhs: &Vec<Coin>) -> Vec<Coin> {
12 changes: 12 additions & 0 deletions x/ibc-hooks/testutils/contracts/counter/src/msg.rs
Original file line number Diff line number Diff line change
@@ -32,3 +32,15 @@ pub struct GetCountResponse {
pub struct GetTotalFundsResponse {
pub total_funds: Vec<Coin>,
}

#[cw_serde]
pub enum SudoMsg {
ReceiveAck {
channel: String,
sequence: u64,
ack: String,
success: bool,
},
#[serde(rename = "ibc_timeout")]
IBCTimeout { channel: String, sequence: u64 },
}
155 changes: 155 additions & 0 deletions x/ibc-hooks/wasm_hook.go
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ import (

wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
sdk "github.com/cosmos/cosmos-sdk/types"
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
ibcexported "github.com/cosmos/ibc-go/v4/modules/core/exported"
@@ -204,3 +205,157 @@ func ValidateAndParseMemo(memo string, receiver string) (isWasmRouted bool, cont

return isWasmRouted, contractAddr, msgBytes, nil
}

func (h WasmHooks) SendPacketOverride(i ICS4Middleware, ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI) error {
concretePacket, ok := packet.(channeltypes.Packet)
if !ok {
return i.channel.SendPacket(ctx, chanCap, packet) // continue
}

isIcs20, data := isIcs20Packet(concretePacket)
if !isIcs20 {
return i.channel.SendPacket(ctx, chanCap, packet) // continue
}

isCallbackRouted, metadata := jsonStringHasKey(data.GetMemo(), types.IBCCallbackKey)
if !isCallbackRouted {
return i.channel.SendPacket(ctx, chanCap, packet) // continue
}

// We remove the callback metadata from the memo as it has already been processed.

// If the only available key in the memo is the callback, we should remove the memo
// from the data completely so the packet is sent without it.
// This way receiver chains that are on old versions of IBC will be able to process the packet

callbackRaw := metadata[types.IBCCallbackKey] // This will be used later.
delete(metadata, types.IBCCallbackKey)
bzMetadata, err := json.Marshal(metadata)
if err != nil {
return sdkerrors.Wrap(err, "Send packet with callback error")
}
stringMetadata := string(bzMetadata)
if stringMetadata == "{}" {
data.Memo = ""
} else {
data.Memo = stringMetadata
}
dataBytes, err := json.Marshal(data)
if err != nil {
return sdkerrors.Wrap(err, "Send packet with callback error")
}

packetWithoutCallbackMemo := channeltypes.Packet{
Sequence: concretePacket.Sequence,
SourcePort: concretePacket.SourcePort,
SourceChannel: concretePacket.SourceChannel,
DestinationPort: concretePacket.DestinationPort,
DestinationChannel: concretePacket.DestinationChannel,
Data: dataBytes,
TimeoutTimestamp: concretePacket.TimeoutTimestamp,
TimeoutHeight: concretePacket.TimeoutHeight,
}

err = i.channel.SendPacket(ctx, chanCap, packetWithoutCallbackMemo)
if err != nil {
return err
}

// Make sure the callback contract is a string and a valid bech32 addr. If it isn't, ignore this packet
contract, ok := callbackRaw.(string)
if !ok {
return nil
}
_, err = sdk.AccAddressFromBech32(contract)
if err != nil {
return nil
}

h.ibcHooksKeeper.StorePacketCallback(ctx, packet.GetSourceChannel(), packet.GetSequence(), contract)
return nil
}

func (h WasmHooks) OnAcknowledgementPacketOverride(im IBCMiddleware, ctx sdk.Context, packet channeltypes.Packet, acknowledgement []byte, relayer sdk.AccAddress) error {
err := im.App.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer)
if err != nil {
return err
}

if !h.ProperlyConfigured() {
// Not configured. Return from the underlying implementation
return nil
}

contract := h.ibcHooksKeeper.GetPacketCallback(ctx, packet.GetSourceChannel(), packet.GetSequence())
if contract == "" {
// No callback configured
return nil
}

contractAddr, err := sdk.AccAddressFromBech32(contract)
if err != nil {
return sdkerrors.Wrap(err, "Ack callback error") // The callback configured is not a bech32. Error out
}

success := "false"
if !osmoutils.IsAckError(acknowledgement) {
success = "true"
}

// Notify the sender that the ack has been received
ackAsJson, err := json.Marshal(acknowledgement)
if err != nil {
// If the ack is not a json object, error
return err
}

sudoMsg := []byte(fmt.Sprintf(
`{"receive_ack": {"channel": "%s", "sequence": %d, "ack": %s, "success": %s}}`,
packet.SourceChannel, packet.Sequence, ackAsJson, success))
_, err = h.ContractKeeper.Sudo(ctx, contractAddr, sudoMsg)
if err != nil {
// error processing the callback
// ToDo: Open Question: Should we also delete the callback here?
return sdkerrors.Wrap(err, "Ack callback error")
}
h.ibcHooksKeeper.DeletePacketCallback(ctx, packet.GetSourceChannel(), packet.GetSequence())
return nil
}

func (h WasmHooks) OnTimeoutPacketOverride(im IBCMiddleware, ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) error {
err := im.App.OnTimeoutPacket(ctx, packet, relayer)
if err != nil {
return err
}

if !h.ProperlyConfigured() {
// Not configured. Return from the underlying implementation
return nil
}

contract := h.ibcHooksKeeper.GetPacketCallback(ctx, packet.GetSourceChannel(), packet.GetSequence())
if contract == "" {
// No callback configured
return nil
}

contractAddr, err := sdk.AccAddressFromBech32(contract)
if err != nil {
return sdkerrors.Wrap(err, "Timeout callback error") // The callback configured is not a bech32. Error out
}

sudoMsg := []byte(fmt.Sprintf(
`{"ibc_timeout": {"channel": "%s", "sequence": %d}}`,
packet.SourceChannel, packet.Sequence))
_, err = h.ContractKeeper.Sudo(ctx, contractAddr, sudoMsg)
if err != nil {
// error processing the callback. This could be because the contract doesn't implement the message type to
// process the callback. Retrying this will not help, so we delete the callback from storage.
// Since the packet has timed out, we don't expect any other responses that may trigger the callback.
h.ibcHooksKeeper.DeletePacketCallback(ctx, packet.GetSourceChannel(), packet.GetSequence())
return sdkerrors.Wrap(err, "Timeout callback error")
}
//
h.ibcHooksKeeper.DeletePacketCallback(ctx, packet.GetSourceChannel(), packet.GetSequence())
return nil
}