diff --git a/internal/tezos/exec_query.go b/internal/tezos/exec_query.go index 2338ba0..3f9e8f3 100644 --- a/internal/tezos/exec_query.go +++ b/internal/tezos/exec_query.go @@ -30,27 +30,20 @@ func (c *tezosConnector) QueryInvoke(ctx context.Context, req *ffcapi.QueryInvok if err != nil { return nil, ffcapi.ErrorReasonTransactionReverted, err } - - outputs, _ := json.Marshal(resp) - if val, ok := resp.(string); ok { - if values := strings.Split(val, ","); len(values) > 1 { - outputs, _ = json.Marshal(values) - } - } return &ffcapi.QueryInvokeResponse{ - Outputs: fftypes.JSONAnyPtrBytes(outputs), + Outputs: convertRunViewResponseToOutputs(resp), }, "", nil } -func (c *tezosConnector) runView(ctx context.Context, entrypoint, addrFrom, addrTo string, args micheline.Prim) (interface{}, error) { +func (c *tezosConnector) runView(ctx context.Context, entrypoint, addrFrom, addrTo string, args micheline.Prim) (rpc.RunViewResponse, error) { toAddress, err := tezos.ParseAddress(addrTo) if err != nil { - return nil, i18n.NewError(ctx, msgs.MsgInvalidToAddress, addrTo, err) + return rpc.RunViewResponse{}, i18n.NewError(ctx, msgs.MsgInvalidToAddress, addrTo, err) } fromAddress, err := tezos.ParseAddress(addrFrom) if err != nil { - return nil, i18n.NewError(ctx, msgs.MsgInvalidFromAddress, addrFrom, err) + return rpc.RunViewResponse{}, i18n.NewError(ctx, msgs.MsgInvalidFromAddress, addrFrom, err) } req := rpc.RunViewRequest{ @@ -66,8 +59,33 @@ func (c *tezosConnector) runView(ctx context.Context, entrypoint, addrFrom, addr var res rpc.RunViewResponse err = c.client.RunView(ctx, rpc.Head, &req, &res) if err != nil { - return nil, err + return rpc.RunViewResponse{}, err } + return res, nil +} - return res.Data.Value(res.Data.OpCode), nil +func convertRunViewResponseToOutputs(resp rpc.RunViewResponse) *fftypes.JSONAny { + var res interface{} + if resp.Data.LooksLikeMap() { + resultMap := make([]map[string]interface{}, len(resp.Data.Args)) + for i, arg := range resp.Data.Args { + mapEntry := make(map[string]interface{}, 2) + key := arg.Args[0] + value := arg.Args[1] + mapEntry["key"] = key.Value(key.OpCode) + mapEntry["value"] = value.Value(value.OpCode) + resultMap[i] = mapEntry + } + res = resultMap + } else { + res = resp.Data.Value(resp.Data.OpCode) + } + + outputs, _ := json.Marshal(res) + if val, ok := res.(string); ok { + if values := strings.Split(val, ","); len(values) > 1 { + outputs, _ = json.Marshal(values) + } + } + return fftypes.JSONAnyPtrBytes(outputs) } diff --git a/internal/tezos/exec_query_test.go b/internal/tezos/exec_query_test.go index 9b413a5..3a5a790 100644 --- a/internal/tezos/exec_query_test.go +++ b/internal/tezos/exec_query_test.go @@ -36,7 +36,7 @@ func TestQueryInvokeSuccess(t *testing.T) { resp, reason, err := c.QueryInvoke(ctx, req) assert.NotNil(t, resp) - assert.Equal(t, resp.Outputs.String(), "\"3\"") + assert.Equal(t, resp.Outputs.String(), `"3"`) assert.Empty(t, reason) assert.NoError(t, err) } @@ -68,7 +68,50 @@ func TestQueryInvokeSuccessArray(t *testing.T) { resp, reason, err := c.QueryInvoke(ctx, req) assert.NotNil(t, resp) - assert.Equal(t, resp.Outputs.String(), "[\"str\",\"1\"]") + assert.Equal(t, resp.Outputs.String(), `["str","1"]`) + assert.Empty(t, reason) + assert.NoError(t, err) +} + +func TestQueryInvokeSuccessMap(t *testing.T) { + ctx, c, mRPC, done := newTestConnector(t) + defer done() + + req := &ffcapi.QueryInvokeRequest{ + TransactionInput: ffcapi.TransactionInput{ + Method: fftypes.JSONAnyPtr("\"simple_view\""), + }, + } + res := rpc.RunViewResponse{ + Data: micheline.Prim{ + Type: micheline.PrimSequence, + Args: micheline.PrimList{ + { + OpCode: micheline.D_ELT, + Args: []micheline.Prim{ + {Type: micheline.PrimString, String: "str1"}, + {Type: micheline.PrimInt, Int: big.NewInt(1)}, + }, + }, + { + OpCode: micheline.D_ELT, + Args: []micheline.Prim{ + {Type: micheline.PrimString, String: "str2"}, + {Type: micheline.PrimInt, Int: big.NewInt(2)}, + }, + }, + }, + }, + } + mRPC.On("RunView", ctx, mock.Anything, mock.Anything, mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(3).(*rpc.RunViewResponse) + *arg = res + }) + + resp, reason, err := c.QueryInvoke(ctx, req) + + assert.NotNil(t, resp) + assert.Equal(t, resp.Outputs.String(), `[{"key":"str1","value":"1"},{"key":"str2","value":"2"}]`) assert.Empty(t, reason) assert.NoError(t, err) }