github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/common/ledger/blockledger/fileledger/impl_test.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package fileledger 8 9 import ( 10 "errors" 11 "fmt" 12 "io/ioutil" 13 "os" 14 "testing" 15 16 "github.com/hechain20/hechain/common/flogging" 17 cl "github.com/hechain20/hechain/common/ledger" 18 "github.com/hechain20/hechain/common/ledger/blkstorage/blkstoragetest" 19 "github.com/hechain20/hechain/common/ledger/blockledger" 20 "github.com/hechain20/hechain/common/ledger/testutil" 21 "github.com/hechain20/hechain/common/metrics/disabled" 22 "github.com/hechain20/hechain/protoutil" 23 cb "github.com/hyperledger/fabric-protos-go/common" 24 ab "github.com/hyperledger/fabric-protos-go/orderer" 25 "github.com/hyperledger/fabric-protos-go/peer" 26 "github.com/stretchr/testify/mock" 27 "github.com/stretchr/testify/require" 28 ) 29 30 var genesisBlock = protoutil.NewBlock(0, nil) 31 32 func init() { 33 flogging.ActivateSpec("common.ledger.blockledger.file=DEBUG") 34 } 35 36 type testEnv struct { 37 t *testing.T 38 location string 39 flf blockledger.Factory 40 } 41 42 func initialize(t *testing.T) (*testEnv, *FileLedger) { 43 name, err := ioutil.TempDir("", "hyperledger_fabric") 44 require.NoError(t, err, "Error creating temp dir: %s", err) 45 46 p, err := New(name, &disabled.Provider{}) 47 require.NoError(t, err) 48 flf := p.(*fileLedgerFactory) 49 fl, err := flf.GetOrCreate("testchannelid") 50 require.NoError(t, err, "Error GetOrCreate channel") 51 fl.Append(genesisBlock) 52 return &testEnv{location: name, t: t, flf: flf}, fl.(*FileLedger) 53 } 54 55 func (tev *testEnv) tearDown() { 56 tev.shutDown() 57 err := os.RemoveAll(tev.location) 58 if err != nil { 59 tev.t.Fatalf("Error tearing down env: %s", err) 60 } 61 } 62 63 func (tev *testEnv) shutDown() { 64 tev.flf.Close() 65 } 66 67 type mockBlockStore struct { 68 blockchainInfo *cb.BlockchainInfo 69 resultsIterator cl.ResultsIterator 70 block *cb.Block 71 envelope *cb.Envelope 72 txValidationCode peer.TxValidationCode 73 defaultError error 74 getBlockchainInfoError error 75 retrieveBlockByNumberError error 76 } 77 78 func (mbs *mockBlockStore) AddBlock(block *cb.Block) error { 79 return mbs.defaultError 80 } 81 82 func (mbs *mockBlockStore) GetBlockchainInfo() (*cb.BlockchainInfo, error) { 83 return mbs.blockchainInfo, mbs.getBlockchainInfoError 84 } 85 86 func (mbs *mockBlockStore) RetrieveBlocks(startNum uint64) (cl.ResultsIterator, error) { 87 return mbs.resultsIterator, mbs.defaultError 88 } 89 90 func (mbs *mockBlockStore) RetrieveBlockByHash(blockHash []byte) (*cb.Block, error) { 91 return mbs.block, mbs.defaultError 92 } 93 94 func (mbs *mockBlockStore) RetrieveBlockByNumber(blockNum uint64) (*cb.Block, error) { 95 return mbs.block, mbs.retrieveBlockByNumberError 96 } 97 98 func (mbs *mockBlockStore) RetrieveTxByID(txID string) (*cb.Envelope, error) { 99 return mbs.envelope, mbs.defaultError 100 } 101 102 func (mbs *mockBlockStore) RetrieveTxByBlockNumTranNum(blockNum uint64, tranNum uint64) (*cb.Envelope, error) { 103 return mbs.envelope, mbs.defaultError 104 } 105 106 func (mbs *mockBlockStore) RetrieveBlockByTxID(txID string) (*cb.Block, error) { 107 return mbs.block, mbs.defaultError 108 } 109 110 func (mbs *mockBlockStore) RetrieveTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error) { 111 return mbs.txValidationCode, mbs.defaultError 112 } 113 114 func (*mockBlockStore) Shutdown() { 115 } 116 117 type mockBlockStoreIterator struct { 118 mock.Mock 119 } 120 121 func (m *mockBlockStoreIterator) Next() (cl.QueryResult, error) { 122 args := m.Called() 123 return args.Get(0), args.Error(1) 124 } 125 126 func (m *mockBlockStoreIterator) Close() { 127 m.Called() 128 } 129 130 func TestInitialization(t *testing.T) { 131 tev, fl := initialize(t) 132 defer tev.tearDown() 133 134 require.Equal(t, uint64(1), fl.Height(), "Block height should be 1") 135 136 block := blockledger.GetBlock(fl, 0) 137 require.NotNil(t, block, "Error retrieving genesis block") 138 require.Equal(t, protoutil.BlockHeaderHash(genesisBlock.Header), protoutil.BlockHeaderHash(block.Header), "Block hashes did no match") 139 } 140 141 func TestReinitialization(t *testing.T) { 142 tev, ledger1 := initialize(t) 143 defer tev.tearDown() 144 145 // create a block to add to the ledger 146 envelope := getSampleEnvelopeWithSignatureHeader() 147 b1 := blockledger.CreateNextBlock(ledger1, []*cb.Envelope{envelope}) 148 149 // add the block to the ledger 150 ledger1.Append(b1) 151 152 fl, err := tev.flf.GetOrCreate("testchannelid") 153 ledger1, ok := fl.(*FileLedger) 154 require.NoError(t, err, "Expected to successfully get test channel") 155 require.Equal(t, 1, len(tev.flf.ChannelIDs()), "Exptected not new channel to be created") 156 require.True(t, ok, "Exptected type assertion to succeed") 157 require.Equal(t, uint64(2), ledger1.Height(), "Block height should be 2. Got %v", ledger1.Height()) 158 159 // shut down the ledger provider 160 tev.shutDown() 161 162 // re-initialize the ledger provider (not the test ledger itself!) 163 provider2, err := New(tev.location, &disabled.Provider{}) 164 require.NoError(t, err) 165 166 // assert expected ledgers exist 167 channels := provider2.ChannelIDs() 168 require.Equal(t, 1, len(channels), "Should have recovered the channel") 169 170 // get the existing test channel ledger 171 ledger2, err := provider2.GetOrCreate(channels[0]) 172 require.NoError(t, err, "Unexpected error: %s", err) 173 174 fl = ledger2.(*FileLedger) 175 require.Equal(t, uint64(2), fl.Height(), "Block height should be 2. Got %v", fl.Height()) 176 177 block := blockledger.GetBlock(fl, 1) 178 require.NotNil(t, block, "Error retrieving block 1") 179 require.Equal(t, protoutil.BlockHeaderHash(b1.Header), protoutil.BlockHeaderHash(block.Header), "Block hashes did no match") 180 } 181 182 func TestAddition(t *testing.T) { 183 tev, fl := initialize(t) 184 defer tev.tearDown() 185 info, _ := fl.blockStore.GetBlockchainInfo() 186 prevHash := info.CurrentBlockHash 187 envelope := getSampleEnvelopeWithSignatureHeader() 188 b1 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope}) 189 fl.Append(b1) 190 require.Equal(t, uint64(2), fl.Height(), "Block height should be 2") 191 192 block := blockledger.GetBlock(fl, 1) 193 require.NotNil(t, block, "Error retrieving genesis block") 194 require.Equal(t, prevHash, block.Header.PreviousHash, "Block hashes did no match") 195 } 196 197 func TestRetrieval(t *testing.T) { 198 tev, fl := initialize(t) 199 defer tev.tearDown() 200 envelope := getSampleEnvelopeWithSignatureHeader() 201 b1 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope}) 202 fl.Append(b1) 203 it, num := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Oldest{}}) 204 defer it.Close() 205 require.Zero(t, num, "Expected genesis block iterator, but got %d", num) 206 207 block, status := it.Next() 208 require.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the genesis block") 209 require.Zero(t, block.Header.Number, "Expected to successfully retrieve the genesis block") 210 211 block, status = it.Next() 212 require.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the second block") 213 require.Equal( 214 t, 215 uint64(1), 216 block.Header.Number, 217 "Expected to successfully retrieve the second block but got block number %d", block.Header.Number) 218 } 219 220 func TestBlockedRetrieval(t *testing.T) { 221 tev, fl := initialize(t) 222 defer tev.tearDown() 223 it, num := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}}) 224 defer it.Close() 225 if num != 1 { 226 t.Fatalf("Expected block iterator at 1, but got %d", num) 227 } 228 require.Equal(t, uint64(1), num, "Expected block iterator at 1, but got %d", num) 229 230 envelope := getSampleEnvelopeWithSignatureHeader() 231 b1 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope}) 232 fl.Append(b1) 233 234 block, status := it.Next() 235 require.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the second block") 236 require.Equal( 237 t, 238 uint64(1), 239 block.Header.Number, 240 "Expected to successfully retrieve the second block but got block number %d", block.Header.Number) 241 242 b2 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope}) 243 fl.Append(b2) 244 245 block, status = it.Next() 246 require.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the third block") 247 require.Equal(t, uint64(2), block.Header.Number, "Expected to successfully retrieve the third block") 248 249 // verify NextCommit seek position 250 it2, num2 := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_NextCommit{NextCommit: &ab.SeekNextCommit{}}}) 251 require.Equal(t, uint64(3), num2) 252 defer it2.Close() 253 254 b3 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope}) 255 fl.Append(b3) 256 257 block, status = it.Next() 258 require.Equal(t, cb.Status_SUCCESS, status) 259 require.Equal(t, uint64(3), block.Header.Number) 260 } 261 262 func TestBlockRetrievalWithSnapshot(t *testing.T) { 263 numBlocks := 5 264 // create an extra block to add later for iterator.Next testing 265 blocks := testutil.ConstructTestBlocks(t, numBlocks+1) 266 267 blockStore, cleanup := blkstoragetest.BootstrapBlockstoreFromSnapshot(t, "blockretrievalwithsnapshot", blocks[:numBlocks]) 268 defer cleanup() 269 270 fl := NewFileLedger(blockStore) 271 272 // verify lastBlockInSnapshot, which should be numBlocks - 1 273 bcInfo, err := fl.blockStore.GetBlockchainInfo() 274 require.NoError(t, err) 275 require.Equal(t, uint64(numBlocks-1), bcInfo.BootstrappingSnapshotInfo.LastBlockInSnapshot) 276 277 // verify iterator startingNum for Newest, NextCommit, and Specified 278 it, startingNum := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Newest{}}) 279 defer it.Close() 280 require.Equal(t, uint64(numBlocks), startingNum) 281 282 it2, startingNum2 := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_NextCommit{}}) 283 defer it2.Close() 284 require.Equal(t, uint64(numBlocks), startingNum2) 285 286 it3, startingNum3 := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: uint64(numBlocks)}}}) 287 defer it3.Close() 288 require.Equal(t, uint64(numBlocks), startingNum3) 289 290 it4, _ := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: uint64(numBlocks - 1)}}}) 291 defer it4.Close() 292 require.Equal(t, &blockledger.NotFoundErrorIterator{}, it4) 293 294 it5, _ := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: uint64(numBlocks + 1)}}}) 295 defer it5.Close() 296 require.Equal(t, &blockledger.NotFoundErrorIterator{}, it4) 297 298 // add a block and verify iterator.Next 299 nextBlk := blocks[numBlocks] 300 err = fl.Append(nextBlk) 301 require.NoError(t, err) 302 303 blk, status := it.Next() 304 require.Equal(t, cb.Status_SUCCESS, status) 305 require.Equal(t, nextBlk, blk) 306 307 blk, status = it2.Next() 308 require.Equal(t, cb.Status_SUCCESS, status) 309 require.Equal(t, nextBlk, blk) 310 311 blk, status = it3.Next() 312 require.Equal(t, cb.Status_SUCCESS, status) 313 require.Equal(t, nextBlk, blk) 314 } 315 316 func TestBlockstoreError(t *testing.T) { 317 // Since this test only ensures failed GetBlockchainInfo 318 // is properly handled. We don't bother creating fully 319 // legit ledgers here (without genesis block). 320 { 321 fl := &FileLedger{ 322 blockStore: &mockBlockStore{ 323 blockchainInfo: nil, 324 getBlockchainInfoError: fmt.Errorf("Error getting blockchain info"), 325 }, 326 signal: make(chan struct{}), 327 } 328 require.Panics( 329 t, 330 func() { 331 fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Newest{}}) 332 }, 333 "Expected Iterator() to panic if blockstore operation fails") 334 335 require.Panics( 336 t, 337 func() { fl.Height() }, 338 "Expected Height() to panic if blockstore operation fails ") 339 } 340 341 { 342 fl := &FileLedger{ 343 blockStore: &mockBlockStore{ 344 blockchainInfo: &cb.BlockchainInfo{Height: uint64(1)}, 345 getBlockchainInfoError: nil, 346 retrieveBlockByNumberError: fmt.Errorf("Error retrieving block by number"), 347 }, 348 signal: make(chan struct{}), 349 } 350 it, _ := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 42}}}) 351 defer it.Close() 352 require.IsType( 353 t, 354 &blockledger.NotFoundErrorIterator{}, 355 it, 356 "Expected Not Found Error if seek number is greater than ledger height") 357 } 358 359 { 360 resultsIterator := &mockBlockStoreIterator{} 361 resultsIterator.On("Next").Return(nil, errors.New("a mocked error")) 362 resultsIterator.On("Close").Return() 363 fl := &FileLedger{ 364 blockStore: &mockBlockStore{ 365 blockchainInfo: &cb.BlockchainInfo{Height: uint64(1)}, 366 getBlockchainInfoError: nil, 367 retrieveBlockByNumberError: fmt.Errorf("Error retrieving block by number"), 368 resultsIterator: resultsIterator, 369 }, 370 signal: make(chan struct{}), 371 } 372 it, _ := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Oldest{}}) 373 defer it.Close() 374 _, status := it.Next() 375 require.Equal(t, cb.Status_SERVICE_UNAVAILABLE, status, "Expected service unavailable error") 376 } 377 } 378 379 func getSampleEnvelopeWithSignatureHeader() *cb.Envelope { 380 nonce := protoutil.CreateNonceOrPanic() 381 sighdr := &cb.SignatureHeader{Nonce: nonce} 382 sighdrBytes := protoutil.MarshalOrPanic(sighdr) 383 384 header := &cb.Header{SignatureHeader: sighdrBytes} 385 payload := &cb.Payload{Header: header} 386 payloadBytes := protoutil.MarshalOrPanic(payload) 387 return &cb.Envelope{Payload: payloadBytes} 388 }