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 }