diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index c3e191e7e..f33198403 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -31,7 +31,8 @@ Special thanks to external contributors on this release: - [abci] \#7984 Remove the locks preventing concurrent use of ABCI applications by Tendermint. (@tychoish) - [abci] \#8605 Remove info, log, events, gasUsed and mempoolError fields from ResponseCheckTx as they are not used by Tendermint. (@jmalicevic) - [abci] \#8664 Move `app_hash` parameter from `Commit` to `FinalizeBlock`. (@sergio-mena) - + - [abci] \#8656 Added cli command for `PrepareProposal`. (@jmalicevic) + - P2P Protocol - [p2p] \#7035 Remove legacy P2P routing implementation and associated configuration options. (@tychoish) diff --git a/abci/cmd/abci-cli/abci-cli.go b/abci/cmd/abci-cli/abci-cli.go index b09f3c9a7..237493e0e 100644 --- a/abci/cmd/abci-cli/abci-cli.go +++ b/abci/cmd/abci-cli/abci-cli.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "bytes" "encoding/hex" "errors" "fmt" @@ -130,6 +131,7 @@ func addCommands(cmd *cobra.Command, logger log.Logger) { cmd.AddCommand(commitCmd) cmd.AddCommand(versionCmd) cmd.AddCommand(testCmd) + cmd.AddCommand(prepareProposalCmd) cmd.AddCommand(getQueryCmd()) // examples @@ -170,7 +172,7 @@ This command opens an interactive console for running any of the other commands without opening a new connection each time `, Args: cobra.ExactArgs(0), - ValidArgs: []string{"echo", "info", "finalize_block", "check_tx", "commit", "query"}, + ValidArgs: []string{"echo", "info", "query", "check_tx", "prepare_proposal", "finalize_block", "commit"}, RunE: cmdConsole, } @@ -224,6 +226,14 @@ var versionCmd = &cobra.Command{ }, } +var prepareProposalCmd = &cobra.Command{ + Use: "prepare_proposal", + Short: "prepare proposal", + Long: "prepare proposal", + Args: cobra.MinimumNArgs(1), + RunE: cmdPrepareProposal, +} + func getQueryCmd() *cobra.Command { cmd := &cobra.Command{ Use: "query", @@ -335,6 +345,13 @@ func cmdTest(cmd *cobra.Command, args []string) error { }, nil, []byte{0, 0, 0, 0, 0, 0, 0, 5}) }, func() error { return servertest.Commit(ctx, client) }, + func() error { + return servertest.PrepareProposal(ctx, client, [][]byte{ + {0x01}, + }, []types.TxRecord_TxAction{ + types.TxRecord_UNMODIFIED, + }, nil) + }, }) } @@ -435,6 +452,8 @@ func muxOnCommands(cmd *cobra.Command, pArgs []string) error { return cmdInfo(cmd, actualArgs) case "query": return cmdQuery(cmd, actualArgs) + case "prepare_proposal": + return cmdPrepareProposal(cmd, actualArgs) default: return cmdUnimplemented(cmd, pArgs) } @@ -605,6 +624,64 @@ func cmdQuery(cmd *cobra.Command, args []string) error { return nil } +func inTxArray(txByteArray [][]byte, tx []byte) bool { + for _, txTmp := range txByteArray { + if bytes.Equal(txTmp, tx) { + return true + } + + } + return false +} +func cmdPrepareProposal(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + printResponse(cmd, args, response{ + Code: codeBad, + Info: "Must provide at least one transaction", + Log: "Must provide at least one transaction", + }) + return nil + } + txsBytesArray := make([][]byte, len(args)) + + for i, arg := range args { + txBytes, err := stringOrHexToBytes(arg) + if err != nil { + return err + } + txsBytesArray[i] = txBytes + } + + res, err := client.PrepareProposal(cmd.Context(), &types.RequestPrepareProposal{ + Txs: txsBytesArray, + // kvstore has to have this parameter in order not to reject a tx as the default value is 0 + MaxTxBytes: 65536, + }) + if err != nil { + return err + } + resps := make([]response, 0, len(res.TxResults)+1) + for _, tx := range res.TxRecords { + existingTx := inTxArray(txsBytesArray, tx.Tx) + if tx.Action == types.TxRecord_UNKNOWN || + (existingTx && tx.Action == types.TxRecord_ADDED) || + (!existingTx && (tx.Action == types.TxRecord_UNMODIFIED || tx.Action == types.TxRecord_REMOVED)) { + resps = append(resps, response{ + Code: codeBad, + Log: "Failed. Tx: " + string(tx.GetTx()) + " action: " + tx.Action.String(), + }) + } else { + resps = append(resps, response{ + Code: code.CodeTypeOK, + Log: "Succeeded. Tx: " + string(tx.Tx) + " action: " + tx.Action.String(), + }) + } + } + + printResponse(cmd, args, resps...) + return nil +} + func makeKVStoreCmd(logger log.Logger) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { // Create the application - in memory or persisted to disk @@ -649,7 +726,6 @@ func printResponse(cmd *cobra.Command, args []string, rsps ...response) { fmt.Printf("-> code: OK\n") } else { fmt.Printf("-> code: %d\n", rsp.Code) - } if len(rsp.Data) != 0 { diff --git a/abci/tests/server/client.go b/abci/tests/server/client.go index cddb42ec0..7762c8d03 100644 --- a/abci/tests/server/client.go +++ b/abci/tests/server/client.go @@ -70,6 +70,19 @@ func FinalizeBlock(ctx context.Context, client abciclient.Client, txBytes [][]by return nil } +func PrepareProposal(ctx context.Context, client abciclient.Client, txBytes [][]byte, codeExp []types.TxRecord_TxAction, dataExp []byte) error { + res, _ := client.PrepareProposal(ctx, &types.RequestPrepareProposal{Txs: txBytes}) + for i, tx := range res.TxRecords { + if tx.Action != codeExp[i] { + fmt.Println("Failed test: PrepareProposal") + fmt.Printf("PrepareProposal response code was unexpected. Got %v expected %v.", + tx.Action, codeExp) + return errors.New("PrepareProposal error") + } + } + fmt.Println("Passed test: PrepareProposal") + return nil +} func CheckTx(ctx context.Context, client abciclient.Client, txBytes []byte, codeExp uint32, dataExp []byte) error { res, _ := client.CheckTx(ctx, &types.RequestCheckTx{Tx: txBytes}) code, data := res.Code, res.Data diff --git a/abci/tests/test_cli/ex1.abci b/abci/tests/test_cli/ex1.abci index 56355dc94..dc9e213ec 100644 --- a/abci/tests/test_cli/ex1.abci +++ b/abci/tests/test_cli/ex1.abci @@ -1,5 +1,6 @@ echo hello info +prepare_proposal "abc" finalize_block "abc" commit info @@ -7,3 +8,4 @@ query "abc" finalize_block "def=xyz" "ghi=123" commit query "def" +prepare_proposal "preparedef" \ No newline at end of file diff --git a/abci/tests/test_cli/ex1.abci.out b/abci/tests/test_cli/ex1.abci.out index 9a35290b0..f4f342dfb 100644 --- a/abci/tests/test_cli/ex1.abci.out +++ b/abci/tests/test_cli/ex1.abci.out @@ -8,6 +8,10 @@ -> data: {"size":0} -> data.hex: 0x7B2273697A65223A307D +> prepare_proposal "abc" +-> code: OK +-> log: Succeeded. Tx: abc action: UNMODIFIED + > finalize_block "abc" -> code: OK -> code: OK @@ -48,3 +52,9 @@ -> value: xyz -> value.hex: 78797A +> prepare_proposal "preparedef" +-> code: OK +-> log: Succeeded. Tx: def action: ADDED +-> code: OK +-> log: Succeeded. Tx: preparedef action: REMOVED + diff --git a/abci/tests/test_cli/test.sh b/abci/tests/test_cli/test.sh index 9c02ce6f5..d160d59c9 100755 --- a/abci/tests/test_cli/test.sh +++ b/abci/tests/test_cli/test.sh @@ -30,6 +30,8 @@ function testExample() { cat "${INPUT}.out.new" echo "Expected:" cat "${INPUT}.out" + echo "Diff:" + diff "${INPUT}.out" "${INPUT}.out.new" exit 1 fi