Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions docs/swagger/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,10 @@ paths:
"200":
content:
application/json:
schema: {}
schema:
properties:
id: {}
type: object
description: Success
default:
description: ""
Expand Down Expand Up @@ -2025,7 +2028,10 @@ paths:
"200":
content:
application/json:
schema: {}
schema:
properties:
id: {}
type: object
description: Success
default:
description: ""
Expand Down Expand Up @@ -2409,7 +2415,10 @@ paths:
"200":
content:
application/json:
schema: {}
schema:
properties:
id: {}
type: object
description: Success
default:
description: ""
Expand Down
2 changes: 1 addition & 1 deletion internal/apiserver/route_post_contract_api_invoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ var postContractAPIInvoke = &oapispec.Route{
Description: i18n.MsgTBD,
JSONInputValue: func() interface{} { return &fftypes.ContractCallRequest{} },
JSONInputMask: []string{"Type", "Interface", "Method"},
JSONOutputValue: func() interface{} { return make(map[string]interface{}) },
JSONOutputValue: func() interface{} { return &fftypes.ContractCallResponse{} },
JSONOutputCodes: []int{http.StatusOK},
JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) {
req := r.Input.(*fftypes.ContractCallRequest)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ var postContractInterfaceInvoke = &oapispec.Route{
Description: i18n.MsgTBD,
JSONInputValue: func() interface{} { return &fftypes.ContractCallRequest{} },
JSONInputMask: []string{"Type", "Interface"},
JSONOutputValue: func() interface{} { return make(map[string]interface{}) },
JSONOutputValue: func() interface{} { return &fftypes.ContractCallResponse{} },
JSONOutputCodes: []int{http.StatusOK},
JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) {
req := r.Input.(*fftypes.ContractCallRequest)
Expand Down
2 changes: 1 addition & 1 deletion internal/apiserver/route_post_contract_invoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ var postContractInvoke = &oapispec.Route{
Description: i18n.MsgTBD,
JSONInputValue: func() interface{} { return &fftypes.ContractCallRequest{} },
JSONInputMask: []string{"Type"},
JSONOutputValue: func() interface{} { return make(map[string]interface{}) },
JSONOutputValue: func() interface{} { return &fftypes.ContractCallResponse{} },
JSONOutputCodes: []int{http.StatusOK},
JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) {
req := r.Input.(*fftypes.ContractCallRequest)
Expand Down
18 changes: 5 additions & 13 deletions internal/blockchain/ethereum/ethereum.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,6 @@ type eventStreamWebsocket struct {
Topic string `json:"topic"`
}

type asyncTXSubmission struct {
ID string `json:"id"`
}

type queryOutput struct {
Output string `json:"output"`
}
Expand Down Expand Up @@ -537,24 +533,20 @@ func (e *Ethereum) SubmitBatchPin(ctx context.Context, operationID *fftypes.UUID
return nil
}

func (e *Ethereum) InvokeContract(ctx context.Context, operationID *fftypes.UUID, signingKey string, location *fftypes.JSONAny, method *fftypes.FFIMethod, input map[string]interface{}) (interface{}, error) {
func (e *Ethereum) InvokeContract(ctx context.Context, operationID *fftypes.UUID, signingKey string, location *fftypes.JSONAny, method *fftypes.FFIMethod, input map[string]interface{}) error {
ethereumLocation, err := parseContractLocation(ctx, location)
if err != nil {
return nil, err
return err
}
abi, orderedInput, err := e.prepareRequest(ctx, method, input)
if err != nil {
return nil, err
return err
}
res, err := e.invokeContractMethod(ctx, ethereumLocation.Address, signingKey, abi, operationID.String(), orderedInput)
if err != nil || !res.IsSuccess() {
return nil, restclient.WrapRestErr(ctx, res, err, i18n.MsgEthconnectRESTErr)
}
tx := &asyncTXSubmission{}
if err = json.Unmarshal(res.Body(), tx); err != nil {
return nil, err
return restclient.WrapRestErr(ctx, res, err, i18n.MsgEthconnectRESTErr)
}
return tx, nil
return nil
}

func (e *Ethereum) QueryContract(ctx context.Context, location *fftypes.JSONAny, method *fftypes.FFIMethod, input map[string]interface{}) (interface{}, error) {
Expand Down
47 changes: 8 additions & 39 deletions internal/blockchain/ethereum/ethereum_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ func TestSubmitBatchPinOK(t *testing.T) {
assert.Equal(t, "0x9ffc50ff6bfe4502adc793aea54cc059c5df767cfe444e038eb51c5523097db5", params[1])
assert.Equal(t, ethHexFormatB32(batch.BatchHash), params[2])
assert.Equal(t, "Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD", params[3])
return httpmock.NewJsonResponderOrPanic(200, asyncTXSubmission{})(req)
return httpmock.NewJsonResponderOrPanic(200, "")(req)
})

err := e.SubmitBatchPin(context.Background(), nil, nil, addr, batch)
Expand Down Expand Up @@ -548,7 +548,7 @@ func TestSubmitBatchEmptyPayloadRef(t *testing.T) {
assert.Equal(t, "0x9ffc50ff6bfe4502adc793aea54cc059c5df767cfe444e038eb51c5523097db5", params[1])
assert.Equal(t, ethHexFormatB32(batch.BatchHash), params[2])
assert.Equal(t, "", params[3])
return httpmock.NewJsonResponderOrPanic(200, asyncTXSubmission{})(req)
return httpmock.NewJsonResponderOrPanic(200, "")(req)
})

err := e.SubmitBatchPin(context.Background(), nil, nil, addr, batch)
Expand Down Expand Up @@ -1521,9 +1521,9 @@ func TestInvokeContractOK(t *testing.T) {
assert.Equal(t, "SendTransaction", headers["type"])
assert.Equal(t, float64(1), params[0])
assert.Equal(t, float64(2), params[1])
return httpmock.NewJsonResponderOrPanic(200, asyncTXSubmission{})(req)
return httpmock.NewJsonResponderOrPanic(200, "")(req)
})
_, err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
assert.NoError(t, err)
}

Expand All @@ -1539,7 +1539,7 @@ func TestInvokeContractAddressNotSet(t *testing.T) {
}
locationBytes, err := json.Marshal(location)
assert.NoError(t, err)
_, err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
assert.Regexp(t, "'address' not set", err)
}

Expand All @@ -1561,9 +1561,9 @@ func TestInvokeContractEthconnectError(t *testing.T) {
assert.NoError(t, err)
httpmock.RegisterResponder("POST", `http://localhost:12345/`,
func(req *http.Request) (*http.Response, error) {
return httpmock.NewJsonResponderOrPanic(400, asyncTXSubmission{})(req)
return httpmock.NewJsonResponderOrPanic(400, "")(req)
})
_, err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
assert.Regexp(t, "FF10111", err)
}

Expand All @@ -1590,41 +1590,10 @@ func TestInvokeContractPrepareFail(t *testing.T) {
}
locationBytes, err := json.Marshal(location)
assert.NoError(t, err)
_, err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
assert.Regexp(t, "invalid json", err)
}

func TestInvokeContractUnmarshalResponseError(t *testing.T) {
e, cancel := newTestEthereum()
defer cancel()
httpmock.ActivateNonDefault(e.client.GetClient())
defer httpmock.DeactivateAndReset()
signingKey := ethHexFormatB32(fftypes.NewRandB32())
location := &Location{
Address: "0x12345",
}
method := testFFIMethod()
params := map[string]interface{}{
"x": float64(1),
"y": float64(2),
}
locationBytes, err := json.Marshal(location)
assert.NoError(t, err)
httpmock.RegisterResponder("POST", `http://localhost:12345/`,
func(req *http.Request) (*http.Response, error) {
var body map[string]interface{}
json.NewDecoder(req.Body).Decode(&body)
params := body["params"].([]interface{})
headers := body["headers"].(map[string]interface{})
assert.Equal(t, "SendTransaction", headers["type"])
assert.Equal(t, float64(1), params[0])
assert.Equal(t, float64(2), params[1])
return httpmock.NewStringResponder(200, "[definitely not JSON}")(req)
})
_, err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
assert.Regexp(t, "invalid character", err)
}

func TestQueryContractOK(t *testing.T) {
e, cancel := newTestEthereum()
defer cancel()
Expand Down
18 changes: 5 additions & 13 deletions internal/blockchain/fabric/fabric.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,6 @@ type eventStreamWebsocket struct {
Topic string `json:"topic"`
}

type asyncTXSubmission struct {
ID string `json:"id"`
}

type fabBatchPinInput struct {
Namespace string `json:"namespace"`
UUIDs string `json:"uuids"`
Expand Down Expand Up @@ -553,11 +549,11 @@ func (f *Fabric) SubmitBatchPin(ctx context.Context, operationID *fftypes.UUID,
return nil
}

func (f *Fabric) InvokeContract(ctx context.Context, operationID *fftypes.UUID, signingKey string, location *fftypes.JSONAny, method *fftypes.FFIMethod, input map[string]interface{}) (interface{}, error) {
func (f *Fabric) InvokeContract(ctx context.Context, operationID *fftypes.UUID, signingKey string, location *fftypes.JSONAny, method *fftypes.FFIMethod, input map[string]interface{}) error {
// All arguments must be JSON serialized
args, err := jsonEncodeInput(input)
if err != nil {
return nil, i18n.WrapError(ctx, err, i18n.MsgJSONObjectParseFailed, "params")
return i18n.WrapError(ctx, err, i18n.MsgJSONObjectParseFailed, "params")
}
in := &fabTxNamedInput{
Func: method.Name,
Expand All @@ -579,18 +575,14 @@ func (f *Fabric) InvokeContract(ctx context.Context, operationID *fftypes.UUID,

fabricOnChainLocation, err := parseContractLocation(ctx, location)
if err != nil {
return nil, err
return err
}

res, err := f.invokeContractMethod(ctx, fabricOnChainLocation.Channel, fabricOnChainLocation.Chaincode, signingKey, operationID.String(), in)
if err != nil || !res.IsSuccess() {
return nil, restclient.WrapRestErr(ctx, res, err, i18n.MsgFabconnectRESTErr)
}
tx := &asyncTXSubmission{}
if err = json.Unmarshal(res.Body(), tx); err != nil {
return nil, err
return restclient.WrapRestErr(ctx, res, err, i18n.MsgFabconnectRESTErr)
}
return tx, nil
return nil
}

func jsonEncodeInput(params map[string]interface{}) (output map[string]string, err error) {
Expand Down
52 changes: 9 additions & 43 deletions internal/blockchain/fabric/fabric_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ func TestSubmitBatchPinOK(t *testing.T) {
assert.Equal(t, "0x9ffc50ff6bfe4502adc793aea54cc059c5df767cfe444e038eb51c5523097db5", (body["args"].([]interface{}))[1])
assert.Equal(t, hexFormatB32(batch.BatchHash), (body["args"].([]interface{}))[2])
assert.Equal(t, "Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD", (body["args"].([]interface{}))[3])
return httpmock.NewJsonResponderOrPanic(200, asyncTXSubmission{})(req)
return httpmock.NewJsonResponderOrPanic(200, "")(req)
})

err := e.SubmitBatchPin(context.Background(), nil, nil, signer, batch)
Expand Down Expand Up @@ -438,7 +438,7 @@ func TestSubmitBatchEmptyPayloadRef(t *testing.T) {
assert.Equal(t, "0x9ffc50ff6bfe4502adc793aea54cc059c5df767cfe444e038eb51c5523097db5", (body["args"].([]interface{}))[1])
assert.Equal(t, hexFormatB32(batch.BatchHash), (body["args"].([]interface{}))[2])
assert.Equal(t, "", (body["args"].([]interface{}))[3])
return httpmock.NewJsonResponderOrPanic(200, asyncTXSubmission{})(req)
return httpmock.NewJsonResponderOrPanic(200, "")(req)
})

err := e.SubmitBatchPin(context.Background(), nil, nil, signer, batch)
Expand Down Expand Up @@ -1344,9 +1344,9 @@ func TestInvokeContractOK(t *testing.T) {
assert.Equal(t, "false", req.URL.Query().Get(defaultPrefixShort+"-sync"))
assert.Equal(t, "1", body["args"].(map[string]interface{})["x"])
assert.Equal(t, "2", body["args"].(map[string]interface{})["y"])
return httpmock.NewJsonResponderOrPanic(200, asyncTXSubmission{})(req)
return httpmock.NewJsonResponderOrPanic(200, "")(req)
})
_, err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
assert.NoError(t, err)
}

Expand All @@ -1362,7 +1362,7 @@ func TestInvokeContractChaincodeNotSet(t *testing.T) {
}
locationBytes, err := json.Marshal(location)
assert.NoError(t, err)
_, err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
assert.Regexp(t, "FF10310", err)
}

Expand All @@ -1385,46 +1385,12 @@ func TestInvokeContractFabconnectError(t *testing.T) {
assert.NoError(t, err)
httpmock.RegisterResponder("POST", `http://localhost:12345/transactions`,
func(req *http.Request) (*http.Response, error) {
return httpmock.NewJsonResponderOrPanic(400, asyncTXSubmission{})(req)
return httpmock.NewJsonResponderOrPanic(400, "")(req)
})
_, err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
assert.Regexp(t, "FF10284", err)
}

func TestInvokeContractUnmarshalResponseError(t *testing.T) {
e, cancel := newTestFabric()
defer cancel()
httpmock.ActivateNonDefault(e.client.GetClient())
defer httpmock.DeactivateAndReset()
signingKey := fftypes.NewRandB32().String()
location := &Location{
Channel: "firefly",
Chaincode: "simplestorage",
}
method := testFFIMethod()
params := map[string]interface{}{
"x": float64(1),
"y": float64(2),
}
locationBytes, err := json.Marshal(location)
assert.NoError(t, err)
httpmock.RegisterResponder("POST", `http://localhost:12345/transactions`,
func(req *http.Request) (*http.Response, error) {
var body map[string]interface{}
json.NewDecoder(req.Body).Decode(&body)

assert.Equal(t, signingKey, req.URL.Query().Get(defaultPrefixShort+"-signer"))
assert.Equal(t, "firefly", req.URL.Query().Get(defaultPrefixShort+"-channel"))
assert.Equal(t, "simplestorage", req.URL.Query().Get(defaultPrefixShort+"-chaincode"))
assert.Equal(t, "false", req.URL.Query().Get(defaultPrefixShort+"-sync"))
assert.Equal(t, "1", body["args"].(map[string]interface{})["x"])
assert.Equal(t, "2", body["args"].(map[string]interface{})["y"])
return httpmock.NewStringResponder(200, "[definitely not JSON}")(req)
})
_, err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
assert.Regexp(t, "invalid character", err)
}

func TestQueryContractOK(t *testing.T) {
e, cancel := newTestFabric()
defer cancel()
Expand Down Expand Up @@ -1489,9 +1455,9 @@ func TestInvokeJSONEncodeParamsError(t *testing.T) {
assert.NoError(t, err)
httpmock.RegisterResponder("POST", `http://localhost:12345/transactions`,
func(req *http.Request) (*http.Response, error) {
return httpmock.NewJsonResponderOrPanic(400, asyncTXSubmission{})(req)
return httpmock.NewJsonResponderOrPanic(400, "")(req)
})
_, err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
err = e.InvokeContract(context.Background(), nil, signingKey, fftypes.JSONAnyPtrBytes(locationBytes), method, params)
assert.Regexp(t, "FF10151", err)
}

Expand Down
3 changes: 2 additions & 1 deletion internal/contracts/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,8 @@ func (cm *contractManager) InvokeContract(ctx context.Context, ns string, req *f

switch req.Type {
case fftypes.CallTypeInvoke:
res, err = cm.blockchain.InvokeContract(ctx, op.ID, req.Key, req.Location, req.Method, req.Input)
err = cm.blockchain.InvokeContract(ctx, op.ID, req.Key, req.Location, req.Method, req.Input)
res = &fftypes.ContractCallResponse{ID: op.ID}
case fftypes.CallTypeQuery:
res, err = cm.blockchain.QueryContract(ctx, req.Location, req.Method, req.Input)
default:
Expand Down
8 changes: 4 additions & 4 deletions internal/contracts/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,7 @@ func TestInvokeContract(t *testing.T) {
mdi.On("InsertOperation", mock.Anything, mock.MatchedBy(func(op *fftypes.Operation) bool {
return op.Namespace == "ns1" && op.Type == fftypes.OpTypeBlockchainInvoke && op.Plugin == "mockblockchain"
})).Return(nil)
mbi.On("InvokeContract", mock.Anything, mock.AnythingOfType("*fftypes.UUID"), "key-resolved", req.Location, req.Method, req.Input).Return(struct{}{}, nil)
mbi.On("InvokeContract", mock.Anything, mock.AnythingOfType("*fftypes.UUID"), "key-resolved", req.Location, req.Method, req.Input).Return(nil)

_, err := cm.InvokeContract(context.Background(), "ns1", req)

Expand Down Expand Up @@ -1030,7 +1030,7 @@ func TestInvokeContractFail(t *testing.T) {
mdi.On("InsertOperation", mock.Anything, mock.MatchedBy(func(op *fftypes.Operation) bool {
return op.Namespace == "ns1" && op.Type == fftypes.OpTypeBlockchainInvoke && op.Plugin == "mockblockchain"
})).Return(nil)
mbi.On("InvokeContract", mock.Anything, mock.AnythingOfType("*fftypes.UUID"), "key-resolved", req.Location, req.Method, req.Input).Return(nil, fmt.Errorf("pop"))
mbi.On("InvokeContract", mock.Anything, mock.AnythingOfType("*fftypes.UUID"), "key-resolved", req.Location, req.Method, req.Input).Return(fmt.Errorf("pop"))
mth.On("WriteOperationFailure", mock.Anything, mock.Anything, fmt.Errorf("pop"))

_, err := cm.InvokeContract(context.Background(), "ns1", req)
Expand Down Expand Up @@ -1070,7 +1070,7 @@ func TestInvokeContractFailResolve(t *testing.T) {
}

mim.On("ResolveSigningKey", mock.Anything, "").Return("key-resolved", nil)
mbi.On("InvokeContract", mock.Anything, mock.AnythingOfType("*fftypes.UUID"), "key-resolved", req.Location, req.Method, req.Input).Return(struct{}{}, nil)
mbi.On("InvokeContract", mock.Anything, mock.AnythingOfType("*fftypes.UUID"), "key-resolved", req.Location, req.Method, req.Input).Return(nil)

_, err := cm.InvokeContract(context.Background(), "ns1", req)

Expand Down Expand Up @@ -1389,7 +1389,7 @@ func TestInvokeContractAPI(t *testing.T) {
mdi.On("InsertOperation", mock.Anything, mock.MatchedBy(func(op *fftypes.Operation) bool {
return op.Namespace == "ns1" && op.Type == fftypes.OpTypeBlockchainInvoke && op.Plugin == "mockblockchain"
})).Return(nil)
mbi.On("InvokeContract", mock.Anything, mock.AnythingOfType("*fftypes.UUID"), "key-resolved", req.Location, mock.AnythingOfType("*fftypes.FFIMethod"), req.Input).Return(struct{}{}, nil)
mbi.On("InvokeContract", mock.Anything, mock.AnythingOfType("*fftypes.UUID"), "key-resolved", req.Location, mock.AnythingOfType("*fftypes.FFIMethod"), req.Input).Return(nil)

_, err := cm.InvokeContractAPI(context.Background(), "ns1", "banana", "peel", req)

Expand Down
Loading