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  }