diff --git a/internal/contracts/operations.go b/internal/contracts/operations.go index c308aa51a..cbb9c63e3 100644 --- a/internal/contracts/operations.go +++ b/internal/contracts/operations.go @@ -1,4 +1,4 @@ -// Copyright © 2023 Kaleido, Inc. +// Copyright © 2024 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -17,6 +17,7 @@ package contracts import ( + "bytes" "context" "encoding/json" @@ -38,7 +39,9 @@ type blockchainContractDeployData struct { func addBlockchainReqInputs(op *core.Operation, req interface{}) (err error) { var reqJSON []byte if reqJSON, err = json.Marshal(req); err == nil { - err = json.Unmarshal(reqJSON, &op.Input) + d := json.NewDecoder(bytes.NewReader(reqJSON)) + d.UseNumber() + err = d.Decode(&op.Input) } return err } diff --git a/internal/contracts/operations_test.go b/internal/contracts/operations_test.go index bd425dabc..b3dcb9b4f 100644 --- a/internal/contracts/operations_test.go +++ b/internal/contracts/operations_test.go @@ -1,4 +1,4 @@ -// Copyright © 2023 Kaleido, Inc. +// Copyright © 2024 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -16,7 +16,9 @@ package contracts import ( + "bytes" "context" + "encoding/json" "fmt" "testing" @@ -35,6 +37,21 @@ import ( "github.com/stretchr/testify/mock" ) +const sampleRequestLargeNumberInput = `{ + "location": { + "address": "0x1111" + }, + "key": "0x123", + "method": { + "name": "set", + "params": null, + "returns": null + }, + "input": { + "value": 10000000000000000000000000001 + } +}` + func reqWithMessage(msgType core.MessageType) *core.ContractCallRequest { return &core.ContractCallRequest{ Key: "0x123", @@ -60,6 +77,7 @@ func reqWithMessage(msgType core.MessageType) *core.ContractCallRequest { } func TestPrepareAndRunBlockchainInvoke(t *testing.T) { + cm := newTestContractManager() op := &core.Operation{ @@ -101,6 +119,46 @@ func TestPrepareAndRunBlockchainInvoke(t *testing.T) { mbi.AssertExpectations(t) } +func TestPrepareAndRunBlockchainInvokeLargeNumberInput(t *testing.T) { + + cm := newTestContractManager() + + var req core.ContractCallRequest + d := json.NewDecoder(bytes.NewReader([]byte(sampleRequestLargeNumberInput))) + d.UseNumber() + err := d.Decode(&req) + assert.NoError(t, err) + + op := &core.Operation{ + Type: core.OpTypeBlockchainInvoke, + ID: fftypes.NewUUID(), + Namespace: "ns1", + } + + err = addBlockchainReqInputs(op, req) + assert.NoError(t, err) + + mbi := cm.blockchain.(*blockchainmocks.Plugin) + opaqueData := "anything" + mbi.On("ParseInterface", context.Background(), mock.MatchedBy(func(method *fftypes.FFIMethod) bool { + return method.Name == req.Method.Name + }), req.Errors).Return(opaqueData, nil) + mbi.On("InvokeContract", context.Background(), "ns1:"+op.ID.String(), "0x123", mock.MatchedBy(func(loc *fftypes.JSONAny) bool { + return loc.String() == req.Location.String() + }), opaqueData, req.Input, req.Options, (*blockchain.BatchPin)(nil)).Return(false, nil) + + po, err := cm.PrepareOperation(context.Background(), op) + assert.NoError(t, err) + assert.Equal(t, &req, po.Data.(txcommon.BlockchainInvokeData).Request) + + _, phase, err := cm.RunOperation(context.Background(), po) + + assert.Equal(t, core.OpPhasePending, phase) + assert.NoError(t, err) + + mbi.AssertExpectations(t) +} + func TestPrepareAndRunBlockchainInvokeRejected(t *testing.T) { cm := newTestContractManager() diff --git a/internal/txcommon/contract_inputs.go b/internal/txcommon/contract_inputs.go index 942ca6625..9b900f023 100644 --- a/internal/txcommon/contract_inputs.go +++ b/internal/txcommon/contract_inputs.go @@ -1,4 +1,4 @@ -// Copyright © 2023 Kaleido, Inc. +// Copyright © 2024 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -17,6 +17,7 @@ package txcommon import ( + "bytes" "context" "encoding/json" @@ -39,7 +40,9 @@ type BlockchainInvokeData struct { func RetrieveBlockchainInvokeInputs(ctx context.Context, op *core.Operation) (*core.ContractCallRequest, error) { var req core.ContractCallRequest s := op.Input.String() - if err := json.Unmarshal([]byte(s), &req); err != nil { + d := json.NewDecoder(bytes.NewReader([]byte(s))) + d.UseNumber() + if err := d.Decode(&req); err != nil { return nil, i18n.WrapError(ctx, err, i18n.MsgJSONObjectParseFailed, s) } return &req, nil