From a710d68512cf9983bdf5230e2e99f267521c7210 Mon Sep 17 00:00:00 2001 From: Michael FIG Date: Sat, 30 Dec 2023 22:25:34 -0600 Subject: [PATCH 1/2] feat(cosmos): impose defaults when sending VM actions --- golang/cosmos/vm/action.go | 130 ++++++++++++++++++++++ golang/cosmos/vm/action_test.go | 129 +++++++++++++++++++++ golang/cosmos/vm/controller.go | 6 - golang/cosmos/x/swingset/keeper/keeper.go | 26 ++++- 4 files changed, 281 insertions(+), 10 deletions(-) create mode 100644 golang/cosmos/vm/action.go create mode 100644 golang/cosmos/vm/action_test.go diff --git a/golang/cosmos/vm/action.go b/golang/cosmos/vm/action.go new file mode 100644 index 00000000000..7aaf22c214f --- /dev/null +++ b/golang/cosmos/vm/action.go @@ -0,0 +1,130 @@ +package vm + +import ( + "reflect" + "strconv" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + zeroTime = time.Time{} + actionHeaderType = reflect.TypeOf(ActionHeader{}) + _ Action = &ActionHeader{} +) + +// Jsonable is a value, j, that can be passed through json.Marshal(j). +type Jsonable interface{} + +type Action interface { + GetActionHeader() ActionHeader +} + +// ActionPusher enqueues data for later consumption by the controller. +type ActionPusher func(ctx sdk.Context, action Action) error + +// ActionHeader should be embedded in all actions. It is populated by PopulateAction. +type ActionHeader struct { + // Type defaults to the `actionType:"..."` tag of the embedder's ActionHeader field. + Type string `json:"type"` + // BlockHeight defaults to sdk.Context.BlockHeight(). + BlockHeight int64 `json:"blockHeight,omitempty"` + // BlockTime defaults to sdk.Context.BlockTime().Unix(). + BlockTime int64 `json:"blockTime,omitempty"` +} + +func (ah ActionHeader) GetActionHeader() ActionHeader { + return ah +} + +// SetActionHeaderFromContext provides defaults to an ActionHeader. +func SetActionHeaderFromContext(ctx sdk.Context, actionType string, ah *ActionHeader) { + // Default the action type. + if len(ah.Type) == 0 { + ah.Type = actionType + } + + // Default the block height. + if ah.BlockHeight == 0 { + ah.BlockHeight = ctx.BlockHeight() + } + + if ah.BlockTime == 0 { + // Only default to a non-zero block time. + if blockTime := ctx.BlockTime(); blockTime != zeroTime { + ah.BlockTime = blockTime.Unix() + } + } +} + +// PopulateAction interprets `default:"..."` tags and specially handles an +// embedded ActionHeader struct. +func PopulateAction(ctx sdk.Context, action Action) Action { + oldRv := reflect.Indirect(reflect.ValueOf(action)) + if oldRv.Kind() != reflect.Struct { + return action + } + + // Shallow copy to a new value. + rp := reflect.New(oldRv.Type()) + rv := reflect.Indirect(rp) + for i := 0; i < rv.NumField(); i++ { + oldField := oldRv.Field(i) + field := rv.Field(i) + fieldType := rv.Type().Field(i) + if !field.CanSet() { + continue + } + + // Copy from original. + field.Set(oldField) + + // Populate any ActionHeader struct. + var ahp *ActionHeader + if fieldType.Type == actionHeaderType { + ahp = field.Addr().Interface().(*ActionHeader) + } else if fieldType.Type == reflect.PtrTo(actionHeaderType) { + if field.IsNil() { + ahp = &ActionHeader{} + } else { + ahp = field.Interface().(*ActionHeader) + } + } + if ahp != nil { + actionTypeTag, _ := fieldType.Tag.Lookup("actionType") + ah := *ahp + SetActionHeaderFromContext(ctx, actionTypeTag, &ah) + if field.Kind() == reflect.Ptr { + field.Set(reflect.ValueOf(&ah)) + } else { + field.Set(reflect.ValueOf(ah)) + } + continue + } + + // Still zero value, try default struct field tag. + defaultTag, _ := fieldType.Tag.Lookup("default") + if !field.IsZero() || len(defaultTag) == 0 { + continue + } + + switch field.Kind() { + case reflect.String: + field.SetString(defaultTag) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if val, err := strconv.ParseInt(defaultTag, 0, 64); err == nil { + field.SetInt(val) + } + case reflect.Bool: + if val, err := strconv.ParseBool(defaultTag); err == nil { + field.SetBool(val) + } + case reflect.Float32, reflect.Float64: + if val, err := strconv.ParseFloat(defaultTag, 64); err == nil { + field.SetFloat(val) + } + } + } + return rv.Interface().(Action) +} diff --git a/golang/cosmos/vm/action_test.go b/golang/cosmos/vm/action_test.go new file mode 100644 index 00000000000..bde99144adf --- /dev/null +++ b/golang/cosmos/vm/action_test.go @@ -0,0 +1,129 @@ +package vm_test + +import ( + "encoding/json" + "testing" + "time" + + "github.com/Agoric/agoric-sdk/golang/cosmos/vm" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + _ vm.Action = &Trivial{} + _ vm.Action = &Defaults{} + _ vm.Action = &dataAction{} +) + +type Trivial struct { + vm.ActionHeader + Abc int + def string +} + +type Defaults struct { + String string `default:"abc"` + Int int `default:"123"` + Float float64 `default:"4.56"` + Bool bool `default:"true"` + Any interface{} +} + +func (d Defaults) GetActionHeader() vm.ActionHeader { + return vm.ActionHeader{} +} + +type dataAction struct { + *vm.ActionHeader `actionType:"DATA_ACTION"` + Data []byte +} + +func TestActionContext(t *testing.T) { + emptyCtx := sdk.Context{} + + testCases := []struct { + name string + ctx sdk.Context + in vm.Action + expectedOut interface{} + }{ + {"nil", emptyCtx, nil, nil}, + {"no context", emptyCtx, + &Trivial{Abc: 123, def: "zot"}, + &Trivial{Abc: 123, def: "zot"}, + }, + {"block height", + emptyCtx.WithBlockHeight(998), + &Trivial{Abc: 123, def: "zot"}, + &Trivial{ActionHeader: vm.ActionHeader{BlockHeight: 998}, Abc: 123, def: "zot"}, + }, + {"block time", + emptyCtx.WithBlockTime(time.UnixMicro(1_000_000)), + &Trivial{Abc: 123, def: "zot"}, + &Trivial{Abc: 123, def: "zot", ActionHeader: vm.ActionHeader{BlockTime: 1}}, + }, + {"default tags", + emptyCtx, + &Defaults{}, + &Defaults{"abc", 123, 4.56, true, nil}, + }, + {"data action defaults", + emptyCtx.WithBlockHeight(998).WithBlockTime(time.UnixMicro(1_000_000)), + &dataAction{Data: []byte("hello")}, + &dataAction{Data: []byte("hello"), + ActionHeader: &vm.ActionHeader{Type: "DATA_ACTION", BlockHeight: 998, BlockTime: 1}}, + }, + {"data action override Type", + emptyCtx.WithBlockHeight(998).WithBlockTime(time.UnixMicro(1_000_000)), + &dataAction{Data: []byte("hello2"), + ActionHeader: &vm.ActionHeader{Type: "DATA_ACTION2"}}, + &dataAction{Data: []byte("hello2"), + ActionHeader: &vm.ActionHeader{Type: "DATA_ACTION2", BlockHeight: 998, BlockTime: 1}}, + }, + {"data action override BlockHeight", + emptyCtx.WithBlockHeight(998).WithBlockTime(time.UnixMicro(1_000_000)), + &dataAction{Data: []byte("hello2"), + ActionHeader: &vm.ActionHeader{BlockHeight: 999}}, + &dataAction{Data: []byte("hello2"), + ActionHeader: &vm.ActionHeader{Type: "DATA_ACTION", BlockHeight: 999, BlockTime: 1}}, + }, + {"data action override BlockTime", + emptyCtx.WithBlockHeight(998).WithBlockTime(time.UnixMicro(1_000_000)), + &dataAction{Data: []byte("hello2"), + ActionHeader: &vm.ActionHeader{BlockTime: 2}}, + &dataAction{Data: []byte("hello2"), + ActionHeader: &vm.ActionHeader{Type: "DATA_ACTION", BlockHeight: 998, BlockTime: 2}}, + }, + {"data action override all defaults", + emptyCtx.WithBlockHeight(998).WithBlockTime(time.UnixMicro(1_000_000)), + &dataAction{Data: []byte("hello2"), + ActionHeader: &vm.ActionHeader{Type: "DATA_ACTION2", BlockHeight: 999, BlockTime: 2}}, + &dataAction{Data: []byte("hello2"), + ActionHeader: &vm.ActionHeader{Type: "DATA_ACTION2", BlockHeight: 999, BlockTime: 2}}, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + jstr := func(in interface{}) string { + bz, err := json.Marshal(in) + if err != nil { + t.Fatal(err) + } + return string(bz) + } + jsonIn := jstr(tc.in) + out := vm.PopulateAction(tc.ctx, tc.in) + jsonIn2 := jstr(tc.in) + if jsonIn != jsonIn2 { + t.Errorf("unexpected mutated input: %s to %s", jsonIn, jsonIn2) + } + jsonOut := jstr(out) + jsonExpectedOut := jstr(tc.expectedOut) + if jsonOut != jsonExpectedOut { + t.Errorf("expected %s, got %s", jsonExpectedOut, jsonOut) + } + }) + } +} diff --git a/golang/cosmos/vm/controller.go b/golang/cosmos/vm/controller.go index a414f8fb9ff..36930a4c9b8 100644 --- a/golang/cosmos/vm/controller.go +++ b/golang/cosmos/vm/controller.go @@ -20,12 +20,6 @@ type ControllerAdmissionMsg interface { IsHighPriority(sdk.Context, interface{}) (bool, error) } -// Jsonable is a value, j, that can be passed through json.Marshal(j). -type Jsonable interface{} - -// ActionPusher enqueues data for later consumption by the controller. -type ActionPusher func(ctx sdk.Context, action Jsonable) error - var wrappedEmptySDKContext = sdk.WrapSDKContext( sdk.Context{}.WithContext(context.Background()), ) diff --git a/golang/cosmos/x/swingset/keeper/keeper.go b/golang/cosmos/x/swingset/keeper/keeper.go index 27086661119..339f2643e50 100644 --- a/golang/cosmos/x/swingset/keeper/keeper.go +++ b/golang/cosmos/x/swingset/keeper/keeper.go @@ -110,6 +110,16 @@ func NewKeeper( } } +func populateAction(ctx sdk.Context, action vm.Action) (vm.Action, error) { + action = vm.PopulateAction(ctx, action) + ah := action.GetActionHeader() + if len(ah.Type) == 0 { + return nil, fmt.Errorf("action %q cannot have an empty ActionHeader.Type", action) + } + + return action, nil +} + // pushAction appends an action to the controller's specified inbound queue. // The queue is kept in the kvstore so that changes are properly reverted if the // kvstore is rolled back. By the time the block manager runs, it can commit @@ -118,7 +128,11 @@ func NewKeeper( // // The inbound queue's format is documented by `makeChainQueue` in // `packages/cosmic-swingset/src/helpers/make-queue.js`. -func (k Keeper) pushAction(ctx sdk.Context, inboundQueuePath string, action vm.Jsonable) error { +func (k Keeper) pushAction(ctx sdk.Context, inboundQueuePath string, action vm.Action) error { + action, err := populateAction(ctx, action) + if err != nil { + return err + } txHash, txHashOk := ctx.Context().Value(baseapp.TxHashContextKey).(string) if !txHashOk { txHash = "unknown" @@ -143,12 +157,12 @@ func (k Keeper) pushAction(ctx sdk.Context, inboundQueuePath string, action vm.J } // PushAction appends an action to the controller's actionQueue. -func (k Keeper) PushAction(ctx sdk.Context, action vm.Jsonable) error { +func (k Keeper) PushAction(ctx sdk.Context, action vm.Action) error { return k.pushAction(ctx, StoragePathActionQueue, action) } // PushAction appends an action to the controller's highPriorityQueue. -func (k Keeper) PushHighPriorityAction(ctx sdk.Context, action vm.Jsonable) error { +func (k Keeper) PushHighPriorityAction(ctx sdk.Context, action vm.Action) error { return k.pushAction(ctx, StoragePathHighPriorityQueue, action) } @@ -234,7 +248,11 @@ func (k Keeper) UpdateQueueAllowed(ctx sdk.Context) error { // until the response. It is orthogonal to PushAction, and should only be used // by SwingSet to perform block lifecycle events (BEGIN_BLOCK, END_BLOCK, // COMMIT_BLOCK). -func (k Keeper) BlockingSend(ctx sdk.Context, action vm.Jsonable) (string, error) { +func (k Keeper) BlockingSend(ctx sdk.Context, action vm.Action) (string, error) { + action, err := populateAction(ctx, action) + if err != nil { + return "", err + } bz, err := json.Marshal(action) if err != nil { return "", err From 021fd0f980faf8c0ef4031542e7f3691e202e6f9 Mon Sep 17 00:00:00 2001 From: Michael FIG Date: Sat, 30 Dec 2023 22:17:07 -0600 Subject: [PATCH 2/2] refactor(cosmos): rely on contextual defaults for VM actions --- golang/cosmos/app/app.go | 36 +++-- golang/cosmos/x/lien/keeper/keeper_test.go | 2 +- golang/cosmos/x/swingset/abci.go | 57 ++++---- golang/cosmos/x/swingset/keeper/msg_server.go | 66 +++------ golang/cosmos/x/swingset/keeper/proposal.go | 12 +- golang/cosmos/x/vbank/vbank.go | 15 ++- golang/cosmos/x/vbank/vbank_test.go | 20 +-- golang/cosmos/x/vibc/handler.go | 11 +- golang/cosmos/x/vibc/ibc.go | 126 ++++++------------ 9 files changed, 124 insertions(+), 221 deletions(-) diff --git a/golang/cosmos/app/app.go b/golang/cosmos/app/app.go index 26162e4f671..3e4286a3794 100644 --- a/golang/cosmos/app/app.go +++ b/golang/cosmos/app/app.go @@ -866,18 +866,17 @@ func normalizeModuleAccount(ctx sdk.Context, ak authkeeper.AccountKeeper, name s } type cosmosInitAction struct { - Type string `json:"type"` - ChainID string `json:"chainID"` - BlockTime int64 `json:"blockTime,omitempty"` - IsBootstrap bool `json:"isBootstrap"` - Params swingset.Params `json:"params"` - SupplyCoins sdk.Coins `json:"supplyCoins"` - UpgradePlan *upgradetypes.Plan `json:"upgradePlan,omitempty"` - LienPort int `json:"lienPort"` - StoragePort int `json:"storagePort"` - SwingsetPort int `json:"swingsetPort"` - VbankPort int `json:"vbankPort"` - VibcPort int `json:"vibcPort"` + vm.ActionHeader `actionType:"AG_COSMOS_INIT"` + ChainID string `json:"chainID"` + IsBootstrap bool `json:"isBootstrap"` + Params swingset.Params `json:"params"` + SupplyCoins sdk.Coins `json:"supplyCoins"` + UpgradePlan *upgradetypes.Plan `json:"upgradePlan,omitempty"` + LienPort int `json:"lienPort"` + StoragePort int `json:"storagePort"` + SwingsetPort int `json:"swingsetPort"` + VbankPort int `json:"vbankPort"` + VibcPort int `json:"vibcPort"` } // Name returns the name of the App @@ -900,16 +899,9 @@ func (app *GaiaApp) initController(ctx sdk.Context, bootstrap bool) { app.CheckControllerInited(false) app.controllerInited = true - var blockTime int64 = 0 - if bootstrap || app.upgradePlan != nil { - blockTime = ctx.BlockTime().Unix() - } - // Begin initializing the controller here. action := &cosmosInitAction{ - Type: "AG_COSMOS_INIT", ChainID: ctx.ChainID(), - BlockTime: blockTime, IsBootstrap: bootstrap, Params: app.SwingSetKeeper.GetParams(ctx), SupplyCoins: sdk.NewCoins(app.BankKeeper.GetSupply(ctx, "uist")), @@ -920,7 +912,11 @@ func (app *GaiaApp) initController(ctx sdk.Context, bootstrap bool) { VbankPort: app.vbankPort, VibcPort: app.vibcPort, } - // This really abuses `BlockingSend` to get back at `sendToController` + // This uses `BlockingSend` as a friendly wrapper for `sendToController` + // + // CAVEAT: we are restarting after an in-consensus halt or just because this + // node felt like it. The controller must be able to handle either case + // (inConsensus := action.IsBootstrap || action.UpgradePlan != nil). out, err := app.SwingSetKeeper.BlockingSend(ctx, action) // fmt.Fprintf(os.Stderr, "AG_COSMOS_INIT Returned from SwingSet: %s, %v\n", out, err) diff --git a/golang/cosmos/x/lien/keeper/keeper_test.go b/golang/cosmos/x/lien/keeper/keeper_test.go index f4673661172..31d662fb7df 100644 --- a/golang/cosmos/x/lien/keeper/keeper_test.go +++ b/golang/cosmos/x/lien/keeper/keeper_test.go @@ -112,7 +112,7 @@ func makeTestKit() testKit { sk := stakingkeeper.NewKeeper(cdc, stakingStoreKey, wak, bk, stakingSpace) // lien keeper - pushAction := func(sdk.Context, vm.Jsonable) error { + pushAction := func(sdk.Context, vm.Action) error { return nil } keeper := NewKeeper(cdc, lienStoreKey, wak, bk, sk, pushAction) diff --git a/golang/cosmos/x/swingset/abci.go b/golang/cosmos/x/swingset/abci.go index 5644fb2e8eb..df6e360c472 100644 --- a/golang/cosmos/x/swingset/abci.go +++ b/golang/cosmos/x/swingset/abci.go @@ -10,38 +10,34 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" abci "github.com/tendermint/tendermint/abci/types" + "github.com/Agoric/agoric-sdk/golang/cosmos/vm" "github.com/Agoric/agoric-sdk/golang/cosmos/x/swingset/types" ) type beginBlockAction struct { - Type string `json:"type"` - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` - ChainID string `json:"chainID"` - Params types.Params `json:"params"` + vm.ActionHeader `actionType:"BEGIN_BLOCK"` + ChainID string `json:"chainID"` + Params types.Params `json:"params"` } type endBlockAction struct { - Type string `json:"type"` - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` + vm.ActionHeader `actionType:"END_BLOCK"` } type commitBlockAction struct { - Type string `json:"type"` - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` + vm.ActionHeader `actionType:"COMMIT_BLOCK"` +} + +type afterCommitBlockAction struct { + vm.ActionHeader `actionType:"AFTER_COMMIT_BLOCK"` } func BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock, keeper Keeper) error { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) action := &beginBlockAction{ - Type: "BEGIN_BLOCK", - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), - ChainID: ctx.ChainID(), - Params: keeper.GetParams(ctx), + ChainID: ctx.ChainID(), + Params: keeper.GetParams(ctx), } _, err := keeper.BlockingSend(ctx, action) // fmt.Fprintf(os.Stderr, "BEGIN_BLOCK Returned from SwingSet: %s, %v\n", out, err) @@ -60,11 +56,7 @@ var endBlockTime int64 func EndBlock(ctx sdk.Context, req abci.RequestEndBlock, keeper Keeper) ([]abci.ValidatorUpdate, error) { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker) - action := &endBlockAction{ - Type: "END_BLOCK", - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), - } + action := &endBlockAction{} _, err := keeper.BlockingSend(ctx, action) // fmt.Fprintf(os.Stderr, "END_BLOCK Returned from SwingSet: %s, %v\n", out, err) @@ -81,15 +73,18 @@ func EndBlock(ctx sdk.Context, req abci.RequestEndBlock, keeper Keeper) ([]abci. return []abci.ValidatorUpdate{}, nil } +func getEndBlockContext() sdk.Context { + return sdk.Context{}. + WithContext(context.Background()). + WithBlockHeight(endBlockHeight). + WithBlockTime(time.Unix(endBlockTime, 0)) +} + func CommitBlock(keeper Keeper) error { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), "commit_blocker") - action := &commitBlockAction{ - Type: "COMMIT_BLOCK", - BlockHeight: endBlockHeight, - BlockTime: endBlockTime, - } - _, err := keeper.BlockingSend(sdk.Context{}.WithContext(context.Background()), action) + action := &commitBlockAction{} + _, err := keeper.BlockingSend(getEndBlockContext(), action) // fmt.Fprintf(os.Stderr, "COMMIT_BLOCK Returned from SwingSet: %s, %v\n", out, err) if err != nil { @@ -103,12 +98,8 @@ func CommitBlock(keeper Keeper) error { func AfterCommitBlock(keeper Keeper) error { // defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), "commit_blocker") - action := &commitBlockAction{ - Type: "AFTER_COMMIT_BLOCK", - BlockHeight: endBlockHeight, - BlockTime: endBlockTime, - } - _, err := keeper.BlockingSend(sdk.Context{}.WithContext(context.Background()), action) + action := &afterCommitBlockAction{} + _, err := keeper.BlockingSend(getEndBlockContext(), action) // fmt.Fprintf(os.Stderr, "AFTER_COMMIT_BLOCK Returned from SwingSet: %s, %v\n", out, err) if err != nil { diff --git a/golang/cosmos/x/swingset/keeper/msg_server.go b/golang/cosmos/x/swingset/keeper/msg_server.go index 583a9eb2f70..138a6d6803f 100644 --- a/golang/cosmos/x/swingset/keeper/msg_server.go +++ b/golang/cosmos/x/swingset/keeper/msg_server.go @@ -21,15 +21,13 @@ func NewMsgServerImpl(keeper Keeper) types.MsgServer { var _ types.MsgServer = msgServer{} type deliverInboundAction struct { - Type string `json:"type"` - Peer string `json:"peer"` - Messages [][]interface{} `json:"messages"` - Ack uint64 `json:"ack"` - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` + vm.ActionHeader `actionType:"DELIVER_INBOUND"` + Peer string `json:"peer"` + Messages [][]interface{} `json:"messages"` + Ack uint64 `json:"ack"` } -func (keeper msgServer) routeAction(ctx sdk.Context, msg vm.ControllerAdmissionMsg, action vm.Jsonable) error { +func (keeper msgServer) routeAction(ctx sdk.Context, msg vm.ControllerAdmissionMsg, action vm.Action) error { isHighPriority, err := msg.IsHighPriority(ctx, keeper) if err != nil { return err @@ -51,12 +49,9 @@ func (keeper msgServer) DeliverInbound(goCtx context.Context, msg *types.MsgDeli messages[i] = []interface{}{msg.Nums[i], message} } action := &deliverInboundAction{ - Type: "DELIVER_INBOUND", - Peer: msg.Submitter.String(), - Messages: messages, - Ack: msg.Ack, - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), + Peer: msg.Submitter.String(), + Messages: messages, + Ack: msg.Ack, } err := keeper.routeAction(ctx, msg, action) @@ -68,11 +63,9 @@ func (keeper msgServer) DeliverInbound(goCtx context.Context, msg *types.MsgDeli } type walletAction struct { - Type string `json:"type"` // WALLET_ACTION - Owner string `json:"owner"` - Action string `json:"action"` - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` + vm.ActionHeader `actionType:"WALLET_ACTION"` + Owner string `json:"owner"` + Action string `json:"action"` } func (keeper msgServer) WalletAction(goCtx context.Context, msg *types.MsgWalletAction) (*types.MsgWalletActionResponse, error) { @@ -84,11 +77,8 @@ func (keeper msgServer) WalletAction(goCtx context.Context, msg *types.MsgWallet } action := &walletAction{ - Type: "WALLET_ACTION", - Owner: msg.Owner.String(), - Action: msg.Action, - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), + Owner: msg.Owner.String(), + Action: msg.Action, } // fmt.Fprintf(os.Stderr, "Context is %+v\n", ctx) @@ -101,11 +91,9 @@ func (keeper msgServer) WalletAction(goCtx context.Context, msg *types.MsgWallet } type walletSpendAction struct { - Type string `json:"type"` // WALLET_SPEND_ACTION - Owner string `json:"owner"` - SpendAction string `json:"spendAction"` - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` + vm.ActionHeader `actionType:"WALLET_SPEND_ACTION"` + Owner string `json:"owner"` + SpendAction string `json:"spendAction"` } func (keeper msgServer) WalletSpendAction(goCtx context.Context, msg *types.MsgWalletSpendAction) (*types.MsgWalletSpendActionResponse, error) { @@ -117,11 +105,8 @@ func (keeper msgServer) WalletSpendAction(goCtx context.Context, msg *types.MsgW } action := &walletSpendAction{ - Type: "WALLET_SPEND_ACTION", Owner: msg.Owner.String(), SpendAction: msg.SpendAction, - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), } // fmt.Fprintf(os.Stderr, "Context is %+v\n", ctx) err = keeper.routeAction(ctx, msg, action) @@ -132,11 +117,9 @@ func (keeper msgServer) WalletSpendAction(goCtx context.Context, msg *types.MsgW } type provisionAction struct { + vm.ActionHeader `actionType:"PLEASE_PROVISION"` *types.MsgProvision - Type string `json:"type"` // PLEASE_PROVISION - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` - AutoProvision bool `json:"autoProvision"` + AutoProvision bool `json:"autoProvision"` } // provisionIfNeeded generates a provision action if no smart wallet is already @@ -160,9 +143,6 @@ func (keeper msgServer) provisionIfNeeded(ctx sdk.Context, owner sdk.AccAddress) action := &provisionAction{ MsgProvision: msg, - Type: "PLEASE_PROVISION", - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), AutoProvision: true, } @@ -185,9 +165,6 @@ func (keeper msgServer) Provision(goCtx context.Context, msg *types.MsgProvision action := &provisionAction{ MsgProvision: msg, - Type: "PLEASE_PROVISION", - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), } // Create the account, if it doesn't already exist. @@ -207,10 +184,8 @@ func (keeper msgServer) Provision(goCtx context.Context, msg *types.MsgProvision } type installBundleAction struct { + vm.ActionHeader `actionType:"INSTALL_BUNDLE"` *types.MsgInstallBundle - Type string `json:"type"` // INSTALL_BUNDLE - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` } func (keeper msgServer) InstallBundle(goCtx context.Context, msg *types.MsgInstallBundle) (*types.MsgInstallBundleResponse, error) { @@ -222,9 +197,6 @@ func (keeper msgServer) InstallBundle(goCtx context.Context, msg *types.MsgInsta } action := &installBundleAction{ MsgInstallBundle: msg, - Type: "INSTALL_BUNDLE", - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), } err = keeper.routeAction(ctx, msg, action) diff --git a/golang/cosmos/x/swingset/keeper/proposal.go b/golang/cosmos/x/swingset/keeper/proposal.go index 82d3155b2c4..203a190b6d8 100644 --- a/golang/cosmos/x/swingset/keeper/proposal.go +++ b/golang/cosmos/x/swingset/keeper/proposal.go @@ -3,23 +3,19 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/Agoric/agoric-sdk/golang/cosmos/vm" "github.com/Agoric/agoric-sdk/golang/cosmos/x/swingset/types" ) type coreEvalAction struct { - Type string `json:"type"` // CORE_EVAL - Evals []types.CoreEval `json:"evals"` - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` + vm.ActionHeader `actionType:"CORE_EVAL"` + Evals []types.CoreEval `json:"evals"` } // CoreEvalProposal tells SwingSet to evaluate the given JS code. func (k Keeper) CoreEvalProposal(ctx sdk.Context, p *types.CoreEvalProposal) error { action := &coreEvalAction{ - Type: "CORE_EVAL", - Evals: p.Evals, - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), + Evals: p.Evals, } return k.PushHighPriorityAction(ctx, action) diff --git a/golang/cosmos/x/vbank/vbank.go b/golang/cosmos/x/vbank/vbank.go index 974d44df217..056ffc307d6 100644 --- a/golang/cosmos/x/vbank/vbank.go +++ b/golang/cosmos/x/vbank/vbank.go @@ -68,15 +68,15 @@ func (vbu vbankManyBalanceUpdates) Swap(i int, j int) { } type vbankBalanceUpdate struct { - Nonce uint64 `json:"nonce"` - Type string `json:"type"` - Updated vbankManyBalanceUpdates `json:"updated"` + vm.ActionHeader `actionType:"VBANK_BALANCE_UPDATE"` + Nonce uint64 `json:"nonce"` + Updated vbankManyBalanceUpdates `json:"updated"` } // getBalanceUpdate returns a bridge message containing the current bank balance // for the given addresses each for the specified denominations. Coins are used // only to track the set of denoms, not for the particular nonzero amounts. -func getBalanceUpdate(ctx sdk.Context, keeper Keeper, addressToUpdate map[string]sdk.Coins) vm.Jsonable { +func getBalanceUpdate(ctx sdk.Context, keeper Keeper, addressToUpdate map[string]sdk.Coins) vm.Action { nentries := len(addressToUpdate) if nentries == 0 { return nil @@ -84,7 +84,6 @@ func getBalanceUpdate(ctx sdk.Context, keeper Keeper, addressToUpdate map[string nonce := keeper.GetNextSequence(ctx) event := vbankBalanceUpdate{ - Type: "VBANK_BALANCE_UPDATE", Nonce: nonce, Updated: make([]vbankSingleBalanceUpdate, 0, nentries), } @@ -111,7 +110,9 @@ func getBalanceUpdate(ctx sdk.Context, keeper Keeper, addressToUpdate map[string // Ensure we have a deterministic order of updates. sort.Sort(event.Updated) - return event + + // Populate the event default fields (even though event does not embed vm.ActionHeader) + return vm.PopulateAction(ctx, event) } func marshal(event vm.Jsonable) ([]byte, error) { @@ -243,7 +244,7 @@ func (ch portHandler) Receive(cctx context.Context, str string) (ret string, err return } -func (am AppModule) PushAction(ctx sdk.Context, action vm.Jsonable) error { +func (am AppModule) PushAction(ctx sdk.Context, action vm.Action) error { // vbank actions are not triggered by a swingset message in a transaction, so we need to // synthesize unique context information. // We use a fixed placeholder value for the txHash context, and can simply use `0` for the diff --git a/golang/cosmos/x/vbank/vbank_test.go b/golang/cosmos/x/vbank/vbank_test.go index e992f255c8e..978be05d774 100644 --- a/golang/cosmos/x/vbank/vbank_test.go +++ b/golang/cosmos/x/vbank/vbank_test.go @@ -250,7 +250,7 @@ func (b *mockBank) SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, re func makeTestKit(account types.AccountKeeper, bank types.BankKeeper) (Keeper, sdk.Context) { encodingConfig := params.MakeEncodingConfig() cdc := encodingConfig.Marshaler - pushAction := func(ctx sdk.Context, action vm.Jsonable) error { + pushAction := func(ctx sdk.Context, action vm.Action) error { return nil } @@ -517,16 +517,16 @@ func Test_EndBlock_Events(t *testing.T) { }} acct := &mockAuthKeeper{ accounts: map[string]authtypes.AccountI{ - addr1: &authtypes.ModuleAccount{BaseAccount: &authtypes.BaseAccount{ Address: addr1 }}, - addr2: &authtypes.ModuleAccount{BaseAccount: &authtypes.BaseAccount{ Address: addr2 }}, - addr3: &authtypes.BaseAccount{ Address: addr3 }, + addr1: &authtypes.ModuleAccount{BaseAccount: &authtypes.BaseAccount{Address: addr1}}, + addr2: &authtypes.ModuleAccount{BaseAccount: &authtypes.BaseAccount{Address: addr2}}, + addr3: &authtypes.BaseAccount{Address: addr3}, }, } keeper, ctx := makeTestKit(acct, bank) // Turn off rewards. keeper.SetParams(ctx, types.Params{PerEpochRewardFraction: sdk.ZeroDec()}) msgsSent := []string{} - keeper.PushAction = func(ctx sdk.Context, action vm.Jsonable) error { + keeper.PushAction = func(ctx sdk.Context, action vm.Action) error { bz, err := json.Marshal(action) if err != nil { return err @@ -630,7 +630,7 @@ func Test_EndBlock_Rewards(t *testing.T) { } keeper, ctx := makeTestKit(nil, bank) msgsSent := []string{} - keeper.PushAction = func(ctx sdk.Context, action vm.Jsonable) error { + keeper.PushAction = func(ctx sdk.Context, action vm.Action) error { bz, err := json.Marshal(action) if err != nil { return err @@ -746,7 +746,7 @@ func Test_EndBlock_Rewards(t *testing.T) { } } -type mockAuthKeeper struct{ +type mockAuthKeeper struct { accounts map[string]authtypes.AccountI modAddrs map[string]string } @@ -754,11 +754,11 @@ type mockAuthKeeper struct{ func (ma mockAuthKeeper) GetModuleAccount(ctx sdk.Context, name string) authtypes.ModuleAccountI { addr, ok := ma.modAddrs[name] if !ok { - return nil + return nil } acct, ok := ma.accounts[addr] if !ok { - panic("missing module account") + panic("missing module account") } return acct.(authtypes.ModuleAccountI) } @@ -778,7 +778,7 @@ func Test_Module_Account(t *testing.T) { acct := &mockAuthKeeper{ accounts: map[string]authtypes.AccountI{ moduleBech32: authtypes.NewEmptyModuleAccount("vbank/reserve"), - addr1: authtypes.NewBaseAccountWithAddress(sdk.MustAccAddressFromBech32(addr1)), + addr1: authtypes.NewBaseAccountWithAddress(sdk.MustAccAddressFromBech32(addr1)), }, modAddrs: map[string]string{ "vbank/reserve": moduleBech32, diff --git a/golang/cosmos/x/vibc/handler.go b/golang/cosmos/x/vibc/handler.go index 5f22a103d6e..74cc0da6ff0 100644 --- a/golang/cosmos/x/vibc/handler.go +++ b/golang/cosmos/x/vibc/handler.go @@ -3,6 +3,7 @@ package vibc import ( "fmt" + "github.com/Agoric/agoric-sdk/golang/cosmos/vm" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) @@ -23,10 +24,8 @@ func NewHandler(keeper Keeper) sdk.Handler { type sendPacketAction struct { *MsgSendPacket - Type string `json:"type"` // IBC_EVENT - Event string `json:"event"` // sendPacket - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` + vm.ActionHeader `actionType:"IBC_EVENT"` + Event string `json:"event" default:"sendPacket"` } func handleMsgSendPacket(ctx sdk.Context, keeper Keeper, msg *MsgSendPacket) (*sdk.Result, error) { @@ -41,10 +40,6 @@ func handleMsgSendPacket(ctx sdk.Context, keeper Keeper, msg *MsgSendPacket) (*s action := &sendPacketAction{ MsgSendPacket: msg, - Type: "IBC_EVENT", - Event: "sendPacket", - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), } // fmt.Fprintf(os.Stderr, "Context is %+v\n", ctx) diff --git a/golang/cosmos/x/vibc/ibc.go b/golang/cosmos/x/vibc/ibc.go index 868933c113a..d65bb61f200 100644 --- a/golang/cosmos/x/vibc/ibc.go +++ b/golang/cosmos/x/vibc/ibc.go @@ -153,7 +153,7 @@ func (ch IBCModule) Receive(cctx context.Context, str string) (ret string, err e return } -func (im IBCModule) PushAction(ctx sdk.Context, action vm.Jsonable) error { +func (im IBCModule) PushAction(ctx sdk.Context, action vm.Action) error { // fmt.Println("ibc.go upcall", send) return im.keeper.PushAction(ctx, action) // fmt.Println("ibc.go upcall reply", reply, err) @@ -177,16 +177,14 @@ func (im IBCModule) OnChanOpenInit( } type channelOpenTryEvent struct { - Type string `json:"type"` // IBC - Event string `json:"event"` // channelOpenTry - Order string `json:"order"` - ConnectionHops []string `json:"connectionHops"` - PortID string `json:"portID"` - ChannelID string `json:"channelID"` - Counterparty channeltypes.Counterparty `json:"counterparty"` - Version string `json:"version"` - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` + vm.ActionHeader `actionType:"IBC_EVENT"` + Event string `json:"event" default:"channelOpenTry"` + Order string `json:"order"` + ConnectionHops []string `json:"connectionHops"` + PortID string `json:"portID"` + ChannelID string `json:"channelID"` + Counterparty channeltypes.Counterparty `json:"counterparty"` + Version string `json:"version"` } func (im IBCModule) OnChanOpenTry( @@ -200,16 +198,12 @@ func (im IBCModule) OnChanOpenTry( counterpartyVersion string, ) (string, error) { event := channelOpenTryEvent{ - Type: "IBC_EVENT", - Event: "channelOpenTry", Order: orderToString(order), ConnectionHops: connectionHops, PortID: portID, ChannelID: channelID, Counterparty: counterparty, Version: counterpartyVersion, // TODO: don't just use the counterparty version - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), } err := im.PushAction(ctx, event) @@ -226,15 +220,13 @@ func (im IBCModule) OnChanOpenTry( } type channelOpenAckEvent struct { - Type string `json:"type"` // IBC - Event string `json:"event"` // channelOpenAck + vm.ActionHeader `actionType:"IBC_EVENT"` + Event string `json:"event" default:"channelOpenAck"` PortID string `json:"portID"` ChannelID string `json:"channelID"` CounterpartyVersion string `json:"counterpartyVersion"` Counterparty channeltypes.Counterparty `json:"counterparty"` ConnectionHops []string `json:"connectionHops"` - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` } func (im IBCModule) OnChanOpenAck( @@ -250,27 +242,21 @@ func (im IBCModule) OnChanOpenAck( channel.Counterparty.ChannelId = counterpartyChannelID event := channelOpenAckEvent{ - Type: "IBC_EVENT", - Event: "channelOpenAck", PortID: portID, ChannelID: channelID, CounterpartyVersion: counterpartyVersion, Counterparty: channel.Counterparty, ConnectionHops: channel.ConnectionHops, - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), } return im.PushAction(ctx, event) } type channelOpenConfirmEvent struct { - Type string `json:"type"` // IBC - Event string `json:"event"` // channelOpenConfirm - PortID string `json:"portID"` - ChannelID string `json:"channelID"` - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` + vm.ActionHeader `actionType:"IBC_EVENT"` + Event string `json:"event" default:"channelOpenConfirm"` + PortID string `json:"portID"` + ChannelID string `json:"channelID"` } func (im IBCModule) OnChanOpenConfirm( @@ -279,24 +265,18 @@ func (im IBCModule) OnChanOpenConfirm( channelID string, ) error { event := channelOpenConfirmEvent{ - Type: "IBC_EVENT", - Event: "channelOpenConfirm", - PortID: portID, - ChannelID: channelID, - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), + PortID: portID, + ChannelID: channelID, } return im.PushAction(ctx, event) } type channelCloseInitEvent struct { - Type string `json:"type"` // IBC - Event string `json:"event"` // channelCloseInit - PortID string `json:"portID"` - ChannelID string `json:"channelID"` - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` + vm.ActionHeader `actionType:"IBC_EVENT"` + Event string `json:"event" default:"channelCloseInit"` + PortID string `json:"portID"` + ChannelID string `json:"channelID"` } func (im IBCModule) OnChanCloseInit( @@ -305,12 +285,8 @@ func (im IBCModule) OnChanCloseInit( channelID string, ) error { event := channelCloseInitEvent{ - Type: "IBC_EVENT", - Event: "channelCloseInit", - PortID: portID, - ChannelID: channelID, - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), + PortID: portID, + ChannelID: channelID, } err := im.PushAction(ctx, event) @@ -318,12 +294,10 @@ func (im IBCModule) OnChanCloseInit( } type channelCloseConfirmEvent struct { - Type string `json:"type"` // IBC - Event string `json:"event"` // channelCloseConfirm - PortID string `json:"portID"` - ChannelID string `json:"channelID"` - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` + vm.ActionHeader `actionType:"IBC_EVENT"` + Event string `json:"event" default:"channelCloseConfirm"` + PortID string `json:"portID"` + ChannelID string `json:"channelID"` } func (im IBCModule) OnChanCloseConfirm( @@ -332,12 +306,8 @@ func (im IBCModule) OnChanCloseConfirm( channelID string, ) error { event := channelCloseConfirmEvent{ - Type: "IBC_EVENT", - Event: "channelCloseConfirm", - PortID: portID, - ChannelID: channelID, - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), + PortID: portID, + ChannelID: channelID, } err := im.PushAction(ctx, event) @@ -345,11 +315,9 @@ func (im IBCModule) OnChanCloseConfirm( } type receivePacketEvent struct { - Type string `json:"type"` // IBC - Event string `json:"event"` // receivePacket - Packet channeltypes.Packet `json:"packet"` - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` + vm.ActionHeader `actionType:"IBC_EVENT"` + Event string `json:"event" default:"receivePacket"` + Packet channeltypes.Packet `json:"packet"` } func (im IBCModule) OnRecvPacket( @@ -366,11 +334,7 @@ func (im IBCModule) OnRecvPacket( // the same packets. event := receivePacketEvent{ - Type: "IBC_EVENT", - Event: "receivePacket", - Packet: packet, - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), + Packet: packet, } err := im.PushAction(ctx, event) @@ -382,12 +346,10 @@ func (im IBCModule) OnRecvPacket( } type acknowledgementPacketEvent struct { - Type string `json:"type"` // IBC - Event string `json:"event"` // acknowledgementPacket + vm.ActionHeader `actionType:"IBC_EVENT"` + Event string `json:"event" default:"acknowledgementPacket"` Packet channeltypes.Packet `json:"packet"` Acknowledgement []byte `json:"acknowledgement"` - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` } func (im IBCModule) OnAcknowledgementPacket( @@ -397,12 +359,8 @@ func (im IBCModule) OnAcknowledgementPacket( relayer sdk.AccAddress, ) error { event := acknowledgementPacketEvent{ - Type: "IBC_EVENT", - Event: "acknowledgementPacket", Packet: packet, Acknowledgement: acknowledgement, - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), } err := im.PushAction(ctx, event) @@ -414,11 +372,9 @@ func (im IBCModule) OnAcknowledgementPacket( } type timeoutPacketEvent struct { - Type string `json:"type"` // IBC - Event string `json:"event"` // timeoutPacket - Packet channeltypes.Packet `json:"packet"` - BlockHeight int64 `json:"blockHeight"` - BlockTime int64 `json:"blockTime"` + vm.ActionHeader `actionType:"IBC_EVENT"` + Event string `json:"event" default:"timeoutPacket"` + Packet channeltypes.Packet `json:"packet"` } func (im IBCModule) OnTimeoutPacket( @@ -427,11 +383,7 @@ func (im IBCModule) OnTimeoutPacket( relayer sdk.AccAddress, ) error { event := timeoutPacketEvent{ - Type: "IBC_EVENT", - Event: "timeoutPacket", - Packet: packet, - BlockHeight: ctx.BlockHeight(), - BlockTime: ctx.BlockTime().Unix(), + Packet: packet, } err := im.PushAction(ctx, event)