@agoric/cosmos 0.34.2-dev-5fcf452.0 → 0.34.2-dev-d355030.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/app/app.go CHANGED
@@ -1,6 +1,7 @@
1
1
  package gaia
2
2
 
3
3
  import (
4
+ "context"
4
5
  "encoding/json"
5
6
  "fmt"
6
7
  "io"
@@ -287,7 +288,7 @@ func NewGaiaApp(
287
288
  appOpts servertypes.AppOptions,
288
289
  baseAppOptions ...func(*baseapp.BaseApp),
289
290
  ) *GaiaApp {
290
- defaultController := func(needReply bool, str string) (string, error) {
291
+ defaultController := func(ctx context.Context, needReply bool, str string) (string, error) {
291
292
  fmt.Fprintln(os.Stderr, "FIXME: Would upcall to controller with", str)
292
293
  return "", nil
293
294
  }
@@ -299,7 +300,7 @@ func NewGaiaApp(
299
300
  }
300
301
 
301
302
  func NewAgoricApp(
302
- sendToController func(bool, string) (string, error),
303
+ sendToController func(context.Context, bool, string) (string, error),
303
304
  logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, skipUpgradeHeights map[int64]bool,
304
305
  homePath string, invCheckPeriod uint, encodingConfig gaiaappparams.EncodingConfig, appOpts servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp),
305
306
  ) *GaiaApp {
@@ -449,7 +450,7 @@ func NewAgoricApp(
449
450
  app.CheckControllerInited(true)
450
451
  // We use SwingSet-level metering to charge the user for the call.
451
452
  defer vm.SetControllerContext(ctx)()
452
- return sendToController(true, str)
453
+ return sendToController(sdk.WrapSDKContext(ctx), true, str)
453
454
  }
454
455
 
455
456
  setBootstrapNeeded := func() {
@@ -481,7 +482,7 @@ func NewAgoricApp(
481
482
  if err != nil {
482
483
  return "", err
483
484
  }
484
- return sendToController(true, string(bz))
485
+ return sendToController(context.Background(), true, string(bz))
485
486
  },
486
487
  )
487
488
 
@@ -0,0 +1,42 @@
1
+ package main
2
+
3
+ import (
4
+ "fmt"
5
+ "os"
6
+ "os/exec"
7
+
8
+ "github.com/tendermint/tendermint/libs/log"
9
+ )
10
+
11
+ // NewVMCommand creates a new OS command to run the Agoric VM. It sets up the
12
+ // file descriptors for the VM to communicate with agd, and passes their numbers
13
+ // via AGVM_FROM_AGD and AGVM_TO_AGD environment variables.
14
+ func NewVMCommand(logger log.Logger, binary string, args []string, vmFromAgd, vmToAgd *os.File) *exec.Cmd {
15
+ logger.Info("agd connecting to VM", "binary", binary, "args", args)
16
+ cmd := exec.Command(binary, args[1:]...)
17
+
18
+ // Manage the file descriptors.
19
+ cmd.Stdout = os.Stdout
20
+ cmd.Stderr = os.Stderr
21
+ cmd.Stdin = os.Stdin
22
+ numStdFiles := 3 // stdin, stdout, stderr
23
+
24
+ // We start our file descriptor allocations after the standard ones, including
25
+ // Node.js IPC (fd=3).
26
+ fdFromAgd := 4
27
+ fdToAgd := fdFromAgd + 1
28
+
29
+ // ExtraFiles begins at fd numStdFiles, so we need to compute the array.
30
+ cmd.ExtraFiles = make([]*os.File, fdToAgd - numStdFiles + 1)
31
+ cmd.ExtraFiles[fdFromAgd - numStdFiles] = vmFromAgd
32
+ cmd.ExtraFiles[fdToAgd - numStdFiles] = vmToAgd
33
+
34
+ // Pass the file descriptor numbers in the environment.
35
+ cmd.Env = append(
36
+ os.Environ(),
37
+ fmt.Sprintf("AGVM_FROM_AGD=%d", fdFromAgd),
38
+ fmt.Sprintf("AGVM_TO_AGD=%d", fdToAgd),
39
+ )
40
+
41
+ return cmd
42
+ }
package/cmd/agd/main.go CHANGED
@@ -1,36 +1,158 @@
1
1
  package main
2
2
 
3
3
  import (
4
+ "context"
5
+ "errors"
6
+ "net/rpc"
7
+ "net/rpc/jsonrpc"
4
8
  "os"
9
+ "os/exec"
5
10
  "syscall"
11
+ "time"
6
12
 
13
+ "github.com/spf13/cast"
7
14
  "github.com/tendermint/tendermint/libs/log"
8
15
 
9
16
  gaia "github.com/Agoric/agoric-sdk/golang/cosmos/app"
10
17
  "github.com/Agoric/agoric-sdk/golang/cosmos/daemon"
11
18
  daemoncmd "github.com/Agoric/agoric-sdk/golang/cosmos/daemon/cmd"
19
+ "github.com/Agoric/agoric-sdk/golang/cosmos/vm"
20
+ "github.com/Agoric/agoric-sdk/golang/cosmos/vm/jsonrpcconn"
21
+ servertypes "github.com/cosmos/cosmos-sdk/server/types"
12
22
  )
13
23
 
24
+ // TerminateSubprocessGracePeriod is how long we wait between closing the pipe
25
+ // waiting for it to exit, then sending a termination signal.
26
+ const TerminateSubprocessGracePeriod = 3 * time.Second
27
+
28
+ // KillSubprocessGracePeriod is how long we wait between sending a subprocess a
29
+ // termination signal, waiting for it to exit, then killing it.
30
+ const KillSubprocessGracePeriod = 5 * time.Second
31
+
32
+ // makeShutdown returns a function that terminates the vm.
33
+ func makeShutdown(cmd *exec.Cmd, writer *os.File) func() error {
34
+ return func() error {
35
+ // Stop talking to the subprocess.
36
+ _ = writer.Close()
37
+ go func() {
38
+ // Wait a bit.
39
+ time.Sleep(TerminateSubprocessGracePeriod)
40
+ // Then punch it in the shoulder.
41
+ _ = cmd.Process.Signal(os.Interrupt)
42
+ // Wait a bit.
43
+ time.Sleep(KillSubprocessGracePeriod)
44
+ // Then blow it away.
45
+ _ = cmd.Process.Kill()
46
+ }()
47
+ // Wait for it to keel over.
48
+ return cmd.Wait()
49
+ }
50
+ }
51
+
52
+
53
+ // main is the entry point of the agd daemon. It determines whether to
54
+ // initialize JSON-RPC communications with the separate `--split-vm` VM process,
55
+ // or just to give up control entirely to another binary.
14
56
  func main() {
15
- // We need to delegate to our default app for running the actual chain.
16
- launchVM := func(logger log.Logger) {
57
+ var vmClient *rpc.Client
58
+ var shutdown func() error
59
+
60
+ nodePort := 1
61
+ sendToNode := func(ctx context.Context, needReply bool, str string) (string, error) {
62
+ if vmClient == nil {
63
+ return "", errors.New("sendToVM called without VM client set up")
64
+ }
65
+
66
+ if str == "shutdown" {
67
+ // We could ask nicely, but don't bother.
68
+ if shutdown != nil {
69
+ return "", shutdown()
70
+ }
71
+ return "", nil
72
+ }
73
+
74
+ msg := vm.Message{
75
+ Port: nodePort,
76
+ NeedsReply: needReply,
77
+ Data: str,
78
+ }
79
+ var reply string
80
+ err := vmClient.Call(vm.ReceiveMessageMethod, msg, &reply)
81
+ return reply, err
82
+ }
83
+
84
+ exitCode := 0
85
+ launchVM := func(logger log.Logger, appOpts servertypes.AppOptions) error {
17
86
  args := []string{"ag-chain-cosmos", "--home", gaia.DefaultNodeHome}
18
87
  args = append(args, os.Args[1:]...)
19
88
 
20
- binary, lookErr := FindCosmicSwingsetBinary()
21
- if lookErr != nil {
22
- panic(lookErr)
89
+ binary := cast.ToString(appOpts.Get(daemoncmd.FlagSplitVm))
90
+ if binary == "" {
91
+ binary, lookErr := FindCosmicSwingsetBinary()
92
+ if lookErr != nil {
93
+ return lookErr
94
+ }
95
+
96
+ // We completely delegate to our default app for running the actual chain.
97
+ logger.Info("agd delegating to JS executable", "binary", binary, "args", args)
98
+ return syscall.Exec(binary, args, os.Environ())
99
+ }
100
+
101
+ // Split the execution between us and the VM.
102
+ agdFromVm, vmToAgd, err := os.Pipe()
103
+ if err != nil {
104
+ return err
105
+ }
106
+ vmFromAgd, agdToVm, err := os.Pipe()
107
+ if err != nil {
108
+ return err
109
+ }
110
+
111
+ // Start the command running, then continue.
112
+ args[0] = binary
113
+ cmd := NewVMCommand(logger, binary, args, vmFromAgd, vmToAgd)
114
+ shutdown = makeShutdown(cmd, agdToVm)
115
+
116
+ if err := cmd.Start(); err != nil {
117
+ return err
118
+ }
119
+ if vmFromAgd.Close() != nil {
120
+ return err
121
+ }
122
+ if vmToAgd.Close() != nil {
123
+ return err
23
124
  }
24
125
 
25
- logger.Info("agd delegating to JS executable", "binary", binary, "args", args)
26
- execErr := syscall.Exec(binary, args, os.Environ())
27
- if execErr != nil {
28
- panic(execErr)
126
+ // Multiplex bidirectional JSON-RPC over the pipes.
127
+ agvmConn := jsonrpcconn.NewConn(agdFromVm, agdToVm)
128
+ clientConn, serverConn := jsonrpcconn.ClientServerConn(agvmConn)
129
+
130
+ // Set up the VM server.
131
+ vmServer := rpc.NewServer()
132
+ if err := vmServer.RegisterName("agd", vm.NewAgdServer()); err != nil {
133
+ return err
29
134
  }
135
+ go vmServer.ServeCodec(jsonrpc.NewServerCodec(serverConn))
136
+
137
+ // Set up the VM client.
138
+ vmClient = jsonrpc.NewClient(clientConn)
139
+
140
+ go func() {
141
+ // Premature exit from `agd start` should exit the process.
142
+ _ = cmd.Wait()
143
+ os.Exit(exitCode)
144
+ }()
145
+
146
+ return nil
30
147
  }
31
148
 
32
- daemoncmd.OnStartHook = launchVM
33
149
  daemoncmd.OnExportHook = launchVM
150
+ daemoncmd.OnStartHook = func (logger log.Logger, appOpts servertypes.AppOptions) error {
151
+ // We tried running start, which should never exit, so exit with non-zero
152
+ // code if we do.
153
+ exitCode = 99
154
+ return launchVM(logger, appOpts)
155
+ }
34
156
 
35
- daemon.RunWithController(nil)
157
+ daemon.RunWithController(sendToNode)
36
158
  }
@@ -10,8 +10,9 @@ package main
10
10
  import "C"
11
11
 
12
12
  import (
13
+ "context"
13
14
  "encoding/json"
14
- "errors"
15
+ "net/rpc"
15
16
  "os"
16
17
  "path/filepath"
17
18
 
@@ -21,6 +22,7 @@ import (
21
22
  "github.com/Agoric/agoric-sdk/golang/cosmos/daemon"
22
23
  daemoncmd "github.com/Agoric/agoric-sdk/golang/cosmos/daemon/cmd"
23
24
  "github.com/Agoric/agoric-sdk/golang/cosmos/vm"
25
+ servertypes "github.com/cosmos/cosmos-sdk/server/types"
24
26
  )
25
27
 
26
28
  type goReturn = struct {
@@ -30,8 +32,32 @@ type goReturn = struct {
30
32
 
31
33
  const SwingSetPort = 123
32
34
 
33
- var replies = map[int]chan goReturn{}
34
- var lastReply = 0
35
+ var vmClientCodec *vm.ClientCodec
36
+ var agdServer *vm.AgdServer
37
+
38
+ // ConnectVMClientCodec creates an RPC client codec and a sender to the
39
+ // in-process implementation of the VM.
40
+ func ConnectVMClientCodec(ctx context.Context, nodePort int, sendFunc func(int, int, string)) (*vm.ClientCodec, daemoncmd.Sender) {
41
+ vmClientCodec = vm.NewClientCodec(context.Background(), sendFunc)
42
+ vmClient := rpc.NewClientWithCodec(vmClientCodec)
43
+
44
+ sendToNode := func(ctx context.Context, needReply bool, str string) (string, error) {
45
+ if str == "shutdown" {
46
+ return "", vmClientCodec.Close()
47
+ }
48
+
49
+ msg := vm.Message{
50
+ Port: nodePort,
51
+ NeedsReply: needReply,
52
+ Data: str,
53
+ }
54
+ var reply string
55
+ err := vmClient.Call(vm.ReceiveMessageMethod, msg, &reply)
56
+ return reply, err
57
+ }
58
+
59
+ return vmClientCodec, sendToNode
60
+ }
35
61
 
36
62
  //export RunAgCosmosDaemon
37
63
  func RunAgCosmosDaemon(nodePort C.int, toNode C.sendFunc, cosmosArgs []*C.char) C.int {
@@ -42,46 +68,39 @@ func RunAgCosmosDaemon(nodePort C.int, toNode C.sendFunc, cosmosArgs []*C.char)
42
68
 
43
69
  gaia.DefaultNodeHome = filepath.Join(userHomeDir, ".ag-chain-cosmos")
44
70
  daemoncmd.AppName = "ag-chain-cosmos"
71
+ if err := os.Setenv(daemoncmd.EmbeddedVmEnvVar, "libdaemon"); err != nil {
72
+ panic(err)
73
+ }
45
74
 
46
- // FIXME: Decouple the sending logic from the Cosmos app.
47
- sendToNode := func(needReply bool, str string) (string, error) {
48
- var rPort int
49
- if needReply {
50
- lastReply++
51
- rPort = lastReply
52
- replies[rPort] = make(chan goReturn)
53
- }
75
+ var sendToNode daemoncmd.Sender
54
76
 
55
- // Send the message
56
- C.invokeSendFunc(toNode, nodePort, C.int(rPort), C.CString(str))
57
- if !needReply {
58
- // Return immediately
59
- // fmt.Fprintln(os.Stderr, "Don't wait")
60
- return "<no-reply-requested>", nil
61
- }
62
-
63
- // Block the sending goroutine while we wait for the reply
64
- // fmt.Fprintln(os.Stderr, "Waiting for", rPort)
65
- ret := <-replies[rPort]
66
- delete(replies, rPort)
67
- // fmt.Fprintln(os.Stderr, "Woken, got", ret)
68
- return ret.str, ret.err
77
+ sendFunc := func(port int, reply int, str string) {
78
+ C.invokeSendFunc(toNode, C.int(port), C.int(reply), C.CString(str))
69
79
  }
70
80
 
81
+ vmClientCodec, sendToNode = ConnectVMClientCodec(
82
+ context.Background(),
83
+ int(nodePort),
84
+ sendFunc,
85
+ )
86
+ agdServer = vm.NewAgdServer()
87
+
71
88
  args := make([]string, len(cosmosArgs))
72
89
  for i, s := range cosmosArgs {
73
90
  args[i] = C.GoString(s)
74
91
  }
92
+
75
93
  // fmt.Fprintln(os.Stderr, "Starting Cosmos", args)
76
94
  os.Args = args
77
95
  go func() {
78
96
  // We run in the background, but exit when the job is over.
79
97
  // swingset.SendToNode("hello from Initial Go!")
80
98
  exitCode := 0
81
- daemoncmd.OnStartHook = func(logger log.Logger) {
99
+ daemoncmd.OnStartHook = func(logger log.Logger, appOpts servertypes.AppOptions) error {
82
100
  // We tried running start, which should never exit, so exit with non-zero
83
101
  // code if we ever stop.
84
102
  exitCode = 99
103
+ return nil
85
104
  }
86
105
  daemon.RunWithController(sendToNode)
87
106
  // fmt.Fprintln(os.Stderr, "Shutting down Cosmos")
@@ -94,22 +113,10 @@ func RunAgCosmosDaemon(nodePort C.int, toNode C.sendFunc, cosmosArgs []*C.char)
94
113
  //export ReplyToGo
95
114
  func ReplyToGo(replyPort C.int, isError C.int, resp C.Body) C.int {
96
115
  respStr := C.GoString(resp)
97
- // fmt.Fprintln(os.Stderr, "Reply to Go", respStr)
98
- returnCh := replies[int(replyPort)]
99
- if returnCh == nil {
100
- // Unexpected reply.
101
- // This is okay, since the caller decides whether or
102
- // not she wants to listen for replies.
103
- return C.int(0)
116
+ // fmt.Printf("Reply to Go %d %s\n", replyPort, respStr)
117
+ if err := vmClientCodec.Receive(int(replyPort), int(isError) != 0, respStr); err != nil {
118
+ return C.int(1)
104
119
  }
105
- // Wake up the waiting goroutine
106
- ret := goReturn{}
107
- if int(isError) == 0 {
108
- ret.str = respStr
109
- } else {
110
- ret.err = errors.New(respStr)
111
- }
112
- returnCh <- ret
113
120
  return C.int(0)
114
121
  }
115
122
 
@@ -121,20 +128,27 @@ type errorWrapper struct {
121
128
  func SendToGo(port C.int, msg C.Body) C.Body {
122
129
  msgStr := C.GoString(msg)
123
130
  // fmt.Fprintln(os.Stderr, "Send to Go", msgStr)
124
- respStr, err := vm.ReceiveFromController(int(port), msgStr)
131
+ var respStr string
132
+ message := &vm.Message{
133
+ Port: int(port),
134
+ NeedsReply: true,
135
+ Data: msgStr,
136
+ }
137
+
138
+ err := agdServer.ReceiveMessage(message, &respStr)
139
+ if err == nil {
140
+ return C.CString(respStr)
141
+ }
142
+
143
+ // fmt.Fprintln(os.Stderr, "Cannot receive from controller", err)
144
+ errResp := errorWrapper{
145
+ Error: err.Error(),
146
+ }
147
+ respBytes, err := json.Marshal(&errResp)
125
148
  if err != nil {
126
- // fmt.Fprintln(os.Stderr, "Cannot receive from controller", err)
127
- errResp := errorWrapper{
128
- Error: err.Error(),
129
- }
130
- respBytes, err := json.Marshal(&errResp)
131
- if err != nil {
132
- panic(err)
133
- }
134
- // fmt.Fprintln(os.Stderr, "Marshaled", errResp, respBytes)
135
- respStr = string(respBytes)
149
+ panic(err)
136
150
  }
137
- return C.CString(respStr)
151
+ return C.CString(string(respBytes))
138
152
  }
139
153
 
140
154
  // Do nothing in main.
@@ -1,6 +1,7 @@
1
1
  package cmd
2
2
 
3
3
  import (
4
+ "context"
4
5
  "errors"
5
6
  "io"
6
7
  "os"
@@ -38,11 +39,11 @@ import (
38
39
  )
39
40
 
40
41
  // Sender is a function that sends a request to the controller.
41
- type Sender func(needReply bool, str string) (string, error)
42
+ type Sender func(ctx context.Context, needReply bool, str string) (string, error)
42
43
 
43
44
  var AppName = "agd"
44
- var OnStartHook func(logger log.Logger)
45
- var OnExportHook func(logger log.Logger)
45
+ var OnStartHook func(log.Logger, servertypes.AppOptions) error
46
+ var OnExportHook func(log.Logger, servertypes.AppOptions) error
46
47
 
47
48
  // NewRootCmd creates a new root command for simd. It is called once in the
48
49
  // main function.
@@ -60,7 +61,7 @@ func NewRootCmd(sender Sender) (*cobra.Command, params.EncodingConfig) {
60
61
 
61
62
  rootCmd := &cobra.Command{
62
63
  Use: AppName,
63
- Short: "Stargate Agoric App",
64
+ Short: "Agoric Cosmos App",
64
65
  PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
65
66
  // set the default command outputs
66
67
  cmd.SetOut(cmd.OutOrStdout())
@@ -135,10 +136,9 @@ func initRootCmd(sender Sender, rootCmd *cobra.Command, encodingConfig params.En
135
136
  }
136
137
  server.AddCommands(rootCmd, gaia.DefaultNodeHome, ac.newApp, ac.appExport, addModuleInitFlags)
137
138
 
138
- hasVMController := sender != nil
139
139
  for _, command := range rootCmd.Commands() {
140
140
  if command.Name() == "export" {
141
- extendCosmosExportCommand(command, hasVMController)
141
+ extendCosmosExportCommand(command)
142
142
  break
143
143
  }
144
144
  }
@@ -154,8 +154,31 @@ func initRootCmd(sender Sender, rootCmd *cobra.Command, encodingConfig params.En
154
154
  rootCmd.AddCommand(server.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler))
155
155
  }
156
156
 
157
+ const (
158
+ // FlagSplitVm is the command-line flag for subcommands that can use a
159
+ // split-process Agoric VM. The default is to use an embedded VM.
160
+ FlagSplitVm = "split-vm"
161
+ EmbeddedVmEnvVar = "AGD_EMBEDDED_VM"
162
+ )
163
+
164
+ // hasVMController returns true if we have a VM (are running in split-vm mode,
165
+ // or with an embedded VM).
166
+ func hasVMController(serverCtx *server.Context) bool {
167
+ return serverCtx.Viper.GetString(FlagSplitVm) != "" ||
168
+ os.Getenv(EmbeddedVmEnvVar) != ""
169
+ }
170
+
171
+ func addAgoricVMFlags(cmd *cobra.Command) {
172
+ cmd.PersistentFlags().String(
173
+ FlagSplitVm,
174
+ "",
175
+ "Specify the external Agoric VM program",
176
+ )
177
+ }
178
+
157
179
  func addModuleInitFlags(startCmd *cobra.Command) {
158
180
  crisis.AddModuleInitFlags(startCmd)
181
+ addAgoricVMFlags(startCmd)
159
182
  }
160
183
 
161
184
  func queryCommand() *cobra.Command {
@@ -223,7 +246,9 @@ func (ac appCreator) newApp(
223
246
  appOpts servertypes.AppOptions,
224
247
  ) servertypes.Application {
225
248
  if OnStartHook != nil {
226
- OnStartHook(logger)
249
+ if err := OnStartHook(logger, appOpts); err != nil {
250
+ panic(err)
251
+ }
227
252
  }
228
253
 
229
254
  var cache sdk.MultiStorePersistentCache
@@ -297,8 +322,9 @@ const (
297
322
 
298
323
  // extendCosmosExportCommand monkey-patches the "export" command added by
299
324
  // cosmos-sdk to add a required "export-dir" command-line flag, and create the
300
- // genesis export in the specified directory.
301
- func extendCosmosExportCommand(cmd *cobra.Command, hasVMController bool) {
325
+ // genesis export in the specified directory if the VM is running.
326
+ func extendCosmosExportCommand(cmd *cobra.Command) {
327
+ addAgoricVMFlags(cmd)
302
328
  cmd.Flags().String(FlagExportDir, "", "The directory where to create the genesis export")
303
329
  err := cmd.MarkFlagRequired(FlagExportDir)
304
330
  if err != nil {
@@ -329,25 +355,28 @@ func extendCosmosExportCommand(cmd *cobra.Command, hasVMController bool) {
329
355
  // current genesis.
330
356
  serverCtx.Viper.Set(gaia.FlagSwingStoreExportDir, swingStoreExportPath)
331
357
 
332
- // This will fail is a genesis.json already exists in the export-dir
333
- genesisFile, err := os.OpenFile(genesisPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, os.ModePerm)
334
- if err != nil {
335
- return err
358
+ if hasVMController(serverCtx) {
359
+ // Capture the export in the genesisPath.
360
+ // This will fail if a genesis.json already exists in the export-dir
361
+ genesisFile, err := os.OpenFile(
362
+ genesisPath,
363
+ os.O_CREATE|os.O_EXCL|os.O_WRONLY,
364
+ os.ModePerm,
365
+ )
366
+ if err != nil {
367
+ return err
368
+ }
369
+ defer genesisFile.Close()
370
+ cmd.SetOut(genesisFile)
336
371
  }
337
- defer genesisFile.Close()
338
-
339
- cmd.SetOut(genesisFile)
340
372
 
373
+ // If we don't have a VM, appExport will just use the OnExportHook to exec
374
+ // the VM program, which will result in reentering this function with the VM
375
+ // controller set, and activate the above condition.
341
376
  return originalRunE(cmd, args)
342
377
  }
343
378
 
344
- // Only modify the command handler when we have a VM controller to handle
345
- // the full export logic. Otherwise, appExport will just exec the VM program
346
- // (OnExportHook), which will result in re-entering this flow with the VM
347
- // controller set.
348
- if hasVMController {
349
- cmd.RunE = extendedRunE
350
- }
379
+ cmd.RunE = extendedRunE
351
380
  }
352
381
 
353
382
  func (ac appCreator) appExport(
@@ -360,7 +389,9 @@ func (ac appCreator) appExport(
360
389
  appOpts servertypes.AppOptions,
361
390
  ) (servertypes.ExportedApp, error) {
362
391
  if OnExportHook != nil {
363
- OnExportHook(logger)
392
+ if err := OnExportHook(logger, appOpts); err != nil {
393
+ return servertypes.ExportedApp{}, err
394
+ }
364
395
  }
365
396
 
366
397
  homePath, ok := appOpts.Get(flags.FlagHome).(string)
package/daemon/main.go CHANGED
@@ -1,6 +1,7 @@
1
1
  package daemon
2
2
 
3
3
  import (
4
+ "context"
4
5
  "fmt"
5
6
  "os"
6
7
  "os/signal"
@@ -17,7 +18,7 @@ import (
17
18
  )
18
19
 
19
20
  // DefaultController is a stub controller.
20
- var DefaultController = func(needReply bool, str string) (string, error) {
21
+ var DefaultController = func(ctx context.Context, needReply bool, str string) (string, error) {
21
22
  return "", fmt.Errorf("Controller not configured; did you mean to use `ag-chain-cosmos` instead?")
22
23
  }
23
24
 
@@ -34,6 +35,7 @@ func RunWithController(sendToController cmd.Sender) {
34
35
  signal.Notify(sigs, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
35
36
  go func() {
36
37
  <-sigs
38
+ _, _ = sendToController(context.Background(), false, "shutdown")
37
39
  os.Exit(98)
38
40
  }()
39
41
 
package/git-revision.txt CHANGED
@@ -1 +1 @@
1
- 5fcf452
1
+ d355030
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/cosmos",
3
- "version": "0.34.2-dev-5fcf452.0+5fcf452",
3
+ "version": "0.34.2-dev-d355030.0+d355030",
4
4
  "description": "Connect JS to the Cosmos blockchain SDK",
5
5
  "parsers": {
6
6
  "js": "mjs"
@@ -35,5 +35,5 @@
35
35
  "publishConfig": {
36
36
  "access": "public"
37
37
  },
38
- "gitHead": "5fcf4525cd4238b465f925dbf42f80f87800c116"
38
+ "gitHead": "d35503027f8de03dfedf7bc3a5adce00d0985435"
39
39
  }