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:
Hieu Vu 2023-12-14 17:04:34 +07:00 committed by GitHub
parent 8fe6d84f2c
commit bea3f9b7a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 120 additions and 10 deletions

View File

@ -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

View File

@ -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
},
}

View File

@ -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) {

View File

@ -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()