github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/crypto/keys/client/broadcast.go (about) 1 package client 2 3 import ( 4 "context" 5 "flag" 6 "os" 7 8 "github.com/gnolang/gno/tm2/pkg/amino" 9 abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" 10 "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" 11 ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" 12 "github.com/gnolang/gno/tm2/pkg/commands" 13 "github.com/gnolang/gno/tm2/pkg/errors" 14 "github.com/gnolang/gno/tm2/pkg/std" 15 ) 16 17 type BroadcastCfg struct { 18 RootCfg *BaseCfg 19 20 DryRun bool 21 22 // internal 23 tx *std.Tx 24 // Set by SignAndBroadcastHandler, similar to DryRun. 25 // If true, simulation is attempted but not printed; 26 // the result is only returned in case of an error. 27 testSimulate bool 28 } 29 30 func NewBroadcastCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { 31 cfg := &BroadcastCfg{ 32 RootCfg: rootCfg, 33 } 34 35 return commands.NewCommand( 36 commands.Metadata{ 37 Name: "broadcast", 38 ShortUsage: "broadcast [flags] <file-name>", 39 ShortHelp: "broadcasts a signed document", 40 }, 41 cfg, 42 func(_ context.Context, args []string) error { 43 return execBroadcast(cfg, args, commands.NewDefaultIO()) 44 }, 45 ) 46 } 47 48 func (c *BroadcastCfg) RegisterFlags(fs *flag.FlagSet) { 49 fs.BoolVar( 50 &c.DryRun, 51 "dry-run", 52 false, 53 "perform a dry-run broadcast", 54 ) 55 } 56 57 func execBroadcast(cfg *BroadcastCfg, args []string, io commands.IO) error { 58 if len(args) != 1 { 59 return flag.ErrHelp 60 } 61 filename := args[0] 62 63 jsonbz, err := os.ReadFile(filename) 64 if err != nil { 65 return errors.Wrap(err, "reading tx document file "+filename) 66 } 67 var tx std.Tx 68 err = amino.UnmarshalJSON(jsonbz, &tx) 69 if err != nil { 70 return errors.Wrap(err, "unmarshaling tx json bytes") 71 } 72 cfg.tx = &tx 73 74 res, err := BroadcastHandler(cfg) 75 if err != nil { 76 return err 77 } 78 79 if res.CheckTx.IsErr() { 80 return errors.New("transaction failed %#v\nlog %s", res, res.CheckTx.Log) 81 } else if res.DeliverTx.IsErr() { 82 return errors.New("transaction failed %#v\nlog %s", res, res.DeliverTx.Log) 83 } else { 84 io.Println(string(res.DeliverTx.Data)) 85 io.Println("OK!") 86 io.Println("GAS WANTED:", res.DeliverTx.GasWanted) 87 io.Println("GAS USED: ", res.DeliverTx.GasUsed) 88 io.Println("HEIGHT: ", res.Height) 89 io.Println("EVENTS: ", string(res.DeliverTx.EncodeEvents())) 90 } 91 return nil 92 } 93 94 func BroadcastHandler(cfg *BroadcastCfg) (*ctypes.ResultBroadcastTxCommit, error) { 95 if cfg.tx == nil { 96 return nil, errors.New("invalid tx") 97 } 98 99 remote := cfg.RootCfg.Remote 100 if remote == "" { 101 return nil, errors.New("missing remote url") 102 } 103 104 bz, err := amino.Marshal(cfg.tx) 105 if err != nil { 106 return nil, errors.Wrap(err, "remarshaling tx binary bytes") 107 } 108 109 cli, err := client.NewHTTPClient(remote) 110 if err != nil { 111 return nil, err 112 } 113 114 // Both for DryRun and testSimulate, we perform simulation. 115 // However, DryRun always returns here, while in case of success 116 // testSimulate continues onto broadcasting the transaction. 117 if cfg.DryRun || cfg.testSimulate { 118 res, err := SimulateTx(cli, bz) 119 hasError := err != nil || res.CheckTx.IsErr() || res.DeliverTx.IsErr() 120 if cfg.DryRun || hasError { 121 return res, err 122 } 123 } 124 125 bres, err := cli.BroadcastTxCommit(bz) 126 if err != nil { 127 return nil, errors.Wrap(err, "broadcasting bytes") 128 } 129 130 return bres, nil 131 } 132 133 func SimulateTx(cli client.ABCIClient, tx []byte) (*ctypes.ResultBroadcastTxCommit, error) { 134 bres, err := cli.ABCIQuery(".app/simulate", tx) 135 if err != nil { 136 return nil, errors.Wrap(err, "simulate tx") 137 } 138 139 var result abci.ResponseDeliverTx 140 err = amino.Unmarshal(bres.Response.Value, &result) 141 if err != nil { 142 return nil, errors.Wrap(err, "unmarshaling simulate result") 143 } 144 145 return &ctypes.ResultBroadcastTxCommit{ 146 DeliverTx: result, 147 }, nil 148 }