Skip to content

Commit

Permalink
Add new Vtransfer module for notifications (#8624)
Browse files Browse the repository at this point in the history
closes: #8583, closes: #9059, closes: #9256

## Description

This PR adds the vTransfer middleware module which acts as a
notification module on the transfer port for contracts that need to act
based on it.

### Security Considerations

As discussed, this does change how inbound assets are handled. 

### Testing Considerations

The middleware module has mock unit tests in ibc_middleware_test.go
  • Loading branch information
mergify[bot] authored Jun 8, 2024
2 parents c19f1b5 + 95d81c2 commit 5a03adc
Show file tree
Hide file tree
Showing 83 changed files with 3,013 additions and 252 deletions.
167 changes: 121 additions & 46 deletions golang/cosmos/app/app.go

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions golang/cosmos/cmd/agd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@ func main() {
var shutdown func() error

nodePort := 1
sendToNode := func(ctx context.Context, needReply bool, str string) (string, error) {
var sendToNode vm.Sender = func(ctx context.Context, needReply bool, jsonRequest string) (jsonReply string, err error) {
if vmClient == nil {
return "", errors.New("sendToVM called without VM client set up")
}

if str == "shutdown" {
if jsonRequest == "shutdown" {
// We could ask nicely, but don't bother.
if shutdown != nil {
return "", shutdown()
Expand All @@ -73,10 +73,10 @@ func main() {
msg := vm.Message{
Port: nodePort,
NeedsReply: needReply,
Data: str,
Data: jsonRequest,
}
var reply string
err := vmClient.Call(vm.ReceiveMessageMethod, msg, &reply)
err = vmClient.Call(vm.ReceiveMessageMethod, msg, &reply)
return reply, err
}

Expand Down
12 changes: 6 additions & 6 deletions golang/cosmos/cmd/libdaemon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,22 @@ var agdServer *vm.AgdServer

// ConnectVMClientCodec creates an RPC client codec and a sender to the
// in-process implementation of the VM.
func ConnectVMClientCodec(ctx context.Context, nodePort int, sendFunc func(int, int, string)) (*vm.ClientCodec, daemoncmd.Sender) {
func ConnectVMClientCodec(ctx context.Context, nodePort int, sendFunc func(int, int, string)) (*vm.ClientCodec, vm.Sender) {
vmClientCodec = vm.NewClientCodec(ctx, sendFunc)
vmClient := rpc.NewClientWithCodec(vmClientCodec)

sendToNode := func(ctx context.Context, needReply bool, str string) (string, error) {
if str == "shutdown" {
var sendToNode vm.Sender = func(ctx context.Context, needReply bool, jsonRequest string) (jsonReply string, err error) {
if jsonRequest == "shutdown" {
return "", vmClientCodec.Close()
}

msg := vm.Message{
Port: nodePort,
NeedsReply: needReply,
Data: str,
Data: jsonRequest,
}
var reply string
err := vmClient.Call(vm.ReceiveMessageMethod, msg, &reply)
err = vmClient.Call(vm.ReceiveMessageMethod, msg, &reply)
return reply, err
}

Expand All @@ -72,7 +72,7 @@ func RunAgCosmosDaemon(nodePort C.int, toNode C.sendFunc, cosmosArgs []*C.char)
panic(err)
}

var sendToNode daemoncmd.Sender
var sendToNode vm.Sender

sendFunc := func(port int, reply int, str string) {
C.invokeSendFunc(toNode, C.int(port), C.int(reply), C.CString(str))
Expand Down
10 changes: 3 additions & 7 deletions golang/cosmos/daemon/cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package cmd

import (
"context"
"errors"
"io"
"os"
Expand Down Expand Up @@ -42,16 +41,13 @@ import (
"github.com/Agoric/agoric-sdk/golang/cosmos/vm"
)

// Sender is a function that sends a request to the controller.
type Sender func(ctx context.Context, needReply bool, str string) (string, error)

var AppName = "agd"
var OnStartHook func(*vm.AgdServer, log.Logger, servertypes.AppOptions) error
var OnExportHook func(*vm.AgdServer, log.Logger, servertypes.AppOptions) error

// NewRootCmd creates a new root command for simd. It is called once in the
// main function.
func NewRootCmd(sender Sender) (*cobra.Command, params.EncodingConfig) {
func NewRootCmd(sender vm.Sender) (*cobra.Command, params.EncodingConfig) {
encodingConfig := gaia.MakeEncodingConfig()
initClientCtx := client.Context{}.
WithCodec(encodingConfig.Marshaler).
Expand Down Expand Up @@ -125,7 +121,7 @@ func initAppConfig() (string, interface{}) {
return serverconfig.DefaultConfigTemplate, *srvCfg
}

func initRootCmd(sender Sender, rootCmd *cobra.Command, encodingConfig params.EncodingConfig) {
func initRootCmd(sender vm.Sender, rootCmd *cobra.Command, encodingConfig params.EncodingConfig) {
cfg := sdk.GetConfig()
cfg.Seal()

Expand Down Expand Up @@ -252,7 +248,7 @@ func txCommand() *cobra.Command {

type appCreator struct {
encCfg params.EncodingConfig
sender Sender
sender vm.Sender
agdServer *vm.AgdServer
}

Expand Down
5 changes: 3 additions & 2 deletions golang/cosmos/daemon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ import (
"github.com/Agoric/agoric-sdk/golang/cosmos/agoric"
app "github.com/Agoric/agoric-sdk/golang/cosmos/app"
"github.com/Agoric/agoric-sdk/golang/cosmos/daemon/cmd"
"github.com/Agoric/agoric-sdk/golang/cosmos/vm"

sdk "github.com/cosmos/cosmos-sdk/types"
)

// DefaultController is a stub controller.
var DefaultController = func(ctx context.Context, needReply bool, str string) (string, error) {
var DefaultController vm.Sender = func(ctx context.Context, needReply bool, jsonRequest string) (jsonReply string, err error) {
return "", fmt.Errorf("Controller not configured; did you mean to use `ag-chain-cosmos` instead?")
}

Expand All @@ -28,7 +29,7 @@ func Run() {
}

// RunWithController starts the app with a custom upcall handler.
func RunWithController(sendToController cmd.Sender) {
func RunWithController(sendToController vm.Sender) {
// Exit on Control-C and kill.
// Without this explicitly, ag-chain-cosmos ignores them.
sigs := make(chan os.Signal, 1)
Expand Down
3 changes: 3 additions & 0 deletions golang/cosmos/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ replace (
// Pick up an IAVL race fix.
github.com/cosmos/iavl => github.com/cosmos/iavl v0.19.7

// Use a version of ibc-go that is compatible with the above forks.
github.com/cosmos/ibc-go/v6 => github.com/agoric-labs/ibc-go/v6 v6.3.1-alpha.agoric.2

// use cometbft
// Use our fork at least until post-v0.34.14 is released with
// https://github.com/tendermint/tendermint/issue/6899 resolved.
Expand Down
4 changes: 2 additions & 2 deletions golang/cosmos/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ github.com/agoric-labs/cosmos-sdk v0.46.16-alpha.agoric.2.4 h1:i5IgChQjTyWulV/y5
github.com/agoric-labs/cosmos-sdk v0.46.16-alpha.agoric.2.4/go.mod h1:d7e4h+w7FNBNmE6ysp6duBVuQg67pqMtvsLwpT9ca3E=
github.com/agoric-labs/cosmos-sdk/ics23/go v0.8.0-alpha.agoric.1 h1:2jvHI/2d+psWAZy6FQ0vXJCHUtfU3ZbbW+pQFL04arQ=
github.com/agoric-labs/cosmos-sdk/ics23/go v0.8.0-alpha.agoric.1/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
github.com/agoric-labs/ibc-go/v6 v6.3.1-alpha.agoric.2 h1:vEzy4JaExzlWNHV3ZSVXEVZcRE9loEFUjieE2TXwDdI=
github.com/agoric-labs/ibc-go/v6 v6.3.1-alpha.agoric.2/go.mod h1:L1xcBjCLIHN7Wd9j6cAQvZertn56pq+eRGFZjRO5bsY=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
Expand Down Expand Up @@ -380,8 +382,6 @@ github.com/cosmos/iavl v0.19.7 h1:ij32FaEnwxfEurtK0QKDNhTWFnz6NUmrI5gky/WnoY0=
github.com/cosmos/iavl v0.19.7/go.mod h1:X9PKD3J0iFxdmgNLa7b2LYWdsGd90ToV5cAONApkEPw=
github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v6 v6.1.2 h1:Hz4nkpStoXIHrC77CIEyu2mRiN2qysGEZPFRf0fpv7w=
github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v6 v6.1.2/go.mod h1:Jo934o/sW7fNxuOa/TjCalSalz+1Fd649eLyANaJx8g=
github.com/cosmos/ibc-go/v6 v6.3.1 h1:/5ur3AsmNW8WuOevfODHlaY5Ze236PBNE3vVo9o3fQA=
github.com/cosmos/ibc-go/v6 v6.3.1/go.mod h1:Dm14j9s094bGyCEE8W4fD+2t8IneHv+cz+80Mvwjr1w=
github.com/cosmos/keyring v1.2.0 h1:8C1lBP9xhImmIabyXW4c3vFjjLiBdGCmfLUfeZlV1Yo=
github.com/cosmos/keyring v1.2.0/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA=
github.com/cosmos/ledger-cosmos-go v0.12.4 h1:drvWt+GJP7Aiw550yeb3ON/zsrgW0jgh5saFCr7pDnw=
Expand Down
18 changes: 18 additions & 0 deletions golang/cosmos/proto/agoric/vtransfer/genesis.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
syntax = "proto3";
package agoric.vtransfer;

import "gogoproto/gogo.proto";

option go_package = "github.com/Agoric/agoric-sdk/golang/cosmos/x/vtransfer/types";

// The initial and exported module state.
message GenesisState {
option (gogoproto.equal) = false;

// The list of account addresses that are being watched by the VM.
repeated bytes watched_addresses = 1 [
(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress",
(gogoproto.jsontag) = "watched_addresses",
(gogoproto.moretags) = "yaml:\"watched_addresses\""
];
}
14 changes: 6 additions & 8 deletions golang/cosmos/vm/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,38 +11,36 @@ import (
"github.com/Agoric/agoric-sdk/golang/cosmos/vm"
)

type Sender func(ctx context.Context, needReply bool, str string) (string, error)

type errorWrapper struct {
Error string `json:"error"`
}

// ConnectVMClientCodec creates an RPC client codec and a sender to the
// in-process implementation of the VM.
func ConnectVMClientCodec(ctx context.Context, nodePort int, sendFunc func(int, int, string)) (*vm.ClientCodec, Sender) {
func ConnectVMClientCodec(ctx context.Context, nodePort int, sendFunc func(int, int, string)) (*vm.ClientCodec, vm.Sender) {
vmClientCodec := vm.NewClientCodec(ctx, sendFunc)
vmClient := rpc.NewClientWithCodec(vmClientCodec)

sendToNode := func(ctx context.Context, needReply bool, str string) (string, error) {
if str == "shutdown" {
sendToNode := func(ctx context.Context, needReply bool, jsonRequest string) (jsonReply string, err error) {
if jsonRequest == "shutdown" {
return "", vmClientCodec.Close()
}

msg := vm.Message{
Port: nodePort,
NeedsReply: needReply,
Data: str,
Data: jsonRequest,
}
var reply string
err := vmClient.Call(vm.ReceiveMessageMethod, msg, &reply)
err = vmClient.Call(vm.ReceiveMessageMethod, msg, &reply)
return reply, err
}

return vmClientCodec, sendToNode
}

type Fixture struct {
SendToNode Sender
SendToNode vm.Sender
SendToGo func(port int, msgStr string) string
ReplyToGo func(replyPort int, isError bool, respStr string) int
}
Expand Down
3 changes: 3 additions & 0 deletions golang/cosmos/vm/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)

// Sender makes a request of our associated VM.
type Sender func(ctx context.Context, needReply bool, jsonRequest string) (jsonReply string, err error)

type ControllerAdmissionMsg interface {
sdk.Msg
CheckAdmissibility(sdk.Context, interface{}) error
Expand Down
2 changes: 2 additions & 0 deletions golang/cosmos/x/swingset/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ type (
Keeper = keeper.Keeper
SwingStoreExportsHandler = keeper.SwingStoreExportsHandler
ExtensionSnapshotter = keeper.ExtensionSnapshotter
ActionContext = types.ActionContext
InboundQueueRecord = types.InboundQueueRecord
Egress = types.Egress
MsgDeliverInbound = types.MsgDeliverInbound
MsgProvision = types.MsgProvision
Expand Down
28 changes: 8 additions & 20 deletions golang/cosmos/x/swingset/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,25 +51,6 @@ const (
swingStoreKeyPrefix = "swingStore."
)

// Contextual information about the message source of an action on an inbound queue.
// This context should be unique per inboundQueueRecord.
type actionContext struct {
// The block height in which the corresponding action was enqueued
BlockHeight int64 `json:"blockHeight"`
// The hash of the cosmos transaction that included the message
// If the action didn't result from a transaction message, a substitute value
// may be used. For example the VBANK_BALANCE_UPDATE actions use `x/vbank`.
TxHash string `json:"txHash"`
// The index of the message within the transaction. If the action didn't
// result from a cosmos transaction, a number should be chosen to make the
// actionContext unique. (for example a counter per block and source module).
MsgIdx int `json:"msgIdx"`
}
type inboundQueueRecord struct {
Action vm.Jsonable `json:"action"`
Context actionContext `json:"context"`
}

// Keeper maintains the link to data vstorage and exposes getter/setter methods for the various parts of the state machine
type Keeper struct {
storeKey storetypes.StoreKey
Expand Down Expand Up @@ -144,7 +125,14 @@ func (k Keeper) pushAction(ctx sdk.Context, inboundQueuePath string, action vm.A
if !txHashOk || !msgIdxOk {
stdlog.Printf("error while extracting context for action %q\n", action)
}
record := inboundQueueRecord{Action: action, Context: actionContext{BlockHeight: ctx.BlockHeight(), TxHash: txHash, MsgIdx: msgIdx}}
record := types.InboundQueueRecord{
Action: action,
Context: types.ActionContext{
BlockHeight: ctx.BlockHeight(),
TxHash: txHash,
MsgIdx: msgIdx,
},
}
bz, err := json.Marshal(record)
if err != nil {
return err
Expand Down
16 changes: 16 additions & 0 deletions golang/cosmos/x/swingset/keeper/test_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package keeper

import (
"testing"

"github.com/Agoric/agoric-sdk/golang/cosmos/x/vstorage"
)

// GetVstorageKeeper returns the vstorage keeper from the swingset keeper
// for testing purposes.
func GetVstorageKeeper(t *testing.T, k Keeper) vstorage.Keeper {
if t == nil {
panic("this function is reserved for testing")
}
return k.vstorageKeeper
}
9 changes: 7 additions & 2 deletions golang/cosmos/x/swingset/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ func (AppModule) Name() string {
return ModuleName
}

// For testing purposes
func (am *AppModule) SetSwingStoreExportDir(dir string) {
am.swingStoreExportDir = dir
}

func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {}

func (am AppModule) Route() sdk.Route {
Expand Down Expand Up @@ -149,9 +154,9 @@ func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.V
return []abci.ValidatorUpdate{}
}

func (am AppModule) checkSwingStoreExportSetup() {
func (am *AppModule) checkSwingStoreExportSetup() {
if am.swingStoreExportDir == "" {
panic(fmt.Errorf("SwingStore export dir not set"))
am.swingStoreExportDir = "/tmp/swingset_export"
}
}

Expand Down
17 changes: 17 additions & 0 deletions golang/cosmos/x/swingset/testing/queue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package testing

import (
"testing"

"github.com/Agoric/agoric-sdk/golang/cosmos/x/swingset/keeper"
vstoragetesting "github.com/Agoric/agoric-sdk/golang/cosmos/x/vstorage/testing"
sdk "github.com/cosmos/cosmos-sdk/types"
)

// GetActionQueueRecords returns the records in the action queue.
// This is a testing utility function.
func GetActionQueueRecords(t *testing.T, ctx sdk.Context, swingsetKeeper keeper.Keeper) ([]string, error) {
vstorageKeeper := keeper.GetVstorageKeeper(t, swingsetKeeper)
actionQueueName := keeper.StoragePathActionQueue
return vstoragetesting.GetQueueItems(ctx, vstorageKeeper, actionQueueName)
}
19 changes: 19 additions & 0 deletions golang/cosmos/x/swingset/types/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,25 @@ var (
_ vm.ControllerAdmissionMsg = &MsgWalletSpendAction{}
)

// Contextual information about the message source of an action on an inbound queue.
// This context should be unique per inboundQueueRecord.
type ActionContext struct {
// The block height in which the corresponding action was enqueued
BlockHeight int64 `json:"blockHeight"`
// The hash of the cosmos transaction that included the message
// If the action didn't result from a transaction message, a substitute value
// may be used. For example the VBANK_BALANCE_UPDATE actions use `x/vbank`.
TxHash string `json:"txHash"`
// The index of the message within the transaction. If the action didn't
// result from a cosmos transaction, a number should be chosen to make the
// actionContext unique. (for example a counter per block and source module).
MsgIdx int `json:"msgIdx"`
}
type InboundQueueRecord struct {
Action vm.Jsonable `json:"action"`
Context ActionContext `json:"context"`
}

const (
// bundleUncompressedSizeLimit is the (exclusive) limit on uncompressed bundle size.
// We must ensure there is an exclusive int64 limit in order to detect an underflow.
Expand Down
2 changes: 0 additions & 2 deletions golang/cosmos/x/vbank/genesis.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package vbank

import (
// "fmt"

"fmt"

"github.com/Agoric/agoric-sdk/golang/cosmos/x/vbank/types"
Expand Down
12 changes: 0 additions & 12 deletions golang/cosmos/x/vbank/types/msgs.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
package types

const RouterKey = ModuleName // this was defined in your key.go file

type VbankSingleBalanceUpdate struct {
Address string `json:"address"`
Denom string `json:"denom"`
Amount string `json:"amount"`
}

type VbankBalanceUpdate struct {
Nonce uint64 `json:"nonce"`
Type string `json:"type"`
Updated []VbankSingleBalanceUpdate `json:"updated"`
}
Loading

0 comments on commit 5a03adc

Please sign in to comment.