github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/blockchain/ethereum/ethereum_test.go (about)

     1  // Copyright © 2021 Kaleido, Inc.
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  
    17  package ethereum
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"net/http"
    24  	"net/url"
    25  	"testing"
    26  
    27  	"github.com/go-resty/resty/v2"
    28  	"github.com/jarcoal/httpmock"
    29  	"github.com/kaleido-io/firefly/internal/config"
    30  	"github.com/kaleido-io/firefly/internal/log"
    31  	"github.com/kaleido-io/firefly/internal/restclient"
    32  	"github.com/kaleido-io/firefly/internal/wsclient"
    33  	"github.com/kaleido-io/firefly/mocks/blockchainmocks"
    34  	"github.com/kaleido-io/firefly/mocks/wsmocks"
    35  	"github.com/kaleido-io/firefly/pkg/blockchain"
    36  	"github.com/kaleido-io/firefly/pkg/fftypes"
    37  	"github.com/stretchr/testify/assert"
    38  	"github.com/stretchr/testify/mock"
    39  )
    40  
    41  var utConfPrefix = config.NewPluginConfig("eth_unit_tests")
    42  var utEthconnectConf = utConfPrefix.SubPrefix(EthconnectConfigKey)
    43  
    44  func resetConf() {
    45  	config.Reset()
    46  	e := &Ethereum{}
    47  	e.InitPrefix(utConfPrefix)
    48  }
    49  
    50  func TestInitMissingURL(t *testing.T) {
    51  	e := &Ethereum{}
    52  	resetConf()
    53  	err := e.Init(context.Background(), utConfPrefix, &blockchainmocks.Callbacks{})
    54  	assert.Regexp(t, "FF10138.*url", err)
    55  }
    56  
    57  func TestInitMissingInstance(t *testing.T) {
    58  	e := &Ethereum{}
    59  	resetConf()
    60  	utEthconnectConf.Set(restclient.HTTPConfigURL, "http://localhost:12345")
    61  	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
    62  
    63  	err := e.Init(context.Background(), utConfPrefix, &blockchainmocks.Callbacks{})
    64  	assert.Regexp(t, "FF10138.*instance", err)
    65  }
    66  
    67  func TestInitMissingTopic(t *testing.T) {
    68  	e := &Ethereum{}
    69  	resetConf()
    70  	utEthconnectConf.Set(restclient.HTTPConfigURL, "http://localhost:12345")
    71  	utEthconnectConf.Set(EthconnectConfigInstancePath, "/instances/0x12345")
    72  
    73  	err := e.Init(context.Background(), utConfPrefix, &blockchainmocks.Callbacks{})
    74  	assert.Regexp(t, "FF10138.*topic", err)
    75  }
    76  
    77  func TestInitAllNewStreamsAndWSEvent(t *testing.T) {
    78  
    79  	log.SetLevel("trace")
    80  	e := &Ethereum{}
    81  
    82  	toServer, fromServer, wsURL, done := wsclient.NewTestWSServer(nil)
    83  	defer done()
    84  
    85  	mockedClient := &http.Client{}
    86  	httpmock.ActivateNonDefault(mockedClient)
    87  	defer httpmock.DeactivateAndReset()
    88  
    89  	u, _ := url.Parse(wsURL)
    90  	u.Scheme = "http"
    91  	httpURL := u.String()
    92  
    93  	httpmock.RegisterResponder("GET", fmt.Sprintf("%s/eventstreams", httpURL),
    94  		httpmock.NewJsonResponderOrPanic(200, []eventStream{}))
    95  	httpmock.RegisterResponder("POST", fmt.Sprintf("%s/eventstreams", httpURL),
    96  		httpmock.NewJsonResponderOrPanic(200, eventStream{ID: "es12345"}))
    97  	httpmock.RegisterResponder("GET", fmt.Sprintf("%s/subscriptions", httpURL),
    98  		httpmock.NewJsonResponderOrPanic(200, []subscription{}))
    99  	httpmock.RegisterResponder("POST", fmt.Sprintf("%s/instances/0x12345/BatchPin", httpURL),
   100  		func(req *http.Request) (*http.Response, error) {
   101  			var body map[string]interface{}
   102  			json.NewDecoder(req.Body).Decode(&body)
   103  			assert.Equal(t, "es12345", body["streamID"])
   104  			return httpmock.NewJsonResponderOrPanic(200, subscription{ID: "sub12345"})(req)
   105  		})
   106  
   107  	resetConf()
   108  	utEthconnectConf.Set(restclient.HTTPConfigURL, httpURL)
   109  	utEthconnectConf.Set(restclient.HTTPCustomClient, mockedClient)
   110  	utEthconnectConf.Set(EthconnectConfigInstancePath, "/instances/0x12345")
   111  	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
   112  
   113  	err := e.Init(context.Background(), utConfPrefix, &blockchainmocks.Callbacks{})
   114  	assert.NoError(t, err)
   115  
   116  	assert.Equal(t, "ethereum", e.Name())
   117  	assert.Equal(t, 4, httpmock.GetTotalCallCount())
   118  	assert.Equal(t, "es12345", e.initInfo.stream.ID)
   119  	assert.Equal(t, "sub12345", e.initInfo.subs[0].ID)
   120  	assert.True(t, e.Capabilities().GlobalSequencer)
   121  
   122  	err = e.Start()
   123  	assert.NoError(t, err)
   124  
   125  	startupMessage := <-toServer
   126  	assert.Equal(t, `{"type":"listen","topic":"topic1"}`, startupMessage)
   127  	startupMessage = <-toServer
   128  	assert.Equal(t, `{"type":"listenreplies"}`, startupMessage)
   129  	fromServer <- `[]` // empty batch, will be ignored, but acked
   130  	reply := <-toServer
   131  	assert.Equal(t, `{"topic":"topic1","type":"ack"}`, reply)
   132  
   133  	// Bad data will be ignored
   134  	fromServer <- `!json`
   135  	fromServer <- `{"not": "a reply"}`
   136  	fromServer <- `42`
   137  
   138  }
   139  
   140  func TestWSInitFail(t *testing.T) {
   141  
   142  	e := &Ethereum{}
   143  
   144  	resetConf()
   145  	utEthconnectConf.Set(restclient.HTTPConfigURL, "!!!://")
   146  	utEthconnectConf.Set(EthconnectConfigInstancePath, "/instances/0x12345")
   147  	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
   148  	utEthconnectConf.Set(EthconnectConfigSkipEventstreamInit, true)
   149  
   150  	err := e.Init(context.Background(), utConfPrefix, &blockchainmocks.Callbacks{})
   151  	assert.Regexp(t, "FF10162", err)
   152  
   153  }
   154  
   155  func TestWSConnectFail(t *testing.T) {
   156  
   157  	wsm := &wsmocks.WSClient{}
   158  	e := &Ethereum{
   159  		ctx:    context.Background(),
   160  		wsconn: wsm,
   161  	}
   162  	wsm.On("Connect").Return(fmt.Errorf("pop"))
   163  
   164  	err := e.Start()
   165  	assert.EqualError(t, err, "pop")
   166  }
   167  
   168  func TestInitAllExistingStreams(t *testing.T) {
   169  
   170  	e := &Ethereum{}
   171  
   172  	mockedClient := &http.Client{}
   173  	httpmock.ActivateNonDefault(mockedClient)
   174  	defer httpmock.DeactivateAndReset()
   175  
   176  	httpmock.RegisterResponder("GET", "http://localhost:12345/eventstreams",
   177  		httpmock.NewJsonResponderOrPanic(200, []eventStream{{ID: "es12345", WebSocket: eventStreamWebsocket{Topic: "topic1"}}}))
   178  	httpmock.RegisterResponder("GET", "http://localhost:12345/subscriptions",
   179  		httpmock.NewJsonResponderOrPanic(200, []subscription{
   180  			{ID: "sub12345", Name: "BatchPin"},
   181  		}))
   182  
   183  	resetConf()
   184  	utEthconnectConf.Set(restclient.HTTPConfigURL, "http://localhost:12345")
   185  	utEthconnectConf.Set(restclient.HTTPCustomClient, mockedClient)
   186  	utEthconnectConf.Set(EthconnectConfigInstancePath, "/instances/0x12345")
   187  	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
   188  
   189  	err := e.Init(context.Background(), utConfPrefix, &blockchainmocks.Callbacks{})
   190  
   191  	assert.Equal(t, 2, httpmock.GetTotalCallCount())
   192  	assert.Equal(t, "es12345", e.initInfo.stream.ID)
   193  	assert.Equal(t, "sub12345", e.initInfo.subs[0].ID)
   194  
   195  	assert.NoError(t, err)
   196  
   197  }
   198  
   199  func TestStreamQueryError(t *testing.T) {
   200  
   201  	e := &Ethereum{}
   202  
   203  	mockedClient := &http.Client{}
   204  	httpmock.ActivateNonDefault(mockedClient)
   205  	defer httpmock.DeactivateAndReset()
   206  
   207  	httpmock.RegisterResponder("GET", "http://localhost:12345/eventstreams",
   208  		httpmock.NewStringResponder(500, `pop`))
   209  
   210  	resetConf()
   211  	utEthconnectConf.Set(restclient.HTTPConfigURL, "http://localhost:12345")
   212  	utEthconnectConf.Set(restclient.HTTPConfigRetryEnabled, false)
   213  	utEthconnectConf.Set(restclient.HTTPCustomClient, mockedClient)
   214  	utEthconnectConf.Set(EthconnectConfigInstancePath, "/instances/0x12345")
   215  	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
   216  
   217  	err := e.Init(context.Background(), utConfPrefix, &blockchainmocks.Callbacks{})
   218  
   219  	assert.Regexp(t, "FF10111", err)
   220  	assert.Regexp(t, "pop", err)
   221  
   222  }
   223  
   224  func TestStreamCreateError(t *testing.T) {
   225  
   226  	e := &Ethereum{}
   227  
   228  	mockedClient := &http.Client{}
   229  	httpmock.ActivateNonDefault(mockedClient)
   230  	defer httpmock.DeactivateAndReset()
   231  
   232  	httpmock.RegisterResponder("GET", "http://localhost:12345/eventstreams",
   233  		httpmock.NewJsonResponderOrPanic(200, []eventStream{}))
   234  	httpmock.RegisterResponder("POST", "http://localhost:12345/eventstreams",
   235  		httpmock.NewStringResponder(500, `pop`))
   236  
   237  	resetConf()
   238  	utEthconnectConf.Set(restclient.HTTPConfigURL, "http://localhost:12345")
   239  	utEthconnectConf.Set(restclient.HTTPConfigRetryEnabled, false)
   240  	utEthconnectConf.Set(restclient.HTTPCustomClient, mockedClient)
   241  	utEthconnectConf.Set(EthconnectConfigInstancePath, "/instances/0x12345")
   242  	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
   243  
   244  	err := e.Init(context.Background(), utConfPrefix, &blockchainmocks.Callbacks{})
   245  
   246  	assert.Regexp(t, "FF10111", err)
   247  	assert.Regexp(t, "pop", err)
   248  
   249  }
   250  
   251  func TestSubQueryError(t *testing.T) {
   252  
   253  	e := &Ethereum{}
   254  
   255  	mockedClient := &http.Client{}
   256  	httpmock.ActivateNonDefault(mockedClient)
   257  	defer httpmock.DeactivateAndReset()
   258  
   259  	httpmock.RegisterResponder("GET", "http://localhost:12345/eventstreams",
   260  		httpmock.NewJsonResponderOrPanic(200, []eventStream{}))
   261  	httpmock.RegisterResponder("POST", "http://localhost:12345/eventstreams",
   262  		httpmock.NewJsonResponderOrPanic(200, eventStream{ID: "es12345"}))
   263  	httpmock.RegisterResponder("GET", "http://localhost:12345/subscriptions",
   264  		httpmock.NewStringResponder(500, `pop`))
   265  
   266  	resetConf()
   267  	utEthconnectConf.Set(restclient.HTTPConfigURL, "http://localhost:12345")
   268  	utEthconnectConf.Set(restclient.HTTPConfigRetryEnabled, false)
   269  	utEthconnectConf.Set(restclient.HTTPCustomClient, mockedClient)
   270  	utEthconnectConf.Set(EthconnectConfigInstancePath, "/instances/0x12345")
   271  	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
   272  
   273  	err := e.Init(context.Background(), utConfPrefix, &blockchainmocks.Callbacks{})
   274  
   275  	assert.Regexp(t, "FF10111", err)
   276  	assert.Regexp(t, "pop", err)
   277  
   278  }
   279  
   280  func TestSubQueryCreateError(t *testing.T) {
   281  
   282  	e := &Ethereum{}
   283  
   284  	mockedClient := &http.Client{}
   285  	httpmock.ActivateNonDefault(mockedClient)
   286  	defer httpmock.DeactivateAndReset()
   287  
   288  	httpmock.RegisterResponder("GET", "http://localhost:12345/eventstreams",
   289  		httpmock.NewJsonResponderOrPanic(200, []eventStream{}))
   290  	httpmock.RegisterResponder("POST", "http://localhost:12345/eventstreams",
   291  		httpmock.NewJsonResponderOrPanic(200, eventStream{ID: "es12345"}))
   292  	httpmock.RegisterResponder("GET", "http://localhost:12345/subscriptions",
   293  		httpmock.NewJsonResponderOrPanic(200, []subscription{}))
   294  	httpmock.RegisterResponder("POST", "http://localhost:12345/instances/0x12345/BatchPin",
   295  		httpmock.NewStringResponder(500, `pop`))
   296  
   297  	resetConf()
   298  	utEthconnectConf.Set(restclient.HTTPConfigURL, "http://localhost:12345")
   299  	utEthconnectConf.Set(restclient.HTTPConfigRetryEnabled, false)
   300  	utEthconnectConf.Set(restclient.HTTPCustomClient, mockedClient)
   301  	utEthconnectConf.Set(EthconnectConfigInstancePath, "/instances/0x12345")
   302  	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
   303  
   304  	err := e.Init(context.Background(), utConfPrefix, &blockchainmocks.Callbacks{})
   305  
   306  	assert.Regexp(t, "FF10111", err)
   307  	assert.Regexp(t, "pop", err)
   308  
   309  }
   310  
   311  func newTestEthereum() *Ethereum {
   312  	return &Ethereum{
   313  		ctx:          context.Background(),
   314  		client:       resty.New().SetHostURL("http://localhost:12345"),
   315  		instancePath: "/instances/0x12345",
   316  		topic:        "topic1",
   317  	}
   318  }
   319  
   320  func TestSubmitBatchPinOK(t *testing.T) {
   321  
   322  	e := newTestEthereum()
   323  	httpmock.ActivateNonDefault(e.client.GetClient())
   324  	defer httpmock.DeactivateAndReset()
   325  
   326  	addr := ethHexFormatB32(fftypes.NewRandB32())
   327  	batch := &blockchain.BatchPin{
   328  		TransactionID:  fftypes.MustParseUUID("9ffc50ff-6bfe-4502-adc7-93aea54cc059"),
   329  		BatchID:        fftypes.MustParseUUID("c5df767c-fe44-4e03-8eb5-1c5523097db5"),
   330  		BatchHash:      fftypes.NewRandB32(),
   331  		BatchPaylodRef: fftypes.NewRandB32(),
   332  		Contexts: []*fftypes.Bytes32{
   333  			fftypes.NewRandB32(),
   334  			fftypes.NewRandB32(),
   335  		},
   336  	}
   337  
   338  	httpmock.RegisterResponder("POST", `http://localhost:12345/instances/0x12345/pinBatch`,
   339  		func(req *http.Request) (*http.Response, error) {
   340  			var body map[string]interface{}
   341  			json.NewDecoder(req.Body).Decode(&body)
   342  			assert.Equal(t, addr, req.FormValue("fly-from"))
   343  			assert.Equal(t, "false", req.FormValue("fly-sync"))
   344  			assert.Equal(t, "0x9ffc50ff6bfe4502adc793aea54cc059c5df767cfe444e038eb51c5523097db5", body["uuids"])
   345  			assert.Equal(t, ethHexFormatB32(batch.BatchHash), body["batchHash"])
   346  			assert.Equal(t, ethHexFormatB32(batch.BatchPaylodRef), body["payloadRef"])
   347  			return httpmock.NewJsonResponderOrPanic(200, asyncTXSubmission{ID: "abcd1234"})(req)
   348  		})
   349  
   350  	txid, err := e.SubmitBatchPin(context.Background(), nil, &fftypes.Identity{OnChain: addr}, batch)
   351  
   352  	assert.NoError(t, err)
   353  	assert.Equal(t, "abcd1234", txid)
   354  
   355  }
   356  
   357  func TestSubmitBatchNilPayloadRef(t *testing.T) {
   358  
   359  	e := newTestEthereum()
   360  	httpmock.ActivateNonDefault(e.client.GetClient())
   361  	defer httpmock.DeactivateAndReset()
   362  
   363  	addr := ethHexFormatB32(fftypes.NewRandB32())
   364  	batch := &blockchain.BatchPin{
   365  		TransactionID: fftypes.MustParseUUID("9ffc50ff-6bfe-4502-adc7-93aea54cc059"),
   366  		BatchID:       fftypes.MustParseUUID("c5df767c-fe44-4e03-8eb5-1c5523097db5"),
   367  		BatchHash:     fftypes.NewRandB32(),
   368  		Contexts: []*fftypes.Bytes32{
   369  			fftypes.NewRandB32(),
   370  			fftypes.NewRandB32(),
   371  		},
   372  	}
   373  
   374  	httpmock.RegisterResponder("POST", `http://localhost:12345/instances/0x12345/pinBatch`,
   375  		func(req *http.Request) (*http.Response, error) {
   376  			var body map[string]interface{}
   377  			json.NewDecoder(req.Body).Decode(&body)
   378  			assert.Equal(t, addr, req.FormValue("fly-from"))
   379  			assert.Equal(t, "false", req.FormValue("fly-sync"))
   380  			assert.Equal(t, "0x9ffc50ff6bfe4502adc793aea54cc059c5df767cfe444e038eb51c5523097db5", body["uuids"])
   381  			assert.Equal(t, ethHexFormatB32(batch.BatchHash), body["batchHash"])
   382  			assert.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000000000", body["payloadRef"])
   383  			return httpmock.NewJsonResponderOrPanic(200, asyncTXSubmission{ID: "abcd1234"})(req)
   384  		})
   385  
   386  	txid, err := e.SubmitBatchPin(context.Background(), nil, &fftypes.Identity{OnChain: addr}, batch)
   387  
   388  	assert.NoError(t, err)
   389  	assert.Equal(t, "abcd1234", txid)
   390  
   391  }
   392  
   393  func TestSubmitBatchPinFail(t *testing.T) {
   394  
   395  	e := newTestEthereum()
   396  	httpmock.ActivateNonDefault(e.client.GetClient())
   397  	defer httpmock.DeactivateAndReset()
   398  
   399  	addr := ethHexFormatB32(fftypes.NewRandB32())
   400  	batch := &blockchain.BatchPin{
   401  		TransactionID:  fftypes.NewUUID(),
   402  		BatchID:        fftypes.NewUUID(),
   403  		BatchHash:      fftypes.NewRandB32(),
   404  		BatchPaylodRef: fftypes.NewRandB32(),
   405  		Contexts: []*fftypes.Bytes32{
   406  			fftypes.NewRandB32(),
   407  			fftypes.NewRandB32(),
   408  		},
   409  	}
   410  
   411  	httpmock.RegisterResponder("POST", `http://localhost:12345/instances/0x12345/pinBatch`,
   412  		httpmock.NewStringResponder(500, "pop"))
   413  
   414  	_, err := e.SubmitBatchPin(context.Background(), nil, &fftypes.Identity{OnChain: addr}, batch)
   415  
   416  	assert.Regexp(t, "FF10111", err)
   417  	assert.Regexp(t, "pop", err)
   418  
   419  }
   420  
   421  func TestVerifyEthAddress(t *testing.T) {
   422  	e := &Ethereum{}
   423  
   424  	id := &fftypes.Identity{OnChain: "0x12345"}
   425  	err := e.VerifyIdentitySyntax(context.Background(), id)
   426  	assert.Regexp(t, "FF10141", err)
   427  
   428  	id = &fftypes.Identity{OnChain: "0x2a7c9D5248681CE6c393117E641aD037F5C079F6"}
   429  	err = e.VerifyIdentitySyntax(context.Background(), id)
   430  	assert.NoError(t, err)
   431  	assert.Equal(t, "0x2a7c9d5248681ce6c393117e641ad037f5c079f6", id.OnChain)
   432  
   433  }
   434  
   435  func TestHandleMessageBatchPinOK(t *testing.T) {
   436  	data := []byte(`
   437  [
   438    {
   439      "address": "0x1C197604587F046FD40684A8f21f4609FB811A7b",
   440      "blockNumber": "38011",
   441      "transactionIndex": "0x0",
   442      "transactionHash": "0xc26df2bf1a733e9249372d61eb11bd8662d26c8129df76890b1beb2f6fa72628",
   443      "data": {
   444        "author": "0X91D2B4381A4CD5C7C0F27565A7D4B829844C8635",
   445  			"namespace": "ns1",
   446  			"uuids": "0xe19af8b390604051812d7597d19adfb9847d3bfd074249efb65d3fed15f5b0a6",
   447        "batchHash": "0xd71eb138d74c229a388eb0e1abc03f4c7cbb21d4fc4b839fbf0ec73e4263f6be",
   448        "payloadRef": "0xeda586bd8f3c4bc1db5c4b5755113b9a9b4174abe28679fdbc219129400dd7ae",
   449  			"contexts": [
   450  				"0x68e4da79f805bca5b912bcda9c63d03e6e867108dabb9b944109aea541ef522a",
   451  				"0x19b82093de5ce92a01e333048e877e2374354bf846dd034864ef6ffbd6438771"
   452  			],
   453        "timestamp": "1620576488"
   454      },
   455      "subID": "sb-b5b97a4e-a317-4053-6400-1474650efcb5",
   456      "signature": "BatchPin(address,uint256,string,bytes32,bytes32,bytes32,bytes32[])",
   457      "logIndex": "50"
   458    },
   459    {
   460      "address": "0x1C197604587F046FD40684A8f21f4609FB811A7b",
   461      "blockNumber": "38011",
   462      "transactionIndex": "0x1",
   463      "transactionHash": "0x0c50dff0893e795293189d9cc5ba0d63c4020d8758ace4a69d02c9d6d43cb695",
   464      "data": {
   465        "author": "0x91d2b4381a4cd5c7c0f27565a7d4b829844c8635",
   466  			"namespace": "ns1",
   467  			"uuids": "0x8a578549e56b49f9bd78d731f22b08d7a04c7cc37d444c2ba3b054e21326697e",
   468        "batchHash": "0x20e6ef9b9c4df7fdb77a7de1e00347f4b02d996f2e56a7db361038be7b32a154",
   469        "payloadRef": "0x23ad1bc340ac7516f0cbf1be677122303ffce81f32400c440295c44d7963d185",
   470        "timestamp": "1620576488",
   471  			"contexts": [
   472  				"0x8a63eb509713b0cf9250a8eee24ee2dfc4b37225e3ad5c29c95127699d382f85"
   473  			]
   474      },
   475      "subID": "sb-b5b97a4e-a317-4053-6400-1474650efcb5",
   476      "signature": "BatchPin(address,uint256,string,bytes32,bytes32,bytes32,bytes32[])",
   477      "logIndex": "51"
   478    },
   479  	{
   480      "address": "0x06d34B270F15a0d82913EFD0627B0F62Fd22ecd5",
   481      "blockNumber": "38011",
   482      "transactionIndex": "0x2",
   483      "transactionHash": "0x0c50dff0893e795293189d9cc5ba0d63c4020d8758ace4a69d02c9d6d43cb695",
   484      "data": {
   485        "author": "0x91d2b4381a4cd5c7c0f27565a7d4b829844c8635",
   486  			"namespace": "ns1",
   487  			"uuids": "0x8a578549e56b49f9bd78d731f22b08d7a04c7cc37d444c2ba3b054e21326697e",
   488        "batchHash": "0x892b31099b8476c0692a5f2982ea23a0614949eacf292a64a358aa73ecd404b4",
   489        "payloadRef": "0x23ad1bc340ac7516f0cbf1be677122303ffce81f32400c440295c44d7963d185",
   490        "timestamp": "1620576488",
   491  			"contexts": [
   492  				"0xdab67320f1a0d0f1da572975e3a9ab6ef0fed315771c99fea0bfb54886c1aa94"
   493  			]
   494      },
   495      "subID": "sb-b5b97a4e-a317-4053-6400-1474650efcb5",
   496      "signature": "Random(address,uint256,bytes32,bytes32,bytes32)",
   497      "logIndex": "51"
   498    }
   499  ]`)
   500  
   501  	em := &blockchainmocks.Callbacks{}
   502  	e := &Ethereum{
   503  		callbacks: em,
   504  	}
   505  
   506  	em.On("BatchPinComplete", mock.Anything, "0x91d2b4381a4cd5c7c0f27565a7d4b829844c8635", mock.Anything, mock.Anything).Return(nil)
   507  
   508  	var events []interface{}
   509  	err := json.Unmarshal(data, &events)
   510  	assert.NoError(t, err)
   511  	err = e.handleMessageBatch(context.Background(), events)
   512  	assert.NoError(t, err)
   513  
   514  	b := em.Calls[0].Arguments[0].(*blockchain.BatchPin)
   515  	assert.Equal(t, "ns1", b.Namespace)
   516  	assert.Equal(t, "e19af8b3-9060-4051-812d-7597d19adfb9", b.TransactionID.String())
   517  	assert.Equal(t, "847d3bfd-0742-49ef-b65d-3fed15f5b0a6", b.BatchID.String())
   518  	assert.Equal(t, "d71eb138d74c229a388eb0e1abc03f4c7cbb21d4fc4b839fbf0ec73e4263f6be", b.BatchHash.String())
   519  	assert.Equal(t, "eda586bd8f3c4bc1db5c4b5755113b9a9b4174abe28679fdbc219129400dd7ae", b.BatchPaylodRef.String())
   520  	assert.Equal(t, "0x91d2b4381a4cd5c7c0f27565a7d4b829844c8635", em.Calls[0].Arguments[1])
   521  	assert.Equal(t, "0xc26df2bf1a733e9249372d61eb11bd8662d26c8129df76890b1beb2f6fa72628", em.Calls[0].Arguments[2])
   522  	assert.Len(t, b.Contexts, 2)
   523  	assert.Equal(t, "68e4da79f805bca5b912bcda9c63d03e6e867108dabb9b944109aea541ef522a", b.Contexts[0].String())
   524  	assert.Equal(t, "19b82093de5ce92a01e333048e877e2374354bf846dd034864ef6ffbd6438771", b.Contexts[1].String())
   525  
   526  	em.AssertExpectations(t)
   527  
   528  }
   529  
   530  func TestHandleMessageNilPayloadRef(t *testing.T) {
   531  	data := []byte(`
   532  [
   533    {
   534      "address": "0x1C197604587F046FD40684A8f21f4609FB811A7b",
   535      "blockNumber": "38011",
   536      "transactionIndex": "0x0",
   537      "transactionHash": "0xc26df2bf1a733e9249372d61eb11bd8662d26c8129df76890b1beb2f6fa72628",
   538      "data": {
   539        "author": "0X91D2B4381A4CD5C7C0F27565A7D4B829844C8635",
   540  			"namespace": "ns1",
   541  			"uuids": "0xe19af8b390604051812d7597d19adfb9847d3bfd074249efb65d3fed15f5b0a6",
   542        "batchHash": "0xd71eb138d74c229a388eb0e1abc03f4c7cbb21d4fc4b839fbf0ec73e4263f6be",
   543        "payloadRef": "0x0000000000000000000000000000000000000000000000000000000000000000",
   544  			"contexts": [
   545  				"0x68e4da79f805bca5b912bcda9c63d03e6e867108dabb9b944109aea541ef522a",
   546  				"0x19b82093de5ce92a01e333048e877e2374354bf846dd034864ef6ffbd6438771"
   547  			],
   548        "timestamp": "1620576488"
   549      },
   550      "subID": "sb-b5b97a4e-a317-4053-6400-1474650efcb5",
   551      "signature": "BatchPin(address,uint256,string,bytes32,bytes32,bytes32,bytes32[])",
   552      "logIndex": "50"
   553    }
   554  ]`)
   555  
   556  	em := &blockchainmocks.Callbacks{}
   557  	e := &Ethereum{
   558  		callbacks: em,
   559  	}
   560  
   561  	em.On("BatchPinComplete", mock.Anything, "0x91d2b4381a4cd5c7c0f27565a7d4b829844c8635", mock.Anything, mock.Anything).Return(nil)
   562  
   563  	var events []interface{}
   564  	err := json.Unmarshal(data, &events)
   565  	assert.NoError(t, err)
   566  	err = e.handleMessageBatch(context.Background(), events)
   567  	assert.NoError(t, err)
   568  
   569  	b := em.Calls[0].Arguments[0].(*blockchain.BatchPin)
   570  	assert.Equal(t, "ns1", b.Namespace)
   571  	assert.Equal(t, "e19af8b3-9060-4051-812d-7597d19adfb9", b.TransactionID.String())
   572  	assert.Equal(t, "847d3bfd-0742-49ef-b65d-3fed15f5b0a6", b.BatchID.String())
   573  	assert.Equal(t, "d71eb138d74c229a388eb0e1abc03f4c7cbb21d4fc4b839fbf0ec73e4263f6be", b.BatchHash.String())
   574  	assert.Nil(t, b.BatchPaylodRef)
   575  	assert.Equal(t, "0x91d2b4381a4cd5c7c0f27565a7d4b829844c8635", em.Calls[0].Arguments[1])
   576  	assert.Equal(t, "0xc26df2bf1a733e9249372d61eb11bd8662d26c8129df76890b1beb2f6fa72628", em.Calls[0].Arguments[2])
   577  	assert.Len(t, b.Contexts, 2)
   578  	assert.Equal(t, "68e4da79f805bca5b912bcda9c63d03e6e867108dabb9b944109aea541ef522a", b.Contexts[0].String())
   579  	assert.Equal(t, "19b82093de5ce92a01e333048e877e2374354bf846dd034864ef6ffbd6438771", b.Contexts[1].String())
   580  
   581  	em.AssertExpectations(t)
   582  
   583  }
   584  
   585  func TestHandleMessageBatchPinExit(t *testing.T) {
   586  	data := []byte(`
   587  [
   588    {
   589      "address": "0x1C197604587F046FD40684A8f21f4609FB811A7b",
   590      "blockNumber": "38011",
   591      "transactionIndex": "0x1",
   592      "transactionHash": "0x0c50dff0893e795293189d9cc5ba0d63c4020d8758ace4a69d02c9d6d43cb695",
   593      "data": {
   594        "author": "0x91d2b4381a4cd5c7c0f27565a7d4b829844c8635",
   595  			"namespace": "ns1",
   596  			"uuids": "0xe19af8b390604051812d7597d19adfb9a04c7cc37d444c2ba3b054e21326697e",
   597  			"batchHash": "0x9c19a93b6e85fee041f60f097121829e54cd4aa97ed070d1bc76147caf911fed",
   598        "payloadRef": "0x23ad1bc340ac7516f0cbf1be677122303ffce81f32400c440295c44d7963d185",
   599        "timestamp": "1620576488"
   600      },
   601      "subID": "sb-b5b97a4e-a317-4053-6400-1474650efcb5",
   602      "signature": "BatchPin(address,uint256,string,bytes32,bytes32,bytes32,bytes32[])",
   603      "logIndex": "51"
   604    }
   605  ]`)
   606  
   607  	em := &blockchainmocks.Callbacks{}
   608  	e := &Ethereum{
   609  		callbacks: em,
   610  	}
   611  
   612  	em.On("BatchPinComplete", mock.Anything, "0x91d2b4381a4cd5c7c0f27565a7d4b829844c8635", mock.Anything, mock.Anything).Return(fmt.Errorf("pop"))
   613  
   614  	var events []interface{}
   615  	err := json.Unmarshal(data, &events)
   616  	assert.NoError(t, err)
   617  	err = e.handleMessageBatch(context.Background(), events)
   618  	assert.EqualError(t, err, "pop")
   619  
   620  }
   621  
   622  func TestHandleMessageBatchPinEmpty(t *testing.T) {
   623  	em := &blockchainmocks.Callbacks{}
   624  	e := &Ethereum{callbacks: em}
   625  	var events []interface{}
   626  	err := json.Unmarshal([]byte(`[{"signature": "BatchPin(address,uint256,string,bytes32,bytes32,bytes32,bytes32[])"}]`), &events)
   627  	assert.NoError(t, err)
   628  	err = e.handleMessageBatch(context.Background(), events)
   629  	assert.NoError(t, err)
   630  	assert.Equal(t, 0, len(em.Calls))
   631  }
   632  
   633  func TestHandleMessageBatchPinBadTransactionID(t *testing.T) {
   634  	em := &blockchainmocks.Callbacks{}
   635  	e := &Ethereum{callbacks: em}
   636  	data := []byte(`[{
   637  		"signature": "BatchPin(address,uint256,string,bytes32,bytes32,bytes32,bytes32[])",
   638      "blockNumber": "38011",
   639      "transactionIndex": "0x1",
   640      "transactionHash": "0x0c50dff0893e795293189d9cc5ba0d63c4020d8758ace4a69d02c9d6d43cb695",
   641  		"data": {
   642        "author": "0X91D2B4381A4CD5C7C0F27565A7D4B829844C8635",
   643  			"namespace": "ns1",
   644  			"uuids": "!good",
   645  			"batchHash": "0xd71eb138d74c229a388eb0e1abc03f4c7cbb21d4fc4b839fbf0ec73e4263f6be",
   646        "payloadRef": "0xeda586bd8f3c4bc1db5c4b5755113b9a9b4174abe28679fdbc219129400dd7ae",
   647  			"contexts": [
   648  				"0xb41753f11522d4ef5c4a467972cf54744c04628ff84a1c994f1b288b2f6ec836",
   649  				"0xc6c683a0fbe15e452e1ecc3751657446e2f645a8231e3ef9f3b4a8eae03c4136"
   650  			],
   651  			"timestamp": "!1620576488"
   652  		}
   653  	}]`)
   654  	var events []interface{}
   655  	err := json.Unmarshal(data, &events)
   656  	assert.NoError(t, err)
   657  	err = e.handleMessageBatch(context.Background(), events)
   658  	assert.NoError(t, err)
   659  	assert.Equal(t, 0, len(em.Calls))
   660  }
   661  
   662  func TestHandleMessageBatchPinBadIDentity(t *testing.T) {
   663  	em := &blockchainmocks.Callbacks{}
   664  	e := &Ethereum{callbacks: em}
   665  	data := []byte(`[{
   666  		"signature": "BatchPin(address,uint256,string,bytes32,bytes32,bytes32,bytes32[])",
   667      "blockNumber": "38011",
   668      "transactionIndex": "0x1",
   669      "transactionHash": "0x0c50dff0893e795293189d9cc5ba0d63c4020d8758ace4a69d02c9d6d43cb695",
   670  		"data": {
   671        "author": "!good",
   672  			"namespace": "ns1",
   673  			"uuids": "0xe19af8b390604051812d7597d19adfb9847d3bfd074249efb65d3fed15f5b0a6",
   674  			"batchHash": "0xd71eb138d74c229a388eb0e1abc03f4c7cbb21d4fc4b839fbf0ec73e4263f6be",
   675        "payloadRef": "0xeda586bd8f3c4bc1db5c4b5755113b9a9b4174abe28679fdbc219129400dd7ae",
   676  			"contexts": [
   677  				"0xb41753f11522d4ef5c4a467972cf54744c04628ff84a1c994f1b288b2f6ec836",
   678  				"0xc6c683a0fbe15e452e1ecc3751657446e2f645a8231e3ef9f3b4a8eae03c4136"
   679  			],
   680  			"timestamp": "1620576488"
   681  		}
   682  	}]`)
   683  	var events []interface{}
   684  	err := json.Unmarshal(data, &events)
   685  	assert.NoError(t, err)
   686  	err = e.handleMessageBatch(context.Background(), events)
   687  	assert.NoError(t, err)
   688  	assert.Equal(t, 0, len(em.Calls))
   689  }
   690  
   691  func TestHandleMessageBatchPinBadBatchHash(t *testing.T) {
   692  	em := &blockchainmocks.Callbacks{}
   693  	e := &Ethereum{callbacks: em}
   694  	data := []byte(`[{
   695  		"signature": "BatchPin(address,uint256,string,bytes32,bytes32,bytes32,bytes32[])",
   696      "blockNumber": "38011",
   697      "transactionIndex": "0x1",
   698      "transactionHash": "0x0c50dff0893e795293189d9cc5ba0d63c4020d8758ace4a69d02c9d6d43cb695",
   699  		"data": {
   700        "author": "0X91D2B4381A4CD5C7C0F27565A7D4B829844C8635",
   701  			"namespace": "ns1",
   702  			"uuids": "0xe19af8b390604051812d7597d19adfb9847d3bfd074249efb65d3fed15f5b0a6",
   703  			"batchHash": "!good",
   704        "payloadRef": "0xeda586bd8f3c4bc1db5c4b5755113b9a9b4174abe28679fdbc219129400dd7ae",
   705  			"contexts": [
   706  				"0xb41753f11522d4ef5c4a467972cf54744c04628ff84a1c994f1b288b2f6ec836",
   707  				"0xc6c683a0fbe15e452e1ecc3751657446e2f645a8231e3ef9f3b4a8eae03c4136"
   708  			],
   709  			"timestamp": "1620576488"
   710  		}
   711  	}]`)
   712  	var events []interface{}
   713  	err := json.Unmarshal(data, &events)
   714  	assert.NoError(t, err)
   715  	err = e.handleMessageBatch(context.Background(), events)
   716  	assert.NoError(t, err)
   717  	assert.Equal(t, 0, len(em.Calls))
   718  }
   719  
   720  func TestHandleMessageBatchPinBadPayloadRef(t *testing.T) {
   721  	em := &blockchainmocks.Callbacks{}
   722  	e := &Ethereum{callbacks: em}
   723  	data := []byte(`[{
   724  		"signature": "BatchPin(address,uint256,string,bytes32,bytes32,bytes32,bytes32[])",
   725      "blockNumber": "38011",
   726      "transactionIndex": "0x1",
   727      "transactionHash": "0x0c50dff0893e795293189d9cc5ba0d63c4020d8758ace4a69d02c9d6d43cb695",
   728  		"data": {
   729        "author": "0X91D2B4381A4CD5C7C0F27565A7D4B829844C8635",
   730  			"namespace": "ns1",
   731  			"uuids": "0xe19af8b390604051812d7597d19adfb9847d3bfd074249efb65d3fed15f5b0a6",
   732  			"batchHash": "0xd71eb138d74c229a388eb0e1abc03f4c7cbb21d4fc4b839fbf0ec73e4263f6be",
   733        "payloadRef": "!good",
   734  			"contexts": [
   735  				"0xb41753f11522d4ef5c4a467972cf54744c04628ff84a1c994f1b288b2f6ec836",
   736  				"0xc6c683a0fbe15e452e1ecc3751657446e2f645a8231e3ef9f3b4a8eae03c4136"
   737  			],
   738  			"timestamp": "1620576488"
   739  		}
   740  	}]`)
   741  	var events []interface{}
   742  	err := json.Unmarshal(data, &events)
   743  	assert.NoError(t, err)
   744  	err = e.handleMessageBatch(context.Background(), events)
   745  	assert.NoError(t, err)
   746  	assert.Equal(t, 0, len(em.Calls))
   747  }
   748  
   749  func TestHandleMessageBatchPinBadPin(t *testing.T) {
   750  	em := &blockchainmocks.Callbacks{}
   751  	e := &Ethereum{callbacks: em}
   752  	data := []byte(`[{
   753  		"signature": "BatchPin(address,uint256,string,bytes32,bytes32,bytes32,bytes32[])",
   754      "blockNumber": "38011",
   755      "transactionIndex": "0x1",
   756      "transactionHash": "0x0c50dff0893e795293189d9cc5ba0d63c4020d8758ace4a69d02c9d6d43cb695",
   757  		"data": {
   758        "author": "0X91D2B4381A4CD5C7C0F27565A7D4B829844C8635",
   759  			"namespace": "ns1",
   760  			"uuids": "0xe19af8b390604051812d7597d19adfb9847d3bfd074249efb65d3fed15f5b0a6",
   761  			"batchHash": "0xd71eb138d74c229a388eb0e1abc03f4c7cbb21d4fc4b839fbf0ec73e4263f6be",
   762        "payloadRef": "0xeda586bd8f3c4bc1db5c4b5755113b9a9b4174abe28679fdbc219129400dd7ae",
   763  			"contexts": [
   764  				"0xb41753f11522d4ef5c4a467972cf54744c04628ff84a1c994f1b288b2f6ec836",
   765  				"!good"
   766  			],
   767  			"timestamp": "1620576488"
   768  		}
   769  	}]`)
   770  	var events []interface{}
   771  	err := json.Unmarshal(data, &events)
   772  	assert.NoError(t, err)
   773  	err = e.handleMessageBatch(context.Background(), events)
   774  	assert.NoError(t, err)
   775  	assert.Equal(t, 0, len(em.Calls))
   776  }
   777  
   778  func TestHandleMessageBatchBadJSON(t *testing.T) {
   779  	em := &blockchainmocks.Callbacks{}
   780  	e := &Ethereum{callbacks: em}
   781  	err := e.handleMessageBatch(context.Background(), []interface{}{10, 20})
   782  	assert.NoError(t, err)
   783  	assert.Equal(t, 0, len(em.Calls))
   784  }
   785  
   786  func TestEventLoopContextCancelled(t *testing.T) {
   787  	em := &blockchainmocks.Callbacks{}
   788  	wsm := &wsmocks.WSClient{}
   789  	ctxCancelled, cancel := context.WithCancel(context.Background())
   790  	cancel()
   791  	e := &Ethereum{
   792  		ctx:       ctxCancelled,
   793  		topic:     "topic1",
   794  		callbacks: em,
   795  		wsconn:    wsm,
   796  	}
   797  	r := make(<-chan []byte)
   798  	wsm.On("Receive").Return(r)
   799  	e.eventLoop() // we're simply looking for it exiting
   800  }
   801  
   802  func TestEventLoopReceiveClosed(t *testing.T) {
   803  	em := &blockchainmocks.Callbacks{}
   804  	wsm := &wsmocks.WSClient{}
   805  	e := &Ethereum{
   806  		ctx:       context.Background(),
   807  		topic:     "topic1",
   808  		callbacks: em,
   809  		wsconn:    wsm,
   810  	}
   811  	r := make(chan []byte)
   812  	close(r)
   813  	wsm.On("Receive").Return((<-chan []byte)(r))
   814  	e.eventLoop() // we're simply looking for it exiting
   815  }
   816  
   817  func TestEventLoopSendClosed(t *testing.T) {
   818  	em := &blockchainmocks.Callbacks{}
   819  	wsm := &wsmocks.WSClient{}
   820  	e := &Ethereum{
   821  		ctx:       context.Background(),
   822  		topic:     "topic1",
   823  		callbacks: em,
   824  		wsconn:    wsm,
   825  	}
   826  	r := make(chan []byte, 1)
   827  	r <- []byte(`[]`)
   828  	wsm.On("Receive").Return((<-chan []byte)(r))
   829  	wsm.On("Send", mock.Anything, mock.Anything).Return(fmt.Errorf("pop"))
   830  	e.eventLoop() // we're simply looking for it exiting
   831  }
   832  
   833  func TestHandleReceiptTXSuccess(t *testing.T) {
   834  	em := &blockchainmocks.Callbacks{}
   835  	wsm := &wsmocks.WSClient{}
   836  	e := &Ethereum{
   837  		ctx:       context.Background(),
   838  		topic:     "topic1",
   839  		callbacks: em,
   840  		wsconn:    wsm,
   841  	}
   842  
   843  	var reply fftypes.JSONObject
   844  	data := []byte(`{
   845      "_id": "4373614c-e0f7-47b0-640e-7eacec417a9e",
   846      "blockHash": "0xad269b2b43481e44500f583108e8d24bd841fb767c7f526772959d195b9c72d5",
   847      "blockNumber": "209696",
   848      "cumulativeGasUsed": "24655",
   849      "from": "0x91d2b4381a4cd5c7c0f27565a7d4b829844c8635",
   850      "gasUsed": "24655",
   851      "headers": {
   852        "id": "4603a151-f212-446e-5c15-0f36b57cecc7",
   853        "requestId": "4373614c-e0f7-47b0-640e-7eacec417a9e",
   854        "requestOffset": "zzn4y4v4si-zzjjepe9x4-requests:0:12",
   855        "timeElapsed": 3.966414429,
   856        "timeReceived": "2021-05-28T20:54:27.481245697Z",
   857        "type": "TransactionSuccess"
   858      },
   859      "nonce": "0",
   860      "receivedAt": 1622235271565,
   861      "status": "1",
   862      "to": "0xd3266a857285fb75eb7df37353b4a15c8bb828f5",
   863      "transactionHash": "0x71a38acb7a5d4a970854f6d638ceb1fa10a4b59cbf4ed7674273a1a8dc8b36b8",
   864      "transactionIndex": "0"
   865    }`)
   866  
   867  	em.On("TxSubmissionUpdate",
   868  		"4373614c-e0f7-47b0-640e-7eacec417a9e",
   869  		fftypes.OpStatusSucceeded,
   870  		"0x71a38acb7a5d4a970854f6d638ceb1fa10a4b59cbf4ed7674273a1a8dc8b36b8",
   871  		"",
   872  		mock.Anything).Return(nil)
   873  
   874  	err := json.Unmarshal(data, &reply)
   875  	assert.NoError(t, err)
   876  	err = e.handleReceipt(context.Background(), reply)
   877  	assert.NoError(t, err)
   878  
   879  }
   880  
   881  func TestHandleReceiptTXFail(t *testing.T) {
   882  	em := &blockchainmocks.Callbacks{}
   883  	wsm := &wsmocks.WSClient{}
   884  	e := &Ethereum{
   885  		ctx:       context.Background(),
   886  		topic:     "topic1",
   887  		callbacks: em,
   888  		wsconn:    wsm,
   889  	}
   890  
   891  	var reply fftypes.JSONObject
   892  	data := []byte(`{
   893  		"_id": "6fb94fff-81d3-4094-567d-e031b1871694",
   894  		"errorMessage": "Packing arguments for method 'broadcastBatch': abi: cannot use [3]uint8 as type [32]uint8 as argument",
   895  		"headers": {
   896  			"id": "3a37b17b-13b6-4dc5-647a-07c11eae0be3",
   897  			"requestId": "6fb94fff-81d3-4094-567d-e031b1871694",
   898  			"requestOffset": "zzn4y4v4si-zzjjepe9x4-requests:0:0",
   899  			"timeElapsed": 0.020969053,
   900  			"timeReceived": "2021-05-31T02:35:11.458880504Z",
   901  			"type": "Error"
   902  		},
   903  		"receivedAt": 1622428511616,
   904  		"requestPayload": "{\"from\":\"0x91d2b4381a4cd5c7c0f27565a7d4b829844c8635\",\"gas\":0,\"gasPrice\":0,\"headers\":{\"id\":\"6fb94fff-81d3-4094-567d-e031b1871694\",\"type\":\"SendTransaction\"},\"method\":{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"txnId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"batchId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"payloadRef\",\"type\":\"bytes32\"}],\"name\":\"broadcastBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},\"params\":[\"12345\",\"!\",\"!\"],\"to\":\"0xd3266a857285fb75eb7df37353b4a15c8bb828f5\",\"value\":0}"
   905  	}`)
   906  
   907  	em.On("TxSubmissionUpdate",
   908  		"6fb94fff-81d3-4094-567d-e031b1871694",
   909  		fftypes.OpStatusFailed,
   910  		"",
   911  		"Packing arguments for method 'broadcastBatch': abi: cannot use [3]uint8 as type [32]uint8 as argument",
   912  		mock.Anything).Return(nil)
   913  
   914  	err := json.Unmarshal(data, &reply)
   915  	assert.NoError(t, err)
   916  	err = e.handleReceipt(context.Background(), reply)
   917  	assert.NoError(t, err)
   918  
   919  }