Skip to content

Commit

Permalink
cli: when using sync bcast, do query-tx (#451)
Browse files Browse the repository at this point in the history
* cli: when using sync bcast, do query-tx

* trigger ci

---------

Co-authored-by: Brennan Lamey <66885902+brennanjl@users.noreply.github.com>
  • Loading branch information
jchappelow and brennanjl committed Feb 26, 2024
1 parent e933102 commit b765206
Show file tree
Hide file tree
Showing 12 changed files with 269 additions and 155 deletions.
88 changes: 88 additions & 0 deletions cmd/common/display/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,53 @@ import (
"encoding/hex"
"encoding/json"
"fmt"

"github.com/kwilteam/kwil-db/core/types/transactions"
)

// TxHashAndExecResponse is meant to combine the "tx_hash" marshalling of
// RespTxHash with a RespTxQuery in an "exec_result" field.
type TxHashAndExecResponse struct {
Hash RespTxHash // embedding breaks MarshalJSON of composing types
QueryResp *RespTxQuery `json:"exec_result"`
}

// NewTxHashAndExecResponse makes a TxHashAndExecResponse from a TcTxQueryResponse.
func NewTxHashAndExecResponse(resp *transactions.TcTxQueryResponse) *TxHashAndExecResponse {
return &TxHashAndExecResponse{
Hash: RespTxHash(resp.Hash),
QueryResp: &RespTxQuery{Msg: resp},
}
}

func (h *TxHashAndExecResponse) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
TxHash string `json:"tx_hash"`
QueryResp *RespTxQuery `json:"exec_result"`
}{
TxHash: h.Hash.Hex(),
QueryResp: h.QueryResp,
})
}

// MarshalText deduplicates the tx hash for a compact readable output, unlike
// the JSON marshalling that is meant to be a composition of both RespTxHash and
// RespTxQuery.
func (h TxHashAndExecResponse) MarshalText() ([]byte, error) {
return []byte(fmt.Sprintf(`TxHash: %s
Status: %s
Height: %d
Log: %s`, h.Hash.Hex(),
heightStatus(h.QueryResp.Msg),
h.QueryResp.Msg.Height,
h.QueryResp.Msg.TxResult.Log,
),
), nil
}

var _ MsgFormatter = (*TxHashAndExecResponse)(nil)
var _ MsgFormatter = (*RespTxQuery)(nil)

type TxHashResponse struct {
TxHash string `json:"tx_hash"`
}
Expand Down Expand Up @@ -38,3 +83,46 @@ func (s RespString) MarshalJSON() ([]byte, error) {
func (s RespString) MarshalText() ([]byte, error) {
return []byte(s), nil
}

// RespTxQuery is used to represent a transaction response in cli
type RespTxQuery struct {
Msg *transactions.TcTxQueryResponse
}

func (r *RespTxQuery) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Hash string `json:"hash"` // HEX
Height int64 `json:"height"`
Tx transactions.Transaction `json:"tx"`
TxResult transactions.TransactionResult `json:"tx_result"`
}{
Hash: hex.EncodeToString(r.Msg.Hash),
Height: r.Msg.Height,
Tx: r.Msg.Tx,
TxResult: r.Msg.TxResult,
})
}

func heightStatus(res *transactions.TcTxQueryResponse) string {
status := "failed"
if res.Height == -1 {
status = "pending"
} else if res.TxResult.Code == transactions.CodeOk.Uint32() {
status = "success"
}
return status
}

func (r *RespTxQuery) MarshalText() ([]byte, error) {
msg := fmt.Sprintf(`Transaction ID: %s
Status: %s
Height: %d
Log: %s`,
hex.EncodeToString(r.Msg.Hash),
heightStatus(r.Msg),
r.Msg.Height,
r.Msg.TxResult.Log,
)

return []byte(msg), nil
}
118 changes: 118 additions & 0 deletions cmd/common/display/message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ package display

import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"io"
"math/big"
"os"
"testing"

"github.com/kwilteam/kwil-db/core/crypto/auth"
"github.com/kwilteam/kwil-db/core/types/transactions"

"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -72,3 +78,115 @@ func ExampleRespTxHash_json_withError() {
// "error": "an error"
// }
}

func getExampleTxQueryResponse() *transactions.TcTxQueryResponse {
secp256k1EpSigHex := "cb3fed7f6ff36e59054c04a831b215e514052753ee353e6fe31d4b4ef736acd6155127db555d3006ba14fcb4c79bbad56c8e63b81a9896319bb053a9e253475800"
secp256k1EpSigBytes, _ := hex.DecodeString(secp256k1EpSigHex)
secpSig := auth.Signature{
Signature: secp256k1EpSigBytes,
Type: auth.EthPersonalSignAuth,
}

rawPayload := transactions.ActionExecution{
DBID: "xf617af1ca774ebbd6d23e8fe12c56d41d25a22d81e88f67c6c6ee0d4",
Action: "create_user",
Arguments: [][]string{
{"foo", "32"},
},
}

payloadRLP, err := rawPayload.MarshalBinary()
if err != nil {
panic(err)
}

return &transactions.TcTxQueryResponse{
Hash: []byte("1024"),
Height: 10,
Tx: transactions.Transaction{
Body: &transactions.TransactionBody{
Payload: payloadRLP,
PayloadType: rawPayload.Type(),
Fee: big.NewInt(100),
Nonce: 10,
ChainID: "asdf",
Description: "This is a test transaction for cli",
},
Serialization: transactions.SignedMsgConcat,
Signature: &secpSig,
},
TxResult: transactions.TransactionResult{
Code: 0,
Log: "This is log",
GasUsed: 10,
GasWanted: 10,
Data: nil,
Events: nil,
},
}
}

func Example_respTxQuery_text() {
Print(&RespTxQuery{Msg: getExampleTxQueryResponse()}, nil, "text")
// Output:
// Transaction ID: 31303234
// Status: success
// Height: 10
// Log: This is log
}

func Example_respTxQuery_json() {
Print(&RespTxQuery{Msg: getExampleTxQueryResponse()}, nil, "json")
// Output:
// {
// "result": {
// "hash": "31303234",
// "height": 10,
// "tx": {
// "Signature": {
// "signature_bytes": "yz/tf2/zblkFTASoMbIV5RQFJ1PuNT5v4x1LTvc2rNYVUSfbVV0wBroU/LTHm7rVbI5juBqYljGbsFOp4lNHWAA=",
// "signature_type": "secp256k1_ep"
// },
// "Body": {
// "Description": "This is a test transaction for cli",
// "Payload": "AAH4ULg5eGY2MTdhZjFjYTc3NGViYmQ2ZDIzZThmZTEyYzU2ZDQxZDI1YTIyZDgxZTg4ZjY3YzZjNmVlMGQ0i2NyZWF0ZV91c2VyyMeDZm9vgjMy",
// "PayloadType": "execute_action",
// "Fee": 100,
// "Nonce": 10,
// "ChainID": "asdf"
// },
// "Serialization": "concat",
// "Sender": null
// },
// "tx_result": {
// "code": 0,
// "log": "This is log",
// "gas_used": 10,
// "gas_wanted": 10
// }
// },
// "error": ""
// }
}

func Test_TxHashAndExecResponse(t *testing.T) {
hash := []byte{1, 2, 3, 4, 5}
qr := getExampleTxQueryResponse()
qr.Hash = hash
resp := &TxHashAndExecResponse{
Hash: hash,
QueryResp: &RespTxQuery{Msg: qr},
}
expectJson := `{"tx_hash":"0102030405","exec_result":{"hash":"0102030405","height":10,"tx":{"Signature":{"signature_bytes":"yz/tf2/zblkFTASoMbIV5RQFJ1PuNT5v4x1LTvc2rNYVUSfbVV0wBroU/LTHm7rVbI5juBqYljGbsFOp4lNHWAA=","signature_type":"secp256k1_ep"},"Body":{"Description":"This is a test transaction for cli","Payload":"AAH4ULg5eGY2MTdhZjFjYTc3NGViYmQ2ZDIzZThmZTEyYzU2ZDQxZDI1YTIyZDgxZTg4ZjY3YzZjNmVlMGQ0i2NyZWF0ZV91c2VyyMeDZm9vgjMy","PayloadType":"execute_action","Fee":100,"Nonce":10,"ChainID":"asdf"},"Serialization":"concat","Sender":null},` +
`"tx_result":{"code":0,"log":"This is log","gas_used":10,"gas_wanted":10}}}`
expectText := "TxHash: 0102030405\nStatus: success\nHeight: 10\nLog: This is log"

outText, err := resp.MarshalText()
assert.NoError(t, err, "MarshalText should not return error")
assert.Equal(t, expectText, string(outText), "MarshalText should return expected text")

outJson, err := resp.MarshalJSON()
fmt.Println(string(outJson))
assert.NoError(t, err, "MarshalJSON should not return error")
assert.Equal(t, expectJson, string(outJson), "MarshalJSON should return expected json")
}
10 changes: 5 additions & 5 deletions cmd/kwil-admin/cmds/utils/query-tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func queryTxCmd() *cobra.Command {
return display.PrintErr(cmd, err)
}

return display.PrintCmd(cmd, &respTxQuery{Msg: res})
return display.PrintCmd(cmd, &display.RespTxQuery{Msg: res})
},
}

Expand All @@ -44,12 +44,12 @@ func queryTxCmd() *cobra.Command {
return cmd
}

// respTxQuery is used to represent a transaction response in cli
type respTxQuery struct {
// RespTxQuery is used to represent a transaction response in cli
type RespTxQuery struct {
Msg *transactions.TcTxQueryResponse
}

func (r *respTxQuery) MarshalJSON() ([]byte, error) {
func (r *RespTxQuery) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Hash string `json:"hash"` // HEX
Height int64 `json:"height"`
Expand All @@ -63,7 +63,7 @@ func (r *respTxQuery) MarshalJSON() ([]byte, error) {
})
}

func (r *respTxQuery) MarshalText() ([]byte, error) {
func (r *RespTxQuery) MarshalText() ([]byte, error) {
status := "failed"
if r.Msg.Height == -1 {
status = "pending"
Expand Down
10 changes: 10 additions & 0 deletions cmd/kwil-cli/cmds/account/transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"math/big"
"time"

"github.com/kwilteam/kwil-db/cmd/common/display"
"github.com/kwilteam/kwil-db/cmd/kwil-cli/cmds/common"
Expand Down Expand Up @@ -38,6 +39,15 @@ func transferCmd() *cobra.Command {
if err != nil {
return display.PrintErr(cmd, fmt.Errorf("transfer failed: %w", err))
}
// If sycnBcast, and we have a txHash (error or not), do a query-tx.
if len(txHash) != 0 && syncBcast {
time.Sleep(500 * time.Millisecond) // otherwise it says not found at first
resp, err := cl.TxQuery(ctx, txHash)
if err != nil {
return display.PrintErr(cmd, fmt.Errorf("tx query failed: %w", err))
}
return display.PrintCmd(cmd, display.NewTxHashAndExecResponse(resp))
}
return display.PrintCmd(cmd, display.RespTxHash(txHash))
})
},
Expand Down
15 changes: 12 additions & 3 deletions cmd/kwil-cli/cmds/database/batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"os"
"strings"
"time"

"github.com/kwilteam/kwil-db/cmd/common/display"
"github.com/kwilteam/kwil-db/cmd/kwil-cli/cmds/common"
Expand Down Expand Up @@ -93,13 +94,21 @@ func batchCmd() *cobra.Command {
return display.PrintErr(cmd, fmt.Errorf("error creating action inputs: %w", err))
}

resp, err := cl.ExecuteAction(ctx, dbid, strings.ToLower(action), tuples,
txHash, err := cl.ExecuteAction(ctx, dbid, strings.ToLower(action), tuples,
client.WithNonce(nonceOverride), client.WithSyncBroadcast(syncBcast))
if err != nil {
return display.PrintErr(cmd, fmt.Errorf("error executing action: %w", err))
}

return display.PrintCmd(cmd, display.RespTxHash(resp))
// If sycnBcast, and we have a txHash (error or not), do a query-tx.
if len(txHash) != 0 && syncBcast {
time.Sleep(500 * time.Millisecond) // otherwise it says not found at first
resp, err := cl.TxQuery(ctx, txHash)
if err != nil {
return display.PrintErr(cmd, fmt.Errorf("tx query failed: %w", err))
}
return display.PrintCmd(cmd, display.NewTxHashAndExecResponse(resp))
}
return display.PrintCmd(cmd, display.RespTxHash(txHash))
})
},
}
Expand Down
11 changes: 10 additions & 1 deletion cmd/kwil-cli/cmds/database/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"io"
"os"
"time"

"github.com/kwilteam/kuneiform/kfparser"
"github.com/kwilteam/kwil-db/cmd/common/display"
Expand Down Expand Up @@ -69,7 +70,15 @@ func deployCmd() *cobra.Command {
if err != nil {
return display.PrintErr(cmd, fmt.Errorf("failed to deploy database: %w", err))
}

// If sycnBcast, and we have a txHash (error or not), do a query-tx.
if len(txHash) != 0 && syncBcast {
time.Sleep(500 * time.Millisecond) // otherwise it says not found at first
resp, err := cl.TxQuery(ctx, txHash)
if err != nil {
return display.PrintErr(cmd, fmt.Errorf("tx query failed: %w", err))
}
return display.PrintCmd(cmd, display.NewTxHashAndExecResponse(resp))
}
return display.PrintCmd(cmd, display.RespTxHash(txHash))
})
},
Expand Down
15 changes: 12 additions & 3 deletions cmd/kwil-cli/cmds/database/drop.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package database
import (
"context"
"fmt"
"time"

"github.com/kwilteam/kwil-db/cmd/common/display"
"github.com/kwilteam/kwil-db/cmd/kwil-cli/cmds/common"
Expand Down Expand Up @@ -33,13 +34,21 @@ func dropCmd() *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
return common.DialClient(cmd.Context(), cmd, 0, func(ctx context.Context, cl common.Client, conf *config.KwilCliConfig) error {
var err error
resp, err := cl.DropDatabase(ctx, args[0], client.WithNonce(nonceOverride),
txHash, err := cl.DropDatabase(ctx, args[0], client.WithNonce(nonceOverride),
client.WithSyncBroadcast(syncBcast))
if err != nil {
return display.PrintErr(cmd, fmt.Errorf("error dropping database: %w", err))
}

return display.PrintCmd(cmd, display.RespTxHash(resp))
// If sycnBcast, and we have a txHash (error or not), do a query-tx.
if len(txHash) != 0 && syncBcast {
time.Sleep(500 * time.Millisecond) // otherwise it says not found at first
resp, err := cl.TxQuery(ctx, txHash)
if err != nil {
return display.PrintErr(cmd, fmt.Errorf("tx query failed: %w", err))
}
return display.PrintCmd(cmd, display.NewTxHashAndExecResponse(resp))
}
return display.PrintCmd(cmd, display.RespTxHash(txHash))
})
},
}
Expand Down
Loading

0 comments on commit b765206

Please sign in to comment.