github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/events/batch_pin_complete_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 events
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"encoding/json"
    23  	"fmt"
    24  	"io/ioutil"
    25  	"testing"
    26  
    27  	"github.com/kaleido-io/firefly/mocks/blockchainmocks"
    28  	"github.com/kaleido-io/firefly/mocks/databasemocks"
    29  	"github.com/kaleido-io/firefly/mocks/identitymocks"
    30  	"github.com/kaleido-io/firefly/mocks/publicstoragemocks"
    31  	"github.com/kaleido-io/firefly/pkg/blockchain"
    32  	"github.com/kaleido-io/firefly/pkg/database"
    33  	"github.com/kaleido-io/firefly/pkg/fftypes"
    34  	"github.com/stretchr/testify/assert"
    35  	"github.com/stretchr/testify/mock"
    36  )
    37  
    38  func TestBatchPinCompleteOkBroadcast(t *testing.T) {
    39  	em, cancel := newTestEventManager(t)
    40  	defer cancel()
    41  
    42  	batch := &blockchain.BatchPin{
    43  		Namespace:      "ns1",
    44  		TransactionID:  fftypes.NewUUID(),
    45  		BatchID:        fftypes.NewUUID(),
    46  		BatchPaylodRef: fftypes.NewRandB32(),
    47  		Contexts:       []*fftypes.Bytes32{fftypes.NewRandB32()},
    48  	}
    49  	batchData := &fftypes.Batch{
    50  		ID:         batch.BatchID,
    51  		Namespace:  "ns1",
    52  		Author:     "0x12345",
    53  		PayloadRef: batch.BatchPaylodRef,
    54  		Payload: fftypes.BatchPayload{
    55  			TX: fftypes.TransactionRef{
    56  				Type: fftypes.TransactionTypeBatchPin,
    57  				ID:   batch.TransactionID,
    58  			},
    59  			Messages: []*fftypes.Message{},
    60  			Data:     []*fftypes.Data{},
    61  		},
    62  	}
    63  	batchDataBytes, err := json.Marshal(&batchData)
    64  	assert.NoError(t, err)
    65  	batchReadCloser := ioutil.NopCloser(bytes.NewReader(batchDataBytes))
    66  
    67  	mpi := em.publicstorage.(*publicstoragemocks.Plugin)
    68  	mpi.On("RetrieveData", mock.Anything, mock.
    69  		MatchedBy(func(pr *fftypes.Bytes32) bool { return *pr == *batch.BatchPaylodRef })).
    70  		Return(batchReadCloser, nil)
    71  
    72  	mdi := em.database.(*databasemocks.Plugin)
    73  	rag := mdi.On("RunAsGroup", mock.Anything, mock.Anything).Return(nil)
    74  	rag.RunFn = func(a mock.Arguments) {
    75  		// Call through to persistBatch - the hash of our batch will be invalid,
    76  		// which is swallowed without error as we cannot retry (it is logged of course)
    77  		rag.ReturnArguments = mock.Arguments{
    78  			a[1].(func(ctx context.Context) error)(a[0].(context.Context)),
    79  		}
    80  	}
    81  	mdi.On("GetTransactionByID", mock.Anything, uuidMatches(batchData.Payload.TX.ID)).Return(nil, nil)
    82  	mdi.On("UpsertTransaction", mock.Anything, mock.Anything, true, false).Return(nil)
    83  	mdi.On("UpsertPin", mock.Anything, mock.Anything).Return(nil)
    84  	mbi := &blockchainmocks.Plugin{}
    85  
    86  	mii := em.identity.(*identitymocks.Plugin)
    87  	mii.On("Resolve", mock.Anything, "0x12345").Return(&fftypes.Identity{OnChain: "0x12345"}, nil)
    88  
    89  	err = em.BatchPinComplete(mbi, batch, "0x12345", "tx1", nil)
    90  	assert.NoError(t, err)
    91  
    92  	mdi.AssertExpectations(t)
    93  }
    94  
    95  func TestBatchPinCompleteOkPrivate(t *testing.T) {
    96  	em, cancel := newTestEventManager(t)
    97  	defer cancel()
    98  
    99  	batch := &blockchain.BatchPin{
   100  		Namespace:     "ns1",
   101  		TransactionID: fftypes.NewUUID(),
   102  		BatchID:       fftypes.NewUUID(),
   103  		Contexts:      []*fftypes.Bytes32{fftypes.NewRandB32()},
   104  	}
   105  	batchData := &fftypes.Batch{
   106  		ID:         batch.BatchID,
   107  		Namespace:  "ns1",
   108  		PayloadRef: batch.BatchPaylodRef,
   109  		Payload: fftypes.BatchPayload{
   110  			TX: fftypes.TransactionRef{
   111  				Type: fftypes.TransactionTypeBatchPin,
   112  				ID:   batch.TransactionID,
   113  			},
   114  			Messages: []*fftypes.Message{},
   115  			Data:     []*fftypes.Data{},
   116  		},
   117  	}
   118  	batchDataBytes, err := json.Marshal(&batchData)
   119  	assert.NoError(t, err)
   120  	batchReadCloser := ioutil.NopCloser(bytes.NewReader(batchDataBytes))
   121  
   122  	mpi := em.publicstorage.(*publicstoragemocks.Plugin)
   123  	mpi.On("RetrieveData", mock.Anything, mock.
   124  		MatchedBy(func(pr *fftypes.Bytes32) bool { return *pr == *batch.BatchPaylodRef })).
   125  		Return(batchReadCloser, nil)
   126  
   127  	mdi := em.database.(*databasemocks.Plugin)
   128  	mdi.On("RunAsGroup", mock.Anything, mock.Anything).Return(nil)
   129  	mdi.On("GetTransactionByID", mock.Anything, uuidMatches(batchData.Payload.TX.ID)).Return(nil, nil)
   130  	mdi.On("UpsertTransaction", mock.Anything, mock.Anything, true, false).Return(nil)
   131  	mdi.On("UpsertPin", mock.Anything, mock.Anything).Return(nil)
   132  	mbi := &blockchainmocks.Plugin{}
   133  
   134  	err = em.BatchPinComplete(mbi, batch, "0x12345", "tx1", nil)
   135  	assert.NoError(t, err)
   136  
   137  	// Call through to persistBatch - the hash of our batch will be invalid,
   138  	// which is swallowed without error as we cannot retry (it is logged of course)
   139  	fn := mdi.Calls[0].Arguments[1].(func(ctx context.Context) error)
   140  	err = fn(context.Background())
   141  	assert.NoError(t, err)
   142  
   143  	mdi.AssertExpectations(t)
   144  }
   145  
   146  func TestSequencedBroadcastRetrieveIPFSFail(t *testing.T) {
   147  	em, cancel := newTestEventManager(t)
   148  
   149  	batch := &blockchain.BatchPin{
   150  		TransactionID:  fftypes.NewUUID(),
   151  		BatchID:        fftypes.NewUUID(),
   152  		BatchPaylodRef: fftypes.NewRandB32(),
   153  		Contexts:       []*fftypes.Bytes32{fftypes.NewRandB32()},
   154  	}
   155  
   156  	cancel() // to avoid retry
   157  	mpi := em.publicstorage.(*publicstoragemocks.Plugin)
   158  	mpi.On("RetrieveData", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop"))
   159  	mbi := &blockchainmocks.Plugin{}
   160  
   161  	err := em.BatchPinComplete(mbi, batch, "0x12345", "tx1", nil)
   162  	mpi.AssertExpectations(t)
   163  	assert.Regexp(t, "FF10158", err)
   164  }
   165  
   166  func TestBatchPinCompleteBadData(t *testing.T) {
   167  	em, cancel := newTestEventManager(t)
   168  	defer cancel()
   169  
   170  	batch := &blockchain.BatchPin{
   171  		TransactionID:  fftypes.NewUUID(),
   172  		BatchID:        fftypes.NewUUID(),
   173  		BatchPaylodRef: fftypes.NewRandB32(),
   174  		Contexts:       []*fftypes.Bytes32{fftypes.NewRandB32()},
   175  	}
   176  	batchReadCloser := ioutil.NopCloser(bytes.NewReader([]byte(`!json`)))
   177  
   178  	mpi := em.publicstorage.(*publicstoragemocks.Plugin)
   179  	mpi.On("RetrieveData", mock.Anything, mock.Anything).Return(batchReadCloser, nil)
   180  	mbi := &blockchainmocks.Plugin{}
   181  
   182  	err := em.BatchPinComplete(mbi, batch, "0x12345", "tx1", nil)
   183  	assert.NoError(t, err) // We do not return a blocking error in the case of bad data stored in IPFS
   184  }
   185  
   186  func TestPersistBatchMissingID(t *testing.T) {
   187  	em, cancel := newTestEventManager(t)
   188  	defer cancel()
   189  	err := em.persistBatch(context.Background(), &fftypes.Batch{})
   190  	assert.NoError(t, err)
   191  }
   192  
   193  func TestPersistBatchAuthorResolveFail(t *testing.T) {
   194  	em, cancel := newTestEventManager(t)
   195  	defer cancel()
   196  	batchHash := fftypes.NewRandB32()
   197  	batch := &fftypes.Batch{
   198  		ID:     fftypes.NewUUID(),
   199  		Author: "0x23456",
   200  		Payload: fftypes.BatchPayload{
   201  			TX: fftypes.TransactionRef{
   202  				Type: fftypes.TransactionTypeBatchPin,
   203  				ID:   fftypes.NewUUID(),
   204  			},
   205  		},
   206  		Hash: batchHash,
   207  	}
   208  	mii := em.identity.(*identitymocks.Plugin)
   209  	mii.On("Resolve", mock.Anything, "0x23456").Return(nil, fmt.Errorf("pop"))
   210  	batch.Hash = batch.Payload.Hash()
   211  	err := em.persistBatchFromBroadcast(context.Background(), batch, batchHash, "0x12345")
   212  	assert.NoError(t, err)
   213  }
   214  
   215  func TestPersistBatchBadAuthor(t *testing.T) {
   216  	em, cancel := newTestEventManager(t)
   217  	defer cancel()
   218  	batchHash := fftypes.NewRandB32()
   219  	batch := &fftypes.Batch{
   220  		ID:     fftypes.NewUUID(),
   221  		Author: "0x23456",
   222  		Payload: fftypes.BatchPayload{
   223  			TX: fftypes.TransactionRef{
   224  				Type: fftypes.TransactionTypeBatchPin,
   225  				ID:   fftypes.NewUUID(),
   226  			},
   227  		},
   228  		Hash: batchHash,
   229  	}
   230  	mii := em.identity.(*identitymocks.Plugin)
   231  	mii.On("Resolve", mock.Anything, "0x23456").Return(&fftypes.Identity{OnChain: "0x23456"}, nil)
   232  	batch.Hash = batch.Payload.Hash()
   233  	err := em.persistBatchFromBroadcast(context.Background(), batch, batchHash, "0x12345")
   234  	assert.NoError(t, err)
   235  }
   236  
   237  func TestPersistBatchMismatchChainHash(t *testing.T) {
   238  	em, cancel := newTestEventManager(t)
   239  	defer cancel()
   240  	batch := &fftypes.Batch{
   241  		ID:     fftypes.NewUUID(),
   242  		Author: "0x12345",
   243  		Payload: fftypes.BatchPayload{
   244  			TX: fftypes.TransactionRef{
   245  				Type: fftypes.TransactionTypeBatchPin,
   246  				ID:   fftypes.NewUUID(),
   247  			},
   248  		},
   249  		Hash: fftypes.NewRandB32(),
   250  	}
   251  	mii := em.identity.(*identitymocks.Plugin)
   252  	mii.On("Resolve", mock.Anything, "0x12345").Return(&fftypes.Identity{OnChain: "0x12345"}, nil)
   253  	batch.Hash = batch.Payload.Hash()
   254  	err := em.persistBatchFromBroadcast(context.Background(), batch, fftypes.NewRandB32(), "0x12345")
   255  	assert.NoError(t, err)
   256  }
   257  
   258  func TestPersistBatchUpsertBatchMismatchHash(t *testing.T) {
   259  	em, cancel := newTestEventManager(t)
   260  	defer cancel()
   261  	batch := &fftypes.Batch{
   262  		ID:     fftypes.NewUUID(),
   263  		Author: "0x12345",
   264  		Payload: fftypes.BatchPayload{
   265  			TX: fftypes.TransactionRef{
   266  				Type: fftypes.TransactionTypeBatchPin,
   267  				ID:   fftypes.NewUUID(),
   268  			},
   269  		},
   270  	}
   271  	batch.Hash = batch.Payload.Hash()
   272  
   273  	mdi := em.database.(*databasemocks.Plugin)
   274  	mdi.On("UpsertBatch", mock.Anything, mock.Anything, true, false).Return(database.HashMismatch)
   275  
   276  	err := em.persistBatch(context.Background(), batch)
   277  	assert.NoError(t, err)
   278  	mdi.AssertExpectations(t)
   279  }
   280  
   281  func TestPersistBatchUpsertBatchFail(t *testing.T) {
   282  	em, cancel := newTestEventManager(t)
   283  	defer cancel()
   284  	batch := &fftypes.Batch{
   285  		ID:     fftypes.NewUUID(),
   286  		Author: "0x12345",
   287  		Payload: fftypes.BatchPayload{
   288  			TX: fftypes.TransactionRef{
   289  				Type: fftypes.TransactionTypeBatchPin,
   290  				ID:   fftypes.NewUUID(),
   291  			},
   292  		},
   293  	}
   294  	batch.Hash = batch.Payload.Hash()
   295  
   296  	mdi := em.database.(*databasemocks.Plugin)
   297  	mdi.On("UpsertBatch", mock.Anything, mock.Anything, true, false).Return(fmt.Errorf("pop"))
   298  
   299  	err := em.persistBatch(context.Background(), batch)
   300  	assert.EqualError(t, err, "pop")
   301  }
   302  
   303  func TestPersistBatchGetTransactionBadNamespace(t *testing.T) {
   304  	em, cancel := newTestEventManager(t)
   305  	defer cancel()
   306  	batchPin := &blockchain.BatchPin{
   307  		TransactionID:  fftypes.NewUUID(),
   308  		BatchID:        fftypes.NewUUID(),
   309  		BatchHash:      fftypes.NewRandB32(),
   310  		BatchPaylodRef: nil,
   311  		Contexts:       []*fftypes.Bytes32{fftypes.NewRandB32()},
   312  	}
   313  
   314  	mdi := em.database.(*databasemocks.Plugin)
   315  	mdi.On("GetTransactionByID", mock.Anything, mock.Anything).Return(nil, nil)
   316  
   317  	err := em.persistBatchTransaction(context.Background(), batchPin, "0x12345", "txid1", fftypes.JSONObject{})
   318  	assert.NoError(t, err)
   319  	mdi.AssertExpectations(t)
   320  }
   321  
   322  func TestPersistBatchGetTransactionFail(t *testing.T) {
   323  	em, cancel := newTestEventManager(t)
   324  	defer cancel()
   325  	batchPin := &blockchain.BatchPin{
   326  		Namespace:      "ns1",
   327  		TransactionID:  fftypes.NewUUID(),
   328  		BatchID:        fftypes.NewUUID(),
   329  		BatchHash:      fftypes.NewRandB32(),
   330  		BatchPaylodRef: nil,
   331  		Contexts:       []*fftypes.Bytes32{fftypes.NewRandB32()},
   332  	}
   333  
   334  	mdi := em.database.(*databasemocks.Plugin)
   335  	mdi.On("GetTransactionByID", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop"))
   336  
   337  	err := em.persistBatchTransaction(context.Background(), batchPin, "0x12345", "txid1", fftypes.JSONObject{})
   338  	assert.EqualError(t, err, "pop")
   339  	mdi.AssertExpectations(t)
   340  }
   341  
   342  func TestPersistBatchGetTransactionInvalidMatch(t *testing.T) {
   343  	em, cancel := newTestEventManager(t)
   344  	defer cancel()
   345  	batchPin := &blockchain.BatchPin{
   346  		Namespace:      "ns1",
   347  		TransactionID:  fftypes.NewUUID(),
   348  		BatchID:        fftypes.NewUUID(),
   349  		BatchHash:      fftypes.NewRandB32(),
   350  		BatchPaylodRef: nil,
   351  		Contexts:       []*fftypes.Bytes32{fftypes.NewRandB32()},
   352  	}
   353  
   354  	mdi := em.database.(*databasemocks.Plugin)
   355  	mdi.On("GetTransactionByID", mock.Anything, mock.Anything).Return(&fftypes.Transaction{
   356  		ID: fftypes.NewUUID(), // wrong
   357  	}, nil)
   358  
   359  	err := em.persistBatchTransaction(context.Background(), batchPin, "0x12345", "txid1", fftypes.JSONObject{})
   360  	assert.NoError(t, err)
   361  	mdi.AssertExpectations(t)
   362  }
   363  
   364  func TestPersistBatcNewTXUpsertFail(t *testing.T) {
   365  	em, cancel := newTestEventManager(t)
   366  	defer cancel()
   367  	batchPin := &blockchain.BatchPin{
   368  		Namespace:      "ns1",
   369  		TransactionID:  fftypes.NewUUID(),
   370  		BatchID:        fftypes.NewUUID(),
   371  		BatchHash:      fftypes.NewRandB32(),
   372  		BatchPaylodRef: nil,
   373  		Contexts:       []*fftypes.Bytes32{fftypes.NewRandB32()},
   374  	}
   375  
   376  	mdi := em.database.(*databasemocks.Plugin)
   377  	mdi.On("GetTransactionByID", mock.Anything, mock.Anything).Return(nil, nil)
   378  	mdi.On("UpsertTransaction", mock.Anything, mock.Anything, true, false).Return(fmt.Errorf("pop"))
   379  
   380  	err := em.persistBatchTransaction(context.Background(), batchPin, "0x12345", "txid1", fftypes.JSONObject{})
   381  	assert.EqualError(t, err, "pop")
   382  	mdi.AssertExpectations(t)
   383  }
   384  
   385  func TestPersistBatcExistingTXHashMismatch(t *testing.T) {
   386  	em, cancel := newTestEventManager(t)
   387  	defer cancel()
   388  	batchPin := &blockchain.BatchPin{
   389  		Namespace:      "ns1",
   390  		TransactionID:  fftypes.NewUUID(),
   391  		BatchID:        fftypes.NewUUID(),
   392  		BatchHash:      fftypes.NewRandB32(),
   393  		BatchPaylodRef: nil,
   394  		Contexts:       []*fftypes.Bytes32{fftypes.NewRandB32()},
   395  	}
   396  
   397  	mdi := em.database.(*databasemocks.Plugin)
   398  	mdi.On("GetTransactionByID", mock.Anything, mock.Anything).Return(&fftypes.Transaction{
   399  		Subject: fftypes.TransactionSubject{
   400  			Type:      fftypes.TransactionTypeBatchPin,
   401  			Namespace: "ns1",
   402  			Signer:    "0x12345",
   403  			Reference: batchPin.BatchID,
   404  		},
   405  	}, nil)
   406  	mdi.On("UpsertTransaction", mock.Anything, mock.Anything, true, false).Return(database.HashMismatch)
   407  
   408  	err := em.persistBatchTransaction(context.Background(), batchPin, "0x12345", "txid1", fftypes.JSONObject{})
   409  	assert.NoError(t, err)
   410  	mdi.AssertExpectations(t)
   411  }
   412  
   413  func TestPersistBatchSwallowBadData(t *testing.T) {
   414  	em, cancel := newTestEventManager(t)
   415  	defer cancel()
   416  	batch := &fftypes.Batch{
   417  		ID:        fftypes.NewUUID(),
   418  		Author:    "0x12345",
   419  		Namespace: "ns1",
   420  		Payload: fftypes.BatchPayload{
   421  			TX: fftypes.TransactionRef{
   422  				Type: fftypes.TransactionTypeBatchPin,
   423  				ID:   fftypes.NewUUID(),
   424  			},
   425  			Messages: []*fftypes.Message{nil},
   426  			Data:     []*fftypes.Data{nil},
   427  		},
   428  	}
   429  	batch.Hash = batch.Payload.Hash()
   430  
   431  	mdi := em.database.(*databasemocks.Plugin)
   432  	mdi.On("UpsertBatch", mock.Anything, mock.Anything, true, false).Return(nil)
   433  
   434  	err := em.persistBatch(context.Background(), batch)
   435  	assert.NoError(t, err)
   436  	mdi.AssertExpectations(t)
   437  }
   438  
   439  func TestPersistBatchGoodDataUpsertFail(t *testing.T) {
   440  	em, cancel := newTestEventManager(t)
   441  	defer cancel()
   442  	batch := &fftypes.Batch{
   443  		ID:        fftypes.NewUUID(),
   444  		Author:    "0x12345",
   445  		Namespace: "ns1",
   446  		Payload: fftypes.BatchPayload{
   447  			TX: fftypes.TransactionRef{
   448  				Type: fftypes.TransactionTypeBatchPin,
   449  				ID:   fftypes.NewUUID(),
   450  			},
   451  			Data: []*fftypes.Data{
   452  				{ID: fftypes.NewUUID()},
   453  			},
   454  		},
   455  	}
   456  	batch.Payload.Data[0].Hash = batch.Payload.Data[0].Value.Hash()
   457  	batch.Hash = batch.Payload.Hash()
   458  
   459  	mdi := em.database.(*databasemocks.Plugin)
   460  	mdi.On("UpsertBatch", mock.Anything, mock.Anything, true, false).Return(nil)
   461  	mdi.On("UpsertData", mock.Anything, mock.Anything, true, false).Return(fmt.Errorf("pop"))
   462  
   463  	err := em.persistBatch(context.Background(), batch)
   464  	assert.EqualError(t, err, "pop")
   465  }
   466  
   467  func TestPersistBatchGoodDataMessageFail(t *testing.T) {
   468  	em, cancel := newTestEventManager(t)
   469  	defer cancel()
   470  	batch := &fftypes.Batch{
   471  		ID:        fftypes.NewUUID(),
   472  		Author:    "0x12345",
   473  		Namespace: "ns1",
   474  		Payload: fftypes.BatchPayload{
   475  			TX: fftypes.TransactionRef{
   476  				Type: fftypes.TransactionTypeBatchPin,
   477  				ID:   fftypes.NewUUID(),
   478  			},
   479  			Messages: []*fftypes.Message{
   480  				{Header: fftypes.MessageHeader{
   481  					ID:     fftypes.NewUUID(),
   482  					Author: "0x12345",
   483  				}},
   484  			},
   485  		},
   486  	}
   487  	batch.Payload.Messages[0].Header.DataHash = batch.Payload.Messages[0].Data.Hash()
   488  	batch.Payload.Messages[0].Hash = batch.Payload.Messages[0].Header.Hash()
   489  	batch.Hash = batch.Payload.Hash()
   490  
   491  	mdi := em.database.(*databasemocks.Plugin)
   492  	mdi.On("UpsertBatch", mock.Anything, mock.Anything, true, false).Return(nil)
   493  	mdi.On("UpsertMessage", mock.Anything, mock.Anything, true, false).Return(fmt.Errorf("pop"))
   494  
   495  	err := em.persistBatch(context.Background(), batch)
   496  	assert.EqualError(t, err, "pop")
   497  }
   498  
   499  func TestPersistBatchGoodMessageAuthorMismatch(t *testing.T) {
   500  	em, cancel := newTestEventManager(t)
   501  	defer cancel()
   502  	batch := &fftypes.Batch{
   503  		ID:        fftypes.NewUUID(),
   504  		Author:    "0x12345",
   505  		Namespace: "ns1",
   506  		Payload: fftypes.BatchPayload{
   507  			TX: fftypes.TransactionRef{
   508  				Type: fftypes.TransactionTypeBatchPin,
   509  				ID:   fftypes.NewUUID(),
   510  			},
   511  			Messages: []*fftypes.Message{
   512  				{Header: fftypes.MessageHeader{
   513  					ID:     fftypes.NewUUID(),
   514  					Author: "0x9999999",
   515  				}},
   516  			},
   517  		},
   518  	}
   519  	batch.Payload.Messages[0].Header.DataHash = batch.Payload.Messages[0].Data.Hash()
   520  	batch.Payload.Messages[0].Hash = batch.Payload.Messages[0].Header.Hash()
   521  	batch.Hash = batch.Payload.Hash()
   522  
   523  	mdi := em.database.(*databasemocks.Plugin)
   524  	mdi.On("UpsertBatch", mock.Anything, mock.Anything, true, false).Return(nil)
   525  
   526  	err := em.persistBatch(context.Background(), batch)
   527  	assert.NoError(t, err)
   528  }
   529  func TestPersistBatchDataBadHash(t *testing.T) {
   530  	em, cancel := newTestEventManager(t)
   531  	defer cancel()
   532  	batch := &fftypes.Batch{
   533  		ID: fftypes.NewUUID(),
   534  	}
   535  	data := &fftypes.Data{
   536  		ID: fftypes.NewUUID(),
   537  	}
   538  	err := em.persistBatchData(context.Background(), batch, 0, data)
   539  	assert.NoError(t, err)
   540  }
   541  
   542  func TestPersistBatchDataUpsertHashMismatch(t *testing.T) {
   543  	em, cancel := newTestEventManager(t)
   544  	defer cancel()
   545  	batch := &fftypes.Batch{
   546  		ID: fftypes.NewUUID(),
   547  	}
   548  
   549  	data := &fftypes.Data{
   550  		ID: fftypes.NewUUID(),
   551  	}
   552  	data.Hash = data.Value.Hash()
   553  
   554  	mdi := em.database.(*databasemocks.Plugin)
   555  	mdi.On("UpsertData", mock.Anything, mock.Anything, true, false).Return(database.HashMismatch)
   556  
   557  	err := em.persistBatchData(context.Background(), batch, 0, data)
   558  	assert.NoError(t, err)
   559  	mdi.AssertExpectations(t)
   560  }
   561  
   562  func TestPersistBatchDataUpsertDataError(t *testing.T) {
   563  	em, cancel := newTestEventManager(t)
   564  	defer cancel()
   565  	batch := &fftypes.Batch{
   566  		ID: fftypes.NewUUID(),
   567  	}
   568  
   569  	data := &fftypes.Data{
   570  		ID: fftypes.NewUUID(),
   571  	}
   572  	data.Hash = data.Value.Hash()
   573  
   574  	mdi := em.database.(*databasemocks.Plugin)
   575  	mdi.On("UpsertData", mock.Anything, mock.Anything, true, false).Return(fmt.Errorf("pop"))
   576  
   577  	err := em.persistBatchData(context.Background(), batch, 0, data)
   578  	assert.EqualError(t, err, "pop")
   579  }
   580  
   581  func TestPersistBatchDataOk(t *testing.T) {
   582  	em, cancel := newTestEventManager(t)
   583  	defer cancel()
   584  	batch := &fftypes.Batch{
   585  		ID: fftypes.NewUUID(),
   586  	}
   587  
   588  	data := &fftypes.Data{
   589  		ID: fftypes.NewUUID(),
   590  	}
   591  	data.Hash = data.Value.Hash()
   592  
   593  	mdi := em.database.(*databasemocks.Plugin)
   594  	mdi.On("UpsertData", mock.Anything, mock.Anything, true, false).Return(nil)
   595  
   596  	err := em.persistBatchData(context.Background(), batch, 0, data)
   597  	assert.NoError(t, err)
   598  	mdi.AssertExpectations(t)
   599  }
   600  
   601  func TestPersistBatchMessageBadHash(t *testing.T) {
   602  	em, cancel := newTestEventManager(t)
   603  	defer cancel()
   604  	batch := &fftypes.Batch{
   605  		ID: fftypes.NewUUID(),
   606  	}
   607  	msg := &fftypes.Message{
   608  		Header: fftypes.MessageHeader{
   609  			ID: fftypes.NewUUID(),
   610  		},
   611  	}
   612  	err := em.persistBatchMessage(context.Background(), batch, 0, msg)
   613  	assert.NoError(t, err)
   614  }
   615  
   616  func TestPersistBatchMessageUpsertHashMismatch(t *testing.T) {
   617  	em, cancel := newTestEventManager(t)
   618  	defer cancel()
   619  	batch := &fftypes.Batch{
   620  		ID: fftypes.NewUUID(),
   621  	}
   622  	msg := &fftypes.Message{
   623  		Header: fftypes.MessageHeader{
   624  			ID: fftypes.NewUUID(),
   625  		},
   626  	}
   627  	msg.Header.DataHash = msg.Data.Hash()
   628  	msg.Hash = msg.Header.Hash()
   629  	assert.NoError(t, msg.Verify(context.Background()))
   630  
   631  	mdi := em.database.(*databasemocks.Plugin)
   632  	mdi.On("UpsertMessage", mock.Anything, mock.Anything, true, false).Return(database.HashMismatch)
   633  
   634  	err := em.persistBatchMessage(context.Background(), batch, 0, msg)
   635  	assert.NoError(t, err)
   636  	mdi.AssertExpectations(t)
   637  }
   638  
   639  func TestPersistBatchMessageUpsertMessageFail(t *testing.T) {
   640  	em, cancel := newTestEventManager(t)
   641  	defer cancel()
   642  	batch := &fftypes.Batch{
   643  		ID: fftypes.NewUUID(),
   644  	}
   645  	msg := &fftypes.Message{
   646  		Header: fftypes.MessageHeader{
   647  			ID: fftypes.NewUUID(),
   648  		},
   649  	}
   650  	msg.Header.DataHash = msg.Data.Hash()
   651  	msg.Hash = msg.Header.Hash()
   652  	assert.NoError(t, msg.Verify(context.Background()))
   653  
   654  	mdi := em.database.(*databasemocks.Plugin)
   655  	mdi.On("UpsertMessage", mock.Anything, mock.Anything, true, false).Return(fmt.Errorf("pop"))
   656  
   657  	err := em.persistBatchMessage(context.Background(), batch, 0, msg)
   658  	assert.EqualError(t, err, "pop")
   659  }
   660  
   661  func TestPersistBatchMessageOK(t *testing.T) {
   662  	em, cancel := newTestEventManager(t)
   663  	defer cancel()
   664  	batch := &fftypes.Batch{
   665  		ID: fftypes.NewUUID(),
   666  	}
   667  	msg := &fftypes.Message{
   668  		Header: fftypes.MessageHeader{
   669  			ID: fftypes.NewUUID(),
   670  		},
   671  	}
   672  	msg.Header.DataHash = msg.Data.Hash()
   673  	msg.Hash = msg.Header.Hash()
   674  	assert.NoError(t, msg.Verify(context.Background()))
   675  
   676  	mdi := em.database.(*databasemocks.Plugin)
   677  	mdi.On("UpsertMessage", mock.Anything, mock.Anything, true, false).Return(nil)
   678  
   679  	err := em.persistBatchMessage(context.Background(), batch, 0, msg)
   680  	assert.NoError(t, err)
   681  	mdi.AssertExpectations(t)
   682  }
   683  
   684  func TestPersistContextsFail(t *testing.T) {
   685  	em, cancel := newTestEventManager(t)
   686  	defer cancel()
   687  
   688  	mdi := em.database.(*databasemocks.Plugin)
   689  	mdi.On("UpsertPin", mock.Anything, mock.Anything).Return(fmt.Errorf("pop"))
   690  
   691  	err := em.persistContexts(em.ctx, &blockchain.BatchPin{
   692  		Contexts: []*fftypes.Bytes32{
   693  			fftypes.NewRandB32(),
   694  		},
   695  	}, false)
   696  	assert.EqualError(t, err, "pop")
   697  	mdi.AssertExpectations(t)
   698  }