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

feat: Support publishing source bundles to chain and installing hash bundles to Zoe #4975

Merged
merged 17 commits into from
May 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 19 additions & 0 deletions golang/cosmos/proto/agoric/swingset/msgs.proto
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ option go_package = "github.com/Agoric/agoric-sdk/golang/cosmos/x/swingset/types

// Transactions.
service Msg {
// Install a JavaScript sources bundle on the chain's SwingSet controller.
rpc InstallBundle(MsgInstallBundle) returns (MsgInstallBundleResponse);
// Send inbound messages.
rpc DeliverInbound(MsgDeliverInbound) returns (MsgDeliverInboundResponse);
// Perform a low-privilege wallet action.
Expand Down Expand Up @@ -108,3 +110,20 @@ message MsgProvision {

// MsgProvisionResponse is an empty reply.
message MsgProvisionResponse {}

// MsgInstallBundle carries a signed bundle to SwingSet.
message MsgInstallBundle {
string bundle = 1 [
(gogoproto.jsontag) = "bundle",
(gogoproto.moretags) = "yaml:\"bundle\""
];
bytes submitter = 2 [
(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress",
(gogoproto.jsontag) = "submitter",
(gogoproto.moretags) = "yaml:\"submitter\""
];
}

// MsgInstallBundleResponse is an empty acknowledgement that an install bundle
// message has been queued for the SwingSet kernel's consideration.
message MsgInstallBundleResponse {}
43 changes: 43 additions & 0 deletions golang/cosmos/x/swingset/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func GetTxCmd(storeKey string) *cobra.Command {
swingsetTxCmd.AddCommand(
GetCmdDeliver(),
GetCmdProvisionOne(),
GetCmdInstallBundle(),
)

return swingsetTxCmd
Expand Down Expand Up @@ -85,6 +86,48 @@ func GetCmdDeliver() *cobra.Command {
return cmd
}

// GetCmdInstallBundle is the CLI command for constructing or sending an
// InstallBundle message in a transaction.
func GetCmdInstallBundle() *cobra.Command {
cmd := &cobra.Command{
Use: "install-bundle <JSON>/@<FILE>/-",
Args: cobra.ExactArgs(1),

RunE: func(cmd *cobra.Command, args []string) error {
cctx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

jsonIn := args[0]
if jsonIn[0] == '@' {
fname := args[0][1:]
if fname == "-" {
// Reading from stdin.
if _, err := fmt.Scanln(&jsonIn); err != nil {
return err
}
} else {
jsonBytes, err := ioutil.ReadFile(fname)
if err != nil {
return err
}
jsonIn = string(jsonBytes)
}
}

msg := types.NewMsgInstallBundle(jsonIn, cctx.GetFromAddress())
if err := msg.ValidateBasic(); err != nil {
return err
}

return tx.GenerateOrBroadcastTxCLI(cctx, cmd.Flags(), msg)
},
}
flags.AddTxFlagsToCmd(cmd)
return cmd
}

// GetCmdProvision is the CLI command for sending a Provision transaction
func GetCmdProvisionOne() *cobra.Command {
cmd := &cobra.Command{
Expand Down
26 changes: 26 additions & 0 deletions golang/cosmos/x/swingset/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,29 @@ func (keeper msgServer) Provision(goCtx context.Context, msg *types.MsgProvision

return &types.MsgProvisionResponse{}, nil
}

type installBundleAction struct {
*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) {
ctx := sdk.UnwrapSDKContext(goCtx)

action := &installBundleAction{
MsgInstallBundle: msg,
Type: "INSTALL_BUNDLE",
BlockHeight: ctx.BlockHeight(),
BlockTime: ctx.BlockTime().Unix(),
}

err := keeper.PushAction(ctx, action)
// fmt.Fprintln(os.Stderr, "Returned from SwingSet", out, err)
if err != nil {
return nil, err
}

return &types.MsgInstallBundleResponse{}, nil
}
30 changes: 30 additions & 0 deletions golang/cosmos/x/swingset/types/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const RouterKey = ModuleName // this was defined in your key.go file
var (
_ sdk.Msg = &MsgDeliverInbound{}
_ sdk.Msg = &MsgProvision{}
_ sdk.Msg = &MsgInstallBundle{}
_ sdk.Msg = &MsgWalletAction{}
_ sdk.Msg = &MsgWalletSpendAction{}

Expand Down Expand Up @@ -183,3 +184,32 @@ func (msg MsgProvision) GetSignBytes() []byte {
func (msg MsgProvision) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Submitter}
}

func NewMsgInstallBundle(bundleJson string, submitter sdk.AccAddress) *MsgInstallBundle {
return &MsgInstallBundle{
Bundle: bundleJson,
Submitter: submitter,
}
}

// Route should return the name of the module
func (msg MsgInstallBundle) Route() string { return RouterKey }

// Type should return the action
func (msg MsgInstallBundle) Type() string { return "installBundle" }

// ValidateBasic runs stateless checks on the message
func (msg MsgInstallBundle) ValidateBasic() error {
if msg.Submitter.Empty() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "Submitter address cannot be empty")
}
if len(msg.Bundle) == 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing it's not sufficiently convenient+useful to check e.g. JSON-shape, expected keys, base64-ness, or deeper?

I suppose the only benefit of additional checks would be that I might pay for an installation of something bogus, and get it into a block, only to get rejected after that block was executed by validators.

Eventually I think we want to do more extensive checks here to prevent abuse of blocks as a storage mechanism, catch the misshapen junk before it can get added to a block or flooded to all nodes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe keeping the bundle format opaque at this layer keeps with the spirit of Michael’s intention for the type to evolve freely.

return sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "Bundle cannot be empty")
}
return nil
}

// GetSigners defines whose signature is required
func (msg MsgInstallBundle) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Submitter}
}
Loading