github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/broadcast/manager_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 broadcast
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  
    24  	"github.com/kaleido-io/firefly/internal/config"
    25  	"github.com/kaleido-io/firefly/mocks/batchmocks"
    26  	"github.com/kaleido-io/firefly/mocks/blockchainmocks"
    27  	"github.com/kaleido-io/firefly/mocks/databasemocks"
    28  	"github.com/kaleido-io/firefly/mocks/dataexchangemocks"
    29  	"github.com/kaleido-io/firefly/mocks/datamocks"
    30  	"github.com/kaleido-io/firefly/mocks/identitymocks"
    31  	"github.com/kaleido-io/firefly/mocks/publicstoragemocks"
    32  	"github.com/kaleido-io/firefly/pkg/fftypes"
    33  	"github.com/stretchr/testify/assert"
    34  	"github.com/stretchr/testify/mock"
    35  )
    36  
    37  func newTestBroadcast(t *testing.T) (*broadcastManager, func()) {
    38  	config.Reset()
    39  	config.Set(config.OrgIdentity, "UTNodeID")
    40  	mdi := &databasemocks.Plugin{}
    41  	mii := &identitymocks.Plugin{}
    42  	mdm := &datamocks.Manager{}
    43  	mbi := &blockchainmocks.Plugin{}
    44  	mpi := &publicstoragemocks.Plugin{}
    45  	mba := &batchmocks.Manager{}
    46  	mdx := &dataexchangemocks.Plugin{}
    47  	mbi.On("Name").Return("ut_blockchain").Maybe()
    48  	defaultIdentity := &fftypes.Identity{Identifier: "UTNodeID", OnChain: "0x12345"}
    49  	mii.On("Resolve", mock.Anything, "UTNodeID").Return(defaultIdentity, nil).Maybe()
    50  	mbi.On("VerifyIdentitySyntax", mock.Anything, defaultIdentity).Return(nil).Maybe()
    51  	mba.On("RegisterDispatcher", []fftypes.MessageType{fftypes.MessageTypeBroadcast, fftypes.MessageTypeDefinition}, mock.Anything, mock.Anything).Return()
    52  	ctx, cancel := context.WithCancel(context.Background())
    53  	b, err := NewBroadcastManager(ctx, mdi, mii, mdm, mbi, mdx, mpi, mba)
    54  	assert.NoError(t, err)
    55  	return b.(*broadcastManager), cancel
    56  }
    57  
    58  func TestInitFail(t *testing.T) {
    59  	_, err := NewBroadcastManager(context.Background(), nil, nil, nil, nil, nil, nil, nil)
    60  	assert.Regexp(t, "FF10128", err)
    61  }
    62  
    63  func TestBroadcastMessageGood(t *testing.T) {
    64  	bm, cancel := newTestBroadcast(t)
    65  	defer cancel()
    66  
    67  	msg := &fftypes.Message{}
    68  	bm.database.(*databasemocks.Plugin).On("InsertMessageLocal", mock.Anything, msg).Return(nil)
    69  
    70  	err := bm.broadcastMessageCommon(context.Background(), msg)
    71  	assert.NoError(t, err)
    72  
    73  	bm.Start()
    74  	bm.WaitStop()
    75  }
    76  
    77  func TestBroadcastMessageBad(t *testing.T) {
    78  	bm, cancel := newTestBroadcast(t)
    79  	defer cancel()
    80  
    81  	dupID := fftypes.NewUUID()
    82  	msg := &fftypes.Message{
    83  		Data: fftypes.DataRefs{
    84  			{ID: dupID /* missing hash */},
    85  		},
    86  	}
    87  	bm.database.(*databasemocks.Plugin).On("UpsertMessage", mock.Anything, msg, false).Return(nil)
    88  
    89  	err := bm.broadcastMessageCommon(context.Background(), msg)
    90  	assert.Regexp(t, "FF10144", err)
    91  
    92  }
    93  
    94  func TestDispatchBatchInvalidData(t *testing.T) {
    95  	bm, cancel := newTestBroadcast(t)
    96  	defer cancel()
    97  
    98  	err := bm.dispatchBatch(context.Background(), &fftypes.Batch{
    99  		Payload: fftypes.BatchPayload{
   100  			Data: []*fftypes.Data{
   101  				{Value: fftypes.Byteable(`!json`)},
   102  			},
   103  		},
   104  	}, []*fftypes.Bytes32{fftypes.NewRandB32()})
   105  	assert.Regexp(t, "FF10137", err)
   106  }
   107  
   108  func TestDispatchBatchUploadFail(t *testing.T) {
   109  	bm, cancel := newTestBroadcast(t)
   110  	defer cancel()
   111  
   112  	bm.publicstorage.(*publicstoragemocks.Plugin).On("PublishData", mock.Anything, mock.Anything).Return(nil, "", fmt.Errorf("pop"))
   113  
   114  	err := bm.dispatchBatch(context.Background(), &fftypes.Batch{}, []*fftypes.Bytes32{fftypes.NewRandB32()})
   115  	assert.EqualError(t, err, "pop")
   116  }
   117  
   118  func TestDispatchBatchSubmitBatchPinSucceed(t *testing.T) {
   119  	bm, cancel := newTestBroadcast(t)
   120  	defer cancel()
   121  
   122  	mdi := bm.database.(*databasemocks.Plugin)
   123  	mdi.On("RunAsGroup", mock.Anything, mock.Anything).Return(nil)
   124  
   125  	bm.publicstorage.(*publicstoragemocks.Plugin).On("PublishData", mock.Anything, mock.Anything).Return(fftypes.NewRandB32(), "id1", nil)
   126  
   127  	err := bm.dispatchBatch(context.Background(), &fftypes.Batch{}, []*fftypes.Bytes32{fftypes.NewRandB32()})
   128  	assert.NoError(t, err)
   129  }
   130  
   131  func TestGetOrgIdentityEmpty(t *testing.T) {
   132  	bm, cancel := newTestBroadcast(t)
   133  	defer cancel()
   134  
   135  	config.Set(config.OrgIdentity, "")
   136  	mii := bm.identity.(*identitymocks.Plugin)
   137  	mii.On("Resolve", mock.Anything, "").Return(nil, fmt.Errorf("pop"))
   138  	_, err := bm.GetNodeSigningIdentity(bm.ctx)
   139  	assert.Regexp(t, "pop", err)
   140  }
   141  
   142  func TestDispatchBatchSubmitBroadcastBadIdentity(t *testing.T) {
   143  	bm, cancel := newTestBroadcast(t)
   144  	defer cancel()
   145  
   146  	mdi := bm.database.(*databasemocks.Plugin)
   147  	mps := bm.publicstorage.(*publicstoragemocks.Plugin)
   148  	mii := bm.identity.(*identitymocks.Plugin)
   149  	mbi := bm.blockchain.(*blockchainmocks.Plugin)
   150  	mdi.On("RunAsGroup", mock.Anything, mock.Anything).Return(nil)
   151  	mps.On("PublishData", mock.Anything, mock.Anything).Return(fftypes.NewRandB32(), "id1", nil)
   152  	mii.On("Resolve", mock.Anything, "wrong").Return(nil, fmt.Errorf("pop"))
   153  	mbi.On("VerifyIdentitySyntax", mock.Anything, mock.Anything).Return(nil)
   154  
   155  	err := bm.dispatchBatch(context.Background(), &fftypes.Batch{Author: "wrong"}, []*fftypes.Bytes32{fftypes.NewRandB32()})
   156  	assert.NoError(t, err)
   157  
   158  	mdi.On("UpsertOperation", mock.Anything, mock.Anything, false).Return(nil)
   159  	fn := mdi.Calls[0].Arguments[1].(func(ctx context.Context) error)
   160  	err = fn(context.Background())
   161  	assert.Regexp(t, "pop", err)
   162  }
   163  
   164  func TestDispatchBatchSubmitBroadcastBadOnchainIdentity(t *testing.T) {
   165  	bm, cancel := newTestBroadcast(t)
   166  	defer cancel()
   167  
   168  	mdi := bm.database.(*databasemocks.Plugin)
   169  	mps := bm.publicstorage.(*publicstoragemocks.Plugin)
   170  	mii := bm.identity.(*identitymocks.Plugin)
   171  	mbi := bm.blockchain.(*blockchainmocks.Plugin)
   172  	mdi.On("RunAsGroup", mock.Anything, mock.Anything).Return(nil)
   173  	mps.On("PublishData", mock.Anything, mock.Anything).Return(fftypes.NewRandB32(), "id1", nil)
   174  	badID := &fftypes.Identity{OnChain: "0x99999"}
   175  	mii.On("Resolve", mock.Anything, "wrong").Return(badID, nil)
   176  	mbi.On("VerifyIdentitySyntax", mock.Anything, badID).Return(fmt.Errorf("pop"))
   177  
   178  	err := bm.dispatchBatch(context.Background(), &fftypes.Batch{Author: "wrong"}, []*fftypes.Bytes32{fftypes.NewRandB32()})
   179  	assert.NoError(t, err)
   180  
   181  	mdi.On("UpsertOperation", mock.Anything, mock.Anything, false).Return(nil)
   182  	fn := mdi.Calls[0].Arguments[1].(func(ctx context.Context) error)
   183  	err = fn(context.Background())
   184  	assert.Regexp(t, "pop", err)
   185  }
   186  
   187  func TestDispatchBatchSubmitBatchPinFail(t *testing.T) {
   188  	bm, cancel := newTestBroadcast(t)
   189  	defer cancel()
   190  
   191  	mdi := bm.database.(*databasemocks.Plugin)
   192  	mdi.On("RunAsGroup", mock.Anything, mock.Anything).Return(nil)
   193  
   194  	bm.publicstorage.(*publicstoragemocks.Plugin).On("PublishData", mock.Anything, mock.Anything).Return(fftypes.NewRandB32(), "id1", nil)
   195  
   196  	err := bm.dispatchBatch(context.Background(), &fftypes.Batch{Author: "UTNodeID"}, []*fftypes.Bytes32{fftypes.NewRandB32()})
   197  	assert.NoError(t, err)
   198  
   199  	mdi.On("UpsertTransaction", mock.Anything, mock.Anything, true, false).Return(fmt.Errorf("pop"))
   200  	fn := mdi.Calls[0].Arguments[1].(func(ctx context.Context) error)
   201  	err = fn(context.Background())
   202  	assert.Regexp(t, "pop", err)
   203  }
   204  
   205  func TestSubmitTXAndUpdateDBUpdateBatchFail(t *testing.T) {
   206  	bm, cancel := newTestBroadcast(t)
   207  	defer cancel()
   208  
   209  	mdi := bm.database.(*databasemocks.Plugin)
   210  	mdi.On("UpsertTransaction", mock.Anything, mock.Anything, true, false).Return(nil)
   211  	mdi.On("UpdateBatch", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop"))
   212  	bm.blockchain.(*blockchainmocks.Plugin).On("SubmitBatchPin", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", fmt.Errorf("pop"))
   213  
   214  	err := bm.submitTXAndUpdateDB(context.Background(), &fftypes.Batch{Author: "UTNodeID"}, []*fftypes.Bytes32{fftypes.NewRandB32()}, "id1")
   215  	assert.Regexp(t, "pop", err)
   216  }
   217  
   218  func TestSubmitTXAndUpdateDBSubmitFail(t *testing.T) {
   219  	bm, cancel := newTestBroadcast(t)
   220  	defer cancel()
   221  
   222  	mdi := bm.database.(*databasemocks.Plugin)
   223  	mdi.On("UpsertTransaction", mock.Anything, mock.Anything, true, false).Return(nil)
   224  	mdi.On("UpdateBatch", mock.Anything, mock.Anything, mock.Anything).Return(nil)
   225  	bm.blockchain.(*blockchainmocks.Plugin).On("SubmitBatchPin", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", fmt.Errorf("pop"))
   226  
   227  	err := bm.submitTXAndUpdateDB(context.Background(), &fftypes.Batch{Author: "UTNodeID"}, []*fftypes.Bytes32{fftypes.NewRandB32()}, "id1")
   228  	assert.Regexp(t, "pop", err)
   229  }
   230  
   231  func TestSubmitTXAndUpdateDBAddOp1Fail(t *testing.T) {
   232  	bm, cancel := newTestBroadcast(t)
   233  	defer cancel()
   234  
   235  	mdi := bm.database.(*databasemocks.Plugin)
   236  	mbi := bm.blockchain.(*blockchainmocks.Plugin)
   237  	mdi.On("UpsertTransaction", mock.Anything, mock.Anything, true, false).Return(nil)
   238  	mdi.On("UpdateBatch", mock.Anything, mock.Anything, mock.Anything).Return(nil)
   239  	mdi.On("UpsertOperation", mock.Anything, mock.Anything, false).Return(fmt.Errorf("pop"))
   240  	mbi.On("SubmitBatchPin", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("txid", nil)
   241  	mbi.On("Name").Return("unittest")
   242  
   243  	batch := &fftypes.Batch{
   244  		Author: "UTNodeID",
   245  		Payload: fftypes.BatchPayload{
   246  			Messages: []*fftypes.Message{
   247  				{Header: fftypes.MessageHeader{
   248  					ID: fftypes.NewUUID(),
   249  				}},
   250  			},
   251  		},
   252  	}
   253  
   254  	err := bm.submitTXAndUpdateDB(context.Background(), batch, []*fftypes.Bytes32{fftypes.NewRandB32()}, "id1")
   255  	assert.Regexp(t, "pop", err)
   256  }
   257  
   258  func TestSubmitTXAndUpdateDBAddOp2Fail(t *testing.T) {
   259  	bm, cancel := newTestBroadcast(t)
   260  	defer cancel()
   261  
   262  	mdi := bm.database.(*databasemocks.Plugin)
   263  	mbi := bm.blockchain.(*blockchainmocks.Plugin)
   264  	mdi.On("UpsertTransaction", mock.Anything, mock.Anything, true, false).Return(nil)
   265  	mdi.On("UpdateBatch", mock.Anything, mock.Anything, mock.Anything).Return(nil)
   266  	mdi.On("UpsertOperation", mock.Anything, mock.Anything, false).Once().Return(nil)
   267  	mdi.On("UpsertOperation", mock.Anything, mock.Anything, false).Once().Return(fmt.Errorf("pop"))
   268  	mbi.On("SubmitBatchPin", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("txid", nil)
   269  	mbi.On("Name").Return("ut_blockchain")
   270  
   271  	bm.publicstorage.(*publicstoragemocks.Plugin).On("Name").Return("ut_publicstorage")
   272  
   273  	batch := &fftypes.Batch{
   274  		Author: "UTNodeID",
   275  		Payload: fftypes.BatchPayload{
   276  			Messages: []*fftypes.Message{
   277  				{Header: fftypes.MessageHeader{
   278  					ID: fftypes.NewUUID(),
   279  				}},
   280  			},
   281  		},
   282  	}
   283  
   284  	err := bm.submitTXAndUpdateDB(context.Background(), batch, []*fftypes.Bytes32{fftypes.NewRandB32()}, "id1")
   285  	assert.Regexp(t, "pop", err)
   286  }
   287  
   288  func TestSubmitTXAndUpdateDBSucceed(t *testing.T) {
   289  	bm, cancel := newTestBroadcast(t)
   290  	defer cancel()
   291  
   292  	mdi := bm.database.(*databasemocks.Plugin)
   293  	mbi := bm.blockchain.(*blockchainmocks.Plugin)
   294  	mdi.On("UpsertTransaction", mock.Anything, mock.Anything, true, false).Return(nil)
   295  	mdi.On("UpdateBatch", mock.Anything, mock.Anything, mock.Anything).Return(nil)
   296  	mdi.On("UpsertOperation", mock.Anything, mock.Anything, false).Once().Return(nil)
   297  	mdi.On("UpsertOperation", mock.Anything, mock.Anything, false).Once().Return(nil)
   298  	mbi.On("SubmitBatchPin", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("blockchain_id", nil)
   299  
   300  	bm.publicstorage.(*publicstoragemocks.Plugin).On("Name").Return("ut_publicstorage")
   301  
   302  	msgID := fftypes.NewUUID()
   303  	batch := &fftypes.Batch{
   304  		Author: "UTNodeID",
   305  		Payload: fftypes.BatchPayload{
   306  			TX: fftypes.TransactionRef{
   307  				Type: fftypes.TransactionTypeBatchPin,
   308  				ID:   fftypes.NewUUID(),
   309  			},
   310  			Messages: []*fftypes.Message{
   311  				{Header: fftypes.MessageHeader{
   312  					ID: msgID,
   313  				}},
   314  			},
   315  		},
   316  	}
   317  
   318  	err := bm.submitTXAndUpdateDB(context.Background(), batch, []*fftypes.Bytes32{fftypes.NewRandB32()}, "ipfs_id")
   319  	assert.NoError(t, err)
   320  
   321  	op1 := mdi.Calls[2].Arguments[1].(*fftypes.Operation)
   322  	assert.Equal(t, *batch.Payload.TX.ID, *op1.Transaction)
   323  	assert.Equal(t, "ut_blockchain", op1.Plugin)
   324  	assert.Equal(t, "blockchain_id", op1.BackendID)
   325  	assert.Equal(t, fftypes.OpTypeBlockchainBatchPin, op1.Type)
   326  
   327  	op2 := mdi.Calls[3].Arguments[1].(*fftypes.Operation)
   328  	assert.Equal(t, *batch.Payload.TX.ID, *op2.Transaction)
   329  	assert.Equal(t, "ut_publicstorage", op2.Plugin)
   330  	assert.Equal(t, "ipfs_id", op2.BackendID)
   331  	assert.Equal(t, fftypes.OpTypePublicStorageBatchBroadcast, op2.Type)
   332  }