feat(x/auth): batch transactions file format & broadcast multi transactions (#18692)
Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com> Co-authored-by: Marko <marbar3778@yahoo.com>
This commit is contained in:
parent
8fe6d84f2c
commit
bea3f9b7a3
@ -27,6 +27,8 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
### Features
|
||||
|
||||
* [#18281](https://github.com/cosmos/cosmos-sdk/pull/18281) Support broadcasting multiple transactions.
|
||||
|
||||
### Improvements
|
||||
|
||||
### API Breaking Changes
|
||||
|
||||
@ -35,22 +35,32 @@ filename, the command reads from standard input.`),
|
||||
return errors.New("cannot broadcast tx during offline mode")
|
||||
}
|
||||
|
||||
stdTx, err := authclient.ReadTxFromFile(clientCtx, args[0])
|
||||
txs, err := authclient.ReadTxsFromFile(clientCtx, args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
txBytes, err := clientCtx.TxConfig.TxEncoder()(stdTx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
txEncoder := clientCtx.TxConfig.TxEncoder()
|
||||
for _, tx := range txs {
|
||||
txBytes, err1 := txEncoder(tx)
|
||||
if err1 != nil {
|
||||
err = errors.Join(err, err1)
|
||||
continue
|
||||
}
|
||||
|
||||
res, err := clientCtx.BroadcastTx(txBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
res, err2 := clientCtx.BroadcastTx(txBytes)
|
||||
if err2 != nil {
|
||||
err = errors.Join(err, err2)
|
||||
continue
|
||||
}
|
||||
if res != nil {
|
||||
err3 := clientCtx.PrintProto(res)
|
||||
if err3 != nil {
|
||||
err = errors.Join(err, err3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return clientCtx.PrintProto(res)
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -115,6 +115,40 @@ func ReadTxFromFile(ctx client.Context, filename string) (tx sdk.Tx, err error)
|
||||
return ctx.TxConfig.TxJSONDecoder()(bytes)
|
||||
}
|
||||
|
||||
// Read and decode a multi transactions (must be in Txs format) from the given filename.
|
||||
// Can pass "-" to read from stdin.
|
||||
func ReadTxsFromFile(ctx client.Context, filename string) (tx []sdk.Tx, err error) {
|
||||
var fileBuff []byte
|
||||
var txs []sdk.Tx
|
||||
|
||||
if filename == "-" {
|
||||
fileBuff, err = io.ReadAll(os.Stdin)
|
||||
} else {
|
||||
fileBuff, err = os.ReadFile(filename)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read batch txs from file %s: %w", filename, err)
|
||||
}
|
||||
|
||||
// In SignBatchCmd, the output prints each tx line by line separated by "\n".
|
||||
// So we split the output bytes to slice of tx bytes,
|
||||
// last elemet always be empty bytes.
|
||||
txsBytes := bytes.Split(fileBuff, []byte("\n"))
|
||||
txDecoder := ctx.TxConfig.TxJSONDecoder()
|
||||
for _, txBytes := range txsBytes {
|
||||
if len(txBytes) == 0 {
|
||||
continue
|
||||
}
|
||||
tx, err := txDecoder(txBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txs = append(txs, tx)
|
||||
}
|
||||
return txs, nil
|
||||
}
|
||||
|
||||
// ReadTxsFromInput reads multiples txs from the given filename(s). Can pass "-" to read from stdin.
|
||||
// Unlike ReadTxFromFile, this function does not decode the txs.
|
||||
func ReadTxsFromInput(txCfg client.TxConfig, filenames ...string) (scanner *BatchScanner, err error) {
|
||||
|
||||
@ -71,6 +71,70 @@ func TestReadTxFromFile(t *testing.T) {
|
||||
require.Equal(t, txBuilder.GetTx().GetFee(), txBldr.GetTx().GetFee())
|
||||
}
|
||||
|
||||
func TestReadTxsFromFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
encodingConfig := moduletestutil.MakeTestEncodingConfig()
|
||||
interfaceRegistry := encodingConfig.InterfaceRegistry
|
||||
txConfig := encodingConfig.TxConfig
|
||||
|
||||
clientCtx := client.Context{}
|
||||
clientCtx = clientCtx.WithInterfaceRegistry(interfaceRegistry)
|
||||
clientCtx = clientCtx.WithTxConfig(txConfig)
|
||||
|
||||
// Set up 2 txs
|
||||
txBuilders := make([]client.TxBuilder, 2)
|
||||
// Set up tx 1
|
||||
txBuilders[0] = txConfig.NewTxBuilder()
|
||||
txBuilders[0].SetFeeAmount(sdk.Coins{sdk.NewInt64Coin("atom", 150)})
|
||||
txBuilders[0].SetGasLimit(uint64(50000))
|
||||
txBuilders[0].SetMemo("foomemo")
|
||||
// Set up tx 2
|
||||
txBuilders[1] = txConfig.NewTxBuilder()
|
||||
txBuilders[1].SetFeeAmount(sdk.Coins{sdk.NewInt64Coin("atom", 200)})
|
||||
txBuilders[1].SetGasLimit(uint64(60000))
|
||||
txBuilders[1].SetMemo("foomemo2")
|
||||
|
||||
// Write txs to the file
|
||||
encodedTx1, err := txConfig.TxJSONEncoder()(txBuilders[0].GetTx())
|
||||
require.NoError(t, err)
|
||||
encodedTx2, err := txConfig.TxJSONEncoder()(txBuilders[1].GetTx())
|
||||
require.NoError(t, err)
|
||||
|
||||
tx1String := string(encodedTx1) + "\n"
|
||||
tx2String := string(encodedTx2) + "\n"
|
||||
jsonBatchTxsFile := testutil.WriteToNewTempFile(t, tx1String+tx2String)
|
||||
jsonSingleTxFile := testutil.WriteToNewTempFile(t, tx1String)
|
||||
|
||||
// Read it back
|
||||
|
||||
// 2 txs case
|
||||
decodedBatchTxs, err := authclient.ReadTxsFromFile(clientCtx, jsonBatchTxsFile.Name())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(decodedBatchTxs), 2)
|
||||
for i, decodedTx := range decodedBatchTxs {
|
||||
txBldr, err := txConfig.WrapTxBuilder(decodedTx)
|
||||
require.NoError(t, err)
|
||||
|
||||
wantTx := txBuilders[i].GetTx()
|
||||
gotTx := txBldr.GetTx()
|
||||
require.Equal(t, wantTx.GetMemo(), gotTx.GetMemo())
|
||||
require.Equal(t, wantTx.GetFee(), gotTx.GetFee())
|
||||
}
|
||||
|
||||
// single tx case
|
||||
decodedSingleTx, err := authclient.ReadTxsFromFile(clientCtx, jsonSingleTxFile.Name())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(decodedSingleTx), 1)
|
||||
txBldr, err := txConfig.WrapTxBuilder(decodedSingleTx[0])
|
||||
require.NoError(t, err)
|
||||
|
||||
wantTx := txBuilders[0].GetTx()
|
||||
gotTx := txBldr.GetTx()
|
||||
require.Equal(t, wantTx.GetMemo(), gotTx.GetMemo())
|
||||
require.Equal(t, wantTx.GetFee(), gotTx.GetFee())
|
||||
}
|
||||
|
||||
func TestBatchScanner_Scan(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user